Группировка по нескольким столбцам в 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 был обновлен, поэтому доступно более простое решение. Увидеть выбранный ответ.
8 ответов:
поскольку этот вопрос был опубликован, 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
в ваш конвейер после подведения итогов.
чтобы написать код полностью, вот обновление ответа Хэдли с новым синтаксисом:
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
поддержка этого в 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())
строковая спецификация столбцов в
dplyr
теперь поддерживаются через вариантыdplyr
функции с именами, заканчивающимися подчеркиванием. Например, соответствующий
пока 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)
это работает, если вы передаете ему объекты (ну, вы не, но...), а не как вектор символов:
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))
но вы не можете пройти в то, что unevaluated не является именем переменной в объекте данных.
Я предполагаю, что это связано с внутренними методами Хэдли использует, чтобы посмотреть вещи, которые вы передаете через
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))
один (крошечный) случай, который отсутствует в ответах здесь, что я хотел сделать явным, когда переменные для группировки генерируются динамически в середине потока в конвейере:
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 = ...)
для достижения этой цели.