【问题标题】:What is the best way to loop through function arguments?遍历函数参数的最佳方法是什么?
【发布时间】:2021-03-28 03:35:29
【问题描述】:

这感觉就像一个非常简单的操作 - 从一个数据帧中按组计算平均值并将其合并到另一个预先格式化的数据帧 - 我的 UDF 会这样做,而这并不是我真正苦苦挣扎的部分。

我想要的是让我的函数遍历一系列参数(列表、向量等)。

我希望能够快速构建一个变量列表(或向量 - 我没有设置使用列表)并将其作为参数传递给函数,以便它构建一个包含我输入的所有变量的数据框那个清单。我的真实数据库有 50 多个变量,我想用不同的变量组合制作所有不同类型的新数据框。一个列表可能有 5 个变量,另一个可能有 25 个。但我认为我在概念上有些错误,我应该使用循环、呼噜声、映射、应用、其他包等或更改我的函数是怎么写的?我错过了什么?

library(tidyverse)

data_sample <- data.frame(
  Name = c("Dalton Campbell", "Dalton Campbell", "Dalton Campbell", "Andre Walker", "Andre Walker", "Andre Walker"),
  Defense_Grade = c(88, 86, 92, 94, 97, 95),
  Tackle_Grade = c(66, 69, 72, 74, 76, 78),
  Coverage_Grade = c(44, 43, 44, 76, 73, 78)
)

#Here I set up the dataframe which the function will bind to 
data_sample_averages <-  data_sample %>% 
  group_by(Name) %>% 
  dplyr::summarise(Defense_Grade_Average = mean(Defense_Grade))
#> `summarise()` ungrouping output (override with `.groups` argument)


#Function which computes average of variable (the only argument) and merges it back to data_sample_averages
get_avg2 <- function(v_name) {
  
  avg <- "_Average"      
  
  data_1 <-  data_sample %>% 
    dplyr::group_by(Name) %>% 
    dplyr::summarise("{{ v_name }}_{avg}" := mean({{ v_name }}, na.rm = TRUE))
  
  data_sample_averages <- merge(data_sample_averages, data_1, by = "Name")
  
  return(data_sample_averages)

}

#This works - it computers the average of Tackle_Grade and binds it to data_sample_averages
#However my real dataframe has 50+ columns and I don't want to copy and paste this line 50 times, changing the argument every time.
data_sample_averages <- get_avg2(Tackle_Grade)
#> `summarise()` ungrouping output (override with `.groups` argument)

#shows you the averages
print(data_sample_averages)
#>              Name Defense_Grade_Average Tackle_Grade__Average
#> 1    Andre Walker              95.33333                    76
#> 2 Dalton Campbell              88.66667                    69


#Neither of these work - this is where I'm stuck
#I want my function to iterate through a list of arguments which are essentially just character #strings in order for the UDF to work 
variable_list <- list("Defense_Grade", "Tackle_Grade", "Coverage Grade")

data_sample_averages <- lapply(variable_list, get_avg2)
#> Warning in mean.default(~"Defense_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA

#> Warning in mean.default(~"Defense_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)
#> Warning in mean.default(~"Tackle_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> Warning in mean.default(~"Tackle_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)
#> Warning in mean.default(~"Coverage Grade", na.rm = TRUE): argument is not
#> numeric or logical: returning NA
#> Warning in mean.default(~"Coverage Grade", na.rm = TRUE): argument is not
#> numeric or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)

data_sample_averages <- purrr::map(variable_list, get_avg2)
#> Warning in mean.default(~"Defense_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> Warning in mean.default(~"Defense_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)
#> Warning in mean.default(~"Tackle_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> Warning in mean.default(~"Tackle_Grade", na.rm = TRUE): argument is not numeric
#> or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)
#> Warning in mean.default(~"Coverage Grade", na.rm = TRUE): argument is not
#> numeric or logical: returning NA
#> Warning in mean.default(~"Coverage Grade", na.rm = TRUE): argument is not
#> numeric or logical: returning NA
#> `summarise()` ungrouping output (override with `.groups` argument)

【问题讨论】:

    标签: r function dataframe dplyr sapply


    【解决方案1】:

    你说你没有设置使用列表,所以我使用向量。

    我的解决方案依赖于dplyr 最新版本中的一个函数:across() 函数。

    library(tidyverse)
    
    data_sample <- data.frame(
      Name = c("Dalton Campbell", "Dalton Campbell", "Dalton Campbell", "Andre Walker", "Andre Walker", "Andre Walker"),
      Defense_Grade = c(88, 86, 92, 94, 97, 95),
      Tackle_Grade = c(66, 69, 72, 74, 76, 78),
      Coverage_Grade = c(44, 43, 44, 76, 73, 78)
    )
    
    # The function
    compute_avg <- function(.data, names){
        
        names_quo <- enquos(names)  
      
        .data %>%
        group_by(Name) %>%
        summarise(
          across(
            .cols = !!!names_quo,
            .fns = ~ mean(.x, na.rm = TRUE),
            .names = "{.col}_Average"
          )
        )
    }
    
    compute_avg(.data = data_sample, names = c(Defense_Grade, Tackle_Grade))
    
    # A tibble: 2 x 3
      Name            Defense_Grade_Average Tackle_Grade_Average
      <chr>                           <dbl>                <dbl>
    1 Andre Walker                     95.3                   76
    2 Dalton Campbell                  88.7                   69
    

    【讨论】:

    • 我一直在为 NSE 苦苦挣扎,这个答案真的很有帮助!!!看来我也真的需要学习cross()。您不必回答我,但我对这行感到困惑:``` .fns = ~ mean(.x, na.rm = TRUE),``` .x 是什么?
    • 学习新版dplyr (>= 1.0.0) 中的函数会给你带来显着的生产力提升:rebeccabarter.com/blog/2020-07-09-across 看来你对匿名函数(又名lambda函数)在 tidyverse 中。 ~ 符号在许多 tidyverse 函数中用于动态创建函数。在这种情况下,由于.cols 是Defense_Grade 和Tackle_Grade,.x 参数在mean() 函数中一个接一个地取它们的值。最后,如果它解决了您的问题,请考虑将我的答案标记为解决方案。
    • 哇,感谢您的教育!我有很多东西要学。今年刚开始R!谢谢,我标记为解决方案!
    猜你喜欢
    • 2012-07-16
    • 2011-04-24
    • 1970-01-01
    • 1970-01-01
    • 2010-09-05
    • 2019-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多