【问题标题】:Count grouped observations that meet conditions using data.table使用 data.table 计算满足条件的分组观察
【发布时间】:2018-01-24 15:11:02
【问题描述】:

我有类似于以下的每小时/每日股票回报数据:

  set.seed(1)
  dt <- data.table(stock = c(rep("a",24),rep("b",24),rep("c",24),rep("d",24)),
  hour = rep(1:24,4), day1 = sample(-5:5,96,replace = TRUE), 
  day2 = sample(-10:-1,96,replace = TRUE), day3 = sample(0:10,96,replace = TRUE),
  day4 = 0)

看起来像:

  stock hour    day1    day2    day3    day4
  a      1      -3       -6      1       0
  a      2      -1       -6      10      0
  a      3       1       -2      3       0
  ...                   
  d      22      4       -5      1       0
  d      23      3       -3      3       0
  d      24      3       -7      1       0

我想计算以下每一天:

  1. 有多少股票在所有时间都显示负回报
  2. 有多少股票在所有时间都显示正回报
  3. 有多少个股票在 24 小时内呈现正负混合收益
  4. 有多少个股票在 24 小时内至少显示一个零
  5. 24 小时内有多少股票显示为零

输出应该类似于:

  counts                            day1    day2    day3    day4
  stocks_where_all_hours_negative   0       4       0       0
  stocks_where_all_hours_positive   0       0       4       0
  stocks_where_mixed_pos_and_neg    4       0       0       0
  stocks_where_at_least_one_zero    4       0       2       4
  stocks_where_all_zero             0       0       0       4

非常感谢。

【问题讨论】:

    标签: r data.table


    【解决方案1】:
    mdt <- melt(
      dt,
      id.vars = c('stock', 'hour'),
      measure.vars = c('day1', 'day2', 'day3', 'day4'),
      variable.name = 'day',
      value.name = 'position'
    )
    counts <- mdt[ ,.(stocks_where_all_hours_negative = all(position < 0),
            stocks_where_all_hours_positive = all(position > 0),
            stocks_where_mixed_pos_and_neg = any(position < 0) & any(position > 0),
            stocks_where_at_least_one_zero = any(position != 0),
            stocks_where_all_zero = all(position == 0)),
         by = c('day',
                'stock')][ ,.(stocks_where_all_hours_negative = sum(stocks_where_all_hours_negative),
                              stocks_where_all_hours_positive = sum(stocks_where_all_hours_positive),
                              stocks_where_mixed_pos_and_neg = sum(stocks_where_mixed_pos_and_neg),
                              stocks_where_at_least_one_zero = sum(stocks_where_at_least_one_zero),
                              stocks_where_all_zero = sum(stocks_where_all_zero)),
                           by = 'day']
    t(counts)
    

    首先将数据重塑为“长”格式,以便您可以按天分组。 anyall 函数将检查每个向量的逻辑并返回您设置的场景。

    【讨论】:

    • 感谢@troh 的精彩回答。它工作得很好。当position 的所有小时数小于零时,all(position &lt; 0) 计数。当所有非零位置都小于零时,我将如何计算(即在计算all(position &lt; 0) 时忽略零)?
    • @FG7 我不确定我是否在关注。 all(position &lt; 0) 只计算非零负数。 all(position &lt;=0) 将计数为零和非零。
    • any(position &lt; 0) & !any(position &gt; 0)
    • 感谢您的贡献@Frank。它肯定有助于简化代码。这是我在第二步中使用的所有计数的总和:[, lapply(.SD, sum), .SDcols = 3:ncol(counts), by = "day"]
    【解决方案2】:

    由于您所做的摘要非常专业,您可以编写自己的函数:

    summarize_stocks <- function(stock, day, return_names = FALSE){
      if(!return_names) return( c(length(unique(stock[as.logical(ave(day, stock, FUN = function(x) all(x<0)))])),
                                  length(unique(stock[as.logical(ave(day, stock, FUN = function(x) all(x>0)))])),
                                  length(unique(stock[as.logical(ave(day, stock, FUN = function(x) any(x>0) & any(x<0)))])),
                                  length(unique(stock[as.logical(ave(day, stock, FUN = function(x) any(x==0)))])),
                                  length(unique(stock[as.logical(ave(day, stock, FUN = function(x) all(x==0)))]))
                                  ))
      else return(c("stocks_where_all_hours_negative", "stocks_where_all_hours_positive", "stocks_where_mixed_pos_and_neg",
                      "stocks_where_at_least_one_zero", "stocks_where_all_zero"))
    }
    
    res <- dt[, lapply(.SD, summarize_stocks, stock = stock), .SDcols = c("day1","day2", "day3", "day4")]
    res[, counts := summarize_stocks(return_names = TRUE)]
    
    res
    #       day1 day2 day3 day4                          counts
    #1:    0    4    0    0 stocks_where_all_hours_negative
    #2:    0    0    2    0 stocks_where_all_hours_positive
    #3:    4    0    0    0  stocks_where_mixed_pos_and_neg
    #4:    4    0    2    4  stocks_where_at_least_one_zero
    #5:    0    0    0    4           stocks_where_all_zero
    

    或者,如果您想变得花哨,可以将您的 summarize_stocks 更改为:

    summarize_stocks <- function(stock, day, return_names = FALSE){
      funs <- list(function(x) all(x<0),
                   function(x) all(x>0),
                   function(x) any(x>0) & any(x<0),
                   function(x) any(x==0),
                   function(x) all(x==0))
    
      if(!return_names) return( vapply(funs, function(f) length(unique(stock[as.logical(ave(day, stock, FUN = function(x) f(x)))])), numeric(1L)))
    
      else return(c("stocks_where_all_hours_negative", "stocks_where_all_hours_positive", "stocks_where_mixed_pos_and_neg",
                      "stocks_where_at_least_one_zero", "stocks_where_all_zero"))
    }
    

    【讨论】:

    • 非常感谢您的所有帮助@Mike H。您的回答完美无缺。我选择了 troh 的答案,主要是因为我的原始数据已经是长格式(所以我可以跳过融化步骤),并且作为个人喜好,我发现脚本更具可读性。跨度>
    猜你喜欢
    • 1970-01-01
    • 2015-03-21
    • 1970-01-01
    • 2016-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多