在dplyr中有效地分配具有多个输出的函数变异或汇总

时间:2016-07-06 11:33:09

标签: r dplyr

我注意到这里有很多例子,它们使用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(...))将其拆分,但这意味着您然后必须在以后添加列名,语法不是很漂亮。

5 个答案:

答案 0 :(得分:3)

罗曼·弗朗索瓦(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以及其中引用的其他帖子中考虑了在摘要中使用返回向量(不是标量)的函数的问题。

答案 1 :(得分: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()功能可以解决这个问题。 e.g。

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

答案 2 :(得分:2)

我无法在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的输出不是列表,因此必须强制列表才能使其生效。

答案 3 :(得分:1)

这也可以使用 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 年 4 月 19 日创建

答案 4 :(得分:0)

我设法这样做了。它使用我拥有的4500万行数据集,工作速度相当快。

{{1}}