如何有效地为依赖 dplyr 的自定义函数使用不同的参数?

How do I efficiently use different arguments for a custom function that relies on dplyr?

有一个数据框,例如:

df <- data.frame(k = sample(1:2, 100, replace = TRUE),
                 l = sample(1:2, 100, replace = TRUE),
                 g = sample(1:3, 100, replace = TRUE, prob = c(0.2, 0.6, 0.2)))

我需要按 g 分组的 l 和 k 的比例图,所以我自己写了一个函数:

library(tidyverse)

fun_gg_factor <- function(p) {
  df %>%
    group_by(g) %>%
    count({{p}}) %>%
    mutate(Anteil = n / sum(n)) %>%
    ggplot(aes(x = {{p}}, y = Anteil)) +
    geom_col(position = position_dodge()) +
    facet_grid(.~g)
}

它按预期工作:

fun_gg_factor(k)

太好了。但是我的 rl df 比 k 和 l 有更多的变量。多得多。所以我不想像这样手动调用函数几十次:

fun_gg_factor(k)
fun_gg_factor(l)
fun_gg_factor(m)
.
.
.
fun_gg_factor(z)

sapply() 及其形式浮现在脑海中:

sapply(c(k, l), fun_gg_factor)

这不起作用,因为 k 和 l 不是对象。即使他们是,那也不是我想要的。我不需要 df$k 的每个元素的图 - 我想要不同列的图。
也许我试试循环:

for (i in c(k, l)) {
  fun_gg_factor(i)
}

但是不,k 和 l 仍然不是对象。
显然我缺乏对问题的表述。我如何有效地为此或任何类似的自定义函数使用不同的参数?

利用 rlang 中的 .data 代词,您可以将变量名称作为字符串传递给您的函数,这使得使用例如循环遍历一组变量变得容易lapply。为此,在您的函数中将 {{ p }} 替换为 .data[[p]]

set.seed(42)

df <- data.frame(
  k = sample(1:2, 100, replace = TRUE),
  l = sample(1:2, 100, replace = TRUE),
  g = sample(1:3, 100, replace = TRUE, prob = c(0.2, 0.6, 0.2))
)

library(ggplot2)
library(dplyr)

fun_gg_factor <- function(p) {
  df %>%
    group_by(g) %>%
    count(.data[[p]]) %>%
    mutate(Anteil = n / sum(n)) %>%
    ggplot(aes(x = .data[[p]], y = Anteil)) +
    geom_col(position = position_dodge()) +
    facet_grid(. ~ g)
}

lapply(names(df)[!names(df) %in% "g"], fun_gg_factor)
#> [[1]]

#> 
#> [[2]]

这是另一种强制评估字符串形式的用户定义参数的方法。我们首先使用 rlang::sym 将参数转换为符号,然后使用称为 bang-bang 运算符的 !! 强制对其求值:

library(rlang)

fun_gg_factor <- function(p) {
  df %>%
    group_by(g) %>%
    count(!!sym(p)) %>%
    mutate(Anteil = n / sum(n)) %>%
    ggplot(aes(x = !!sym(p), y = Anteil)) +
    geom_col(position = position_dodge()) +
    facet_grid(.~ g)
}