【问题标题】:Sum by distinct column value in R按 R 中的不同列值求和
【发布时间】:2012-08-02 16:40:03
【问题描述】:

我在 R 中有一个非常大的数据框,并且想将其他列中每个不同值的两列相加,例如,假设我们在一天内有不同商店的交易数据框的数据,如下所示

shop <- data.frame('shop_id' = c(1, 1, 1, 2, 3, 3), 
  'shop_name' = c('Shop A', 'Shop A', 'Shop A', 'Shop B', 'Shop C', 'Shop C'), 
  'city' = c('London', 'London', 'London', 'Cardiff', 'Dublin', 'Dublin'), 
  'sale' = c(12, 5, 9, 15, 10, 18), 
  'profit' = c(3, 1, 3, 6, 5, 9))

这是:

shop_id  shop_name    city      sale profit
   1     Shop A       London    12   3
   1     Shop A       London    5    1
   1     Shop A       London    9    3
   2     Shop B       Cardiff   15   6
   3     Shop C       Dublin    10   5
   3     Shop C       Dublin    18   9

我想总结每家商店的销售额和利润:

shop_id  shop_name    city      sale profit
   1     Shop A       London    26   7
   2     Shop B       Cardiff   15   6
   3     Shop C       Dublin    28   14

我目前正在使用以下代码来执行此操作:

 shop_day <-ddply(shop, "shop_id", transform, sale=sum(sale), profit=sum(profit))
 shop_day <- subset(shop_day, !duplicated(shop_id))

它工作得非常好,但正如我所说,我的数据框很大(140,000 行,37 列和近 100,000 个唯一行,我想求和)并且我的代码需要很长时间才能运行,然后最终说它内存不足.

有谁知道最有效的方法。

提前致谢!

【问题讨论】:

  • ...我觉得data.table 的答案即将到来...

标签: r sum unique data.table


【解决方案1】:

** 必填数据表答案**

> library(data.table)
data.table 1.8.0  For help type: help("data.table")
> shop.dt <- data.table(shop)
> shop.dt[,list(sale=sum(sale), profit=sum(profit)), by='shop_id']
     shop_id sale profit
[1,]       1   26      7
[2,]       2   15      6
[3,]       3   28     14
> 

在事情变得更大之前,这听起来不错……

shop <- data.frame(shop_id = letters[1:10], profit=rnorm(1e7), sale=rnorm(1e7))
shop.dt <- data.table(shop)

> system.time(ddply(shop, .(shop_id), summarise, sale=sum(sale), profit=sum(profit)))
   user  system elapsed 
  4.156   1.324   5.514 
> system.time(shop.dt[,list(sale=sum(sale), profit=sum(profit)), by='shop_id'])
   user  system elapsed 
  0.728   0.108   0.840 
> 

如果您使用键创建 data.table,您将获得额外的速度提升:

shop.dt <- data.table(shop, key='shop_id')

> system.time(shop.dt[,list(sale=sum(sale), profit=sum(profit)), by='shop_id'])
   user  system elapsed 
  0.252   0.084   0.336 
> 

【讨论】:

  • 请注意,贾斯汀在他的ddply 通话中使用summarise 而不是transform;尽管其他解决方案肯定更快。
  • @Aaron 谢谢!我留下了那个解释,因为有一个较早的答案解释了它。然而那已经被删除了!
  • 谢谢贾斯汀,快多了。另一个快速的问题,有没有办法将其他列(例如 shop_name、city)保留在最终数据表中?我可以重新加入初始数据帧以获取此信息,但如果有办法在初始查询中执行此操作会更整洁。
  • 另外,将我的数据框现在存储为以后的数据表是否会产生任何影响?它对任何特定查询的执行是否有任何不同?谢谢
  • 稍后可能会有一些惊喜,但您始终可以使用as.data.frame 删除数据表属性。至于保留原始列,您当然可以。您可以合并它们或将它们添加到 list()unique(shop_name)。查看?merge.data.table 了解有关合并的更多信息。
【解决方案2】:

我认为最简洁的方法是dplyr

library(dplyr)
shop %>% 
  group_by(shop_id, shop_name, city) %>% 
  summarise_all(sum)

【讨论】:

    【解决方案3】:

    下面是如何使用基本 R 来加速这样的操作:

    idx <- split(1:nrow(shop), shop$shop_id)
    a2 <- data.frame(shop_id=sapply(idx, function(i) shop$shop_id[i[1]]),
                     sale=sapply(idx, function(i) sum(shop$sale[i])), 
                     profit=sapply(idx, function(i) sum(shop$profit[i])) )
    

    时间减少到 0.75 秒,而我系统上的 ddply 汇总版本为 5.70 秒。

    【讨论】:

    • 如果我在上面的例子中有很多列,比如我想总结的销售和利润,是否可以调用一个函数将上面代码中的第三行和第四行合并为一行。跨度>
    • 没有真正使用这种确切的方法,但有一些方法可以做到这一点。用一个可重复性极低的例子开始一个新问题,你会得到很多建议。
    【解决方案4】:

    以防万一,如果您的列列表很长, 使用summarize_if()

    如果数据类型为 int,则汇总所有列

    library(dplyr)
    shop %>% 
      group_by(shop_id, shop_name, city) %>% 
      summarise_if(is.integer, sum)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-11
      • 1970-01-01
      • 2022-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-19
      相关资源
      最近更新 更多