在一次调用中按组对多个变量应用多个汇总函数(总和、平均值等)

Apply several summary functions (sum, mean, etc.) on several variables by group in one call

提问人:broccoli 提问时间:8/22/2012 最后编辑:Gregor Thomasbroccoli 更新时间:11/29/2022 访问量:118288

问:

我有以下数据框

x <- read.table(text = "  id1 id2 val1 val2
1   a   x    1    9
2   a   x    2    4
3   a   y    3    5
4   a   y    4    9
5   b   x    1    7
6   b   y    4    4
7   b   x    3    9
8   b   y    2    8", header = TRUE)

我想计算按 id1 和 id2 分组的 val1 和 val2 的平均值,并同时计算每个 id1-id2 组合的行数。我可以单独执行每个计算:

# calculate mean
aggregate(. ~ id1 + id2, data = x, FUN = mean)

# count rows
aggregate(. ~ id1 + id2, data = x, FUN = length)

为了在一次调用中同时进行两个计算,我尝试了

do.call("rbind", aggregate(. ~ id1 + id2, data = x, FUN = function(x) data.frame(m = mean(x), n = length(x))))

但是,我收到一个乱码输出以及警告:

#     m   n
# id1 1   2
# id2 1   1
#     1.5 2
#     2   2
#     3.5 2
#     3   2
#     6.5 2
#     8   2
#     7   2
#     6   2
# Warning message:
#   In rbind(id1 = c(1L, 2L, 1L, 2L), id2 = c(1L, 1L, 2L, 2L), val1 = list( :
#   number of columns of result is not a multiple of vector length (arg 1)

我可以使用 plyr 包,但我的数据集非常大,当数据集的大小增长时,plyr 非常慢(几乎无法使用)。

如何使用或其他函数在一次调用中执行多个计算?aggregate

聚合 R-FAQ

评论

0赞 Roman Luštrik 8/22/2012
除了答案中提到的之外,还有 和 .aggregatebytapply

答:

10赞 neilfws 8/22/2012 #1

也许你想合并

x.mean <- aggregate(. ~ id1+id2, p, mean)
x.len  <- aggregate(. ~ id1+id2, p, length)

merge(x.mean, x.len, by = c("id1", "id2"))

  id1 id2 val1.x val2.x val1.y val2.y
1   a   x    1.5    6.5      2      2
2   a   y    3.5    7.0      2      2
3   b   x    2.0    8.0      2      2
4   b   y    3.0    6.0      2      2
181赞 IRTFM 8/22/2012 #2

您可以在一个步骤中完成所有操作并获得适当的标签:

> aggregate(. ~ id1+id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) )
#   id1 id2 val1.mn val1.n val2.mn val2.n
# 1   a   x     1.5    2.0     6.5    2.0
# 2   b   x     2.0    2.0     8.0    2.0
# 3   a   y     3.5    2.0     7.0    2.0
# 4   b   y     3.0    2.0     6.0    2.0

这将创建一个包含两个 id 列和两个矩阵列的 DataFrame:

str( aggregate(. ~ id1+id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) ) )
'data.frame':   4 obs. of  4 variables:
 $ id1 : Factor w/ 2 levels "a","b": 1 2 1 2
 $ id2 : Factor w/ 2 levels "x","y": 1 1 2 2
 $ val1: num [1:4, 1:2] 1.5 2 3.5 3 2 2 2 2
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr  "mn" "n"
 $ val2: num [1:4, 1:2] 6.5 8 7 6 2 2 2 2
  ..- attr(*, "dimnames")=List of 2
  .. ..$ : NULL
  .. ..$ : chr  "mn" "n"

正如下面的 @lord.garbage 所指出的,这可以通过以下命令将其转换为具有“简单”列的 DataFramedo.call(data.frame, ...)

str( do.call(data.frame, aggregate(. ~ id1+id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) ) ) 
    )
'data.frame':   4 obs. of  6 variables:
 $ id1    : Factor w/ 2 levels "a","b": 1 2 1 2
 $ id2    : Factor w/ 2 levels "x","y": 1 1 2 2
 $ val1.mn: num  1.5 2 3.5 3
 $ val1.n : num  2 2 2 2
 $ val2.mn: num  6.5 8 7 6
 $ val2.n : num  2 2 2 2

以下是 LHS 上多个变量的语法:

aggregate(cbind(val1, val2) ~ id1 + id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x) ) )

评论

