You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用purrr::reduce()将表达式列表应用于任意数据框?

表达式列表适配任意数据框的通用实现方案

问题背景

我用rlang::enexprs()生成了一组处理数据框的表达式列表,想把这组表达式依次应用到任意数据框上,最终得到处理后的单一数据框。但自己写的apply.expr()函数有问题:

  • 当函数参数命名为df时,执行结果不符合预期
  • 改成data后,只能适配命名为data的数据框,没法通用到其他命名的数据集

现有尝试代码

初始版本(参数为df,结果不符合预期)

select.expr <- function(...) rlang::enexprs(...)

apply.expr <- function(df, filtre.list) { 
    eval_df <- function(df, expr) rlang::eval_tidy(expr, data = df)
    purrr::reduce(filtre.list, eval_df, .init = df) 
}

测试示例

data <- data.frame(a = c(1, 1, 1, 1),
                   b = c(2, 2, 2, 2),
                   c = c(3, 3, 3, 3))

expr <- select.expr(replace(data, data == 2, "two"), 
                    replace(data, data == 3, "three"))

apply.expr(data, expr)
##   a b     c
## 1 1 2 three
## 2 1 2 three
## 3 1 2 three
## 4 1 2 three

修改参数名后的版本(仅适配data数据框)

apply.expr <- function(df, filtre.list) { 
    eval_df <- function(data, expr) rlang::eval_tidy(expr, data = data)
    purrr::reduce(filtre.list, eval_df, .init = df) 
}

apply.expr(data, expr)
##   a   b     c
## 1 1 two three
## 2 1 two three
## 3 1 two three
## 4 1 two three

问题根源

核心问题是表达式里硬编码了数据框的名称(比如data),执行eval_tidy时,会优先去创建表达式的环境中查找这个硬编码的变量,而不是使用apply.expr传入的动态数据框参数。修改参数名为data只是刚好匹配了硬编码的名称,换个数据框名就失效了。

通用解决方案

方案1:创建表达式时使用占位符(推荐)

修改select.expr,让表达式用占位符(比如.)代替具体的数据框名,然后在apply.expr中动态绑定占位符到当前处理的数据框:

# 生成表达式列表,用`.`作为数据框占位符
select.expr <- function(...) rlang::enexprs(...)

# 通用的表达式应用函数
apply.expr <- function(df, filtre.list) {
  purrr::reduce(filtre.list, function(current_df, expr) {
    # 把表达式中的`.`绑定到当前迭代的数据集
    rlang::eval_tidy(expr, data = list(. = current_df))
  }, .init = df)
}

测试验证(适配任意命名的数据框)

# 测试命名为test_df的数据框
test_df <- data.frame(a = c(1, 1), b = c(2, 2), c = c(3, 3))
# 创建表达式时用`.`代替具体数据框名
expr <- select.expr(replace(., . == 2, "two"), 
                    replace(., . == 3, "three"))

apply.expr(test_df, expr)
##   a   b     c
## 1 1 two three
## 2 1 two three

方案2:动态替换表达式中的硬编码数据框名

如果已经有了硬编码数据框名的表达式,可以在apply.expr中动态替换这个名称为传入的数据框:

apply.expr <- function(df, filtre.list, original_name = rlang::sym("data")) {
  eval_df <- function(current_df, expr) {
    # 替换表达式中的硬编码名称为当前数据集
    modified_expr <- rlang::expr_replace(expr, original_name, rlang::enexpr(current_df))
    rlang::eval_tidy(modified_expr)
  }
  purrr::reduce(filtre.list, eval_df, .init = df)
}

# 使用示例:表达式里硬编码的是data
apply.expr(test_df, expr)
# 如果表达式里硬编码的是other_df,指定original_name参数
# apply.expr(test_df, expr, original_name = rlang::sym("other_df"))

说明

方案1更灵活,不需要关心表达式对应的原始数据框名称,只要用统一的占位符就能适配任意数据框;方案2适合处理已经存在的、硬编码了数据框名的表达式列表。

内容的提问来源于stack exchange,提问作者ian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 10:20:38