首页 文章

有没有办法将dplyr的`do`函数传递给其他参数的向量?

提问于
浏览
2

我很好奇是否有办法将dplyr的 do 函数传递给附加参数的向量,这些参数将依次应用于每个组?例如,考虑是否要将 mtcars 数据集按其 cyl 变量进行分组,并将 head 函数应用于结果组(分别为4,6和8),其中n = 1表示4组,n = 2表示6组,8组n = 3,将最终结果合并到一个数据帧中 .

我可以使用 mapply 完成此操作,如下所示:

temp <- mtcars %>% 
  split(mtcars$cyl) %>%
  mapply(FUN = head, x = ., n = 1:3, SIMPLIFY = FALSE)
rbind(temp[[1]], temp[[2]], temp[[3]])

我很好奇是否有与dplyr相同的方法吗?我到目前为止,但是如何传递 head 是一个额外的参数,表示我们想要选择的行数:

# only selects first row of each group
mtcars %>%
  group_by(cyl) %>%
  do(data.frame(head(x = ., n = 1)))

# throws an error because n expects a single number
mtcars %>%
  group_by(cyl) %>%
  do(data.frame(head(x = ., n = 1:3)))

4 回答

  • 4

    没有分组也可以这样做,

    mtcars %>% arrange(cyl) %>% slice(rep(c(0, which(diff(cyl)>0)), 1:3) + sequence(1:3))
    
    #    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
    # 1 22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
    # 2 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
    # 3 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
    # 4 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
    # 5 14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
    # 6 16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
    

    更直接地回答关于 do 的问题,因为它的实现方式(通过子集在循环中计算表达式),使得 head 函数工作的一种方法是让它在每次调用时递增一个变量 .

    ## Define a function that increments a variable each time it is called
    heads <- (function() { n <- 0; function(dat) { n <<- n+1; dat[1:n, ] } })()
    
    mtcars %>% group_by(cyl) %>% do(heads(.))
    #    mpg cyl  disp  hp drat    wt  qsec vs am gear carb
    # 1 22.8   4 108.0  93 3.85 2.320 18.61  1  1    4    1
    # 2 21.0   6 160.0 110 3.90 2.620 16.46  0  1    4    4
    # 3 21.0   6 160.0 110 3.90 2.875 17.02  0  1    4    4
    # 4 18.7   8 360.0 175 3.15 3.440 17.02  0  0    3    2
    # 5 14.3   8 360.0 245 3.21 3.570 15.84  0  0    3    4
    # 6 16.4   8 275.8 180 3.07 4.070 17.40  0  0    3    3
    
  • 2

    如果我们想要通过其cyl变量对mtcars数据集进行分组,并将head函数应用于结果组(分别为4,6和8),对于4组,n = 1,对于6组,n = 2,对于8组,n = 3

    首先,在data.frame中形式化这个概念:

    heads = data.frame(cyl=c(4,6,8), n = 1:3)
    

    然后你可以合并它:

    mtcars %>% left_join(heads) %>% group_by(cyl) %>% slice(seq(first(n)))
    
    #     mpg   cyl  disp    hp  drat    wt  qsec    vs    am  gear  carb     n
    #   (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (dbl) (int)
    # 1  22.8     4 108.0    93  3.85 2.320 18.61     1     1     4     1     1
    # 2  21.0     6 160.0   110  3.90 2.620 16.46     0     1     4     4     2
    # 3  21.0     6 160.0   110  3.90 2.875 17.02     0     1     4     4     2
    # 4  18.7     8 360.0   175  3.15 3.440 17.02     0     0     3     2     3
    # 5  14.3     8 360.0   245  3.21 3.570 15.84     0     0     3     4     3
    # 6  16.4     8 275.8   180  3.07 4.070 17.40     0     0     3     3     3
    

    我也会考虑躲避额外的括号

    ... %>% slice(n %>% first %>% seq)
    

    当其他dplyr函数不能胜任工作时, do 仅作为hack存在,应该避免 .

  • 2

    嗯,我敢打赌,这是一种更优雅的方式,但是:

    group_index = 
      mtcars %>%
      group_by(cyl) %>%
      group_indices
    
    mtcars %>%
      mutate(group_index = group_index) %>%
      group_by(cyl) %>%
      slice(group_index %>% first %>% seq)
    
  • 0

    会像这样的工作 . 这个解决方案特定于mtcars示例,但是这样的事情可能适用于您的情况 . 它涉及创建自己的函数,该函数具有基于您正在分组的列的条件语句:

    head_custom <- function(df, n){
    
        if(df$cyl == 4){
        ans <- head(df, n[1])
        }
    
        if(df$cyl == 6){
        ans <- head(df, n[2])
        }
    
        if(df$cyl == 8){
        ans <- head(df, n[3])
        }
    
        return(ans)
    }
    
    mtcars %>%
    group_by(cyl) %>%
    do(head_custom(., n = 1:3))
    

相关问题