Группировать по нескольким столбцам в dplyr, используя ввод векторной строки
Я пытаюсь передать свое понимание plyr в dplyr, но я не могу понять, как группировать по нескольким столбцам.
# make data with weird column names that can't be hard coded
data = data.frame(
asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# get the columns we want to average within
columns = names(data)[-3]
# plyr - works
ddply(data, columns, summarize, value=mean(value))
# dplyr - raises error
data %.%
group_by(columns) %.%
summarise(Value = mean(value))
#> Error in eval(expr, envir, enclos) : index out of bounds
Что мне не хватает для перевода примера plyr в синтаксис dplyr-esque?
Изменить 2017: Dplyr обновлен, поэтому доступно более простое решение. См. Текущий выбранный ответ.
Ответы
Ответ 1
Поскольку этот вопрос был отправлен, dplyr добавила в него версии group_by
(здесь). Это позволяет использовать те же функции, которые вы использовали бы с помощью select
, например:
data = data.frame(
asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# get the columns we want to average within
columns = names(data)[-3]
library(dplyr)
df1 <- data %>%
group_by_at(vars(one_of(columns))) %>%
summarize(Value = mean(value))
#compare plyr for reference
df2 <- plyr::ddply(data, columns, plyr::summarize, value=mean(value))
table(df1 == df2, useNA = 'ifany')
## TRUE
## 27
Вывод из вашего примерного вопроса, как и ожидалось (см. сравнение с plyr выше и ниже):
# A tibble: 9 x 3
# Groups: asihckhdoydkhxiydfgfTgdsx [?]
asihckhdoydkhxiydfgfTgdsx a30mvxigxkghc5cdsvxvyv0ja Value
<fctr> <fctr> <dbl>
1 A A 0.04095002
2 A B 0.24943935
3 A C -0.25783892
4 B A 0.15161805
5 B B 0.27189974
6 B C 0.20858897
7 C A 0.19502221
8 C B 0.56837548
9 C C -0.22682998
Обратите внимание, что поскольку dplyr::summarize
удаляет только один слой группировки за один раз, вы все равно получаете некоторую группировку в итоговой форме (которая может когда-нибудь улавливать людей в порядке убывания позже строки). Если вы хотите быть абсолютно безопасным от неожиданного поведения группировки, вы всегда можете добавить %>% ungroup
к своему конвейеру после того, как суммируете.
Ответ 2
Просто чтобы написать код в полном объеме, здесь обновление по Hadley отвечает новым синтаксисом:
library(dplyr)
df <- data.frame(
asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# Columns you want to group by
grp_cols <- names(df)[-3]
# Convert character vector to list of symbols
dots <- lapply(grp_cols, as.symbol)
# Perform frequency counts
df %>%
group_by_(.dots=dots) %>%
summarise(n = n())
выход:
Source: local data frame [9 x 3]
Groups: asihckhdoydk
asihckhdoydk a30mvxigxkgh n
1 A A 10
2 A B 10
3 A C 13
4 B A 14
5 B B 10
6 B C 12
7 C A 9
8 C B 12
9 C C 10
Ответ 3
Поддержка этого в dplyr в настоящее время довольно слаба, в конце концов я думаю, что синтаксис будет примерно таким:
df %.% group_by(.groups = c("asdfgfTgdsx", "asdfk30v0ja"))
Но это, вероятно, не будет там какое-то время (потому что мне нужно продумать все последствия).
Тем временем вы можете использовать regroup()
, который принимает список символов:
library(dplyr)
df <- data.frame(
asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
df %.%
regroup(list(quote(asihckhdoydk), quote(a30mvxigxkgh))) %.%
summarise(n = n())
Если у вас есть вектор символов имен столбцов, вы можете преобразовать их в нужную структуру с помощью lapply()
и as.symbol()
:
vars <- setdiff(names(df), "value")
vars2 <- lapply(vars, as.symbol)
df %.% regroup(vars2) %.% summarise(n = n())
Ответ 4
Строковая спецификация столбцов в dplyr
теперь поддерживается вариантами функций dplyr
с именами, заканчивающимися в подчеркивании. Например, для функции group_by
существует функция group_by_
, которая может принимать строковые аргументы. Эта виньетка подробно описывает синтаксис этих функций.
Следующий фрагмент полностью решает проблему, которую изначально поставил @sharoz (обратите внимание на необходимость выписать аргумент .dots
):
# Given data and columns from the OP
data %>%
group_by_(.dots = columns) %>%
summarise(Value = mean(value))
(Обратите внимание, что dplyr теперь использует оператор %>%
, а %.%
устарел).
Ответ 5
До тех пор, пока dplyr не будет полностью поддерживать строковые аргументы, возможно, этот смысл полезен:
https://gist.github.com/skranz/9681509
Он содержит множество функций-оболочек, таких как s_group_by, s_mutate, s_filter и т.д., которые используют строковые аргументы. Вы можете смешивать их с обычными функциями dplyr. Например
cols = c("cyl","gear")
mtcars %.%
s_group_by(cols) %.%
s_summarise("avdisp=mean(disp), max(disp)") %.%
arrange(avdisp)
Ответ 6
Он работает, если вы передаете ему объекты (ну, вы не, но...), а не как вектор символов:
df %.%
group_by(asdfgfTgdsx, asdfk30v0ja) %.%
summarise(Value = mean(value))
> df %.%
+ group_by(asdfgfTgdsx, asdfk30v0ja) %.%
+ summarise(Value = mean(value))
Source: local data frame [9 x 3]
Groups: asdfgfTgdsx
asdfgfTgdsx asdfk30v0ja Value
1 A C 0.046538002
2 C B -0.286359899
3 B A -0.305159419
4 C A -0.004741504
5 B B 0.520126476
6 C C 0.086805492
7 B C -0.052613078
8 A A 0.368410146
9 A B 0.088462212
где df
был вашим data
.
?group_by
говорит:
...: variables to group by. All tbls accept variable names, some
will also accept functons of variables. Duplicated groups
will be silently dropped.
который я интерпретирую как означающий не имена символов имен, но как вы относитесь к ним в foo$bar
; bar
здесь не цитируется. Или как вы относитесь к переменным в формуле: foo ~ bar
.
@Arun также упоминает, что вы можете сделать:
df %.%
group_by("asdfgfTgdsx", "asdfk30v0ja") %.%
summarise(Value = mean(value))
Но вы не можете передать что-то, что не оценено, это не имя переменной в объекте данных.
Я предполагаю, что это связано с внутренними методами, которые Хэдли использует для поиска вещей, которые вы проходите через аргумент ...
.
Ответ 7
data = data.frame(
my.a = sample(LETTERS[1:3], 100, replace=TRUE),
my.b = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
group_by(data,newcol=paste(my.a,my.b,sep="_")) %>% summarise(Value=mean(value))
Ответ 8
Один (крошечный) случай, отсутствующий в ответах здесь, который я хотел бы сделать явным, - это когда переменные, которые группируются, генерируются динамически в потоке в конвейере:
library(wakefield)
df_foo = r_series(rnorm, 10, 1000)
df_foo %>%
# 1. create quantized versions of base variables
mutate_each(
funs(Quantized = . > 0)
) %>%
# 2. group_by the indicator variables
group_by_(
.dots = grep("Quantized", names(.), value = TRUE)
) %>%
# 3. summarize the base variables
summarize_each(
funs(sum(., na.rm = TRUE)), contains("X_")
)
В основном это показывает, как использовать grep
в сочетании с group_by_(.dots = ...)
для достижения этого.
Ответ 9
Общий пример использования аргумента .dots
качестве входного вектора символов для функции dplyr::group_by
:
iris %>%
group_by(.dots ="Species") %>%
summarise(meanpetallength = mean(Petal.Length))
Или без жестко закодированного имени для группирующей переменной (согласно запросу OP):
iris %>%
group_by(.dots = names(iris)[5]) %>%
summarise_at("Petal.Length", mean)
На примере ОП:
data %>%
group_by(.dots =names(data)[-3]) %>%
summarise_at("value", mean)
См. Также виньетка dplyr по программированию, которая объясняет местоимения, квази-цитаты, фразы и тидевал.