从data.table行创建向量而不使用apply

时间:2018-04-04 00:47:31

标签: r data.table

假设我想在data.table中创建一个列,其中每行中的值等于同一行中其他三个单元格中值的标准差。例如,如果我做

DT <- data.table(a = 1:4, b = c(5, 7, 9, 11), c = c(13, 16, 19, 22), d = c(25, 29, 33, 37))
DT
   a  b  c  d
1: 1  5 13 25
2: 2  7 16 29
3: 3  9 19 33
4: 4 11 22 37

我想添加一个包含每行a,b和d标准差的列,如下所示:

   a  b  c  d abdSD
1: 1  5 13 23 12.86
2: 2  7 16 27 14.36
3: 3  9 19 31 15.87
4: 4 11 22 35 17.39

我当然可以写一个for循环或使用apply函数来计算它。不幸的是,我真正想做的事情需要应用于数百万行,不像计算标准偏差那么简单,需要在几分之一秒内完成,所以我真的需要一个矢量化解决方案。我想写点像

DT[, abdSD := sd(c(a, b, d))]

但不幸的是,这没有给出正确的答案。是否有任何data.table语法可以在同一行中创建不同值的向量,并使该向量可以访问填充该行中新单元格的函数?任何帮助将不胜感激。 @Arun

5 个答案:

答案 0 :(得分:2)

根据数据的大小,您可能希望将数据转换为长格式,然后按如下方式计算结果:

complexFunc <- function(x) sd(x)

cols <- c("a", "b", "d")
rowres <- melt(DT[, rn:=.I], id.vars="rn", variable.factor=FALSE)[, 
    list(abdRes=complexFunc(value[variable %chin% cols])), by=.(rn)]
DT[rowres, on=.(rn)]

或者如果您的复杂函数有3个参数,您可以执行类似

的操作
DT[, abdSD := mapply(complexFunc, a, b, d)]

答案 1 :(得分:1)

正如@Frank所提到的,我可以通过执行by=1:nrow(DT)

来避免添加列
DT[, abdSD:=sd(c(a,b,d)),by=1:nrow(DT)]

输出:

   a  b  c  d    abdSD
1: 1  5 13 25 12.85820
2: 2  7 16 29 14.36431
3: 3  9 19 33 15.87451
4: 4 11 22 37 17.38774

如果你添加一个row_name列,那将非常容易

DT$row_id<-row.names(DT)

只需= row_id,就可以得到你想要的结果

DT[, abdSD:=sd(c(a,b,d)),by=row_id]

结果会有:

   a  b  c  d row_id    abdSD
1: 1  5 13 25      1 12.85820
2: 2  7 16 29      2 14.36431
3: 3  9 19 33      3 15.87451
4: 4 11 22 37      4 17.38774

如果您想删除row_id,只需添加[,row_id:=NULL]

即可
DT[, abdSD:=sd(c(a,b,d)),by=row_id][,row_id:=NULL]

此行将获得您想要的一切

   a  b  c  d    abdSD
1: 1  5 13 25 12.85820
2: 2  7 16 29 14.36431
3: 3  9 19 33 15.87451
4: 4 11 22 37 17.38774

你必须按行进行。

data.frame在默认情况下按行执行,data.table在默认情况下按列执行。这有点棘手

希望这有帮助

答案 2 :(得分:0)

我认为你应该试试matrixStats

library(matrixStats)

#sample data
dt <- data.table(a = 1:4, b = c(5, 7, 9, 11), c = c(13, 16, 19, 22), d = c(25, 29, 33, 37))

dt[, `:=`(abdSD = rowSds(as.matrix(.SD), na.rm=T)), .SDcols=c('a','b','d')]
dt

输出为:

   a  b  c  d    abdSD
1: 1  5 13 25 12.85820
2: 2  7 16 29 14.36431
3: 3  9 19 33 15.87451
4: 4 11 22 37 17.38774

答案 3 :(得分:0)

不是答案,只是试图展示使用apply与上面提供的解决方案之间的区别:

我已将样本数据炸成40,000行以显示稳定的时差:

library(matrixStats)

#sample data
dt <- data.table(a = 1:40000, b = rep(c(5, 7, 9, 11),10000), c = rep(c(13, 16, 19, 22),10000), d = rep(c(25, 29, 33, 37),10000))
df <- data.frame(a = 1:40000, b = rep(c(5, 7, 9, 11),10000), c = rep(c(13, 16, 19, 22),10000), d = rep(c(25, 29, 33, 37),10000))

t0 = Sys.time()
dt[, `:=`(abdSD = rowSds(as.matrix(.SD), na.rm=T)), .SDcols=c('a','b','d')]
print(paste("Time taken for data table operation = ",Sys.time() - t0))
# [1] "Time taken for data table operation =  0.117115020751953"


t0 = Sys.time()
df$abdSD <- apply(df[,c("a","b","d")],1, function(x){sd(x)})
print(paste("Time taken for apply opertaion = ",Sys.time() - t0))
# [1] "Time taken for apply opertaion =  2.93488311767578"

使用DTmatrixStats明显赢得比赛

答案 4 :(得分:0)

对于这种情况,sd不易渲染:

vecSD = function(x) {
  n = ncol(x)
  sqrt((n/(n-1)) * (Reduce(`+`, x*x)/n - (Reduce(`+`, x)/n)^2))
}

DT[, vecSD(.SD), .SDcols = c('a', 'b', 'd')]
#[1] 12.85820 14.36431 15.87451 17.38774