【问题标题】:Efficiently replicate matrices in R在 R 中有效地复制矩阵
【发布时间】:2012-10-13 15:29:59
【问题描述】:

我有一个矩阵并寻找一种有效的方法来复制它 n 次(其中 n 是数据集中的观察数)。例如,如果我有一个矩阵 A

A <- matrix(1:15, nrow=3)

然后我想要表单的输出

rbind(A, A, A, ...) #n times.

显然,有很多方法可以构建如此大的矩阵,例如使用for 循环或apply 或类似函数。但是,对“矩阵复制函数”的调用发生在我的优化算法的核心中,在我的程序一次运行期间它被调用了数万次。因此,循环、应用类型的函数和任何类似的东西都不够有效。 (这样的解决方案基本上意味着在 n 上循环执行数万次,这显然是低效的。)我已经尝试使用普通的rep 函数,但还没有找到安排输出的方法rep 在所需格式的矩阵中。

解决方案 do.call("rbind", replicate(n, A, simplify=F)) 也太低效了,因为在这种情况下rbind 被使用得太频繁了。 (然后,我的程序总运行时间的大约 30% 用于执行 rbind。)

有人知道更好的解决方案吗?

【问题讨论】:

  • rbind 仅以 do.call 方式使用一次。可能是复制让它陷入困境。
  • 我用Rprofrbind测试它所花费的时间大约是replicate的两倍。我也对此感到惊讶。

标签: r matrix replication


【解决方案1】:

另外两个解决方案:

首先是对问题中例子的修改

do.call("rbind", rep(list(A), n))

第二个涉及展开矩阵、复制它并重新组装它。

matrix(rep(t(A),n), ncol=ncol(A), byrow=TRUE)

既然要求的是效率,那么基准测试是必要的

library("rbenchmark")
A <- matrix(1:15, nrow=3)
n <- 10

benchmark(rbind(A, A, A, A, A, A, A, A, A, A),
          do.call("rbind", replicate(n, A, simplify=FALSE)),
          do.call("rbind", rep(list(A), n)),
          apply(A, 2, rep, n),
          matrix(rep(t(A),n), ncol=ncol(A), byrow=TRUE),
          order="relative", replications=100000)

给出:

                                                 test replications elapsed
1                 rbind(A, A, A, A, A, A, A, A, A, A)       100000    0.91
3                   do.call("rbind", rep(list(A), n))       100000    1.42
5  matrix(rep(t(A), n), ncol = ncol(A), byrow = TRUE)       100000    2.20
2 do.call("rbind", replicate(n, A, simplify = FALSE))       100000    3.03
4                                 apply(A, 2, rep, n)       100000    7.75
  relative user.self sys.self user.child sys.child
1    1.000      0.91        0         NA        NA
3    1.560      1.42        0         NA        NA
5    2.418      2.19        0         NA        NA
2    3.330      3.03        0         NA        NA
4    8.516      7.73        0         NA        NA

