【问题标题】:R dplyr summarise multiple functions to selected variablesR dplyr 将多个函数汇总到选定的变量
【发布时间】:2017-04-27 19:46:03
【问题描述】:

我有一个数据集,我想按平均值对其进行汇总,但还要计算其中一个变量的最大值。

让我从一个我想要实现的例子开始:

iris %>%
  group_by(Species) %>%
  filter(Sepal.Length > 5) %>%
  summarise_at("Sepal.Length:Petal.Width",funs(mean))

这给了我以下结果

# A tibble: 3 × 5
     Species Sepal.Length Sepal.Width Petal.Length Petal.Width
      <fctr>        <dbl>       <dbl>        <dbl>       <dbl>
1     setosa          5.8         4.4          1.9         0.5
2 versicolor          7.0         3.4          5.1         1.8
3  virginica          7.9         3.8          6.9         2.5

有没有简单的方法可以添加,例如max(Petal.Width)进行总结?

到目前为止,我已经尝试了以下方法:

iris %>%
  group_by(Species) %>%
  filter(Sepal.Length > 5) %>%
  summarise_at("Sepal.Length:Petal.Width",funs(mean)) %>%
  mutate(Max.Petal.Width = max(iris$Petal.Width))

但是使用这种方法,我会丢失上面代码中的 group_byfilter 并给出错误的结果。

我能够实现的唯一解决方案如下:

iris %>%
  group_by(Species) %>%
  filter(Sepal.Length > 5) %>%
  summarise_at("Sepal.Length:Petal.Width",funs(mean,max)) %>%
  select(Species:Petal.Width_mean,Petal.Width_max) %>% 
  rename(Max.Petal.Width = Petal.Width_max) %>%
  rename_(.dots = setNames(names(.), gsub("_.*$","",names(.))))

这有点令人费解,并且需要大量输入才能添加具有不同摘要的列。

谢谢

