在R中与Matlab的bsxfun(@times,...,...)等价的函数是什么?

4

R是否有与Matlabbsxfun(@times,a,b)等效的功能?假设想要在矩阵a,b上执行逐元素乘法

Matlab:

a=[1 0 3 -4];
b=[0 1 5 7; 2 9 -3 4];

bsxfun(@times,a,b) = [0 0 15 -28; 2 0 -9 -16]

R:

a<-c(1,0,3,-4)
b<-matrix(c(0,2,1,9,5,-3,7,4),nrow = 2,ncol = 4)

a * b = matrix(c(0,0,3,-36,5,0,21,-16),nrow = 2,ncol = 4)

你有没有想过 R 是如何得到上述a*b的结果的,因为我本来期望它与Matlab中的 bsxfun(@times,a,b) 完全相同。

编辑:

bsxfun("*",repmat(a,2,1),b) # using R {pracma}

最好的


只是为了澄清:实际上(使用基本的R方法)回答问题的答案实际上在下面被接受的答案的_注释_中:使用sweep函数。 - Hugo Raguet
(它在2016年被添加到答案正文中。) - IRTFM
3个回答

2
使用列主元矩阵进行操作,因为这是R语言的惯用法:
> b<-matrix(c(0,2,1,9,5,-3,7,4),nrow = 4,ncol = 2)
> a*b
     [,1] [,2]
[1,]    0    5
[2,]    0    0
[3,]    3   21
[4,]  -36  -16

如果你使用原始的构造 b ,当你尝试使用 sweep时,你会遇到一个不太愉快的惊喜:
> b2<-matrix(c(0,2,1,9,5,-3,7,4),nrow = 2,ncol = 4)
> sweep(b2, 2, a, '*')
     [,1] [,2] [,3] [,4]
[1,]    0    0   15  -28
[2,]    2    0   -9  -16

由于matrix函数使用列主要填充位置,且您在其调用中未指定byrow=TRUE,因此b矩阵与您的Matlab矩阵不同。

> b3<-matrix(c(0,2,1,9,5,-3,7,4),nrow = 2,ncol = 4, byrow=TRUE)
> sweep(b3, 2, a, '*')
     [,1] [,2] [,3] [,4]
[1,]    0    0    3  -36
[2,]    5    0   21  -16

好的,谢谢你的建议,它符合我的期望。然而,对于“这就是R规范”的说法,人们可能会感到有些不够满足。祝好! - owner
作为 R 约定的替代方案,我已经编辑了这篇文章,并提供了一个我一直在研究的解决方案,供您参考。 - owner
如果你想使用基础R函数实现的话,可以使用sweep操作。我会发一个例子。(这并不是反对pkg:pracma。它是一个富有有用函数的重要来源,已经存在了很长时间并且经过了充分的测试。) - IRTFM
感谢您提供sweep的另一种选择。最好的祝福, - owner
R和包是开源的。显然,没有人想到通知'pracma'的维护者有更好的版本或提供一些代码。好的老习惯去哪里了? - Hans W.
@HansW:感谢你的反讽式赞美。我并没有想象自己能够提出比pkg:pracma作者更好的编程策略。这个包里有很多宝藏,我对作者的整体才华非常敬佩。我特别喜欢vectorfield - IRTFM

2

从执行速度的角度来看,“bsxfun”和“sweep”两种方法都不是最优的。实际上,“bsxfun”的性能非常差,对于非玩具应用程序似乎毫无用处。

在我的基准测试中,最快的方法是:

matrix(a, ncol = n_col, nrow = nsample, byrow = TRUE) * b

sweep() 在矩阵大小增加时变得更有竞争力,但即使对于中等大小的矩阵(1e6-by-4),它所需的时间也是双倍的。

以下是完整的基准测试结果


# test of recycling efficiency

rm(list=ls())

library(microbenchmark)
library(pracma)

a = c(1,0,3,-4)
n_col = 4

# make example more realistic by expanding number of rows of b
nsample = 1e3
b = repmat(matrix(c(0,2,1,9,5,-3,7,4),nrow = 2,ncol = n_col), nsample / 2, 1)

print(microbenchmark(
  erg_1 = matrix(a, ncol = n_col,  nrow = nsample, byrow = TRUE) * b,
  erg_2 = matrix(rep.int(a, nsample), nrow = nsample, ncol = n_col, byrow = TRUE) * b,
  erg_3 = matrix(a * c(t(b)), nrow = nsample, ncol = n_col, byrow = TRUE),
  erg_4 = sweep(b, 2, a, '*'),
  erg_5 = bsxfun('*', repmat(a, nsample, 1), b) 
  ))


#same as above but now larger matrices
nsample = 1e6
b = repmat(matrix(c(0,2,1,9,5,-3,7,4),nrow = 2,ncol = n_col), nsample / 2, 1)

print(microbenchmark(
  erg_1 = matrix(a, ncol = n_col,  nrow = nsample, byrow = TRUE) * b,
  erg_2 = matrix(rep.int(a, nsample), nrow = nsample, ncol = n_col, byrow = TRUE) * b,
  erg_3 = matrix(a + c(t(b)), nrow = nsample, ncol = n_col, byrow = TRUE),
  erg_4 = sweep(b, 2, a, '*')
  #erg_5 = bsxfun('*', repmat(a, nsample, 1), b) #bsxfun is non-competitive
))

>Unit: microseconds
  expr      min        lq       mean    median        uq      max neval
 erg_1    9.057   10.1135   11.93394   11.0195   12.6790   36.226   100
 erg_2   14.189   15.3970   18.75324   16.9060   19.9250   41.358   100
 erg_3   26.263   28.8295   35.04538   30.9430   34.8675   86.941   100
 erg_4   40.452   44.0750   56.88289   51.4705   66.4130  109.279   100
 erg_5 2694.827 2968.4755 3243.76025 3208.9185 3417.5125 5575.306   100
>Unit: milliseconds
  expr      min       lq     mean   median       uq      max neval
 erg_1 10.85538 11.30668 20.58625 12.93408 13.28290 69.17918   100
 erg_2 16.07206 18.00058 29.17394 18.24751 20.09845 75.30993   100
 erg_3 22.41231 24.58957 30.83620 24.99544 26.49047 79.71910   100
 erg_4 20.74838 21.53673 29.52071 22.88867 23.30420 81.07150   100

R和其包是开源的。显然,没有人想到通知“pracma”的维护者有更好的版本或提供一些代码。好习惯都去哪了? - Hans W.

0

更新:确实存在一些问题,例如sweep在处理(1,10,2)这样的形状时会出现错误。因此,我又写了另一个基于sweep的实现。由于重新转换的原因,速度有所降低,但仍然比之前快4倍。该实现已经上传到github存储库bsxfun.R(基准测试取自@g g)

我想缩写要被扫描的MARGIN,但是pracma::bsxfun()没有使用sweep()。因此,我编写了以下代码(请参见github链接),希望没有错误。

标签:R,pracma,数组


R和其包是开源的。显然,没有人想到通知“pracma”的维护者有更好的版本或提供一些代码。好习惯都去哪了? - Hans W.
作为一名新手程序员,我现在大多数问题都在 StackOverflow 上提问。也许我们应该鼓励包维护者监控这个论坛以寻求可能的改进。 - shouldsee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接