使用 dplyr 进行函数式编程
Functional programming with dplyr
寻找一种更有效/优雅的方式将多个参数传递给一个组——通过在使用 dplyr 的函数中使用非标准评估。我不想使用 ... 运算符,而是要单独指定函数。
我的具体用例是一个函数,它接受一个数据框并使用更简单的语法创建一个 ggplot 对象。这是我想用我的函数自动化的代码示例:
# create data frame
my_df <- data.frame(month = sample(1:12, 1000, replace = T),
category = sample(head(letters, 3), 1000, replace = T),
approved = as.numeric(runif(1000) < 0.5))
my_df$converted <- my_df$approved * as.numeric(runif(1000) < 0.5)
my_df %>%
group_by(month, category) %>%
summarize(conversion_rate = sum(converted) / sum(approved)) %>%
ggplot + geom_line(aes(x = month, y = conversion_rate, group = category,
color = category))
我想将 group_by、summarize、ggplot 和 geom_line 组合成一个简单的函数,我可以输入 x、y 和组,并让它执行所有脏活在引擎盖下。这是我的工作:
# create the function that does the grouping and plotting
plot_lines <- function(df, x, y, group) {
x <- enquo(x)
group <- enquo(group)
group_bys <- quos(!! x, !! group)
df %>%
group_by(!!! group_bys) %>%
my_smry %>%
ggplot + geom_line(aes_(x = substitute(x), y = substitute(y),
group = substitute(group), color = substitute(group)))
}
# create a function to do the summarization
my_smry <- function(x) {
x %>%
summarize(conversion_rate = sum(converted) / sum(approved))
}
# use my function
my_df %>%
plot_lines(x = month, y = conversion_rate, group = category)
我觉得 group_by 处理很不优雅:用 enquo
引用 x
和 group
,然后在另一个里面用 !!
取消引用它们引用函数 quos
,只是在下一行用 !!!
重新取消引用它们,但这是我唯一能够开始工作的东西。有更好的方法吗?
此外,有没有办法让 ggplot 使用 !!
而不是 substitute
?我在做什么感觉不一致。
问题是 ggplot 还没有更新来处理 quosures,所以你必须给它传递表达式,你可以用 rlang::quo_expr
:
从 quosures 创建
library(tidyverse)
set.seed(47)
my_df <- data_frame(month = sample(1:12, 1000, replace = TRUE),
category = sample(head(letters, 3), 1000, replace = TRUE),
approved = as.numeric(runif(1000) < 0.5),
converted = approved * as.numeric(runif(1000) < 0.5))
plot_lines <- function(df, x, y, group) {
x <- enquo(x)
y <- enquo(y)
group <- enquo(group)
df %>%
group_by(!! x, !! group) %>%
summarise(conversion_rate = sum(converted) / sum(approved)) %>%
ggplot(aes_(x = rlang::quo_expr(x),
y = rlang::quo_expr(y),
color = rlang::quo_expr(group))) +
geom_line()
}
my_df %>% plot_lines(month, conversion_rate, category)
但是,请记住,ggplot 几乎不可避免地会从 lazyeval 更新为 rlang,因此虽然此界面可能会继续工作,但可能很快就会出现一个更简单、更一致的界面。
你可以像这样直接eval.parent(substitute(...))
。作为基础 R,它在 R 中始终如一地工作并且很容易做到。甚至可以使用普通的 aes
.
plot_lines <- function(df, x, y, group) eval.parent(substitute(
df %>%
group_by(x, group) %>%
my_smry %>%
ggplot + geom_line(aes(x = x, y = y, group = group, color = group))
))
plot_lines(my_df, month, conversion_rate, category)
寻找一种更有效/优雅的方式将多个参数传递给一个组——通过在使用 dplyr 的函数中使用非标准评估。我不想使用 ... 运算符,而是要单独指定函数。
我的具体用例是一个函数,它接受一个数据框并使用更简单的语法创建一个 ggplot 对象。这是我想用我的函数自动化的代码示例:
# create data frame
my_df <- data.frame(month = sample(1:12, 1000, replace = T),
category = sample(head(letters, 3), 1000, replace = T),
approved = as.numeric(runif(1000) < 0.5))
my_df$converted <- my_df$approved * as.numeric(runif(1000) < 0.5)
my_df %>%
group_by(month, category) %>%
summarize(conversion_rate = sum(converted) / sum(approved)) %>%
ggplot + geom_line(aes(x = month, y = conversion_rate, group = category,
color = category))
我想将 group_by、summarize、ggplot 和 geom_line 组合成一个简单的函数,我可以输入 x、y 和组,并让它执行所有脏活在引擎盖下。这是我的工作:
# create the function that does the grouping and plotting
plot_lines <- function(df, x, y, group) {
x <- enquo(x)
group <- enquo(group)
group_bys <- quos(!! x, !! group)
df %>%
group_by(!!! group_bys) %>%
my_smry %>%
ggplot + geom_line(aes_(x = substitute(x), y = substitute(y),
group = substitute(group), color = substitute(group)))
}
# create a function to do the summarization
my_smry <- function(x) {
x %>%
summarize(conversion_rate = sum(converted) / sum(approved))
}
# use my function
my_df %>%
plot_lines(x = month, y = conversion_rate, group = category)
我觉得 group_by 处理很不优雅:用 enquo
引用 x
和 group
,然后在另一个里面用 !!
取消引用它们引用函数 quos
,只是在下一行用 !!!
重新取消引用它们,但这是我唯一能够开始工作的东西。有更好的方法吗?
此外,有没有办法让 ggplot 使用 !!
而不是 substitute
?我在做什么感觉不一致。
问题是 ggplot 还没有更新来处理 quosures,所以你必须给它传递表达式,你可以用 rlang::quo_expr
:
library(tidyverse)
set.seed(47)
my_df <- data_frame(month = sample(1:12, 1000, replace = TRUE),
category = sample(head(letters, 3), 1000, replace = TRUE),
approved = as.numeric(runif(1000) < 0.5),
converted = approved * as.numeric(runif(1000) < 0.5))
plot_lines <- function(df, x, y, group) {
x <- enquo(x)
y <- enquo(y)
group <- enquo(group)
df %>%
group_by(!! x, !! group) %>%
summarise(conversion_rate = sum(converted) / sum(approved)) %>%
ggplot(aes_(x = rlang::quo_expr(x),
y = rlang::quo_expr(y),
color = rlang::quo_expr(group))) +
geom_line()
}
my_df %>% plot_lines(month, conversion_rate, category)
但是,请记住,ggplot 几乎不可避免地会从 lazyeval 更新为 rlang,因此虽然此界面可能会继续工作,但可能很快就会出现一个更简单、更一致的界面。
你可以像这样直接eval.parent(substitute(...))
。作为基础 R,它在 R 中始终如一地工作并且很容易做到。甚至可以使用普通的 aes
.
plot_lines <- function(df, x, y, group) eval.parent(substitute(
df %>%
group_by(x, group) %>%
my_smry %>%
ggplot + geom_line(aes(x = x, y = y, group = group, color = group))
))
plot_lines(my_df, month, conversion_rate, category)