R: 为什么 group_by 仍然需要 "do" 即使使用 quosures
R: why group_by still requires "do" even when using quosures
如何使用户定义的函数很好地与管道和 group_by 一起工作?这是一个简单的函数:
library(tidyverse)
fun_head <- function(df, column) {
column <- enquo(column)
df %>% select(!!column) %>% head(1)
}
该函数与管道配合得很好,并允许按另一列进行过滤:
mtcars %>% filter(cyl == 4) %>% fun_head(mpg)
> mpg
1 22.8
然而,同样的管道工作失败 group_by
mtcars %>% group_by(cyl) %>% fun_head(mpg)
Adding missing grouping variables: `cyl`
# A tibble: 1 x 2
# Groups: cyl [1]
cyl mpg
<dbl> <dbl>
1 6 21
在 group_by 之后使用 "do" 使其工作:
> mtcars %>% group_by(cyl) %>% do(fun_head(., mpg))
# A tibble: 3 x 2
# Groups: cyl [3]
cyl mpg
<dbl> <dbl>
1 4 22.8
2 6 21
3 8 18.7
该函数应该如何更改才能与过滤器和 group_by 统一工作而不需要 "do"?
或者 quosures 与问题无关,group_by 只需要使用 "do" 因为示例中的函数有多个参数?
如您所写,该函数从 df
中选择 column
,然后取 head
,这是 df
的第一行(head
不是 tidyverse 函数,并且不知道任何分组)。 dplyr::slice(1)
取每一组的第一行,这就是你想要的。您可以使用
fun_head <- function(df, column) {
column <- enquo(column)
df %>% slice(1) %>% select(!!column)
}
mtcars %>% group_by(cyl) %>% fun_head(mpg)
# # A tibble: 3 x 2
# # Groups: cyl [3]
# cyl mpg
# <dbl> <dbl>
# 1 4 22.8
# 2 6 21
# 3 8 18.7
这与quosures无关。在 fun_head()
:
中没有非标准评估的情况下存在同样的问题
fun_head <- function(df) {df %>% select(mpg) %>% head(1)}
mtcars %>% group_by( cyl ) %>% fun_head()
# Adding missing grouping variables: `cyl`
# # A tibble: 1 x 2
# # Groups: cyl [1]
# cyl mpg
# <dbl> <dbl>
# 1 6 21
如其他问题中所述 and , do
is the connector that allows you to apply arbitrary functions to each group. The reason dplyr
verbs such as mutate
and filter
don't require do
is because they handle grouped data frames internally as special cases (see, e.g., the implementation of mutate)。如果您希望自己的函数模拟此行为,则需要区分分组数据帧和未分组数据帧:
fun_head2 <- function( df )
{
if( !is.null(groups(df)) )
df %>% do( fun_head2(.) )
else
df %>% select(mpg) %>% head(1)
}
mtcars %>% group_by(cyl) %>% fun_head2()
# # A tibble: 3 x 2
# # Groups: cyl [3]
# cyl mpg
# <dbl> <dbl>
# 1 4 22.8
# 2 6 21
# 3 8 18.7
编辑: 我想指出 group_by
+ do
的另一种选择是使用 tidyr::nest
+ purrr::map
反而。回到带有两个参数的原始函数定义:
fhead <- function(.df, .var) { .df %>% select(!!ensym(.var)) %>% head(1) }
以下两个链是等价的(直到行的排序,因为 group_by
按分组变量排序而 nest
不):
# Option 1: group_by + do
mtcars %>% group_by(cyl) %>% do( fhead(., mpg) ) %>% ungroup
# Option 2: nest + map
mtcars %>% nest(-cyl) %>% mutate_at( "data", map, fhead, "mpg" ) %>% unnest
如何使用户定义的函数很好地与管道和 group_by 一起工作?这是一个简单的函数:
library(tidyverse)
fun_head <- function(df, column) {
column <- enquo(column)
df %>% select(!!column) %>% head(1)
}
该函数与管道配合得很好,并允许按另一列进行过滤:
mtcars %>% filter(cyl == 4) %>% fun_head(mpg)
> mpg
1 22.8
然而,同样的管道工作失败 group_by
mtcars %>% group_by(cyl) %>% fun_head(mpg)
Adding missing grouping variables: `cyl`
# A tibble: 1 x 2
# Groups: cyl [1]
cyl mpg
<dbl> <dbl>
1 6 21
在 group_by 之后使用 "do" 使其工作:
> mtcars %>% group_by(cyl) %>% do(fun_head(., mpg))
# A tibble: 3 x 2
# Groups: cyl [3]
cyl mpg
<dbl> <dbl>
1 4 22.8
2 6 21
3 8 18.7
该函数应该如何更改才能与过滤器和 group_by 统一工作而不需要 "do"?
或者 quosures 与问题无关,group_by 只需要使用 "do" 因为示例中的函数有多个参数?
如您所写,该函数从 df
中选择 column
,然后取 head
,这是 df
的第一行(head
不是 tidyverse 函数,并且不知道任何分组)。 dplyr::slice(1)
取每一组的第一行,这就是你想要的。您可以使用
fun_head <- function(df, column) {
column <- enquo(column)
df %>% slice(1) %>% select(!!column)
}
mtcars %>% group_by(cyl) %>% fun_head(mpg)
# # A tibble: 3 x 2
# # Groups: cyl [3]
# cyl mpg
# <dbl> <dbl>
# 1 4 22.8
# 2 6 21
# 3 8 18.7
这与quosures无关。在 fun_head()
:
fun_head <- function(df) {df %>% select(mpg) %>% head(1)}
mtcars %>% group_by( cyl ) %>% fun_head()
# Adding missing grouping variables: `cyl`
# # A tibble: 1 x 2
# # Groups: cyl [1]
# cyl mpg
# <dbl> <dbl>
# 1 6 21
如其他问题中所述do
is the connector that allows you to apply arbitrary functions to each group. The reason dplyr
verbs such as mutate
and filter
don't require do
is because they handle grouped data frames internally as special cases (see, e.g., the implementation of mutate)。如果您希望自己的函数模拟此行为,则需要区分分组数据帧和未分组数据帧:
fun_head2 <- function( df )
{
if( !is.null(groups(df)) )
df %>% do( fun_head2(.) )
else
df %>% select(mpg) %>% head(1)
}
mtcars %>% group_by(cyl) %>% fun_head2()
# # A tibble: 3 x 2
# # Groups: cyl [3]
# cyl mpg
# <dbl> <dbl>
# 1 4 22.8
# 2 6 21
# 3 8 18.7
编辑: 我想指出 group_by
+ do
的另一种选择是使用 tidyr::nest
+ purrr::map
反而。回到带有两个参数的原始函数定义:
fhead <- function(.df, .var) { .df %>% select(!!ensym(.var)) %>% head(1) }
以下两个链是等价的(直到行的排序,因为 group_by
按分组变量排序而 nest
不):
# Option 1: group_by + do
mtcars %>% group_by(cyl) %>% do( fhead(., mpg) ) %>% ungroup
# Option 2: nest + map
mtcars %>% nest(-cyl) %>% mutate_at( "data", map, fhead, "mpg" ) %>% unnest