如何用简洁方式按命名函数列表对数据框列执行指定变换?
问题描述
我有一个命名函数列表transform_functions,列表的名称对应数据框的列名,每个函数是对应列需要执行的变换。例如transform_functions$height为function(x) x+1,期望实现等价于starwars |> mutate(height = height + 1)的效果,整体需等价于starwars |> mutate(height = height + 1, birth_year = birth_year / 2)的操作。
目前使用purrr::reduce2可实现,但代码可读性差且繁琐;dplyr::across并不适用,因为它是将一组函数应用于多列。请问是否有更简洁的实现方式?
附代码示例:
library(dplyr) library(purrr) transform_functions <- list( height = function(x) x+1, birth_year = function(x) x/2 ) # 可行但可读性差、繁琐的实现 purrr::reduce2( .x = transform_functions, .y = names(transform_functions), .f = function(df, fun, col) df |> mutate(!!col := fun(!!sym(col))), .init = starwars) # 不可行,会将函数赋值给列而非执行变换 starwars |> mutate(!!!transform_functions)
解决方案
方法1:构造表达式后注入
通过purrr::imap遍历函数列表,为每个列生成对应的mutate表达式,再用非标准注入的方式传入mutate:
library(dplyr) library(purrr) library(rlang) # 生成每个列的变换表达式 transform_exprs <- imap(transform_functions, function(fun, col) { expr(!!sym(col) := !!fun(!!sym(col))) }) # 执行变换 starwars |> mutate(!!!transform_exprs)
方法2:简化版reduce实现
保留reduce的思路,但简化代码结构,让逻辑更清晰:
starwars |> reduce(names(transform_functions), ~ .x |> mutate(!!.y := transform_functions[[.y]](!!sym(.y))), .init = _)
方法3:结合map2_dfc的管道写法
利用purrr::map2_dfc批量生成变换后的列,直接传入mutate:
starwars |> mutate( map2_dfc(names(transform_functions), transform_functions, ~ .y(!!sym(.x))) )
方法4:用rlang::exec动态调用mutate
借助rlang::exec动态构建mutate的参数列表,实现更简洁的调用:
starwars |> exec(mutate, !!!imap(transform_functions, ~ expr(!!sym(.y) := !! .x(!!sym(.y)))))
以上几种方法都能实现目标效果,代码可读性和简洁性都优于原有的reduce2实现。
内容的提问来源于stack exchange,提问作者meow
相关产品推荐
相关产品推荐

