Умножить строки матрицы на вектор?

Я оптимизирую функцию и хочу избавиться от медленных циклов. Я ищу более быстрый способ умножить каждую строку матрицы на вектор.

Есть идеи?

РЕДАКТИРОВАТЬ:

Я не ищу "классического" умножения.

Например. У меня есть матрица, которая имеет 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 значительно быстрее, чем все, что предлагается здесь.

Другие вопросы по тегам