【问题讨论】:

    标签: r dplyr summarize


    【解决方案1】:

    如果您想做更复杂的事情,您可以编写自己的summarize_at 版本。在这个版本中,您可以提供列名、函数和命名规则的三元组。例如

    这是一个艰难的开始

    my_summarise_at<-function (.tbl, ...) 
    {
        dots <- list(...)
        stopifnot(length(dots)%%3==0)
        vars <- do.call("append", Map(function(.cols, .funs, .name) {
            cols <- select_colwise_names(.tbl, .cols)
            funs <- as.fun_list(.funs, .env = parent.frame())
            val<-colwise_(.tbl, funs, cols)
            names <- sapply(names(val), function(x) gsub("%", x, .name))
            setNames(val, names)
        }, dots[seq_along(dots)%%3==1], dots[seq_along(dots)%%3==2], dots[seq_along(dots)%%3==0]))
        summarise_(.tbl, .dots = vars)
    }
    environment(my_summarise_at)<-getNamespace("dplyr")
    

    你可以调用它

    iris %>%
      group_by(Species) %>%
      filter(Sepal.Length > 5) %>%
      my_summarise_at("Sepal.Length:Petal.Width", mean, "%_mean", 
          "Petal.Width", max, "%_max")
    

    对于名称,我们只需将“%”替换为默认名称。这个想法只是动态构建summarize_ 表达式。 summarize_at 函数实际上只是该基本函数的便捷包装。

    【讨论】:

    • 弗利克先生,谢谢您的回复。我已经尝试过您的代码,但是由于 summarise 删除了原始表,因此“max”函数无法呈现正确的结果。
    • 哦,好的。由于转换是按照它们在汇总中列出的顺序应用的,因此第一步是重命名 Petal.Width 列,因此它采用了错误的最大值。我已经更新了我的答案以重命名汇总列,但将顺序更改为 my_summarise_at("Petal.Width", max, "%_max", "Sepal.Length:Petal.Width", mean, "%") 也应该有效。
    【解决方案2】:

    虽然这是一个老问题,但它仍然是一个有趣的问题,我有两个解决方案,我认为任何找到此页面的人都可以使用。

    解决方案一

    我自己的看法:

    mapply(summarise_at, 
           .vars = lst(names(iris)[!names(iris)%in%"Species"], "Petal.Width"), 
           .funs = lst(mean, max), 
           MoreArgs = list(.tbl = iris %>% group_by(Species) %>% filter(Sepal.Length > 5))) 
    %>% reduce(merge, by = "Species")
    
        #         Species Sepal.Length Sepal.Width Petal.Length Petal.Width.x Petal.Width.y
        #    1     setosa        5.314       3.714        1.509        0.2773           0.5
        #    2 versicolor        5.998       2.804        4.317        1.3468           1.8
        #    3  virginica        6.622       2.984        5.573        2.0327           2.5
    

    解决方案二

    一个优雅的解决方案,使用来自 tidyverse 本身的包 purrr,灵感来自 this discussion

    list(.vars = lst(names(iris)[!names(iris)%in%"Species"], "Petal.Width"),
         .funs = lst("mean" = mean, "max" = max)) %>% 
          pmap(~ iris %>% group_by(Species) %>% filter(Sepal.Length > 5) %>% summarise_at(.x, .y)) 
          %>% reduce(inner_join, by = "Species")
    
    + + + # A tibble: 3 x 6
      Species    Sepal.Length Sepal.Width Petal.Length Petal.Width.x Petal.Width.y
      <fct>             <dbl>       <dbl>        <dbl>         <dbl>         <dbl>
    1 setosa             5.31        3.71         1.51         0.277           0.5
    2 versicolor         6.00        2.80         4.32         1.35            1.8
    3 virginica          6.62        2.98         5.57         2.03            2.5
    

    简短讨论

    data.frame 和 tibble 是期望的结果,最后一列是 petal.widthmax,其他列是所有其他列的平均值(按组和过滤器)。

    两种解决方案都取决于三个实现:

    1. summarise_at 接受两个列表作为参数,一个 n 变量和一个 m 函数,并将所有 m 函数应用于所有 n 个变量,因此在一个小标题中生成 m X n 个向量。因此,该解决方案可能意味着强制该函数以某种方式在由我们希望应用一个特定函数的所有变量和一个函数形成的“对”之间循环,然后是另一组变量和它们自己的函数,等等!
    2. 现在,R 中的上述内容是什么?什么会强制对两个列表的对应元素进行操作?诸如mapply 或函数族map2pmap 及其变体,来自dplyr 的tidyverse 同胞purrr。两者都接受两个 l 元素列表,并对两个列表的对应元素(按位置匹配)执行给定操作。
    3. 因为产品不是 tibble 或 data.frame,而是列表,所以你 只需将reduceinner_joinmerge 一起使用。

    请注意,我获得的手段与 OP 的不同,但它们也是我通过他的可重现示例获得的手段(也许我们有两个不同版本的 iris 数据集?)。

    【讨论】:

      【解决方案3】:

      我正在寻找类似的东西并尝试了以下方法。与建议的解决方案相比,它运行良好且更易于阅读。

      iris %>% 
      group_by(Species) %>%
      filter(Sepal.Length > 5) %>% 
      summarise(MeanSepalLength=mean(Sepal.Length), 
      MeanSepalWidth = mean(Sepal.Width),
      MeanPetalLength=mean(Petal.Length),
      MeanPetalWidth=mean(Petal.Width), 
      MaxPetalWidth=max(Petal.Width))
      
      # A tibble: 3 x 6
      Species    MeanSepalLength MeanSepalWidth MeanPetalLength MeanPetalWidth MaxPetalWidth
      <fct>                <dbl>          <dbl>           <dbl>          <dbl>         <dbl>
      1 setosa                5.01           3.43            1.46          0.246           0.6
      2 versicolor            5.94           2.77            4.26          1.33            1.8
      3 virginica             6.59           2.97            5.55          2.03            2.5
      

      在 summarise() 部分,定义您的列名,并在您选择的函数中为您的列提供汇总。

      【讨论】:

      • 是的,这行得通,但原始帖子的想法是避免重复输入相同的摘要。
      【解决方案4】:

      如果您尝试使用 dplyr 完成所有操作(这可能更容易记住),那么您可以利用 dplyr 1.0.0 提供的新 across 函数。

      iris %>%
        group_by(Species) %>%
        filter(Sepal.Length > 5) %>% 
        summarize(across(Sepal.Length:Petal.Width, mean)) %>% 
        cbind(iris %>% 
                group_by(Species) %>% 
                summarize(across(Petal.Width, max)) %>% 
                select(-Species)
        )
      

      它表明唯一的困难是将同一列 Petal.Width 上的两个计算组合在一个分组变量上 - 您必须再次进行分组,但可以将其嵌套到 cbind 中。 这会正确返回结果:

           Species Sepal.Length Sepal.Width Petal.Length Petal.Width Petal.Width
      1     setosa     5.313636    3.713636     1.509091   0.2772727         0.6
      2 versicolor     5.997872    2.804255     4.317021   1.3468085         1.8
      3  virginica     6.622449    2.983673     5.573469   2.0326531         2.5
      

      如果任务不指定两个计算,但在同一列Petal.Width 上只指定一个,那么这可以优雅地写成:

      iris %>%
        group_by(Species) %>%
        filter(Sepal.Length > 5) %>% 
        summarize(
          across(Sepal.Length:Petal.Length, mean),
          across(Petal.Width, max)
        )
      

      【讨论】:

        猜你喜欢
        • 2016-04-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-30
        • 2021-09-05
        • 2018-01-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多