Умножить строки матрицы на вектор?
Я оптимизирую функцию и хочу избавиться от медленных циклов. Я ищу более быстрый способ умножить каждую строку матрицы на вектор.
Есть идеи?
РЕДАКТИРОВАТЬ:
Я не ищу "классического" умножения.
Например. У меня есть матрица, которая имеет 23 столбца и 25 строк, а вектор имеет длину 23. В результате я хочу иметь матрицу 25x23, в которой каждая строка умножается на вектор.
7 ответов
Я думаю, что вы ищете sweep()
,
> (mat <- matrix(rep(1:3,each=5),nrow=3,ncol=5,byrow=TRUE))
[,1] [,2] [,3] [,4] [,5]
[1,] 1 1 1 1 1
[2,] 2 2 2 2 2
[3,] 3 3 3 3 3
> vec <- 1:5
> sweep(mat,MARGIN=2,vec,`*`)
[,1] [,2] [,3] [,4] [,5]
[1,] 1 2 3 4 5
[2,] 2 4 6 8 10
[3,] 3 6 9 12 15
Это была одна из основных функций R, хотя с годами в нее были внесены улучшения.
> MyMatrix <- matrix(c(1,2,3, 11,12,13), nrow = 2, ncol=3, byrow=TRUE)
> MyMatrix
[,1] [,2] [,3]
[1,] 1 2 3
[2,] 11 12 13
> MyVector <- c(1:3)
> MyVector
[1] 1 2 3
Вы можете использовать либо:
> t(t(MyMatrix) * MyVector)
[,1] [,2] [,3]
[1,] 1 4 9
[2,] 11 24 39
или же:
> MyMatrix %*% diag(MyVector)
[,1] [,2] [,3]
[1,] 1 4 9
[2,] 11 24 39
На самом деле, sweep
не самый быстрый вариант на моем компьютере:
MyMatrix <- matrix(c(1:1e6), ncol=1e4, byrow=TRUE)
MyVector <- c(1:1e4)
Rprof(tmp <- tempfile(),interval = 0.001)
t(t(MyMatrix) * MyVector) # first option
Rprof()
MyTimerTranspose=summaryRprof(tmp)$sampling.time
unlink(tmp)
Rprof(tmp <- tempfile(),interval = 0.001)
MyMatrix %*% diag(MyVector) # second option
Rprof()
MyTimerDiag=summaryRprof(tmp)$sampling.time
unlink(tmp)
Rprof(tmp <- tempfile(),interval = 0.001)
sweep(MyMatrix ,MARGIN=2,MyVector,`*`) # third option
Rprof()
MyTimerSweep=summaryRprof(tmp)$sampling.time
unlink(tmp)
Rprof(tmp <- tempfile(),interval = 0.001)
t(t(MyMatrix) * MyVector) # first option again, to check order
Rprof()
MyTimerTransposeAgain=summaryRprof(tmp)$sampling.time
unlink(tmp)
MyTimerTranspose
MyTimerDiag
MyTimerSweep
MyTimerTransposeAgain
Это дает:
> MyTimerTranspose
[1] 0.04
> MyTimerDiag
[1] 40.722
> MyTimerSweep
[1] 33.774
> MyTimerTransposeAgain
[1] 0.043
Помимо самой медленной опции, вторая опция достигает предела памяти (2046 МБ). Однако, учитывая оставшиеся варианты, двойная транспозиция кажется намного лучше, чем sweep
по-моему.
редактировать
Просто повторяю меньшие данные несколько раз:
MyMatrix <- matrix(c(1:1e3), ncol=1e1, byrow=TRUE)
MyVector <- c(1:1e1)
n=100000
[...]
for(i in 1:n){
# your option
}
[...]
> MyTimerTranspose
[1] 5.383
> MyTimerDiag
[1] 6.404
> MyTimerSweep
[1] 12.843
> MyTimerTransposeAgain
[1] 5.428
Для скорости можно создать матрицу из вектора перед умножением
mat <- matrix(rnorm(1e6), ncol=1e4)
vec <- c(1:1e4)
mat * matrix(vec, dim(mat)[1], length(vec))
library(microbenchmark)
microbenchmark(
transpose = t(t(mat) * vec),
make_matrix = mat * matrix(vec, dim(mat)[1], length(vec)),
sweep = sweep(mat,MARGIN=2,vec,`*`))
#Unit: milliseconds
# expr min lq mean median uq max neval
# transpose 10.554500 11.459181 14.344774 11.917376 16.235005 86.74867 100
# make_matrix 4.104297 4.537571 6.338247 4.767895 8.537845 13.52492 100
# sweep 8.332441 8.743813 13.396708 9.175766 14.843811 83.42181 100
Если вам нужна скорость, вы можете использовать
Rfast::eachrow
. Это самый быстрый из всех...
Вот еще один вариант:
X <- matrix(rep(1:3, 5), nrow = 3)
v <- 1:5
X * v[col(X)]
[,1] [,2] [,3] [,4] [,5]
[1,] 1 2 3 4 5
[2,] 2 4 6 8 10
[3,] 3 6 9 12 15
Это работает для любой операции, а не только для умножения.
Эти решения с использованием
outer()
или же
collapse::TRA()
are значительно быстрее, чем все, что предлагается здесь.