Dplyr: Как использовать group_by внутри функции?
Я хочу использовать функцию dplyr::group_by
внутри другой функции, но я не знаю, как передать аргументы этой функции.
Может ли кто-нибудь предоставить рабочий пример?
library(dplyr)
data(iris)
iris %.% group_by(Species) %.% summarise(n = n()) #
## Source: local data frame [3 x 2]
## Species n
## 1 virginica 50
## 2 versicolor 50
## 3 setosa 50
mytable0 <- function(x, ...) x %.% group_by(...) %.% summarise(n = n())
mytable0(iris, "Species") # OK
## Source: local data frame [3 x 2]
## Species n
## 1 virginica 50
## 2 versicolor 50
## 3 setosa 50
mytable1 <- function(x, key) x %.% group_by(as.name(key)) %.% summarise(n = n())
mytable1(iris, "Species") # Wrong!
# Error: unsupported type for column 'as.name(key)' (SYMSXP)
mytable2 <- function(x, key) x %.% group_by(key) %.% summarise(n = n())
mytable2(iris, "Species") # Wrong!
# Error: index out of bounds
Ответы
Ответ 1
Для программирования group_by_
является аналогом group_by
:
library(dplyr)
mytable <- function(x, ...) x %>% group_by_(...) %>% summarise(n = n())
mytable(iris, "Species")
# or iris %>% mytable("Species")
который дает:
Species n
1 setosa 50
2 versicolor 50
3 virginica 50
Обновление В то время, когда это было написано, dplyr использовал %.%
Который был изначально использован выше, но теперь %>%
предпочитается, поэтому изменилось выше, чтобы это было актуально.
Обновить группу 2 теперь не рекомендуется, вместо этого используйте group_by_.
Обновление 3 group_by_(list(...))
теперь становится group_by_(...)
в новой версии dplyr в соответствии с комментарием Роберто.
Обновление 4 Добавлены незначительные вариации, предложенные в комментариях.
Обновление 5: С помощью rlang/tidyeval теперь можно сделать следующее:
library(rlang)
mytable <- function(x, ...) {
group_ <- syms(...)
x %>%
group_by(!!!group_) %>%
summarise(n = n())
}
mytable(iris, "Species")
или мимоходом Необученные Species
, т.е. вокруг него нет кавычек:
library(rlang)
mytable <- function(x, ...) {
group_ <- quos(...)
x %>%
group_by(!!!group_) %>%
summarise(n = n())
}
mytable(iris, Species)
Ответ 2
ОБНОВЛЕНИЕ: с dplyr 0.7.0 вы можете использовать опрятный eval для достижения этого.
Подробнее см. Http://dplyr.tidyverse.org/articles/programming.html.
library(tidyverse)
data("iris")
my_table <- function(df, group_var) {
group_var <- enquo(group_var) # Create quosure
df %>%
group_by(!!group_var) %>% # Use !! to unquote the quosure
summarise(n = n())
}
my_table(iris, Species)
> my_table(iris, Species)
# A tibble: 3 x 2
Species n
<fctr> <int>
1 setosa 50
2 versicolor 50
3 virginica 50
Ответ 3
Уродливые, когда они приходят, но она работает:
mytable3 <- function(x, key) {
my.call <- bquote(summarise(group_by(.(substitute(x)), NULL), n = n()))
my.call[[2]][[3]] <- as.name(key)
eval(my.call, parent.frame())
}
mytable3(iris, "Species")
# Source: local data frame [3 x 2]
#
# Species n
# 1 virginica 50
# 2 versicolor 50
# 3 setosa 50
Есть почти наверняка случаи, которые заставят это сломаться, но вы получите эту идею. Я не думаю, что вы можете столкнуться с вызовом. Еще одна вещь, которая работала, но была еще более уродливой:
mytable4 <- function(x, key) summarise(group_by(x, x[[key]]), n = n())