在R数据表中创建参数化函数

jv4diomz  于 2023-04-27  发布在  其他
关注(0)|答案(2)|浏览(92)

这个问题与one有关。
我需要一个参数化函数来实现以下代码:

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]

预期的结果是在所选列中获得加倍的数字,如上面的代码所示。
我试过:

change_value <- function(dt_, fun, regex, column_name) {
  #browser()
  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")

有趣的是,在调试时,get(column_name) |> str_extract(get(regex)) |> fun()的评估似乎是正确的(246 2)。
我该怎么解决这个问题?
PS〉我也尝试了不同的方法(通过data.table编程),没有成功:

change_value <- function(dt_, fun, regex, column_name) {
  #browser()
  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)
}
r8uurelv

r8uurelv1#

错误不发生在第一遍,只发生在第二遍(和随后的),

dt
#           text       text2
#         <char>      <char>
# 1: AAA 123 BBB AAA 123 BBB
# 2:       1 CCC       1 CCC
change_value(dt, function(x) x |>
  as.numeric ()|>
  double_number(), "regex_identify_num", "text")[]
#           text       text2 column_name
#         <char>      <char>      <char>
# 1: AAA 123 BBB AAA 123 BBB AAA 246 BBB
# 2:       1 CCC       1 CCC       2 CCC
dt
#           text       text2 column_name
#         <char>      <char>      <char>
# 1: AAA 123 BBB AAA 123 BBB AAA 246 BBB
# 2:       1 CCC       1 CCC       2 CCC
change_value(dt, function(x) x |>
  as.numeric ()|>
  double_number(), "regex_identify_num", "text")[]
# Error in get(column_name) : first argument has length > 1

部分原因是,由于data.table的引用语义,您的dt被修改了 * 就地 *。也许这是设计好的。但是当您这样做时,非标准求值的行为有点不同:虽然get(column_name)位于data.table命名空间中,但column_name既作为函数 * 中的向量存在,又作为框架中的列存在,即上面的第三列。
我们可以通过使用data.table..varname语义来解决这个问题。

change_value <- function(dt_, fun, regex, column_name) {
  #browser()
  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")[]
#           text       text2 column_name
#         <char>      <char>      <char>
# 1: AAA 123 BBB AAA 123 BBB AAA 246 BBB
# 2:       1 CCC       1 CCC       2 CCC
change_value(dt, function(x) x |>
  as.numeric ()|>
  double_number(), "regex_identify_num", "text")[]
#           text       text2 column_name
#         <char>      <char>      <char>
# 1: AAA 123 BBB AAA 123 BBB AAA 246 BBB
# 2:       1 CCC       1 CCC       2 CCC

(重复运行不会出错)。
另一种选择是将参数和新列命名为不同的名称。
Factoid:..-referencing在data.table::[j=部分工作,但在i=中不工作。

gg0vcinb

gg0vcinb2#

基于r2evans的观察,关键点是在将结果返回给column_name时,将column_name括在parentesis中:

dt_[, (column_name) := mapply(function(x, y) gsub(get(regex), x, y, perl = T), new_column, get(column_name))]

通过这样做,我们避免了名称冲突,并且可以跳过..
然后完整的代码变为:

change_value <- function(dt_, fun, regex, column_name) {
  #browser()
  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)
}

相关问题