【问题标题】:Replace multiple `summarize`statements by function用函数替换多个“summarize”语句
【发布时间】:2019-09-18 01:21:17
【问题描述】:

我目前正在重复很多代码,因为我需要 summarize 始终为不同的组提供相同的列。如何通过只编写一次 summarize 函数(始终相同)来有效地做到这一点,但逐个定义输出名称和 group_by 参数?

一个最小的例子:

col1 <- c("UK", "US", "UK", "US")
col2 <- c("Tech", "Social", "Social", "Tech")
col3 <- c("0-5years", "6-10years", "0-5years", "0-5years")
col4 <- 1:4
col5 <- 5:8

df <- data.frame(col1, col2, col3, col4, col5)

result1 <- df %>% 
  group_by(col1, col2) %>% 
  summarize(sum1 = sum(col4, col5))

result2 <- df %>% 
  group_by(col2, col3) %>% 
  summarize(sum1 = sum(col4, col5))

result3 <- df %>% 
  group_by(col1, col3) %>% 
  summarize(sum1 = sum(col4, col5))

【问题讨论】:

  • ddply 函数比group_by %&gt;% summarise 更简洁。您可以将第一个重写为ddply(df, .(col1, col2), summarise, sum1=sum(col5, col5))。不回答您的实际问题,但会减少您使用的行数

标签: r function tidyverse summarize


【解决方案1】:

使用combn

combn(colnames(df)[1:3], 2, FUN = function(x){
  df %>% 
    group_by(.dots = x) %>% 
    summarize(sum1 = sum(col4, col5))
  }, simplify = FALSE)

【讨论】:

    【解决方案2】:

    要在自己的函数中使用 dplyr,您可以使用 tidy 评估。其原因是 dplyr 评估 dplyr 代码的方式,称为非标准评估,它包装了与正常 R 代码不同的所有内容。我建议阅读此内容:

    https://tidyeval.tidyverse.org/modifying-inputs.html#modifying-quoted-expressions

    summarizefunction <- function(data, ..., sumvar1, sumvar2) {
    
        groups <- enquos(...)
        sumvar1 <- enquo(sumvar1)
        sumvar2 <- enquo(sumvar2)
    
        result <- data %>%
            group_by(!!!groups) %>%
            summarise(sum1 = sum(!!sumvar1, !!sumvar2))
        return(result)
    }
    
    summarizefunction(df, col1, col2, sumvar1 = col4, sumvar2 = col5)
    

    您可以使用enquo 关键字来包装引号参数,以防止它们被立即评估。您可以使用!!(称为bang bang)运算符取消对参数的引用。我认为这是最灵活和可重用的解决方案,即使您必须编写更多初始代码。

    【讨论】:

    • 这种方法似乎最适合我。只有一个问题:我有数百个不同的变量要求和、除等。有没有办法不在function() 部分中输入/复制所有这些变量?
    • 我的组组合数量要少得多(8)。
    • 我建议你看看 reshape2 包。这样,您可以以整齐的长格式重组数据集。例如:reshape2::melt(df)。然后考虑split 函数,它将您的长df 转换为较小的df 列表。然后将 lapply 函数与 summarizefunction 结合使用。聚合多列几乎总是一个“不太好的”想法。如果您喜欢我的解决方案,请接受我的回答:-)
    【解决方案3】:

    你也可以在这些情况下使用purrr::partial

    library(purrr)
    summarize45 <- partial(summarize, sum1 = sum(col4, col5))
    
    result1b <- df %>% 
      group_by(col1, col2) %>%
      summarize45()
    
    identical(result1, result1b)
    # [1] TRUE
    

    或进一步推动:

    gb_df <- partial(group_by, df)
    
    result1c <- gb_df(col1, col2) %>% summarize45()
    
    identical(result1, result1c)
    # [1] TRUE
    

    【讨论】:

    • 这太棒了@Moody_Mudskipper。正是我需要的,立即将我的代码减少到 1/6,我什至还没有完成一半的工作。如果可以的话,我会给你超过 +1。
    【解决方案4】:

    首先,您需要使用这样的函数评估变量:

    library(tidyverse)
    res_func <- function(x, y){
      df %>% 
      group_by(!!as.symbol(x), !!as.symbol(y)) %>% 
      summarize(sum1 = sum(col4, col5))
    }
    

    很有魅力:

    res_func("col1", "col2")
    
    # A tibble: 4 x 3
    # Groups:   col1 [2]
      col1  col2    sum1
      <fct> <fct>  <int>
    1 UK    Social    10
    2 UK    Tech       6
    3 US    Social     8
    4 US    Tech      12
    

    我们可以使用assign 创建一个函数,根据您通过该函数传入的参数来命名您的框架:

    res_func2 <- function(x, y){
      assign(paste0("result_", x, y),
             df %>% 
               group_by(!!as.symbol(x), !!as.symbol(y)) %>% 
               summarize(sum1 = sum(col4, col5)), 
             envir = parent.frame())
    }
    

    这会创建一个名为 result_col1col2 的新 df,只需运行 res_func2("col1", "col2")

    > result_col1col2
    # A tibble: 4 x 3
    # Groups:   col1 [2]
      col1  col2    sum1
      <fct> <fct>  <int>
    1 UK    Social    10
    2 UK    Tech       6
    3 US    Social     8
    4 US    Tech      12
    

    【讨论】:

    • 如果你将一个参数传递给函数,它看起来会更简单。 res_func &lt;- function(x){ df %&gt;% group_by(!!as.symbol(x)) %&gt;% summarize(sum1 = sum(col4, col5)) }
    • 会不会按要求轻松分配给新df的名称?
    • 是的,paste(x, collapse = "_")
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 2019-07-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多