R:为什么即使使用定额,group_by仍然需要“做”

时间:2018-10-21 01:49:06

标签: r dplyr tidyverse quosure

如何使用户定义的函数与管道和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

应如何更改功能,以使其与filter和group_by一致地工作而无需“做”呢?
还是与问题无关,而group_by只需要使用“ do”,因为示例中的函数具有多个参数?

2 个答案:

答案 0 :(得分:3)

编写该函数时,该函数从column中选择df,然后采用head,它是dfhead的第一行不是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

答案 1 :(得分:3)

这与确定无关。在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

如其他问题herehere所述,do是允许您将任意功能应用于每个组的连接器。 dplyrmutate之类的filter动词不需要do的原因是因为它们在内部将分组数据帧作为特殊情况处理(例如,参见{{3} }。如果您希望自己的函数模拟此行为,则需要区分分组数据帧和非分组数据帧:

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