it-swarm.com.de

Schnellster Weg, um Matrixspalten mit Vektorelementen in R zu multiplizieren

Ich habe eine Matrix m und einen Vektor v. Ich möchte die erste Spalte der Matrix m mit dem ersten Element des Vektors v multiplizieren und die zweite Spalte der Matrix m mit dem zweiten Element des Vektors v usw. multiplizieren. Ich kann es mit dem folgenden Code machen, aber ich suche nach einem Weg, der die beiden Transponierungsaufrufe nicht erfordert. Wie kann ich das in R schneller machen?

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

Verwenden Sie eine lineare Algebra und führen Sie eine Matrixmultiplikation durch, die in R recht schnell ist.

z.B

m %*% diag(v)

einige Benchmarking

m = matrix(rnorm(1200000), ncol=6)

 v=c(1.5, 3.5, 4.5, 5.5, 6.5, 7.5)
library(microbenchmark)
microbenchmark(m %*% diag(v), t(t(m) * v))
##    Unit: milliseconds
##            expr      min       lq   median       uq      max neval
##   m %*% diag(v) 16.57174 16.78104 16.86427 23.13121 109.9006   100
##     t(t(m) * v) 26.21470 26.59049 32.40829 35.38097 122.9351   100
40
mnel

Wenn Sie eine größere Anzahl von Spalten haben, übertrifft Ihre Lösung t(t(m) * v) die Matrixmultiplikationslösung mit großem Abstand. Es gibt jedoch eine schnellere Lösung, die jedoch mit hohen Speicherkosten verbunden ist. Sie erstellen eine Matrix in der Größe m mit rep () und multiplizieren elementweise. Hier ist der Vergleich, der das Beispiel von mnel modifiziert:

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

Wie Sie sehen, bringt die Verwendung von "each" in rep () der Übersichtlichkeit halber Geschwindigkeit bei. Der Unterschied zwischen rep.int und rep scheint vernachlässigbar, beide Implementierungen tauschen bei wiederholten Mikrobenchmarkierungen () die Positionen aus. Denken Sie daran, dass ncol (m) == Länge (v) ist. 

 autoplot

17
bluegrue

Wie @Arun feststellt, weiß ich nicht, dass Sie Ihre Lösung in Bezug auf Zeiteffizienz schlagen werden. In Bezug auf die Verständlichkeit von Code gibt es jedoch andere Optionen:

Eine 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

Und ein anderer:

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

Der Vollständigkeit halber habe ich der Benchmark sweep hinzugefügt. Trotz der etwas irreführenden Attributnamen denke ich, dass es lesbarer als andere Alternativen ist und auch ziemlich schnell:

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

Wie bei Bluegrue würde auch eine einfache Wiederholung ausreichen, um eine elementweise Multiplikation durchzuführen. 

Die Anzahl der Multiplikationen und Summierungen wird um ein großes Maß reduziert, als ob eine einfache Matrixmultiplikation mit diag() durchgeführt würde, wobei für diesen Fall viele Nullmultiplikationen vermieden werden können.

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

Sparse Matrizen

Wenn Sie mit spärlichen Matrizen arbeiten, ist es wahrscheinlich, dass elementweise Methoden (d. H. Das Erweitern von v zu einer dichten Matrix) die Speicherbeschränkungen überschreiten. Daher schließe ich diese vorgeschlagenen Methoden von dieser Analyse aus.

Das Matrix -Paket bietet eine Diagonal -Methode, die den diagonalen Multiplikationsansatz erheblich verbessert.

Benchmarks

library(Matrix)
library(microbenchmark)

N_ROW = 5000
N_COL = 10000

M <- rsparsematrix(N_ROW, N_COL, density=0.1)
v <- rnorm(N_COL)

microbenchmark(
  M %*% Diagonal(length(v), v),
  t(t(M) * v), 
  sweep(M, MARGIN=2, STATS=v, FUN='*'))

# Unit: milliseconds
#                                        expr        min         lq       mean     median         uq       max neval
#                M %*% Diagonal(length(v), v)   36.46755   39.03379   47.75535   40.41116   43.30241  248.1036   100
#                                 t(t(M) * v)  207.70560  223.35126  269.08112  250.25379  284.49382  511.0403   100
#  sweep(M, MARGIN = 2, STATS = v, FUN = "*") 1682.45263 1869.87220 1941.54691 1924.80218 1999.62484 3104.8305   100
0
merv