使用 := 将 data.table 列名传递给函数

Pass a data.table column name to a function using :=

此问题 data.table 等同于 Pass a data.frame column name to a function

假设我有一个非常简单的 data.table:

dat <- data.table(x = 1:4,
                  y = 5:8)

现在我想为任何给定函数创建一个新列:

new_column <- function(df,col_name,expr){
    col_name <- deparse(substitute(col_name))
    df[[col_name]] <- eval(substitute(expr),df,parent.frame())
    df
}

以便正确提供:

> new_column (dat,z,x+y)
  x y  z
1 1 5  6
2 2 6  8
3 3 7 10
4 4 8 12

但是,因为它是 data.table,所以我想使用 :=:

创建这个新列
new_column_byref <- function(df,col_name,expr){
   col_name <- deparse(substitute(col_name))
  df[, col_name:=eval(substitute(expr)
                      ,df
                      ,parent.frame()
                      )]
  df
}

但是不行:

> a <- new_column_byref(dat,z,x+y)
 Error: Check that is.data.table(DT) == TRUE. Otherwise, :=, `:=`(...) and let(...) are defined for use in j, once only and in particular ways. See help(":=").

我该如何解决这个问题?谢谢。

new_column_byref <- function(df,col_name,expr){
  col_name <- deparse(substitute(col_name))
  set(df,j=col_name,value=eval(substitute(expr),df,parent.frame()))
}


dat <- data.table(x = 1:4,y = 5:8)

new_column_byref(dat,z,x+y)[]

   x y  z
1: 1 5  6
2: 2 6  8
3: 3 7 10
4: 4 8 12

set是data.table-idiomatic的方式。如果您需要做其他事情,例如使用 byrlang 有一种延迟评估的通用方法,即 enexpr args(如果您希望在原始环境中评估它们,则为 enquo ) 和 !! 它们在 inject 中,使用您通常使用的表达式。

library(rlang)
#> Warning: package 'rlang' was built under R version 4.1.2
library(data.table)
#> 
#> Attaching package: 'data.table'
#> The following object is masked from 'package:rlang':
#> 
#>     :=

dat <- data.table(x = 1:4,
                  y = 5:8)

new_column <- function(df, col_name, expr) {
    col_name <- enexpr(col_name)
    expr <- enexpr(expr)
    inject(df[, !!col_name := !!expr])
}

new_column(dat, z, x + y)

dat
#>        x     y     z
#>    <int> <int> <int>
#> 1:     1     5     6
#> 2:     2     6     8
#> 3:     3     7    10
#> 4:     4     8    12

reprex package (v2.0.1)

于 2022-02-25 创建

或者,同样没有 rlang

library(data.table)

dat <- data.table(x = 1:4,
                  y = 5:8)

new_column <- function(df, col_name, expr) {
    col_name <- deparse(substitute(col_name))
    expr <- substitute(expr)
    df[, (col_name) := eval(expr)]
}

new_column(dat, z, x + y)

dat
#>        x     y     z
#>    <int> <int> <int>
#> 1:     1     5     6
#> 2:     2     6     8
#> 3:     3     7    10
#> 4:     4     8    12

reprex package (v2.0.1)

于 2022-02-25 创建

在开发版中使用“语言编程”界面。

https://rdatatable.gitlab.io/data.table/news/index.html

library(data.table)
dat <- data.table(x = 1:4,
                  y = 5:8)

new_column <- function(df, col_name, expr) {
    df[, col_name := expr, 
       env = list(col_name = substitute(col_name), 
                  expr = substitute(expr))]
}

new_column(dat, z, x + y)

dat
#>        x     y     z
#>    <int> <int> <int>
#> 1:     1     5     6
#> 2:     2     6     8
#> 3:     3     7    10
#> 4:     4     8    12

reprex package (v2.0.1)

于 2022-03-01 创建

或使用match.call

library(data.table)
dat <- data.table(x = 1:4,
                  y = 5:8)

new_column <- function(df, col_name, expr) {
    fun_call <- match.call()
    df[, col_name := expr, 
       env = as.list(fun_call)[c('col_name', 'expr')]]
}

new_column(dat, z, x + y)

dat
#>        x     y     z
#>    <int> <int> <int>
#> 1:     1     5     6
#> 2:     2     6     8
#> 3:     3     7    10
#> 4:     4     8    12

reprex package (v2.0.1)

于 2022-03-01 创建