【问题标题】:Adding column of summed data by group with empty rows with R用 R 按空行按组添加总和数据列
【发布时间】:2017-06-13 14:47:24
【问题描述】:

我正在尝试向数据集中添加一列,该列显示另一列中每个组 ID 的一列中的数据总和。 sum 或 total 列将有空行,每组一个总和。

aggregate(Diff ~ Group, data.set, sum) 给了我正确的总和,但去掉了所有其他行。虽然像: data.set$Total <- ave(data.set$Diff, factor(data.set$Group), FUN=sum);给了我新的专栏 Total 但没有值。例如,输入数据集如下所示:

Group  Diff
1 
1     -16055
1     -1313
1      45707
1      6569
2 
2     -7249
2      2
3 
3     -384724

我希望输出如下所示:

Group   Diff  Total
1 
1     -16055
1     -1313
1      45707
1      6569     34908 
2 
2     -7249
2      2       -7247
3 
3     -384724  -384724

Diff 列是先前计算的结果,该计算找到另一列中值的差异,因此每个组的第一行为空。类似的示例显示了获取列中值的总和,有些示例按组显示,但似乎没有一个示例显示如何获得与我需要的输出一致的结果。感谢您的帮助

【问题讨论】:

  • 你的Diff 列是空白的地方实际上是NAs 吗?还是不是数值变量?
  • 它们是空白的。还有另一列称为对每行中的 a 值进行排名,而 Diff 是从值 2 中减去值 1、从值 3 中减去值 2 等的结果。计算如下:data.set$Diff

标签: r


【解决方案1】:

假设您的 Diff 列是数字,而这些空白确实是 NA,您可以这样做:

library(data.table)
dt <- data.table(Group = c(1,1,1,1,1,2,2,2,3,3), Diff = c(NA,-16055,-1313, 45707,6569,NA,-7249,2,NA,-384724))

dt[,total := ifelse(seq_len(.N) == .N, sum(Diff, na.rm = T), NA), by = Group]

 #   Group    Diff   total
 #1:     1      NA      NA
 #2:     1  -16055      NA
 #3:     1   -1313      NA
 #4:     1   45707      NA
 #5:     1    6569   34908
 #6:     2      NA      NA
 #7:     2   -7249      NA
 #8:     2       2   -7247
 #9:     3      NA      NA
#10:     3 -384724 -384724

或者,您可以按照 Frank 在 cmets 中的建议进行合并:

dt[dt[, sum(Diff, na.rm=TRUE), by=Group], on=.(Group), mult="last", total := i.V1 ]

时序对比:

为了查看 @Frank 的合并选项和我的原始解决方案之间的时间比较,我改变了 # 个组和 # 个观察值(两者的高和低选项),并在 4 个单独的数据集上运行了一个微基准。结果如下,看起来无论如何其他 Frank 的合并选项是最快的。我认为我的解决方案中的瓶颈是ifelse,如果您可以删除它,它可能会更快,尽管不确定多少。

set.seed(1)
high_grp <- 1:10000; high_obs = 1000000;
low_grp <- 1:100; low_obs = 50000;
low_grp_high_obs <- data.table(Group = sample(low_grp, high_obs, replace = T), Diff = sample(-60000:60000, high_obs, replace = T)) 
high_grp_high_obs <- data.table(Group = sample(high_grp, high_obs, replace = T), Diff = sample(-60000:60000, high_obs, replace = T)) 
low_grp_low_obs <- data.table(Group = sample(low_grp, low_obs, replace = T), Diff = sample(-60000:60000, low_obs, replace = T)) 
high_grp_low_obs <- data.table(Group = sample(high_grp, low_obs, replace = T), Diff = sample(-60000:60000, low_obs, replace = T)) 

comparison_sets <- list("Low Group; High Obs" = low_grp_high_obs, "High Group; High Obs" = high_grp_high_obs, 
                        "Low Group; Low Obs" = low_grp_low_obs, "High Group; Low Obs" = high_grp_low_obs)

comparison <- lapply(comparison_sets, function(dt) {microbenchmark::microbenchmark(orig = dt[,total := ifelse(seq_len(.N) == .N, sum(Diff, na.rm = T), NA), by = Group],
                                                                     merge = dt[dt[, sum(Diff, na.rm=TRUE), by=Group], on=.(Group), mult="last", total := i.V1 ])} )


comparison

