【问题标题】:Efficiently compute the row sums of a 3d array in R有效地计算 R 中 3d 数组的行和
【发布时间】:2011-02-27 19:36:33
【问题描述】:

考虑数组a

> a <- array(c(1:9, 1:9), c(3,3,2))
> a
, , 1

     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9

, , 2

     [,1] [,2] [,3]
[1,]    1    4    7
[2,]    2    5    8
[3,]    3    6    9

我们如何有效地计算第三维索引的矩阵的行和,使得结果是:

     [,1] [,2]
[1,]   12   12
[2,]   15   15
[3,]   18   18

??

通过colSums()'dims' 参数可以轻松实现列总和:

> colSums(a, dims = 1)

但我找不到在数组上使用rowSums() 来实现所需结果的方法,因为它对'dims' 的解释与colSums() 的解释不同。

使用以下方法计算所需的行总和很简单:

> apply(a, 3, rowSums)
     [,1] [,2]
[1,]   12   12
[2,]   15   15
[3,]   18   18

但这只是隐藏循环。还有其他有效的、真正矢量化的方法来计算所需的行总和吗?

【问题讨论】:

    标签: arrays r rowsum


    【解决方案1】:

    @Fojtasek 的回答提到拆分数组让我想起了aperm() 函数,它允许置换数组的维度。由于colSums() 有效,我们可以使用aperm() 交换前两个维度并在输出上运行colSums()

    > colSums(aperm(a, c(2,1,3)))
         [,1] [,2]
    [1,]   12   12
    [2,]   15   15
    [3,]   18   18
    

    这个和其他建议的基于 R 的答案的一些比较时间:

    > b <- array(c(1:250000, 1:250000),c(5000,5000,2))
    > system.time(rs1 <- apply(b, 3, rowSums))
       user  system elapsed 
      1.831   0.394   2.232 
    > system.time(rs2 <- rowSums3d(b))
       user  system elapsed 
      1.134   0.183   1.320 
    > system.time(rs3 <- sapply(1:dim(b)[3], function(i) rowSums(b[,,i])))
       user  system elapsed 
      1.556   0.073   1.636
    > system.time(rs4 <- colSums(aperm(b, c(2,1,3))))
       user  system elapsed 
      0.860   0.103   0.966 
    

    所以在我的系统上,aperm() 解决方案看起来稍微快了一点:

    > sessionInfo()
    R version 2.12.1 Patched (2011-02-06 r54249)
    Platform: x86_64-unknown-linux-gnu (64-bit)
    

    但是,rowSums3d() 给出的答案与其他解决方案不同:

    > all.equal(rs1, rs2)
    [1] "Mean relative difference: 0.01999992"
    > all.equal(rs1, rs3)
    [1] TRUE
    > all.equal(rs1, rs4)
    [1] TRUE
    

    【讨论】:

    • 太棒了,我什至不知道有一个 aperm() 函数!对我来说有趣的是 rs1 和 rs3 不一样,因为我的输出看起来很相似。在某个时候将不得不对此进行调查:)
    • 啊,我现在看到问题了,你需要去掉 rs3 中的“[3]”。我只是用它来获取最终经过的时间,然后我可以运行 summary() 以解决运行间波动:)
    • @Tony,当然!我已经更新了这个效果的答案,再次感谢您的建议。
    • 嗯,在没有任何进一步的答案的情况下,我将接受我自己的,因为它是提供的解决方案中最快的,而来自@Fojtasek 的答案甚至不会产生所要求的答案虽然它把我带到了aperm()。如果有人提出比aperm() 版本更好的东西,我会更改接受的答案。
    • 哦,我的...为什么没有类似 R 数组的 numpy 接口-.-
    【解决方案2】:

    您可以将数组分成二维,计算行总和,然后按照您想要的方式将输出重新组合在一起。像这样:

    rowSums3d <- function(a){
        m <- matrix(a,ncol=ncol(a))
        rs <- rowSums(m)
        matrix(rs,ncol=2)
    }
    
    > a <- array(c(1:250000, 1:250000),c(5000,5000,2))
    > system.time(rowSums3d(a))
       user  system elapsed 
       1.73    0.17    1.96 
    > system.time(apply(a, 3, rowSums))
       user  system elapsed 
       3.09    0.46    3.74 
    

    【讨论】:

    • 谢谢,但您的功能有一些不妥之处。 i) 创建z 的行似乎没有在其他任何地方使用,并且ii) 函数参数是xa 始终使用。
    • 糟糕!这就是我在交互式会话和单独窗口中在不同想法之间跳来跳去的结果。我已经编辑了这些问题。
    • 您为此使用了哪个平台和版本的 R?对于您的代码,我在我的机器上获得了不同的相对时间(请参阅下面的答案)。仅在差异是由于平台/R版本引起的情况下询问。
    • 感谢您更新您的函数,但是,在我的系统上,rowSums3d() 不会产生与 apply() 相同的结果 - 请参阅下面的答案。
    【解决方案3】:

    我不知道最有效的方法,但sapply 似乎做得很好

    a <- array(c(1:9, 1:9), c(3,3,2))
    x1 <- sapply(1:dim(a)[3], function(i) rowSums(a[,,i]))
    x1
         [,1] [,2]
    [1,]   12   12
    [2,]   15   15
    [3,]   18   18
    
    x2 <- apply(a, 3, rowSums)
    all.equal(x1, x2)
    [1] TRUE
    

    这样可以提高速度,如下所示:

    > a <- array(c(1:250000, 1:250000),c(5000,5000,2))
    
    > summary(replicate(10, system.time(rowSums3d(a))[3]))
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      2.784   2.799   2.810   2.814   2.821   2.862 
    
    > summary(replicate(10, system.time(apply(a, 3, rowSums))[3]))
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      2.730   2.755   2.766   2.776   2.788   2.839 
    
    > summary(replicate(10, system.time( sapply(1:dim(a)[3], function(i) rowSums(a[,,i])) )[3]))
       Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
      1.840   1.852   1.867   1.872   1.893   1.914 
    

    时间安排在:

    # Ubuntu 10.10
    # Kernal Linux 2.6.35-27-generic
    > sessionInfo()
    R version 2.12.1 (2010-12-16)
    Platform: x86_64-pc-linux-gnu (64-bit)
    

    【讨论】:

    • 感谢您的回答。但是,您的 sapply() 代码有些问题,因为它似乎返回了一个标量值。请参阅我的答案以获取显示返回值与 apply() 不同的比较代码。
    • @Gavin 在您的回答中查看我的评论,标量值的原因是因为您的比较不需要“[3]” :)
    【解决方案4】:

    如果您有一个多核系统,您可以编写一个简单的 C 函数并使用 Open MP 并行线程库。我为我的一个问题做了类似的事情,我在 8 核系统上得到了 8 倍的增长。该代码仍然可以在单处理器系统上运行,甚至可以在没有 OpenMP 的系统上编译,可能会到处乱加#ifdef _OPENMP。

    当然,如果您知道大部分时间都需要这样做,那么它当然值得这样做。请在优化之前分析您的代码。

    【讨论】:

    • R 中的多核应用系列函数再简单不过了(实际上可能是这样,但让我们想象它不可能)。使用snowfall,在你初始化你的核心之后,你会做sfSapply(...),然后你就设置好了。
    • 当您可以使用colSums() 在快速 R 代码中执行类似操作时,似乎会遇到很多麻烦。 rowSums() 采用与 'dims' 不同的方法有点令人沮丧,但我希望我在使用 rowSums() 时忽略了一些事情。此外,多线程的速度需要显着提高,以克服调度和管理线程/进程的成本。
    • 在我的可能性代码中,它执行类似于 rowSums 的操作,我得到了 8 倍的加速 - 这是每天完成几件事与每两天完成一件事之间的区别!非常值得付出近乎零的努力(我首先在 R 中编写了整个代码,然后在 C 中实现了 10 倍的加速,添加了 OpenMP 以实现最终的 80 倍加速)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-10
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 2011-01-06
    • 1970-01-01
    相关资源
    最近更新 更多