Группировать по нескольким столбцам в 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 обновлен, поэтому доступно более простое решение. См. Текущий выбранный ответ.
- Ошибка в : цель присвоения расширяется до неязыкового объекта
- Collapse / concatenate / aggregate column для отдельной строки, разделенной запятой, в каждой группе
- Ошибка чтения данных в R
- использовать ggpairs для создания этого участка
- Как удалить пустые факторы из граней ggplot2?
- Roxygen2 - как правильно документировать методы S3
- Найти индексы дублированных строк
- Поместите одну числовую переменную в n числовых переменных в n графиках
- Роллинг соединяется на data.table с дублирующимися ключами
- Динамическое создание вкладок с графиками в блестящем состоянии без повторного создания существующих вкладок
- Условно изменить фон панели с facet_grid?
- Выровняйте несколько графиков в ggplot2, когда у некоторых есть легенды, а у других нет
- Заказать бары в гистограмме ggplot2
Поскольку этот вопрос был отправлен, 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
Вывод из вашего примерного вопроса выглядит так, как ожидалось (см. Сравнение с выше и выше ниже):
# A tibble: 9 x 3 # Groups: asihckhdoydkhxiydfgfTgdsx [?] asihckhdoydkhxiydfgfTgdsx a30mvxigxkghc5cdsvxvyv0ja Value 1 AA 0.04095002 2 AB 0.24943935 3 AC -0.25783892 4 BA 0.15161805 5 BB 0.27189974 6 BC 0.20858897 7 CA 0.19502221 8 CB 0.56837548 9 CC -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 AA 10 2 AB 10 3 AC 13 4 BA 14 5 BB 10 6 BC 12 7 CA 9 8 CB 12 9 CC 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
с именами, заканчивающимися в подчеркивании. Например, для функции group_by
существует функция group_by_
которая может принимать строковые аргументы. Эта виньетка подробно описывает синтаксис этих функций.
Следующий fragment полностью решает проблему, которую первоначально поставил @sharoz (обратите внимание на необходимость записи аргумента .dots
):
# Given data and columns from the OP data %>% group_by_(.dots = columns) %>% summarise(Value = mean(value))
(Обратите внимание, что 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 AC 0.046538002 2 CB -0.286359899 3 BA -0.305159419 4 CA -0.004741504 5 BB 0.520126476 6 CC 0.086805492 7 BC -0.052613078 8 AA 0.368410146 9 AB 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))
Но вы не можете передать что-то, что не оценено, это не имя переменной в объекте данных.
Я предполагаю, что это связано с внутренними методами, которые использует Хэдли для поиска вещей, которые вы проходите через аргумент ...
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))
Один (крошечный) случай, отсутствующий в ответах здесь, который я хотел бы сделать явным, – это когда переменные, которые группируются, генерируются динамически в streamе в конвейере:
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 = ...)
для достижения этого.