首页 > 解决方案 > 避免覆盖 data.table 函数中的现有列

问题描述

我正在编写一个以 data.table 作为参数的函数。data.table 的列名部分指定为参数,但并非所有列名都指定,所有原始列都需要维护。在函数内部,需要将一些列添加到 data.table 中。即使在函数内部复制了 data.table,我也想以一种保证不会覆盖现有列的方式添加这些列。鉴于列名未知,确保我不会覆盖列的最佳方法是什么?

这是一种方法:

#x is a data.table and knownvar is a column name of that data.table
f <- function(x,knownvar){
x <- copy(x)
tempcol <- "z"
while(tempcol %in% names(x))
tempcol <- paste0("i.",tempcol)

tempcol2 <- "q"
while(tempcol2 %in% names(x))
tempcol2 <- paste0("i.",tempcol2)

x[, (tempcol):=3] 
eval(parse(text=paste0("x[,(tempcol2):=",tempcol,"+4]"))) 
x
}

请注意,即使我在这里复制 x,我仍然需要这个过程来提高内存效率。有没有更简单的方法来做到这一点?可能不使用eval(parse(text=?

显然我可以在函数环境中创建一个局部变量(例如向量)(而不是将其显式添加为 data.table 的列),但如果我需要排序/加入 data.table 这将不起作用. 另外,我可能想显式返回一个包含原始变量和新列的 data.table。

标签: rdata.table

解决方案


set这是使用和非标准评估来编写函数的一种方法substitute() + eval()

注 1:如果新列是基于 中的列名newcols(而不是 中的列名knownvar)创建的,则中的字符名将newcols转换为带有as.name()(或等效as.symbol())的符号。

注2:新列newvals只能以合理的顺序添加,即如果列q需要列z,列z应在列之前添加q

library(data.table)

f <- function(x, knownvar) {

  ## remove if x should be modified in-place
  x <- copy(x)

  ## new column names
  newcols <- setdiff(make.unique(c(names(x), c("z", "q"))), names(x))

  ## new column values based on knownvar or new column names
  zcol <- as.name(newcols[1])
  newvals <- list(substitute(3 * knownvar), substitute(zcol + 4))

  for(i in seq_along(newvals)) {
    set(x, j = newcols[i], value = eval(newvals[[i]], envir = x))
  } 

  return(x)

}

## example data
x <- as.data.table(mtcars)
x[, c("q", "q.1") := .(mpg, 2 * mpg)]

head(f(x, mpg))
#>     mpg cyl disp  hp drat    wt  qsec vs am gear carb    q  q.1    z  q.2
#> 1: 21.0   6  160 110 3.90 2.620 16.46  0  1    4    4 21.0 42.0 63.0 67.0
#> 2: 21.0   6  160 110 3.90 2.875 17.02  0  1    4    4 21.0 42.0 63.0 67.0
#> 3: 22.8   4  108  93 3.85 2.320 18.61  1  1    4    1 22.8 45.6 68.4 72.4
#> 4: 21.4   6  258 110 3.08 3.215 19.44  1  0    3    1 21.4 42.8 64.2 68.2
#> 5: 18.7   8  360 175 3.15 3.440 17.02  0  0    3    2 18.7 37.4 56.1 60.1
#> 6: 18.1   6  225 105 2.76 3.460 20.22  1  0    3    1 18.1 36.2 54.3 58.3

推荐阅读