【问题标题】:Creating a vector from data.table row without using apply从 data.table 行创建向量而不使用 apply
【发布时间】:2018-04-04 00:47:30
【问题描述】:

假设我想在 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 语法可以从同一行中的不同值创建一个向量,并使该向量可供填充该行中的新单元格的函数访问?任何帮助将不胜感激。 @阿伦

【问题讨论】:

标签: r data.table


【解决方案1】:

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

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)]

【讨论】:

    【解决方案2】:

    正如@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 默认按列执行。这有点棘手

    希望对你有帮助

    【讨论】:

    • 仅供参考,您也可以在不创建新列的情况下使用by=1:nrow(DT)
    • 谢谢弗兰克!我想知道该怎么做。我做了by=nrow(DT),但没有用。原来1:nrow(DT)
    【解决方案3】:

    我觉得你应该试试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
    

    【讨论】:

      【解决方案4】:

      不是答案,只是想说明使用 apply 和上面 Prem 提供的解决方案之间的区别:

      我已将样本数据扩大到 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 显然会赢得比赛

      【讨论】:

        【解决方案5】:

        在这种情况下矢量化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
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-07-29
          • 2020-05-04
          • 2021-12-04
          • 2020-12-23
          • 2017-06-14
          • 2021-12-13
          • 2015-12-15
          • 1970-01-01
          相关资源
          最近更新 更多