如何有效地为依赖 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)
}
有一个数据框,例如:
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)
}