data.table 中从列名字符串向量生成多列的最佳方法
Optimal way in data.table to make multiple columns from vectors of column name strings
我目前正在试验 data.table 并寻找 'optimal' 做事的方法。
我想在下面的示例中做的是有一个包含列名的字符串,为正常增量附加“.d”,为百分比增量附加“.dP”。 (请记住,字符串中的列只是一个子集,而不是完整的 data.table,即使我的示例仅限于这些。)
我希望代码尽可能紧凑和快速,尽可能多地利用 R 和 data.table 的优点。
我目前想到的解决方案是这样的:
percentDelta<-function(newvalue,basevalue){
return((newvalue-basevalue)/basevalue)
}
normalDelta<-function(newvalue,basevalue){
return(newvalue-basevalue)
}
DT = data.table(price=c(2,3,4,5,6,7,8), otherMetric=c(3,4,5,1,3,4,5))
deltaColsNames = c("otherMetric")
deltaColsNewNames <- paste0(deltaColsNames,'.d')
percentColsNewNames <- paste0(deltaColsNames,'.dP')
DT[,eval(deltaColsNewNames) := lapply(DT[,eval(deltaColsNames),with=F],normalDelta,price)]
DT[,eval(percentColsNewNames) := lapply(DT[,eval(deltaColsNames),with=F],percentDelta,price)]
我不太确定 data.table 生成多列的调用是否正确?将 "lapply" 与 "eval" 一起使用是解决此问题的方式吗?
编辑:我应该避免使用 "with=F" 吗?
DT[,eval(deltaColsNewNames) := lapply(DT[,which(names(DT) %in% deltaColsNames)],normalDelta,price)]
DT[,eval(percentColsNewNames) := lapply(DT[,which(names(DT) %in% deltaColsNames)],percentDelta,price)]
所以为了回答问题并添加评论中的优化,出现了以下答案:
require(data.table) #version 1.9.5 from github needed!
normalDelta<-function(newvalue,basevalue){
return(newvalue-basevalue)
}
DT = data.table(price=rep(c(3,4,5),each=200000000), otherMetric=sample(c(1,3,6),200000000,T))
deltaColsNames = c("otherMetric")
deltaColsNewNames <- paste0(deltaColsNames,'.d')
场景 1,使用 "eval" 和 "with=F":
system.time(DT[,(deltaColsNewNames) := lapply(DT[,eval(deltaColsNames),with=F],normalDelta,price)])
# user system elapsed
#2.134 1.747 3.880
场景 2,使用 "which(names) %in%" 避免字符串作为列索引:
system.time(DT[,(deltaColsNewNames) := lapply(DT[,which(names(DT) %in% deltaColsNames)],normalDelta,price)])
#user system elapsed
#1.652 1.105 2.756
场景 3,在 1.9.5 中使用“.SD”语法和 eval()(在 1.9.4 中,速度较慢):
system.time(DT[,(deltaColsNewNames) := lapply(.SD[, eval(deltaColsNames),with=F], normalDelta, price)])
#user system elapsed
#2.148 1.847 4.764
场景 4,在 1.9.5 中使用“.SD”语法和 which()(在 1.9.4 中,这也较慢):
system.time(DT[,(deltaColsNewNames) := lapply(.SD[, which(names(DT) %in% deltaColsNames)], normalDelta, price)])
#user system elapsed
#1.701 1.117 2.817
场景 5,使用 mget():
system.time(DT[, (deltaColsNewNames) := lapply(mget(deltaColsNames), normalDelta, price)])
#user system elapsed
#1.426 1.166 2.591
场景 6:mget 和 .SD 组合:
system.time(DT[, (deltaColsNewNames) := lapply(.SD[, mget(deltaColsNames)], normalDelta, price)])
#user system elapsed
#2.149 1.788 4.974
更新:增加数据集的大小后:
情景 2、4 和 5 即将出现。
然而,场景 5 的内存占用比 2 和 4 高得多,因为我 运行 在使用更大的数据集测试时遇到笔记本电脑上的内存问题
(请参阅上面的更新结果)
问题 #495 is solved now with this recent commit,我们现在可以做到这一点:
require(data.table) # v1.9.7+
DT[, (deltaColsNewNames) := lapply(.SD, normalDelta, price), .SDcols=deltaColsNames]
我目前正在试验 data.table 并寻找 'optimal' 做事的方法。
我想在下面的示例中做的是有一个包含列名的字符串,为正常增量附加“.d”,为百分比增量附加“.dP”。 (请记住,字符串中的列只是一个子集,而不是完整的 data.table,即使我的示例仅限于这些。)
我希望代码尽可能紧凑和快速,尽可能多地利用 R 和 data.table 的优点。
我目前想到的解决方案是这样的:
percentDelta<-function(newvalue,basevalue){
return((newvalue-basevalue)/basevalue)
}
normalDelta<-function(newvalue,basevalue){
return(newvalue-basevalue)
}
DT = data.table(price=c(2,3,4,5,6,7,8), otherMetric=c(3,4,5,1,3,4,5))
deltaColsNames = c("otherMetric")
deltaColsNewNames <- paste0(deltaColsNames,'.d')
percentColsNewNames <- paste0(deltaColsNames,'.dP')
DT[,eval(deltaColsNewNames) := lapply(DT[,eval(deltaColsNames),with=F],normalDelta,price)]
DT[,eval(percentColsNewNames) := lapply(DT[,eval(deltaColsNames),with=F],percentDelta,price)]
我不太确定 data.table 生成多列的调用是否正确?将 "lapply" 与 "eval" 一起使用是解决此问题的方式吗?
编辑:我应该避免使用 "with=F" 吗?
DT[,eval(deltaColsNewNames) := lapply(DT[,which(names(DT) %in% deltaColsNames)],normalDelta,price)]
DT[,eval(percentColsNewNames) := lapply(DT[,which(names(DT) %in% deltaColsNames)],percentDelta,price)]
所以为了回答问题并添加评论中的优化,出现了以下答案:
require(data.table) #version 1.9.5 from github needed!
normalDelta<-function(newvalue,basevalue){
return(newvalue-basevalue)
}
DT = data.table(price=rep(c(3,4,5),each=200000000), otherMetric=sample(c(1,3,6),200000000,T))
deltaColsNames = c("otherMetric")
deltaColsNewNames <- paste0(deltaColsNames,'.d')
场景 1,使用 "eval" 和 "with=F":
system.time(DT[,(deltaColsNewNames) := lapply(DT[,eval(deltaColsNames),with=F],normalDelta,price)])
# user system elapsed
#2.134 1.747 3.880
场景 2,使用 "which(names) %in%" 避免字符串作为列索引:
system.time(DT[,(deltaColsNewNames) := lapply(DT[,which(names(DT) %in% deltaColsNames)],normalDelta,price)])
#user system elapsed
#1.652 1.105 2.756
场景 3,在 1.9.5 中使用“.SD”语法和 eval()(在 1.9.4 中,速度较慢):
system.time(DT[,(deltaColsNewNames) := lapply(.SD[, eval(deltaColsNames),with=F], normalDelta, price)])
#user system elapsed
#2.148 1.847 4.764
场景 4,在 1.9.5 中使用“.SD”语法和 which()(在 1.9.4 中,这也较慢):
system.time(DT[,(deltaColsNewNames) := lapply(.SD[, which(names(DT) %in% deltaColsNames)], normalDelta, price)])
#user system elapsed
#1.701 1.117 2.817
场景 5,使用 mget():
system.time(DT[, (deltaColsNewNames) := lapply(mget(deltaColsNames), normalDelta, price)])
#user system elapsed
#1.426 1.166 2.591
场景 6:mget 和 .SD 组合:
system.time(DT[, (deltaColsNewNames) := lapply(.SD[, mget(deltaColsNames)], normalDelta, price)])
#user system elapsed
#2.149 1.788 4.974
更新:增加数据集的大小后: 情景 2、4 和 5 即将出现。 然而,场景 5 的内存占用比 2 和 4 高得多,因为我 运行 在使用更大的数据集测试时遇到笔记本电脑上的内存问题 (请参阅上面的更新结果)
问题 #495 is solved now with this recent commit,我们现在可以做到这一点:
require(data.table) # v1.9.7+
DT[, (deltaColsNewNames) := lapply(.SD, normalDelta, price), .SDcols=deltaColsNames]