用 dplyr 汇总多列?

我对 dplyr 语法有点纠结。我有一个不同变量的数据框架和一个分组变量。现在我想使用 R 中的 dplyr 计算每个组中每个列的平均值。

df <- data.frame(
a = sample(1:5, n, replace = TRUE),
b = sample(1:5, n, replace = TRUE),
c = sample(1:5, n, replace = TRUE),
d = sample(1:5, n, replace = TRUE),
grp = sample(1:3, n, replace = TRUE)
)
df %>% group_by(grp) %>% summarise(mean(a))

这给出了“ grp”表示的每个组的列“ a”的平均值。

我的问题是: 是否有可能同时获取每个组中每个列的方法?或者我必须为每一列重复 df %>% group_by(grp) %>% summarise(mean(a))吗?

我想要的是

df %>% group_by(grp) %>% summarise(mean(a:d)) # "mean(a:d)" does not work
274761 次浏览

您可以简单地向 summarise传递更多参数:

df %>% group_by(grp) %>% summarise(mean(a), mean(b), mean(c), mean(d))

资料来源: 本地数据框架[3x5]

  grp  mean(a)  mean(b)  mean(c) mean(d)
1   1 2.500000 3.500000 2.000000     3.0
2   2 3.800000 3.200000 3.200000     2.8
3   3 3.666667 3.333333 2.333333     3.0

