2015-05-04 13 views
41

Ich schaute auf die Benchmarks in this answer, und wollte sie mit diag vergleichen (in einer anderen Antwort verwendet). Leider scheint es, dass diag Alter nimmt:Warum ist die Diagnosefunktion so langsam? [in R 3.2.0 oder früher]

nc <- 1e4 
set.seed(1) 
m <- matrix(sample(letters,nc^2,replace=TRUE), ncol = nc) 

microbenchmark(
    diag = diag(m), 
    cond = m[row(m)==col(m)], 
    vec = m[(1:nc-1L)*nc+1:nc], 
    mat = m[cbind(1:nc,1:nc)], 
times=10) 

Kommentare: Getestet habe ich diese mit identical. Ich nahm "cond" von einer der Antworten zu this homework question. Die Ergebnisse sind ähnlich mit einer Matrix von Ganzzahlen, 1:26 anstelle von letters.

Ergebnisse:

Unit: microseconds 
expr   min   lq   mean  median   uq   max neval 
diag 604343.469 629819.260 710371.3320 706842.3890 793144.019 837115.504 10 
cond 3862039.512 3985784.025 4175724.0390 4186317.5260 4312493.742 4617117.706 10 
    vec  317.088  329.017  432.9099  350.1005  629.460  651.376 10 
    mat  272.147  292.953  441.7045  345.9400  637.506  706.860 10 

Es ist nur eine Matrix-subsetting Betrieb, so dass ich weiß nicht, warum so viel Aufwand ist da. Wenn ich in die Funktion schaue, sehe ich ein paar Überprüfungen und dann c(m)[v], wobei v der gleiche Vektor ist, der im "vec" -Benchmark verwendet wird. Timing diese beiden ...

v <- (1:nc-1L)*nc+1:nc 
microbenchmark(diaglike=c(m)[v],vec=m[v]) 
# Unit: microseconds 
#  expr  min   lq  mean  median   uq  max neval 
# diaglike 579224.436 664853.7450 720372.8105 712649.706 767281.5070 931976.707 100 
#  vec 334.843 339.8365 568.7808 646.799 663.5825 1445.067 100 

... es scheint, dass ich meinen Schuldigen gefunden habe. Also, die neue Variante meiner Frage ist: Warum gibt es einen scheinbar unnötigen und sehr zeitaufwendigen c in diag?

+2

Aufwand in Bezug auf, ich war zu dieser ähnlichen Frage suchen: http://stackoverflow.com/questions/18604406/why-is-mean-so-slow und ich dachte, „Wow, Algebra Matrix ist nicht in Primitiven kodiert! " – Frank

+0

'c()' erzwingt die Eingabe in einen Vektor. Könnte sein, sich mit verschiedenen Eingabearten zu befassen, indem man zu einem Vektor gezwungen wird, um alle Typen auf die gleiche Weise zu verarbeiten. Oder als eine schnelle und schmutzige Möglichkeit, den Eingabetyp zu überprüfen (ein Datenrahmeneingang gibt einen Fehler wegen "c" aus). –

+0

@AlexA. Vielen Dank. Es erreicht nur diesen Punkt, wenn 'is.matrix' wahr ist. Es ist eine ziemlich kleine Funktion, wenn Sie schauen wollen - geben Sie einfach 'diag' ein. (Oh und Vanille data.frames sind keine Matrizen, es ergibt sich 'is.matrix (data.frame (1)) # FALSE'.) – Frank

Antwort

12

Zusammenfassung

Ab R version 3.2.1 (World-Famous Astronaut) diag() hat ein Update erhalten. Die Diskussion wurde auf verschoben, wo angemerkt wurde, dass c() Attribute, die keine Namen sind, entfernt und möglicherweise deshalb platziert wurde. Während einige Leute besorgt, dass c() Entfernen unbekannte Probleme auf matrixartige Objekte verursachen würde, fand Peter Dalgaard, dass: „Der einzige Fall, in dem die c() innerhalb diag() eine Wirkung hat, wo M[i,j] != M[(i-1)*m+j] UND c(M)M in Spalte-Großauftrag stringize wird, so dass M[i,j] == c(M)[(i-1)*m+j]. "

Luke Tierney getestet @Frank ‚s Entfernung von c(), finden es nichts auf CRAN oder BIOC nicht beeinflusste und wurde so umgesetzt c ersetzen (x) [...] mit x [...] auf line 27. Dies führt zu relativ großen Beschleunigungen in diag(). Unten ist ein Geschwindigkeitstest, der die Verbesserung mit der Version diag() von R 3.2.1 zeigt.

library(microbenchmark) 
nc <- 1e4 
set.seed(1) 
m <- matrix(sample(letters,nc^2,replace=TRUE), ncol = nc) 

    microbenchmark(diagOld(m),diag(m)) 
    Unit: microseconds 
      expr  min   lq  mean  median   uq  max neval 
    diagOld(m) 451189.242 526622.2775 545116.5668 531905.5635 540008.704 682223.733 100 
     diag(m) 222.563 646.8675 644.7444 714.4575 740.701 1015.459 100 
+1

Danke. Haben Sie die Quelle für die alte Funktion nach 'diagOld' kopiert und den Benchmark in R 3.2.1 ausgeführt, dann? – Frank

+1

Ja, ich wollte den Code posten, habe aber keinen Grund gesehen. Einen Schnitt gemacht, um das zu reflektieren. –