#$`Low Group; High Obs`
#Unit: milliseconds
#  expr      min       lq     mean   median       uq      max neval
#  orig 53.16160 58.00227 69.93443 60.08673 62.57489 191.1628   100
# merge 12.93931 15.15634 17.90187 15.56495 18.33738 147.9433   100
#
#$`High Group; High Obs`
#Unit: milliseconds
#  expr       min       lq      mean    median       uq      max neval
#  orig 143.60222 151.8497 161.65825 154.85638 158.2183 281.2311   100
# merge  23.18698  23.7380  29.20126  24.86465  29.9832 153.7919   100
#
#$`Low Group; Low Obs`
#Unit: milliseconds
#  expr      min       lq     mean   median       uq      max neval
#  orig 3.047569 3.190157 3.957012 3.378145 3.692857 8.087345   100
# merge 1.685882 1.808594 1.928094 1.846520 1.953369 5.998864   100
#
#$`High Group; Low Obs`
#Unit: milliseconds
#  expr       min        lq      mean    median        uq       max neval
#  orig 65.903991 68.727469 69.861163 69.857406 70.950330 76.351860   100
# merge  3.418077  3.595673  3.831805  3.855684  3.952869  5.069314   100

【讨论】:

  • 或者,dt[dt[, sum(Diff, na.rm=TRUE), by=Group], on=.(Group), mult="last", total := i.V1 ] 有点类似于这里的stackoverflow.com/q/32101368
  • @Frank,谢谢!没想到使用合并。我将更新我的答案以包含此附加选项。
  • 好酷。仅供参考,在此类案例的基准测试中,不仅要考虑 # 行,还要考虑 # 组可能很重要……这无疑会很快变得乏味。
  • Mike,你能告诉我在哪里可以了解 .N 表示法,或者它是如何被调用的,很难查找,我仅通过查找 data.table 聚合教程找不到它
  • 错误是我自己的。我没有先调用库(data.table)。
【解决方案2】:

如果34908出现在每个“1”的前面,而不是只在最后一个前面,是否可以?

如果不是,您可以从“sqldf”库中使用它:

library(sqldf)

data_count = sqldf('select groupe, sum(diff) as Total from data group by groupe')
new_data = sqldf('select * from data as a inner join data_count as b on a.groupe = b.groupe')

另外,如果你真的想在你的例子中像 NA 一样,你可以添加这个:

 for (i in 1:(dim(new_data)[1]-1)){
  if (new_data[i,"groupe"] == new_data[i+1,"groupe"]){
    new_data[i,'Total'] = NA
  }
}

【讨论】:

    【解决方案3】:

    使用split/unsplit 的另一种可能方法:

    DF <- data.frame(Group=c(1,1,1,1,1,2,2,2,3,3), 
                     Diff=c(NA,-16055,-1313,45707,6569,NA,-7249,2,NA,-384724))
    
    customSum <- function(x){ 
      v <- x
      v[] <- NA
      v[length(v)] <- sum(x,na.rm = T)
      return(v)
    }
    DF$Total <- unsplit(lapply(split(DF$Diff,DF$Group),customSum),DF$Group)
    
    > DF
       Group    Diff   Total
    1      1      NA      NA
    2      1  -16055      NA
    3      1   -1313      NA
    4      1   45707      NA
    5      1    6569   34908
    6      2      NA      NA
    7      2   -7249      NA
    8      2       2   -7247
    9      3      NA      NA
    10     3 -384724 -384724
    

    【讨论】:

      【解决方案4】:

      试试这个。我们先聚合,然后合并到您现有的数据集

      result <- merge(data.set,setNames(aggregate(Diff ~ Group, data.set, sum),c("Group","Total")),all.x=TRUE)
      

      如果您不想重复总数,请添加 result$Total[-cumsum(table(data.set$Group))] &lt;- ""result$Total[-cumsum(table(data.set$Group))] &lt;- NA

      【讨论】:

      • 此解决方案通常有效,但在组只有一行(空白)的情况下,它会完全删除整行。这是一个修改,在那些只有一行 Diff 可能为空的组的实例中,它只是插入 NA 并离开该行?
      • 我在合并函数中添加了all.x = TRUE,所以所有data.set都被保留了,它现在应该可以工作了
      • 这个解决方案的好奇神器。它不会影响数学,但是当我在另一列(PriceDiff/PriceTotal)上使用相同的解决方案时,Total 在原始 Diff 列中的位置会转移到组内看似随机的行,而不是留在最后一行。同样,这实际上并不影响结果。我只是好奇你是否知道为什么会发生这种情况。新的 PriceTotal 列中的结果始终如预期的那样位于组的最后一行。
      • 这真的很奇怪,我看不出一个值是如何跳行的,如果你能给我一个可重现的例子,我会调查一下
      猜你喜欢
      • 1970-01-01
      • 2021-04-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多