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

如何按序将函数列表全部应用到R语言DataFrame?

问题描述

首先创建如下test数据框:

test <- data.frame(  
  a = c(1, "two", "na", 4),
  b = c(1, 2, 3, 4),
  c = c(1, "nA", 3, 4),
  d = c("na", NA, TRUE, "5*"),
  e = c(3, 4, 0.02, 1.5)
)

通过add.filters函数生成包含两个replace操作的函数列表f:

add.filters <- function(...){
  rlang::enquos(...)
}

f <- add.filters(replace(test, test ==3 , 300), replace(test, test ==4 , 400))

查看f的结构:

<list_of<quosure>>

[[1]]
<quosure>
expr: ^replace(test, test == 3, 300)
env:  global

[[2]]
<quosure>
expr: ^replace(test, test == 4, 400)
env:  global

使用apply.filters函数按序应用操作:

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

apply.filters(test,f)
#     a   b   c    d      e
# 1   1   1   1   na   3.00
# 2 two   2  nA <NA> 400.00
# 3  na   3   3 TRUE   0.02
# 4 400 400 400   5*   1.50

结果仅最后一个replace操作生效(4被替换为400,但3未被替换为300),需要让列表中所有函数按序执行,得到预期的DataFrame。


问题原因与解决方案

原因分析

你定义的f列表里,每个quosure都硬编码了原始的test数据框,而非引用迭代过程中动态更新的df参数。第一个replace操作实际是在原始test上执行,结果没有传递到下一个步骤;第二个操作同样基于原始test执行,最终只有最后一个操作的结果被保留。

修改方案

方法1:用占位符指代当前数据框

调整add.filters的调用,用.作为当前数据框的占位符,替代固定的test:

# 重新定义过滤列表
f <- add.filters(replace(., . ==3 , 300), replace(., . ==4 , 400))

此时f的结构变为:

<list_of<quosure>>

[[1]]
<quosure>
expr: ^replace(., . == 3, 300)
env:  global

[[2]]
<quosure>
expr: ^replace(., . == 4, 400)
env:  global

保持apply.filters不变,执行后即可得到预期结果:

apply.filters(test,f)
#     a   b   c    d      e
# 1   1   1   1   na 300.00
# 2 two   2  nA <NA> 400.00
# 3  na 300 300 TRUE   0.02
# 4 400 400 400   5*   1.50

方法2:直接传递接收数据框的函数

如果希望逻辑更直观,可以将每个过滤操作定义为接收df参数的函数:

# 修改add.filters为接收函数列表
add.filters <- function(...){
  list(...)
}

# 定义每个过滤函数,明确接收当前数据框
f <- add.filters(
  function(df) replace(df, df ==3 , 300),
  function(df) replace(df, df ==4 , 400)
)

# 修改apply.filters直接调用函数
apply.filters <- function(df, list.filtre) {
  purrr::reduce(list.filtre, function(df, filt) {
    filt(df)
  }, .init = df)
}

apply.filters(test,f)
# 同样得到预期结果

补充说明

  • 第一种方法利用rlang中.作为数据上下文占位符的特性,eval_tidy执行时会将.绑定到当前的df。
  • 第二种方法直接传递函数,每个函数明确处理输入数据并返回修改结果,避免了quosure的环境绑定问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 00:45:39