所以最快的是原始的rbind 调用,但假设n 是固定的并且提前知道。如果n不固定,那么最快的是do.call("rbind", rep(list(A), n)。这些是针对 3x5 矩阵和 10 次复制的。不同大小的矩阵可能会给出不同的排序。

编辑:

对于 n=600,结果的顺序不同(省略显式 rbind 版本):

A <- matrix(1:15, nrow=3)
n <- 600

benchmark(do.call("rbind", replicate(n, A, simplify=FALSE)),
          do.call("rbind", rep(list(A), n)),
          apply(A, 2, rep, n),
          matrix(rep(t(A),n), ncol=ncol(A), byrow=TRUE),
          order="relative", replications=10000)

给予

                                                 test replications elapsed
4  matrix(rep(t(A), n), ncol = ncol(A), byrow = TRUE)        10000    1.74
3                                 apply(A, 2, rep, n)        10000    2.57
2                   do.call("rbind", rep(list(A), n))        10000    2.79
1 do.call("rbind", replicate(n, A, simplify = FALSE))        10000    6.68
  relative user.self sys.self user.child sys.child
4    1.000      1.75        0         NA        NA
3    1.477      2.54        0         NA        NA
2    1.603      2.79        0         NA        NA
1    3.839      6.65        0         NA        NA

如果包含显式的rbind 版本,它会比do.call("rbind", rep(list(A), n)) 版本稍快,但不会快很多,并且比applymatrix 版本慢。因此,在这种情况下,对任意 n 的泛化不需要损失速度。

【讨论】:

  • 哇,非常感谢!但是,我自己的小基准测试表明,矩阵版本比 rbind 调用更大的 n(比如 n = 600)更有效。在这种情况下,使用matrix(rep(t(...-调用的版本效率最高。
【解决方案2】:

或许这样更有效率:

apply(A, 2, rep, n)

【讨论】:

  • 正如我之前所说,这种方法不会产生正确的结果。自己试试吧:A &lt;- matrix(1:15, nrow=3);n &lt;- 2;rbind(A,A);matrix(rep(A, n), ncol = ncol(A), byrow = TRUE)不一样...编辑:为什么我不能在评论中创建换行符?
  • @WolfgangPößnecker 对不起,我的错误。查看我的答案的更新。
  • 谢谢,这已经是很大的进步了。不过,我仍在寻找更快的解决方案。 ;)
  • 很好的答案,非常简单
【解决方案3】:

还有这种方式:

rep(1, n) %x% A

【讨论】:

  • 如果你想做一个“每个”版本的复制,A 的每一行都重复n 次,那么它很简单:A %x% rep(1, n)
【解决方案4】:

你可以使用索引

A[rep(seq(nrow(A)), n), ]

【讨论】:

    【解决方案5】:

    我来到这里的原因与原始海报相同,并最终更新了@Brian Diggs 比较以包含所有其他发布的答案。希望我做得对:

    #install.packages("rbenchmark")
    library("rbenchmark")
    A <- matrix(1:15, nrow=3)
    n <- 600
    
    benchmark(do.call("rbind", replicate(n, A, simplify=FALSE)),
              do.call("rbind", rep(list(A), n)),
              apply(A, 2, rep, n),
              matrix(rep(t(A),n), ncol=ncol(A), byrow=TRUE),
              A[rep(seq(nrow(A)), n), ],
              rep(1, n) %x% A,
              apply(A, 2, rep, n),
              matrix(rep(as.integer(t(A)),n),nrow=nrow(A)*n,byrow=TRUE),
         order="relative", replications=10000)
    
    #                                                                test replications elapsed relative user.self sys.self user.child sys.child
    #5                                          A[rep(seq(nrow(A)), n), ]        10000    0.32    1.000      0.33     0.00         NA        NA
    #8 matrix(rep(as.integer(t(A)), n), nrow = nrow(A) * n, byrow = TRUE)        10000    0.36    1.125      0.35     0.02         NA        NA
    #4                 matrix(rep(t(A), n), ncol = ncol(A), byrow = TRUE)        10000    0.38    1.188      0.37     0.00         NA        NA
    #3                                                apply(A, 2, rep, n)        10000    0.59    1.844      0.56     0.03         NA        NA
    #7                                                apply(A, 2, rep, n)        10000    0.61    1.906      0.58     0.03         NA        NA
    #6                                                    rep(1, n) %x% A        10000    1.44    4.500      1.42     0.02         NA        NA
    #2                                  do.call("rbind", rep(list(A), n))        10000    1.67    5.219      1.67     0.00         NA        NA
    #1                do.call("rbind", replicate(n, A, simplify = FALSE))        10000    5.03   15.719      5.02     0.01         NA        NA
    

    【讨论】:

      【解决方案6】:

      如何将其转换为数组,复制内容并使用更新的行数创建一个新矩阵?

      A <- matrix(...)
      n = 2 # just a test
      
      a = as.integer(A)
      multi.a = rep(a,n)
      multi.A = matrix(multi.a,nrow=nrow(A)*n,byrow=T)
      

      【讨论】:

      • 我刚试过这个,它和上面的答案有同样的问题:很遗憾,你的建议没有产生正确的结果。
      • 使用as.integer(t(A))。然后它工作:matrix(rep(as.integer(t(A)),n),nrow=nrow(A)*n,byrow=TRUE)
      猜你喜欢
      • 2021-10-06
      • 2011-05-27
      • 2012-02-20
      • 2021-10-10
      • 1970-01-01
      • 2018-05-25
      • 2012-09-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多