dplyr(> = 1.00)中,你可以使用 summarise中的 across(everything()对所有变量应用函数:

library(dplyr)


df %>% group_by(grp) %>% summarise(across(everything(), list(mean)))
#> # A tibble: 3 x 5
#>     grp     a     b     c     d
#>   <int> <dbl> <dbl> <dbl> <dbl>
#> 1     1  3.08  2.98  2.98  2.91
#> 2     2  3.03  3.04  2.97  2.87
#> 3     3  2.85  2.95  2.95  3.06

或者,purrrlyr软件包提供相同的功能:

library(purrrlyr)
df %>% slice_rows("grp") %>% dmap(mean)
#> # A tibble: 3 x 5
#>     grp     a     b     c     d
#>   <int> <dbl> <dbl> <dbl> <dbl>
#> 1     1  3.08  2.98  2.98  2.91
#> 2     2  3.03  3.04  2.97  2.87
#> 3     3  2.85  2.95  2.95  3.06

也不要忘记 data.table(使用 keyby对排序组进行排序) :

library(data.table)
setDT(df)[, lapply(.SD, mean), keyby = grp]
#>    grp        a        b        c        d
#> 1:   1 3.079412 2.979412 2.979412 2.914706
#> 2:   2 3.029126 3.038835 2.967638 2.873786
#> 3:   3 2.854701 2.948718 2.951567 3.062678

我们来比较一下性能。

library(dplyr)
library(purrrlyr)
library(data.table)
library(bench)
set.seed(123)
n <- 10000
df <- data.frame(
a = sample(1:5, n, replace = TRUE),
b = sample(1:5, n, replace = TRUE),
c = sample(1:5, n, replace = TRUE),
d = sample(1:5, n, replace = TRUE),
grp = sample(1:3, n, replace = TRUE)
)
dt <- setDT(df)
mark(
dplyr = df %>% group_by(grp) %>% summarise(across(everything(), list(mean))),
purrrlyr = df %>% slice_rows("grp") %>% dmap(mean),
data.table = dt[, lapply(.SD, mean), keyby = grp],
check = FALSE
)
#> # A tibble: 3 x 6
#>   expression      min   median `itr/sec` mem_alloc `gc/sec`
#>   <bch:expr> <bch:tm> <bch:tm>     <dbl> <bch:byt>    <dbl>
#> 1 dplyr        2.81ms   2.85ms      328.        NA     17.3
#> 2 purrrlyr     7.96ms   8.04ms      123.        NA     24.5
#> 3 data.table 596.33µs 707.91µs     1409.        NA     10.3

为了完整起见: dplyr v0.2 ddplycolwise也可以这样做:

> ddply(df, .(grp), colwise(mean))
grp        a    b        c        d
1   1 4.333333 4.00 1.000000 2.000000
2   2 2.000000 2.75 2.750000 2.750000
3   3 3.000000 4.00 4.333333 3.666667

但速度较慢,至少在这种情况下是这样:

> microbenchmark(ddply(df, .(grp), colwise(mean)),
df %>% group_by(grp) %>% summarise_each(funs(mean)))
Unit: milliseconds
expr      min       lq     mean
ddply(df, .(grp), colwise(mean))     3.278002 3.331744 3.533835
df %>% group_by(grp) %>% summarise_each(funs(mean)) 1.001789 1.031528 1.109337


median       uq      max neval
3.353633 3.378089 7.592209   100
1.121954 1.133428 2.292216   100

我们可以用 summarize_atsummarize_allsummarize_ifdplyr 0.7.4进行总结。我们可以使用 varsfuns参数设置多个列和函数,如下面的代码所示。Funs 公式的左边被分配到总结的 vars 的后缀。在 dplyr 0.7.4中,summarise_each(和 mutate_each)已经被弃用,因此我们不能使用这些函数。

options(scipen = 100, dplyr.width = Inf, dplyr.print_max = Inf)


library(dplyr)
packageVersion("dplyr")
# [1] ‘0.7.4’


set.seed(123)
df <- data_frame(
a = sample(1:5, 10, replace=T),
b = sample(1:5, 10, replace=T),
c = sample(1:5, 10, replace=T),
d = sample(1:5, 10, replace=T),
grp = as.character(sample(1:3, 10, replace=T)) # For convenience, specify character type
)


df %>% group_by(grp) %>%
summarise_each(.vars = letters[1:4],
.funs = c(mean="mean"))
# `summarise_each()` is deprecated.
# Use `summarise_all()`, `summarise_at()` or `summarise_if()` instead.
# To map `funs` over a selection of variables, use `summarise_at()`
# Error: Strings must match column names. Unknown columns: mean

您应该更改为以下代码。以下代码都具有相同的结果。

# summarise_at
df %>% group_by(grp) %>%
summarise_at(.vars = letters[1:4],
.funs = c(mean="mean"))


df %>% group_by(grp) %>%
summarise_at(.vars = names(.)[1:4],
.funs = c(mean="mean"))


df %>% group_by(grp) %>%
summarise_at(.vars = vars(a,b,c,d),
.funs = c(mean="mean"))


# summarise_all
df %>% group_by(grp) %>%
summarise_all(.funs = c(mean="mean"))


# summarise_if
df %>% group_by(grp) %>%
summarise_if(.predicate = function(x) is.numeric(x),
.funs = funs(mean="mean"))
# A tibble: 3 x 5
# grp a_mean b_mean c_mean d_mean
# <chr>  <dbl>  <dbl>  <dbl>  <dbl>
# 1     1   2.80   3.00    3.6   3.00
# 2     2   4.25   2.75    4.0   3.75
# 3     3   3.00   5.00    1.0   2.00

还可以有多个函数。

df %>% group_by(grp) %>%
summarise_at(.vars = letters[1:2],
.funs = c(Mean="mean", Sd="sd"))
# A tibble: 3 x 5
# grp a_Mean b_Mean      a_Sd     b_Sd
# <chr>  <dbl>  <dbl>     <dbl>    <dbl>
# 1     1   2.80   3.00 1.4832397 1.870829
# 2     2   4.25   2.75 0.9574271 1.258306
# 3     3   3.00   5.00        NA       NA

所有的例子都很棒,但我认为我应该再添加一个例子来展示在“整洁”的格式下工作是如何简化事情的。现在的数据框架是“宽”格式,这意味着变量“ a”到“ d”以列的形式表示。要获得“整洁”(或长)格式,可以使用 tidyr包中的 gather(),它将列“ a”到“ d”中的变量转换为行。然后使用 group_by()summarize()函数得到每组的平均值。如果希望以宽格式显示数据,只需附加一个对 spread()函数的调用即可。


library(tidyverse)


# Create reproducible df
set.seed(101)
df <- tibble(a   = sample(1:5, 10, replace=T),
b   = sample(1:5, 10, replace=T),
c   = sample(1:5, 10, replace=T),
d   = sample(1:5, 10, replace=T),
grp = sample(1:3, 10, replace=T))


# Convert to tidy format using gather
df %>%
gather(key = variable, value = value, a:d) %>%
group_by(grp, variable) %>%
summarize(mean = mean(value)) %>%
spread(variable, mean)
#> Source: local data frame [3 x 5]
#> Groups: grp [3]
#>
#>     grp        a     b        c        d
#> * <int>    <dbl> <dbl>    <dbl>    <dbl>
#> 1     1 3.000000   3.5 3.250000 3.250000
#> 2     2 1.666667   4.0 4.666667 2.666667
#> 3     3 3.333333   3.0 2.333333 2.333333