R:如何在函数内创建和寻址data.table的列

时间:2016-06-16 10:31:37

标签: r data.table

我想编写一个函数,在其中构建几个新列。我在函数内部data.table的地址和命名方面遇到了一些问题。说我有:

library(data.table)
DT <- as.data.table(iris)

并说我有兴趣以相同的方式为不同的列创建新变量(比如说Sepal.LengthSepal.WidthPetal.Length)。我想要一个函数,它按照新的列中的物种对所有观察值求和,然后将Petal.Width的每个观测值的比率乘以该总和。如果我指定列的名称,我有一个工作示例:

thisworks <- function(a){
DT[,named_column1:=sum(eval(a),na.rm=T),by=Species]
DT[,named_column2:=named_column1/Petal.Width]
DT
}
DT <- thisworks(DT[,Sepal.Length])

但是,如果我想对其他变量(Sepal.WidthPetal.Length执行此操作,总共产生6个新列),我想基于此确定列名。我的非工作尝试:

thisdoesntwork <- function(b){
name1 <- paste0("total_",names(b))
#here, I don't know how to get the name of the column
DT[,assign(name1,sum(eval(column_of_interest),na.rm=T)),by=Species]
name2 <- paste0("ratio_",names(b))
DT[,assign(name2,named_column1/Petal.Width)]
DT
}

for (i in c("Sepal.Length", "Sepal.Width", "Petal.Length"){
# I know I know, loops are evil. However, data.table is fast so i dont mind
DT <- thisdoesntwork(DT[,i,with=F])
}

一般提示也非常感谢。构建两个函数是否更明智,例如,每个任务一个?或者我应该在一个大data.table过滤器中编写整个函数?我认为这也可行,不是吗?

编辑:第二种情况:使用两个不同的列创建两个新变量

(罗兰兹编辑回答)

说我想要ratio_Length=Sepal.Length/Petal.Lengthratio_Width=Sepal.Width/Petal.Width。为什么这不起作用?:

DT <- as.data.table(iris)

myfun <- function(d,variables){
d[, paste0("ratio_",substr(variables,7,99)) := 
mapply(.SD, function(x) x / mget(paste0("Petal.",substr(variables,7,99))),
.SDcols = variables)]
d[]
}
DT <- myfun(DT,c("Sepal.Length","Sepal.Width"))

2 个答案:

答案 0 :(得分:6)

没有理由分配功能的结果。无论如何:=通过引用分配。如果你想复制data.table,你需要在函数的开头做一个明确的copy

您可以使用.SD.SDcols。有关详细信息,请参阅data.table vignettes

thisworks <- function(d, cols){
  d[, paste0(cols, 1) := lapply(.SD, sum, na.rm = TRUE), by=Species, .SDcols = cols]
  d[, paste0(cols, 2) := lapply(.SD, function(x) x / d[["Petal.Width"]]), 
     .SDcols = paste0(cols, 1)]
  d[]
}
library(data.table)
DT <- as.data.table(iris)
thisworks(DT, c("Sepal.Length", "Sepal.Width", "Petal.Length"))
print(DT)

根据您的后续问题进行修改:

myfun <- function(d,variables){
  d[, gsub(".*\\.", "Ratio.", variables) := 
      Map("/", mget(variables), mget(gsub(".*\\.", "Petal.", variables)))] 
#might be more efficient to do 
#Map(function(x, y) get(x)/get(y), variables, gsub(".*\\.", "Petal.", variables))
#do some benchmarks
  d[]
}
myfun(DT,c("Sepal.Length","Sepal.Width"))

我只能重申:调用函数时不要分配。传递给它的data.table通过引用进行更改。

对于不熟悉上述gsub()第一个参数的用户,请参阅?regex寻求帮助。

答案 1 :(得分:1)

在回答问题的第一部分时,我提出了这个问题。它更接近作者自己尝试的解决方案,我个人更喜欢这种语法。它使用for循环,但作者说他不介意。

library(data.table)
thisworks <- function(dat,var){
  name1 <- paste0("total.",var)
  name2 <- paste0("ratio.",var)
  DT[,c(name1) := sum(eval(dat[,var,with=F]),na.rm=T),by=Species][,c(name2) := DT[,name1,with=F]/Petal.Width]
}

DT <- as.data.table(iris)
cols <- c("Sepal.Length", "Sepal.Width", "Petal.Length")
for (i in 1:length(cols)){
thisworks(DT,cols[i])
}