【问题标题】:Efficiently computing a linear combination of data.table columns高效计算 data.table 列的线性组合
【发布时间】:2013-10-17 05:28:10
【问题描述】:

我在 data.table 中有 nc 列,在向量中有 nc 标量。我想获取linear combination 的列,但我不知道我将使用哪些列。 最有效的方法是什么?

设置

require(data.table)
set.seed(1)

n  <- 1e5
nc <- 5
cf <- setNames(rnorm(nc),LETTERS[1:nc])
DT <- setnames(data.table(replicate(nc,rnorm(n))),LETTERS[1:nc])

方法

假设我想使用前四列。我可以手动写:

DT[,list(cf['A']*A+cf['B']*B+cf['C']*C+cf['D']*D)]

我可以想到两种自动方式(在不知道应该全部使用 A-E 的情况下工作):

mycols <- LETTERS[1:4] # the first four columns
DT[,list(as.matrix(.SD)%*%cf[mycols]),.SDcols=mycols]
DT[,list(Reduce(`+`,Map(`*`,cf[mycols],.SD))),.SDcols=mycols]

基准测试

我预计as.matrix 会使第二个选项变慢,并且对Map-Reduce 组合的速度真的没有直觉。

require(rbenchmark)
options(datatable.verbose=FALSE) # in case you have it turned on

benchmark(
    manual=DT[,list(cf['A']*A+cf['B']*B+cf['C']*C+cf['D']*D)],
    coerce=DT[,list(as.matrix(.SD)%*%cf[mycols]),.SDcols=mycols],
    maprdc=DT[,list(Reduce(`+`,Map(`*`,cf[mycols],.SD))),.SDcols=mycols]
)[,1:6]

    test replications elapsed relative user.self sys.self
2 coerce          100    2.47    1.342      1.95     0.51
1 manual          100    1.84    1.000      1.53     0.31
3 maprdc          100    2.40    1.304      1.62     0.75

当我重复benchmark 调用时,相对于手动方法,我的速度会降低 5% 到 40%。

我的申请

这里的尺寸——nlength(mycols)——与我正在使用的尺寸接近,但我将多次运行这些计算,改变系数向量 cf

【问题讨论】:

    标签: r performance linear-algebra data.table


    【解决方案1】:

    这对我来说比你的手动版本快 2 倍:

    Reduce("+", lapply(names(DT), function(x) DT[[x]] * cf[x]))
    
    benchmark(manual = DT[, list(cf['A']*A+cf['B']*B+cf['C']*C+cf['D']*D)],
              reduce = Reduce('+', lapply(names(DT), function(x) DT[[x]] * cf[x])))
    #    test replications elapsed relative user.self sys.self user.child sys.child
    #1 manual          100    1.43    1.744      1.08     0.36         NA        NA
    #2 reduce          100    0.82    1.000      0.58     0.24         NA        NA
    

    要仅迭代 mycols,请将 lapply 中的 names(DT) 替换为 mycols

    【讨论】:

    • 谢谢,进步很大!我会等待将其标记为答案,以防您或其他人更快地提出问题。
    【解决方案2】:

    将此选项添加到您的基准调用中:

    ops = as.matrix(DT) %*% cf
    

    在我的设备上,它比您尝试的矩阵乘法快 30%。

    【讨论】:

    • 谢谢,DWin。我已经编辑了这个问题,以澄清我的用例涉及列的子集。也许我还应该将其更改为使用newcol:=... 来澄清我希望在 data.table 中最后包含该列。我没有找到更快的DT[,list(as.matrix(DT[,mycols,with=FALSE]) %*% cf)]
    • @frank 只需为您不想求和的列用零填充向量。或者,如果列数远大于您感兴趣的列子集,则对数据表进行子集化,转换为矩阵,并使用未填充的向量进行调用。无论如何,我敢打赌,这里的向量化线性代数方法将是最快的方法之一。
    • @ClaytonStanley 这是一个很好的、数学上正确的建议,但我想如果 DT 中的列数远大于我想要使用的列数,它的计算成本可能会很高。
    • @ClaytonStanley 啊,我明白了。我的直觉是矩阵运算在这里是多余的,因为输出不会是矩阵;并且编码一个快速的 Rcpp sweep 然后一个 applysum 将是最快的。目前,我将采用 eddi 的方法,但我很想知道任何表明存在显着改进空间的基准。感谢您考虑一下。 :)
    • @Frank 刚刚注意到您还使用不同的系数向量多次运行此计算。如果是这种情况,我可能会重新研究 DWin 的线性代数方法,并将强制矩阵(或子集,如果系数向量中的列没有变化)乘以系数矩阵。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-30
    • 1970-01-01
    • 2011-01-06
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 2012-12-05
    相关资源
    最近更新 更多