【问题标题】:Joining aggregated values back to the original data frame [duplicate]将聚合值连接回原始数据框[重复]
【发布时间】:2011-06-29 04:51:39
【问题描述】:

我反复使用的一种设计模式是对数据框执行“分组依据”或“拆分、应用、组合 (SAC)”,然后将聚合数据连接回原始数据。例如,当在包含许多州和县的数据框中计算每个县与州平均值的偏差时,这很有用。我的汇总计算很少只是一个简单的平均值,但它是一个很好的例子。我经常通过以下方式解决这个问题:

require(plyr)
set.seed(1)

## set up some data
group1 <- rep(1:3, 4)
group2 <- sample(c("A","B","C"), 12, rep=TRUE) 
values <- rnorm(12)
df <- data.frame(group1, group2, values)

## got some data, so let's aggregate

group1Mean <- ddply( df, "group1", function(x) 
                     data.frame( meanValue = mean(x$values) ) )
df <- merge( df, group1Mean )
df

这会产生如下好的聚合数据:

> df
   group1 group2   values meanValue
1       1      A  0.48743 -0.121033
2       1      A -0.04493 -0.121033
3       1      C -0.62124 -0.121033
4       1      C -0.30539 -0.121033
5       2      A  1.51178  0.004804
6       2      B  0.73832  0.004804
7       2      A -0.01619  0.004804
8       2      B -2.21470  0.004804
9       3      B  1.12493  0.758598
10      3      C  0.38984  0.758598
11      3      B  0.57578  0.758598
12      3      A  0.94384  0.758598

这可行,但是否有其他方法可以提高可读性、性能等?

【问题讨论】:

标签: r plyr


【解决方案1】:

dplyr 的可能性:

library(dplyr)
df %>% 
  group_by(group1) %>%
  mutate(meanValue = mean(values))

这会以原始顺序返回数据框。如果您想按“group1”订购,请将arrange(group1) 添加到管道中。

【讨论】:

    【解决方案2】:

    在性能方面,您可以使用 data.table 包执行相同类型的操作,该包内置聚合,并且由于索引和基于 C 的实现而非常快。例如,鉴于您的示例中已经存在 df

    library("data.table")
    dt<-as.data.table(df)
    setkey(dt,group1)
    dt<-dt[,list(group2,values,meanValue=mean(values)),by=group1]
    dt
          group1 group2      values   meanValue
     [1,]      1      A  0.82122120  0.18810771
     [2,]      1      C  0.78213630  0.18810771
     [3,]      1      C  0.61982575  0.18810771
     [4,]      1      A -1.47075238  0.18810771
     [5,]      2      B  0.59390132  0.03354688
     [6,]      2      A  0.07456498  0.03354688
     [7,]      2      B -0.05612874  0.03354688
     [8,]      2      A -0.47815006  0.03354688
     [9,]      3      B  0.91897737 -0.20205707
    [10,]      3      C -1.98935170 -0.20205707
    [11,]      3      B -0.15579551 -0.20205707
    [12,]      3      A  0.41794156 -0.20205707

    我没有对其进行基准测试,但根据我的经验,它要快得多。

    如果您决定走 data.table 之路,我认为如果您使用大型数据集,则值得探索,您真的需要阅读文档,因为与数据框架存在一些差异,如果您不知道他们。然而,值得注意的是,data.table 通常可以与任何需要数据框的函数一起使用,因为 data.table 会声称它的类型是数据框(数据表继承自数据框)。

    [2011 年 2 月]


    [2012 年 8 月] Matthew 更新:

    2012 年 7 月发布到 CRAN 的 v1.8.2 中的新功能是 := 按组。这与上面的答案非常相似,但是将新列通过引用添加到dt,因此不需要复制,也不需要合并步骤或重新列出现有列以与聚合一起返回。无需先setkey,它可以处理不连续的组(即未分组在一起的组)。

    这对于大型数据集来说要快得多,并且语法简单而简短:

    dt <- as.data.table(df)
    dt[, meanValue := mean(values), by = group1]
    

    【讨论】:

      【解决方案3】:

      你不能在传递给ddply的函数中添加x吗?

      df <- ddply( df, "group1", function(x)
                   data.frame( x, meanValue = mean(x$values) ) )
      

      【讨论】:

      • 我没有意识到这会起作用。漂亮。
      【解决方案4】:

      我认为ave() 在这里比你显示的 plyr 调用更有用(我对 plyr 不够熟悉,不知道你是否可以直接用 plyr 做你想做的事,如果你能,我会感到惊讶' t!)或其他基本 R 替代方案(aggregate()tapply())。:

      > with(df, ave(values, group1, FUN = mean))
       [1] -0.121033381  0.004803931  0.758597929 -0.121033381  0.004803931
       [6]  0.758597929 -0.121033381  0.004803931  0.758597929 -0.121033381
      [11]  0.004803931  0.758597929
      

      您可以使用within()transform() 将此结果直接嵌入到df

      > df2 <- within(df, meanValue <- ave(values, group1, FUN = mean))
      > head(df2)
        group1 group2     values    meanValue
      1      1      A  0.4874291 -0.121033381
      2      2      B  0.7383247  0.004803931
      3      3      B  0.5757814  0.758597929
      4      1      C -0.3053884 -0.121033381
      5      2      A  1.5117812  0.004803931
      6      3      C  0.3898432  0.758597929
      > df3 <- transform(df, meanValue = ave(values, group1, FUN = mean))
      > all.equal(df2,df3)
      [1] TRUE
      

      如果顺序很重要:

      > head(df2[order(df2$group1, df2$group2), ])
         group1 group2      values    meanValue
      1       1      A  0.48742905 -0.121033381
      10      1      A -0.04493361 -0.121033381
      4       1      C -0.30538839 -0.121033381
      7       1      C -0.62124058 -0.121033381
      5       2      A  1.51178117  0.004803931
      11      2      A -0.01619026  0.004803931
      

      【讨论】:

      • 我没有意识到 ave() 可以使用其他功能...我之前显然应该阅读过文档。
      【解决方案5】:

      一行代码就可以解决问题:

      new <- ddply( df, "group1", transform, numcolwise(mean))
      new
      
      group1 group2      values    meanValue
      1       1      A  0.48742905 -0.121033381
      2       1      A -0.04493361 -0.121033381
      3       1      C -0.62124058 -0.121033381
      4       1      C -0.30538839 -0.121033381
      5       2      A  1.51178117  0.004803931
      6       2      B  0.73832471  0.004803931
      7       2      A -0.01619026  0.004803931
      8       2      B -2.21469989  0.004803931
      9       3      B  1.12493092  0.758597929
      10      3      C  0.38984324  0.758597929
      11      3      B  0.57578135  0.758597929
      12      3      A  0.94383621  0.758597929
      
      identical(df, new)
      [1] TRUE
      

      【讨论】:

      • 我完全忘记了transform。事后看来很明显。但是感谢您说明我不熟悉的numcolwise
      • 这是一个很好的习惯用法,但是当一些变量应该是总和而其他变量时,它会很棘手。
      • @richiemorrisroe 比任何其他成语都更棘手?
      • @Andrie 不,我只是希望你能回答一些我错过的明显解决方案
      猜你喜欢
      • 2013-01-26
      • 2017-07-18
      • 2019-10-23
      • 1970-01-01
      • 2018-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多