web-dev-qa-db-fra.com

Moyen le plus rapide de multiplier les colonnes de la matrice avec des éléments vectoriels en R

J'ai une matrice m et un vecteur v. Je voudrais multiplier la première colonne de la matrice m par le premier élément du vecteur v et multiplier la deuxième colonne de la matrice m par le deuxième élément du vecteur v et ainsi de suite. Je peux le faire avec le code suivant, mais je cherche un moyen qui n'exige pas les deux appels de transposition. Comment puis-je faire cela plus vite dans R?

m <- matrix(rnorm(120000), ncol=6)
v <- c(1.5, 3.5, 4.5, 5.5, 6.5, 7.5)

system.time(t(t(m) * v))

#   user  system elapsed 
#   0.02    0.00    0.02 
32
rose

Si vous avez un plus grand nombre de colonnes, votre solution t(t(m) * v) surpasse largement la solution de multiplication de matrice. Cependant, il existe une solution plus rapide, mais elle entraîne un coût élevé d’utilisation de la mémoire. Vous créez une matrice aussi grande que m en utilisant rep () et multipliez élément par élément. Voici la comparaison, modifiant l'exemple de mnel:

m = matrix(rnorm(1200000), ncol=600)
v = rep(c(1.5, 3.5, 4.5, 5.5, 6.5, 7.5), length = ncol(m))
library(microbenchmark)

microbenchmark(t(t(m) * v), 
  m %*% diag(v),
  m * rep(v, rep.int(nrow(m),length(v))), 
  m * rep(v, rep(nrow(m),length(v))), 
  m * rep(v, each = nrow(m)))

# Unit: milliseconds
#                                   expr        min         lq       mean     median         uq       max neval
#                            t(t(m) * v)  17.682257  18.807218  20.574513  19.239350  19.818331  62.63947   100
#                          m %*% diag(v) 415.573110 417.835574 421.226179 419.061019 420.601778 465.43276   100
#  m * rep(v, rep.int(nrow(m), ncol(m)))   2.597411   2.794915   5.947318   3.276216   3.873842  48.95579   100
#      m * rep(v, rep(nrow(m), ncol(m)))   2.601701   2.785839   3.707153   2.918994   3.855361  47.48697   100
#             m * rep(v, each = nrow(m))  21.766636  21.901935  23.791504  22.351227  23.049006  66.68491   100

Comme vous pouvez le constater, utiliser "each" dans rep () sacrifie la vitesse pour plus de clarté. La différence entre rep.int et rep semble être négligeable, les deux implémentations échangent leurs places sur des exécutions répétées de microbenchmark (). N'oubliez pas que ncol (m) == longueur (v). 

 autoplot

17
bluegrue

Comme @Arun le souligne, je ne pense pas que vous allez battre votre solution en termes de gain de temps. En termes de compréhensibilité du code, il existe cependant d'autres options:

Une option:

> mapply("*",as.data.frame(m),v)
      V1  V2  V3
[1,] 0.0 0.0 0.0
[2,] 1.5 0.0 0.0
[3,] 1.5 3.5 0.0
[4,] 1.5 3.5 4.5

Et un autre:

sapply(1:ncol(m),function(x) m[,x] * v[x] )
3
thelatemail

Par souci d’exhaustivité, j’ai ajouté sweep au repère. Malgré ses noms d'attributs quelque peu trompeurs, je pense qu'il peut être plus lisible que d'autres alternatives, et aussi assez rapide:

n = 1000
M = matrix(rnorm(2 * n * n), nrow = n)
v = rnorm(2 * n)

microbenchmark::microbenchmark(
  M * rep(v, rep.int(nrow(M), length(v))),
  sweep(M, MARGIN = 2, STATS = v, FUN = `*`),
  t(t(M) * v),
  M * rep(v, each = nrow(M)),
  M %*% diag(v)
)

Unit: milliseconds
                                       expr         min          lq        mean
    M * rep(v, rep.int(nrow(M), length(v)))    5.259957    5.535376    9.994405
 sweep(M, MARGIN = 2, STATS = v, FUN = `*`)   16.083039   17.260790   22.724433
                                t(t(M) * v)   19.547392   20.748929   29.868819
                 M * rep(v, each = nrow(M))   34.803229   37.088510   41.518962
                              M %*% diag(v) 1827.301864 1876.806506 2004.140725
      median          uq        max neval
    6.158703    7.606777   66.21271   100
   20.479928   23.830074   85.24550   100
   24.722213   29.222172   92.25538   100
   39.920664   42.659752  106.70252   100
 1986.152972 2096.172601 2432.88704   100
2
mbiron

Comme le fait Bluegrue, un simple représentant suffirait également pour effectuer la multiplication élément par élément. 

Le nombre de multiplications et de sommations est considérablement réduit, comme si une simple multiplication de matrice avec diag() était effectuée, ce qui permet d'éviter beaucoup de multiplications nulles.

m = matrix(rnorm(1200000), ncol=6)
v=c(1.5, 3.5, 4.5, 5.5, 6.5, 7.5)
v2 <- rep(v,each=dim(m)[1])
library(microbenchmark)
microbenchmark(m %*% diag(v), t(t(m) * v), m*v2)

Unit: milliseconds
          expr       min        lq      mean    median        uq      max neval cld
 m %*% diag(v) 11.269890 13.073995 16.424366 16.470435 17.700803 95.78635   100   b
   t(t(m) * v)  9.794000 11.226271 14.018568 12.995839 15.010730 88.90111   100   b
        m * v2  2.322188  2.559024  3.777874  3.011185  3.410848 67.26368   100  a 
1
Druss2k