将变量名称作为参数传递给data.table

时间:2019-08-06 15:27:08

标签: r data.table

我正在尝试创建一个修改data.table的函数,并想使用一些非标准的评估,但是我意识到我并不真正知道如何在data.tables中使用它。 我的功能基本上是这样的:

do_stuff <- function(dt, col) {
  copy(dt)[, new_col := some_fun(col)][]
}

,我想这样称呼它:

do_stuff(data, column)

“ column”是“ data”内部存在的列的名称。如果我运行该函数,则会出现错误:

#> Error in some_fun(col) : object 'column' not found 

对我说,data.table显然正在将正确的名称传递给函数(“列”),但由于某种原因,找不到它。这是一个最小的可重现示例

library(data.table)

data <- data.table(x = 1:10, y = rnorm(10))

plus <- function(x, y) {
   x + y
}

add_one <- function(data, col) {
   copy(data)[, z := plus(col, 1)][]
}

add_one(data, y)
#> Error in plus(col, 1): object 'y' not found

不幸的是,使用deparse(substitute(col))似乎无效:(

add_one <- function(data, col) {
   copy(data)[, z := plus(deparse(substitute(col)), 1)][]
}

add_one(data, y)
#> Error in x + y: non-numeric argument to binary operator

5 个答案:

答案 0 :(得分:13)

通常,引用和评估将起作用:

library(data.table)
plus <- function(x, y) {
   x + y
}

add_one <- function(data, col) {
   expr0 = quote(copy(data)[, z := plus(col, 1)][])

   expr  = do.call(substitute, list(expr0, list(col = substitute(col))))
   cat("Evaluated expression:\n"); print(expr); cat("\n")

   eval(expr)
}

set.seed(1)
library(magrittr)
data.table(x = 1:10, y = rnorm(10)) %>% 
   add_one(y)

给出

Evaluated expression:
copy(data)[, `:=`(z, plus(y, 1))][]

     x          y         z
 1:  1 -0.6264538 0.3735462
 2:  2  0.1836433 1.1836433
 3:  3 -0.8356286 0.1643714
 4:  4  1.5952808 2.5952808
 5:  5  0.3295078 1.3295078
 6:  6 -0.8204684 0.1795316
 7:  7  0.4874291 1.4874291
 8:  8  0.7383247 1.7383247
 9:  9  0.5757814 1.5757814
10: 10 -0.3053884 0.6946116

答案 1 :(得分:2)

一种选择是使用deparse(substitute将未加引号的参数提取为字符串,并在.SDcols中指定

add_one <- function(data, col) {
   copy(data)[, z := plus(.SD[[1]], 1), .SDcols = deparse(substitute(col))][]
 }

add_one(data, y)
#     x           y          z
# 1:  1  0.50269855  1.5026986
# 2:  2 -0.33022414  0.6697759
# 3:  3  0.57517246  1.5751725
# 4:  4  1.09928586  2.0992859
# 5:  5  0.84683311  1.8468331
# 6:  6 -1.42023443 -0.4202344
# 7:  7  0.04539331  1.0453933
# 8:  8  0.11870596  1.1187060
# 9:  9 -1.11735007 -0.1173501
#10: 10 -1.94834136 -0.9483414

或使用get

add_one <- function(data, col) {
   copy(data)[, z := plus(get(deparse(substitute(col)))][]
 }

或使用tidyverse

library(tidyverse)
add_one <- function(data, col, col2) {
   data %>%
         dplyr::mutate(z =plus({{col}}, {{col2}}))
  }

add_one(data, x, y)
#    x           y         z
#1   1 -0.53389875 0.4661013
#2   2  1.28743777 3.2874378
#3   3 -1.26674091 1.7332591
#4   4  0.95017120 4.9501712
#5   5  0.06741833 5.0674183
#6   6 -0.70212949 5.2978705
#7   7 -0.38003803 6.6199620
#8   8 -0.50941072 7.4905893
#9   9  0.54055720 9.5405572
#10 10 -0.87486953 9.1251305

答案 2 :(得分:2)

另一个选项,引用列名并使用get

add_one <- function(data, col) {
  copy(data)[, z := plus(get(col), 1)][]
}

add_one(data, "y")

答案 3 :(得分:2)

虽然可能更容易出错,但是您可以依靠...参数。

data <- data.table(x = 1:10, y = rnorm(10))

plus <- function(x, y) {
  x + y
}

add_one <- function(data, ...) {
  copy(data)[, z:= plus(data[, ...], 1)][]
}

add_one(data, y)

#or
library(dplyr)
data.table(x = 1:10, y = rnorm(10))%>%
  add_one(y)

     x           y          z
 1:  1 -1.29851891 -0.2985189
 2:  2 -1.36494928 -0.3649493
 3:  3  0.38282492  1.3828249
 4:  4  1.24578886  2.2457889
 5:  5  1.12897695  2.1289770
 6:  6 -0.80122005  0.1987800
 7:  7  1.89093661  2.8909366
 8:  8 -0.34525212  0.6547479
 9:  9 -0.07070159  0.9292984
10: 10 -1.94145962 -0.9414596

不幸的是,将其扩展到多个变量将导致失败。不过,您仍然可以使用...来发挥自己的优势。

add_one2 <- function(data, ...){
  copy(data)[...][]
}
add_one2(data, , z:=plus(y, 1))

     x          y          z
 1:  1 -0.1565010  0.8434990
 2:  2  0.6516824  1.6516824
 3:  3  0.5355833  1.5355833
 4:  4  0.1941661  1.1941661
 5:  5  0.2994167  1.2994167
 6:  6 -2.5681215 -1.5681215
 7:  7 -1.4587147 -0.4587147
 8:  8  0.9375132  1.9375132
 9:  9  1.3984343  2.3984343
10: 10 -0.6498709  0.3501291

答案 4 :(得分:1)

我的Rnotebooks的摘录...时间序列数据的整理/分析。我将这种模式用于我自己的用于包装data.table和与时间序列相关的包(即xts)的包。

# Non-standard evaluation & wrapper for data.table

data <- data.table(a = 1:2, b = 3:4)    

## Non-in-place update - - - - - - - - -

do_something <- function(data, col) {
  col <- eval( substitute(col), data )
  data[ , col + 123]
}
data %>% do_something(a)

## In-place update without copies (fast, memory efficient) - - - - - - - - -

# Minimalistic example        
do_something <- function(data, col) {
  col <- eval( substitute(col), data )
  data[ , new_col := col + 123]
}
data %>% do_something(a)   # print `data` to see results

# More example
# optional multi-assignment %<-% operator from {zeallot} for cleaner syntax    
my_func <- function(x, y) x + y

do_something <- function(data, col_1, col_2, col_name) {
  c(col_1, col_2) %<-% map(
    c( substitute(col_1), substitute(col_2) ), ~ eval( ., data )
  )
  data[ , (col_name) := my_func(col_1, col_2)]
}
data %>% do_something(a, b, 'new_col_name')

# Advanced example
# ...