核心问题(它似乎归结为)
如何rlang::quo
使用“左”而不是“右”侧引用透明的表达式构造调用
取自的帮助页面rlang::quo
,这有效
quo(foo(!! quo(bar)))
# <quosure: global>
# ~foo(~bar)
虽然这不是:
quo(!! quo(foo)(bar))
# Error in (function (x) : attempt to apply non-function
问题放在更多的背景下
dplyr::mutate
允许“表达式的两边”在两个表达式部分都可以引用透明的意义上是可变的(参见小插图):
library(dplyr)
set.seed(89234)
df <- data.frame(id = rep(1:3, 3), value = rpois(9, 10))
c_id <- as.name("id")
c_value <- as.name("value")
# NOTE: in our prototyping, actual columns names are often subject to
# change (e.g. `id` might become `id_global`), thus I would like to stay
# as flexible as possible in all of my subsequent `dplyr` calls.
my_multiply <- function(x, by) x * by
df %>% mutate(!!c_value := my_multiply(!!c_value, 10))
# id value
# 1 1 70
# 2 2 90
# 3 3 130
# 4 1 80
# 5 2 80
# 6 3 120
# 7 1 140
# 8 2 120
# 9 3 110
我怎样才能在dplyr::filter
能够使列名(“左侧”)引用透明/灵活的焦点中实现相同/相似的东西。
我想理想地结束这样的事情(伪代码):
v_id <- 1
df %>% filter(!!c_id :== v_id)
我试过的
我知道这dplyr::filter
与dplyr::mutate
他们期望的表达式类型不同。所以基于小插图我想出了这个版本,其中要评估的整个表达式作为参数传递:
my_filter <- function(x, expr) {
quo_expr <- enquo(expr)
print(quo_expr)
x %>% filter(!!quo_expr)
}
v_id <- 1
my_filter(df, id == v_id)
# <quosure: global>
# ~id == v_id
# id value
# 1 1 7
# 2 1 8
# 3 1 14
但是,当我想使用参考时,这“迫使”我真正使用实际的列名c_id
:
my_filter(df, c_id == v_id)
# <quosure: global>
# ~c_id == v_id
# [1] id value
# <0 rows> (or 0-length row.names)
我基本上不知道如何构造调用,dplyr::quo
或者dplyr::enquo
左侧部分包含列名的评估引用,而右侧部分包含要评估的逻辑查询的**未评估*引用:
my_filter <- function(x, left, right) {
quo_expr <- quo(quo(!!left) == right)
print(quo_expr)
x %>% filter(!!quo_expr)
}
my_filter(df, c_id, v_id)
# <quosure: frame>
# ~quo(id) >= right
# [1] id value
# <0 rows> (or 0-length row.names)
换句话说,我认为 quosure 最终应该是~id == right
,我不知道该怎么做