【问题标题】:Using dynamic names with dplyr在 dplyr 中使用动态名称
【发布时间】:2020-12-07 02:03:02
【问题描述】:

我有一个数据框,我想在其中计算一些额外列作为现有列的函数,但想动态指定每个新列名和函数。我有一个列名向量已经在数据框df_daily

DAILY_QUESTIONS  <- c("Q1_Daily",  "Q2_Daily",   "Q3_Daily",  "Q4_Daily",  "Q5_Daily")

数据框的行包含每个用户每次回答问卷时对每个问题的回答,以及一列包含自用户首次回答问卷以来的天数(即第一次使用时 Days_From_First_Use = 0 , = 1 如果第二天使用等)。我想按 Days_From_First_Use 对这些问题的回答进行平均。我首先将我的数据框按 Days_From_First_Use 分组:

df_test <- df_daily %>%
             group_by(Days_From_First_Use)

然后尝试对循环中的响应进行平均,如下所示:

for(i in 1:5){
   df_test <- df_test %>%
                 mutate(!! paste0('Avg_Score_', DAILY_QUESTIONS[i]) := 
                 paste0('mean(', DAILY_QUESTIONS[i], ')'))
}

不幸的是,虽然我的新变量名称是正确的 ("Avg_Score_Q1_Daily", "Avg_Score_Q2_Daily", "Avg_Score_Q3_Daily", "Avg_Score_Q4_Daily", "Avg_Score_Q5_Daily"),但我的答案却不是:我的数据框中的每一行在相关列中都有一个字符串,例如 "mean(Q1_Daily)"

所以我显然做错了什么 - 我需要做些什么来解决这个问题并获得每天所有用户的平均分数?

在此致以诚挚的谢意

托马斯·飞利浦

【问题讨论】:

  • 您是否看过 Tom Roth 对类似名称的常见问题解答 How to use dynamic variable names in dplyr? 它显示了在 := 两侧使用变量的示例。
  • 我做到了,但没有完全理解。我会回去再次查看它,看看它是否能解决我的问题。
  • 其实,再看更多,我认为更当前的方法是使用{{,如Ronak's answer。也可以在Programming with dplyr vignette中看到“如果要在输出中使用变量名,可以使用胶合语法结合:=这句话下的例子Programming with dplyr vignette
  • 不走运:我创建了一个函数my_mutate_mean &lt;- function(df, question) { df %&gt;% mutate( "Avg_score_{{question}}" := mean({{ question }}) ) }' and then call it in a loop 'for(i in 1:5){ df_test &lt;- my_mutate_mean(df_test, DAILY_QUESTIONS[i]) },但我只在名为 Avg_Score_DAILY_QUESTIONS[i] 的变量中得到一列 NAs
  • 您能否分享足够的数据以使您的示例可重现以便我们进行测试?

标签: r dplyr


【解决方案1】:

我采取了一些不同的方法,在group_by(Days_From_First_Use) 之后使用summarize(across(...)) 我通过使用rename_with 和一个将“Q”替换为“Avg_Score_Q”的自定义函数来实现动态名称

library(dplyr, warn.conflicts = FALSE)

# fake data -- 30 normalized "responses" from 0 to 2 days from first use to 5 questions
DAILY_QUESTIONS  <- c("Q1_Daily",  "Q2_Daily",   "Q3_Daily",  "Q4_Daily",  "Q5_Daily")
df_daily <- as.data.frame(do.call('cbind', lapply(1:5, function(i) rnorm(30, i))))
colnames(df_daily) <- DAILY_QUESTIONS
df_daily$Days_From_First_Use <- floor(runif(30, 0, 3))

df_test <- df_daily %>%
  group_by(Days_From_First_Use) %>%
  summarize(across(.fns = mean)) %>%
  rename_with(.fn  = function(x) gsub("^Q","Avg_Score_Q",x))
#> `summarise()` ungrouping output (override with `.groups` argument)

df_test  
#> # A tibble: 3 x 6
#>   Days_From_First… Avg_Score_Q1_Da… Avg_Score_Q2_Da… Avg_Score_Q3_Da…
#>              <dbl>            <dbl>            <dbl>            <dbl>
#> 1                0            1.26              1.75             3.02
#> 2                1            0.966             2.14             3.48
#> 3                2            1.08              2.45             3.01
#> # … with 2 more variables: Avg_Score_Q4_Daily <dbl>, Avg_Score_Q5_Daily <dbl>

reprex package (v0.3.0) 于 2020-12-06 创建

【讨论】:

  • 您可能可以使用across().names arg 而不是renamesummarize(across(all_of(DAILY_QUESTIONS), mean, .names = "Avg_score_{{.col}}"))
  • 糟糕,有一个额外的{ - 试试df_daily %&gt;% group_by(Days_From_First_Use) %&gt;% summarize(across(.fns = mean, .names = "Avg_Score_{.col}")),效果很好!
  • 很好,是的——所有这些方法都有大量的可扩展选项!
  • 完美 - 感谢一百万的帮助!移除额外的大括号就可以了。我使用 mutate 而不是 summarise 以便将新列添加到现有数据框而不是创建新数据框。这是我的代码的最终版本:df_daily &lt;- df_daily %&gt;% group_by(Days_From_First_Use) %&gt;% mutate(across(all_of(DAILY_QUESTIONS), mean, .names = "Avg_Score_{.col}"))
  • 最后一项改进:前缀不必硬编码——我可以使用变量来设置它。我先写MY_PREFIX &lt;- "abc",然后写df_daily &lt;- df_daily %&gt;% group_by(Days_From_First_Use) %&gt;% mutate(across(all_of(DAILY_QUESTIONS), mean, .names = paste0(MY_PREFIX, "{.col}")))
猜你喜欢
  • 2014-11-18
  • 2018-09-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-14
  • 1970-01-01
相关资源
最近更新 更多