在R data.table中创建参数化函数时遇错误的解决方法
参数化实现data.table文本列数字翻倍
需求背景
需要编写一个参数化函数,实现对data.table指定列中的数字提取后翻倍,再将翻倍后的数字替换回原文本的逻辑。原始逻辑代码如下:
dt <- data.table( text = c("AAA 123 BBB", "1 CCC") , text2 = c("AAA 123 BBB", "1 CCC")) double_number <- function(x) x*2 regex_identify_num <- "\\d+" # 提取数字、翻倍、替换回原文本 dt[, calc_value := text |> str_extract(regex_identify_num) |> as.numeric() |> double_number()] dt[, text := mapply(function(x,y) gsub(regex_identify_num, x, y, perl = T), calc_value, text)] dt[, calc_value:=NULL]
预期结果:指定列中的数字翻倍,例如"AAA 123 BBB"变为"AAA 246 BBB","1 CCC"变为"2 CCC"。
问题现象
尝试编写参数化函数时遇到错误:
第一次尝试代码
change_value <- function(dt_, fun, regex, column_name) { dt_[, new_column := get(column_name) |> str_extract(get(regex)) |> fun()] dt_[, column_name := mapply(function(x, y) gsub(get(regex), x, y, perl = T), new_column, get(column_name))] dt_[, new_column := NULL] return(dt) } change_value(dt, function(x) x |> as.numeric() |> double_number(), "regex_identify_num", "text")
报错信息:
Error in get(column_name) : first argument has length > 1
调试发现get(column_name) |> str_extract(get(regex)) |> fun()计算结果正确,但赋值列名和mapply中的变量作用域存在问题。
第二次尝试代码(NSE方式)
change_value <- function(dt_, fun, regex, column_name) { dt_[, new_column:= column_name |> str_extract(regex) |> fun() , env = list( fun = substitute(fun) , column_name = substitute(column_name) , regex = substitute(regex) ) ] dt_[, column_name := mapply(function(x, y) gsub(regex, x, y, perl = T), new_column, column_name)] dt_[, new_column := NULL] return(dt) }
同样未成功执行。
修复方案
方案1:修正字符串列名赋值与变量作用域
change_value <- function(dt_, fun, regex, column_name) { # 计算翻倍后的值,直接使用函数参数regex,无需get dt_[, new_column := get(column_name) |> str_extract(regex) |> fun()] # 用(column_name)实现字符串列名的赋值,同时将regex传入匿名函数 dt_[, (column_name) := mapply(function(x, y, r) gsub(r, x, y, perl = TRUE), new_column, get(column_name), MoreArgs = list(r = regex))] # 删除临时列 dt_[, new_column := NULL] return(dt_) } # 调用示例 dt <- data.table( text = c("AAA 123 BBB", "1 CCC") , text2 = c("AAA 123 BBB", "1 CCC")) double_number <- function(x) x*2 regex_identify_num <- "\\d+" change_value(dt, function(x) x |> as.numeric() |> double_number(), regex_identify_num, "text")
关键修复点:
- 赋值列名时使用
(column_name),让data.table将字符串参数识别为列名,而非字面量。 mapply通过MoreArgs传入regex,避免匿名函数中get(regex)的作用域错误。
方案2:使用data.table非标准求值(NSE)风格
change_value <- function(dt_, fun, regex, column_name) { # 捕获传入的表达式 col_sym <- substitute(column_name) fun_expr <- substitute(fun) regex_expr <- substitute(regex) # 计算临时列,用eval在data.table环境中解析列名和正则 dt_[, new_column := do.call("|>", list( do.call("|>", list(str_extract(eval(col_sym), eval(regex_expr)), as.numeric)), fun_expr ))] # 替换原列,同样用eval处理列名 dt_[, (col_sym) := mapply(function(x, y, r) gsub(r, x, y, perl = TRUE), new_column, eval(col_sym), MoreArgs = list(r = eval(regex_expr)))] dt_[, new_column := NULL] return(dt_) } # 调用时可直接传列名(无需加引号) change_value(dt, function(x) x*2, regex_identify_num, text)
关键修复点:
- 使用
substitute捕获传入的列名、函数、正则表达式的表达式。 - 通过
eval在data.table的计算环境中解析这些表达式,符合data.table的NSE编程习惯。
内容的提问来源于stack exchange,提问作者Fabio Correa
相关产品推荐
相关产品推荐

