【问题标题】:Efficient assignment of a function with multiple outputs in dplyr mutate or summarise在 dplyr mutate 或 summarise 中有效分配具有多个输出的函数
【发布时间】:2016-07-06 11:33:08
【问题描述】:

我注意到这里有很多示例,它们使用dplyr::mutate 结合返回多个输出的函数来创建多个列。例如:

tmp <- mtcars %>%
    group_by(cyl) %>%
    summarise(min = summary(mpg)[1],
              median = summary(mpg)[3],
              mean = summary(mpg)[4],
              max = summary(mpg)[6])

然而,这样的语法意味着 summary 函数被调用了 4 次,在这个例子中,这似乎不是特别有效。有什么方法可以有效地将列表输出分配给summarisemutate 中的列名列表?

例如,从上一个问题:Split a data frame column containing a list into multiple columns using dplyr (or otherwise),我知道您可以将summary 的输出分配为列表,然后使用do(data.frame(...)) 将其拆分,但这意味着您必须随后添加列后面的名字和语法不那么漂亮。

【问题讨论】:

标签: r dplyr


【解决方案1】:

来自 Romain Francois 的 tie 包可以非常巧妙地做到这一点

devtools::install_github("romainfrancois/tie")
library('tidyverse')
library('tie')

tmp <- mtcars %>%
  group_by(cyl) %>%
  bow( tie(min, median, mean, max) := summary(mpg)[c(1,3,4,6)] )

注意使用:= 而不是=

tidyverse 团队在此处 https://github.com/tidyverse/dplyr/issues/154 以及在其中引用的其他帖子中考虑了在 summarise 中使用返回向量(不是标量)的函数的问题。

【讨论】:

    【解决方案2】:

    这解决了您的示例,但可能不是您的主要问题。在您展示的情况下,您可以将其重写为:

    tmp <- mtcars %>%
        group_by(cyl) %>%
        summarise_each(funs(min, median, mean, max), mpg)
    

    这样效率更高,运行时间大约减少 40%:

    microbenchmark(mtcars %>%
                     group_by(cyl) %>%
                     summarise_each(funs(min, median, mean, max), mpg), 
                                    times = 1000L)
    
    
     mtcars %>% group_by(cyl) %>% summarise_each(funs(min, median,mean, max), mpg)
          min       lq     mean   median       uq      max neval
     2.002762 2.159464 2.330703 2.216719 2.271264 7.771477  1000
    
    
    microbenchmark(mtcars %>%
        group_by(cyl) %>%
        summarise(min = summary(mpg)[1],
                  median = summary(mpg)[3],
                  mean = summary(mpg)[4],
                  max = summary(mpg)[6]), times = 1000L)
    
     mtcars %>% group_by(cyl) %>% summarise(min = summary(mpg)[1], median = summary(mpg)[3], mean = summary(mpg)[4], max = summary(mpg)[6])
          min      lq     mean   median       uq      max neval
     4.967731 5.21122 5.571605 5.360689 5.530197 13.26596  1000
    

    但是,当然还有其他情况下这不能解决问题。

    编辑:

    do() 函数可以解决这个问题。例如

    by_cyl <- group_by(mtcars, cyl) %>%
            do(mod = summary(.)[c(1,4,6),])
    

    【讨论】:

    • 嗨,谢谢你,但正如你正确指出的那样,它并没有解决我的主要问题。
    • @Alex 添加了应该解决您正在寻找的内容的编辑。
    • 那不行,结果被分配到一列
    • @Alex 不,他们被分配到一个列表 by_cyl$mod 。所有的结果都在那里,可以访问。
    【解决方案3】:

    我在dplyr 中找不到合适的解决方案,让您以易于记忆的方式分配名称。我发现以下data.table 解决方案可以接受,如果有点罗嗦:

    data.table(mtcars) %>%
        .[, setattr(as.list(summary(mpg)[c(1,3,4,6)]), 
            "names", c("min", "median", "mean", "max")),
             by = cyl]
    

    这来自akrun's answer,其中:

    data.table(mtcars) %>%
        .[, as.list(summary(mpg)[c(1,3,4,6)]), by = cyl]
    

    自动将函数的输出分配到 4 列。因此,唯一剩下的就是使用setattr 函数适当地重命名列。

    请注意,summary 的输出不是列表,因此必须强制转换为列表才能使其工作。

    【讨论】:

      【解决方案4】:

      我设法做到了这一点。它在我拥有的 4500 万行数据集上运行得相当快。

      tmp <- mtcars %>%
        group_by(cyl) %>%
        do(data.frame(t(as.matrix(summary(.$mpg)[c(1, 3, 4, 6)]))))
      
      Source: local data frame [3 x 5]
      Groups: cyl [3]
      
          cyl  Min. Median  Mean  Max.
        <dbl> <dbl>  <dbl> <dbl> <dbl>
      1     4  21.4   26.0 26.66  33.9
      2     6  17.8   19.7 19.74  21.4
      3     8  10.4   15.2 15.10  19.2
      

      【讨论】:

        【解决方案5】:

        这也可以使用tidyr::nestpurrr::map 来完成。请注意,summary 返回的输出需要从命名向量转换为 data.frame 或 tibble,我在下面使用dplyr::bind_rows 来完成此操作,但同样可以使用data.frame(as.list(summary(.$mpg)))

        
        suppressWarnings(library(tidyverse))
        
        mtcars %>%
          group_by(cyl) %>%
          nest() %>% 
          summarise(stats = map(data, ~ bind_rows(summary(.$mpg)))) %>% 
          unnest(stats)
        #> # A tibble: 3 x 7
        #>     cyl Min.    `1st Qu.` Median  Mean     `3rd Qu.` Max.   
        #>   <dbl> <table> <table>   <table> <table>  <table>   <table>
        #> 1     4 21.4    22.80     26.0    26.66364 30.40     33.9   
        #> 2     6 17.8    18.65     19.7    19.74286 21.00     21.4   
        #> 3     8 10.4    14.40     15.2    15.10000 16.25     19.2
        

        reprex package (v0.3.0) 于 2021-04-19 创建

        【讨论】:

          猜你喜欢
          • 2018-04-30
          • 2014-04-10
          • 1970-01-01
          • 2018-08-11
          • 2019-05-15
          • 2013-05-02
          • 2021-06-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多