Суммируя несколько столбцов с dplyr?
Я немного борюсь с dplyr-синтаксисом. У меня есть кадр данных с разными переменными и одна переменная группировки. Теперь я хочу рассчитать среднее значение для каждого столбца в каждой группе, используя dplyr в R.
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=sample(1:3, 10, replace=T))
df %>% group_by(grp) %>% summarise(mean(a))
Это дает мне значение для столбца "a" для каждой группы, обозначенной "grp".
Мой вопрос: возможно ли сразу получить средства для каждого столбца в каждой группе? Или мне нужно повторять df %>% group_by(grp) %>% summarise(mean(a))
для каждого столбца?
То, что я хотел бы иметь, это что-то вроде
df %>% group_by(grp) %>% summarise(mean(a:d)) # "mean(a:d)" does not work
Ответы
Ответ 1
dplyr
0.2 содержит summarise_each
для этой цели:
df %>% group_by(grp) %>% summarise_each(funs(mean))
#> Source: local data frame [3 x 5]
#>
#> grp a b c d
#> (int) (dbl) (dbl) (dbl) (dbl)
#> 1 1 3.000000 2.666667 2.666667 3.333333
#> 2 2 2.666667 2.666667 2.500000 2.833333
#> 3 3 4.000000 1.000000 4.000000 3.000000
В качестве альтернативы пакет purrr
предоставляет ту же функциональность:
df %>% slice_rows("grp") %>% dmap(mean)
#> Source: local data frame [3 x 5]
#>
#> grp a b c d
#> (int) (dbl) (dbl) (dbl) (dbl)
#> 1 1 3.000000 2.666667 2.666667 3.333333
#> 2 2 2.666667 2.666667 2.500000 2.833333
#> 3 3 4.000000 1.000000 4.000000 3.000000
Также не забывайте о data.table
:
setDT(df)[, lapply(.SD, mean), by = grp]
#> grp a b c d
#> 1: 3 3.714286 3.714286 2.428571 2.428571
#> 2: 1 1.000000 4.000000 5.000000 2.000000
#> 3: 2 4.000000 4.500000 3.000000 3.000000
Ответ 2
Мы можем суммировать, используя summarize_at
, summarize_all
и summarize_if
на dplyr 0.7.4
. Мы можем установить несколько столбцов и функций, используя аргументы vars
и funs
, как показано ниже. Левая часть формулы 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
Ответ 3
Вы можете просто передать больше аргументов summarise
:
df %>% group_by(grp) %>% summarise(mean(a), mean(b), mean(c), mean(d))
Источник: локальный фрейм данных [3 x 5]
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
Ответ 4
Для полноты: с dplyr v0.2 ddply
с colwise
также будет делать следующее:
> 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
Ответ 5
Все примеры замечательные, но я полагаю, что добавлю еще одно, чтобы показать, как упрощается работа в "аккуратном" формате. Прямо сейчас кадр данных находится в "широком" формате, что означает, что переменные "a" через "d" представлены в столбцах. Чтобы перейти в "аккуратный" (или длинный) формат, вы можете использовать gather()
из пакета tidyr
, который перемещает переменные в столбцах от "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