我想编写一个函数,在其中构建几个新列。我在函数内部data.table
的地址和命名方面遇到了一些问题。说我有:
library(data.table)
DT <- as.data.table(iris)
并说我有兴趣以相同的方式为不同的列创建新变量(比如说Sepal.Length
,Sepal.Width
和Petal.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.Width
和Petal.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.Length
和ratio_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"))
答案 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])
}