2012-05-01 20 views
19

Sto cercando la massima velocità possibile e resto in base per fare ciò che fa expand.grid. Ho usato outer per scopi simili in passato per creare un vettore; qualcosa di simile:Usa esterno invece di expand.grid

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

Benchmarking mi ha mostrato che outer può essere drasticamente più veloce di expand.grid ma questa volta voglio creare due colonne come expand.grid (tutte le possibili combinazioni per 2 vettori) ma i miei metodi con outer non fare punto di riferimento come veloce con esterno questa volta.

spero di prendere 2 vettori e creare ogni possibile combinazione di due colonne più velocemente possibile (credo outer può essere il percorso, ma sono aperti a qualsiasi metodo di base.

Ecco il metodo expand.grid e . outer metodo

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) 

La microbenchmarking mostra outer è più lento:

#  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 

Penso che il mio uso di outer sia lento perché non so come usare outer per creare direttamente un vettore di lunghezza 2 che io possa do.call('rbind' insieme. Devo rallentare paste e dividere lentamente. Come posso fare questo con outer (o altri metodi in base) in modo più veloce di expand grid?

MODIFICA: Aggiunta dei risultati del microbenchmark.

**

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, ti dispiace aggiungere il mio metodo all'elenco dei benchmark? Dovrebbe arrivare a metà della velocità del più veloce che hai qui. – John

+0

Sì, l'ho appena fatto. È davvero il più veloce. –

risposta

12

Utilizzando 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))) 

Nel mio computer è come 6 volte più veloce di expand.grid.

+0

Ero molto scettico ma è molto veloce. Bella risposta Immagino che l'esterno non fosse l'approccio che avrei dovuto prendere. Ho postato i risultati del microbenchmarking su una macchina WIn 7 qui sopra. –

+1

@TylerRinker Attenzione, c'era un bug nella mia funzione! L'argomento 'nrow' era sbagliato. L'ho riparato ora. –

+0

@ErnestA Ho aggiunto una parentesi all'estrema destra dell'ultima riga di codice, quindi ho dovuto aggiungere un altro testo prima che la modifica potesse essere inviata. –

3

È possibile creare i due colonne separatamente.

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

Grazie per la risposta. Hai risolto il mio problema esterno e l'apprendimento è stato formidabile. L'approccio di Ernest è molto veloce, molto più rapido rispetto all'approccio esterno. –

4

@ErnestA ha un'ottima soluzione ben degna della risposta tick!

... potrebbe essere marginalmente più veloce però:

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

Molto bello, sicuramente un aumento di velocità rispetto a quello di Ernest. +1 Ho intenzione di mantenere il controllo su Ernest anche se è più veloce perché ho già dato l'assegno e mi sento in dovere di riassegnare. Mi farebbe sentire a mio agio se basassi la tua risposta sulla sua risposta :) Posso chiederti se lo hai fatto? –

+1

@TylerRinker - Sì, l'ho fatto. Così puoi sentirti a tuo agio ora ;-) – Tommy

+0

@ Tommy Ho fatto di tutto per evitare "rep (... each =)" perché pensavo che sarebbe stato più lento. In realtà, non lo è. –

13

La documentazione per rep.int non è abbastanza completo. Non è solo il più veloce nel caso più comune perché puoi passare i vettori per l'argomento times, proprio come con rep. Puoi usarlo in modo semplice per entrambe le sequenze, riducendo il tempo di un altro 40% rispetto a quello di Tommy.

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

nel suo stato attuale il tuo codice genera un errore. Penso che manchi una parentesi da qualche parte ma non riesco a capire dove. –

+0

+1 Fantastico! * Non dovrebbe * essere più veloce del mio, ma certamente è :-) A quanto pare, 'rep' deve migliorare il modo in cui gestisce l'argomento' each' ... – Tommy

+0

sì Tommy, dovrebbe ... In realtà penso che expand.grid usa qualcosa di simile a quello che ho scritto internamente ... è solo lento a causa del controllo degli errori e della robustezza. – John

Problemi correlati