2012-05-01 15 views
19

Ich bin auf der Suche nach so viel Geschwindigkeit wie möglich und bleiben in der Basis zu tun, was expand.grid tut. Ich habe outer für ähnliche Zwecke in der Vergangenheit verwendet, um einen Vektor zu erstellen; so etwas wie dieses:Verwenden Sie äußere anstelle von expand.grid

v <- outer(letters, LETTERS, paste0) 
unlist(v[lower.tri(v)]) 

Benchmarking hat mir gezeigt, dass outer drastisch schneller als expand.grid sein kann, aber dieses Mal möchte ich zwei Spalten erstellen, wie expand.grid (alle möglichen Kombinationen für zwei Vektoren), aber meine Methoden mit outer nicht Benchmark so schnell wie möglich mit dieser Zeit.

Ich hoffe, 2 Vektoren zu nehmen und jede mögliche Combo als zwei Spalten so schnell wie möglich zu schaffen (ich glaube outer der Weg sein kann, aber ich bin weit offen für jede Basismethode.

Hier ist die expand.grid Verfahren und . Methode outer

dat <- cbind(mtcars, mtcars, mtcars) 

expand.grid(seq_len(nrow(dat)), seq_len(ncol(dat))) 

FOO <- function(x, y) paste(x, y, sep=":") 
x <- outer(seq_len(nrow(dat)), seq_len(ncol(dat)), FOO) 
apply(do.call("rbind", strsplit(x, ":")), 2, as.integer) 

Die microbenchmarking zeigt outer langsamer ist:

#  expr  min  lq median  uq  max 
# EXPAND.G 812.743 838.6375 894.6245 927.7505 27029.54 
# OUTER 5107.871 5198.3835 5329.4860 5605.2215 27559.08 

Ich denke, meine outer Verwendung ist langsam, weil ich nicht weiß, wie man outer verwendet, um direkt eine Länge 2 Vektor, den ich zusammen do.call('rbind' erstellen kann. Ich muss langsam paste und langsam splitten. Wie kann ich dies mit outer (oder anderen Methoden in base) auf eine Weise tun, die schneller ist als expand grid?

EDIT: Hinzufügen der Microbenchmark Ergebnisse.

**

Unit: microseconds 
     expr  min  lq median  uq  max 
1 ERNEST 34.993 39.1920 52.255 57.854 29170.705 
2  JOHN 13.997 16.3300 19.130 23.329 266.872 
3 ORIGINAL 352.720 372.7815 392.377 418.738 36519.952 
4 TOMMY 16.330 19.5960 23.795 27.061 6217.374 
5 VINCENT 377.447 400.3090 418.505 451.864 43567.334 

**

enter image description here

+0

Tyler, stört es Sie, meine Methode zu der Benchmark-Liste hinzuzufügen? Es sollte in der Hälfte der Geschwindigkeit der schnellsten kommen, die Sie hier haben. – John

+0

Ja, habe ich gerade getan. Es ist in der Tat das schnellste. –

Antwort

12

Mit rep.int:

expand.grid.alt <- function(seq1,seq2) { 
    cbind(rep.int(seq1, length(seq2)), 
     c(t(matrix(rep.int(seq2, length(seq1)), nrow=length(seq2))))) 
} 

expand.grid.alt(seq_len(nrow(dat)), seq_len(ncol(dat))) 

In meinem Computer ist wie 6-mal schneller als expand.grid.

+0

Ich war sehr skeptisch, aber es ist sehr schnell. Schöne Antwort. Ich nehme an, äußerlich war nicht der Ansatz, den ich hätte nehmen sollen. Ich habe die Microbenchmarking-Ergebnisse auf einer oben genannten Win 7-Maschine veröffentlicht. –

+1

@TylerRinker Vorsicht, es gab einen Fehler in meiner Funktion! Das 'nrow'-Argument war falsch. Ich habe es jetzt behoben. –

+0

@ErnestA Ich habe eine Klammer ganz rechts von der letzten Codezeile hinzugefügt und musste dann noch etwas Text hinzufügen, bevor die Änderung übergeben werden konnte. –

3

Sie können die beiden Spalten separat erstellen.

library(microbenchmark) 
n <- nrow(dat) 
m <- ncol(dat) 
f1 <- function() expand.grid(1:n, 1:m) 
f2 <- function() 
    data.frame( 
    Var1 = as.vector(outer(1:n, rep(1,m))), 
    Var2 = as.vector(outer(rep(1,n), 1:m)) 
) 
microbenchmark(f1, f2, times=1e6) 
# Unit: nanoseconds 
# expr min lq median uq max 
# 1 f1 70 489 490 559 168458 
# 2 f2 70 489 490 559 168597 
+0

Danke für die Antwort. Du hast mein äußeres Problem gelöst und das Lernen war großartig. Ernest's Ansatz ist sehr schnell, viel schneller als der äußere Ansatz. –

4

@ErnestA hat eine großartige Lösung, die der Antwort wert ist!

... könnte es allerdings geringfügig schneller sein:

expand.grid.alt2 <- function(seq1,seq2) { 
    cbind(Var1=rep.int(seq1, length(seq2)), Var2=rep(seq2, each=length(seq1))) 
} 

s1=seq_len(2000); s2=seq_len(2000) 
system.time(for(i in 1:10) expand.grid.alt2(s1, s2)) # 1.58 
system.time(for(i in 1:10) expand.grid.alt(s1, s2)) # 1.75 
system.time(for(i in 1:10) expand.grid(s1, s2))  # 2.46 
+0

Sehr nett, definitiv ein Geschwindigkeitsschub gegenüber Ernest. +1 Ich werde den Scheck über Ernest behalten, obwohl das schneller ist, weil ich den Scheck schon gegeben habe und ich mich lustig über die Neuzuweisung fühle. Es würde mir ein Gefühl von Leichtigkeit geben, wenn Sie Ihre Antwort auf seine Antwort stützen würden. Darf ich fragen, ob Sie das getan haben? –

+1

@ TylerRinker - Ja, tat ich. So können Sie sich jetzt ganz entspannt fühlen ;-) – Tommy

+0

@Tommy Ich habe mich sehr bemüht, 'rep (... each =)' zu vermeiden, weil ich annahm, dass es langsamer wäre. In der Tat ist es nicht. –

13

Die Dokumentation für rep.int nicht ganz abgeschlossen ist. Im häufigsten Fall handelt es sich nicht nur um die schnellste Methode, da Sie Vektoren für das Argument times übergeben können, genau wie mit rep. Sie können es einfach für beide Sequenzen verwenden und die Zeit um weitere 40% gegenüber Tommy reduzieren.

expand.grid.jc <- function(seq1,seq2) { 
    cbind(Var1 = rep.int(seq1, length(seq2)), 
    Var2 = rep.int(seq2, rep.int(length(seq1),length(seq2)))) 
} 
+0

im aktuellen Zustand gibt der Code einen Fehler aus. Ich denke, es fehlt irgendwo eine Klammer, aber ich konnte nicht herausfinden, wo. –

+0

+1 Super! Es * sollte nicht * schneller sein als meins, aber es ist sicherlich :-) Anscheinend muss 'rep' verbessern, wie es das' each' Argument behandelt ... – Tommy

+0

ja Tommy, es sollte ... ich denke tatsächlich expand.grid verwendet etwas, was ich intern geschrieben habe ... es ist nur langsam wegen der Fehlerprüfung und Robustheit. – John

Verwandte Themen