"向量化"矩阵乘法

3
假设我有两个矩阵x和y,它们的维度都是100x2。我想创建一个列表,使得对于x和y的每一行,我都有矩阵t(x) %*% y。例如,通过使用for循环:
x = matrix(rnorm(10), nrow = 5)
y = matrix(rnorm(10), nrow = 5)
myList = list()
for(i in 1:5){
    myList[[i]] = t(x[i, , drop = FALSE]) %*% y[i, ]
}

有没有更有效的方法来进行这个计算?我尝试着想出一个将其表达为矩阵乘法的方法,但是一直没有成功。我也考虑过使用mapply,但似乎需要将x和y转换为向量列表而不是矩阵才能使用mapply,我对这种方法的正确性持怀疑态度。


我认为这个代码应该是 Map(function(x,y) matrix(x,ncol=1)%*%y , split(x, row(x)), split(y, row(y))) - akrun
1
你应该预分配 mylist 对象,这将使你的 for 循环方法明显更快。使用 mylist = vector("list", 5) - talat
3个回答

5

使用 Map 的一种方式

Map(function(x,y) matrix(x,ncol=1)%*%y ,
               split(x, row(x)), split(y, row(y))) 

2
我无法想象这比循环更快,虽然编码可能更加简洁。 - thelatemail
2
Map(tcrossprod, split(x, row(x)), split(y, row(y))) - Roland

4

您可以使用以下方法缩短(并可能稍微加快)代码:

NewList <- list()
for (i in 1:nrow(x)) NewList[[i]] <- outer(x[i,],y[i,])
#> all.equal(NewList,myList)
#[1] TRUE

或者,等价地,
for (i in 1:nrow(x)) NewList[[i]] <- x[i,] %o% y[i,]

3

看起来使用地图是最好的方法:

library(rbenchmark)

x = matrix(rnorm(10000), nrow = 5000)
y = matrix(rnorm(10000), nrow = 5000)
myList = list()

loopTest = function(){
    for(i in 1:nrow(x)){
        myList[[i]] = t(x[i, , drop = FALSE]) %*% y[i, ]
    }
}

loopTest2 = function(){
    for(i in 1:nrow(x)){
        myList[[i]] = outer(x[i, ], y[i, ])
    }
}

mapTest = function(){
    Map(function(x,y) matrix(x,ncol=1)%*%y ,
                   split(x, row(x)), split(y, row(y))) 
}

mapplyTest = function(){
    mapply(function(x,y) matrix(x,ncol=1)%*%y,
           x = split(x, row(x)), y = split(y, row(y))) 
}

benchmark(loopTest(), mapTest(), mapplyTest(), replications = 100)

这给了我:
test        elapsed
loopTest()   10.471
loopTest2()  12.225
mapplyTest()  3.100
mapTest()     2.252

然而,在数据集较小的情况下,例如只有5行时,循环方法确实更胜一筹。

1
感谢测试。看起来使用outer()并不能提高速度。 - RHertel

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