1赞 broccoli 8/22/2012
非常感谢。顺便说一句,我如何获得聚合以仅汇总一列。如果我有几个数字列,我不希望它对我不希望的列求和。当然,在聚合完成后,我可以扔掉列,但那时 CPU 周期已经用完了。
0赞 IRTFM 8/22/2012
您只需为其提供要分组的因子和要聚合的列。可能在数据中使用负列索引,或将所需的列放在公式的 LHS 上。(请参阅编辑。
2赞 JHowIX 10/28/2014
我在 Windows 2659402 机器上使用 RStudio 0.98.1014 时遇到了 user7 在他的更新中提到的错误。如果如图所示将数据帧输出到控制台,则它看起来很正常,但是,如果将其保存到 d 中,然后尝试访问 d$val1.mn,则返回 NULL。如果运行 view(d),d 也会显示格式不正确。使用更新中的代码修复了它。
4赞 IRTFM 10/28/2014
您遇到困难的原因是“vals”是作为矩阵返回的,每个矩阵有两列,而不是普通列。尝试使用 .d$val1[ , ""mn"]str
7赞 lord.garbage 10/29/2014
您可以使用以下方法将包含矩阵的列绑定回数据框: 。另请参阅此处agg <- aggregate(cbind(val1, val2) ~ id1 + id2, data = x, FUN = function(x) c(mn = mean(x), n = length(x)))agg_df <- do.call(data.frame, agg)
12赞 flodel 8/22/2012 #3

您可以添加一列,与 聚合,然后缩减以获取:countsummean

x$count <- 1
agg <- aggregate(. ~ id1 + id2, data = x,FUN = sum)
agg
#   id1 id2 val1 val2 count
# 1   a   x    3   13     2
# 2   b   x    4   16     2
# 3   a   y    7   14     2
# 4   b   y    6   12     2

agg[c("val1", "val2")] <- agg[c("val1", "val2")] / agg$count
agg
#   id1 id2 val1 val2 count
# 1   a   x  1.5  6.5     2
# 2   b   x  2.0  8.0     2
# 3   a   y  3.5  7.0     2
# 4   b   y  3.0  6.0     2

它的优点是保留列名并创建单个列。count

31赞 Matt Dowle 8/22/2012 #4

鉴于这个问题:

我可以使用 plyr 包,但我的数据集非常大,当数据集的大小增长时,plyr 非常慢(几乎无法使用)。

那么在data.table()中,你可以试试:1.9.4+

> DT
   id1 id2 val1 val2
1:   a   x    1    9
2:   a   x    2    4
3:   a   y    3    5
4:   a   y    4    9
5:   b   x    1    7
6:   b   y    4    4
7:   b   x    3    9
8:   b   y    2    8

> DT[ , .(mean(val1), mean(val2), .N), by = .(id1, id2)]   # simplest
   id1 id2  V1  V2 N
1:   a   x 1.5 6.5 2
2:   a   y 3.5 7.0 2
3:   b   x 2.0 8.0 2
4:   b   y 3.0 6.0 2

> DT[ , .(val1.m = mean(val1), val2.m = mean(val2), count = .N), by = .(id1, id2)]  # named
   id1 id2 val1.m val2.m count
1:   a   x    1.5    6.5     2
2:   a   y    3.5    7.0     2
3:   b   x    2.0    8.0     2
4:   b   y    3.0    6.0     2

> DT[ , c(lapply(.SD, mean), count = .N), by = .(id1, id2)]   # mean over all columns
   id1 id2 val1 val2 count
1:   a   x  1.5  6.5     2
2:   a   y  3.5  7.0     2
3:   b   x  2.0  8.0     2
4:   b   y  3.0  6.0     2

对于时间比较(用于问题和所有其他 3 个答案)以查看此基准(和案例)。aggregatedata.tableaggagg.x

16赞 Jaap 12/12/2015 #5

使用该软件包,您可以通过使用 .使用此 summarise-function,您可以将其他函数(在本例中为 和 )应用于每个非分组列:dplyrsummarise_allmeann()

x %>%
  group_by(id1, id2) %>%
  summarise_all(funs(mean, n()))

这给了:

     id1    id2 val1_mean val2_mean val1_n val2_n
1      a      x       1.5       6.5      2      2
2      a      y       3.5       7.0      2      2
3      b      x       2.0       8.0      2      2
4      b      y       3.0       6.0      2      2

如果不想将函数应用于所有非分组列,请指定应应用这些函数的列,或者使用函数用减号排除不需要的列:summarise_at()

# inclusion
x %>%
  group_by(id1, id2) %>%
  summarise_at(vars(val1, val2), funs(mean, n()))

# exclusion
x %>%
  group_by(id1, id2) %>%
  summarise_at(vars(-val2), funs(mean, n()))
4赞 heschmat 7/7/2017 #6

您还可以使用 来引入多个函数:plyr::each()

aggregate(cbind(val1, val2) ~ id1 + id2, data = x, FUN = plyr::each(avg = mean, n = length))
2赞 benson23 5/8/2022 #7

在版本 1.0.0 之后,上述函数被 取代,您可以在其中选择要操作的列(此处)。dplyrsummarize_allsummarize_atsummarize(across(...))val1:val2

我们还可以在 中提供函数列表,并使用胶水规范设置列名(= 原始列名,= 列表中的函数名)。across{.col}{.fn}

更多信息可以在官方文档中找到。across

library(dplyr)

x %>% group_by(id1, id2) %>% 
  summarize(across(val1:val2, list(mean = mean, n = length), .names = "{.col}_{.fn}"))

# A tibble: 4 × 6
# Groups:   id1 [2]
  id1   id2   val1_mean val1_n val2_mean val2_n
  <chr> <chr>     <dbl>  <int>     <dbl>  <int>
1 a     x           1.5      2       6.5      2
2 a     y           3.5      2       7        2
3 b     x           2        2       8        2
4 b     y           3        2       6        2