Come riscrivere un comando “sapply” per aumentare le prestazioni?
-
24-10-2019 - |
Domanda
Ho un data.frame denominato "d" del ~ 1.300.000 linee e 4 colonne ed un altro data.frame denominato "GC" di ~ 12.000 righe e 2 colonne (ma si veda l'esempio più piccolo in basso).
d <- data.frame( gene=rep(c("a","b","c"),4), val=rnorm(12), ind=c( rep(rep("i1",3),2), rep(rep("i2",3),2) ), exp=c( rep("e1",3), rep("e2",3), rep("e1",3), rep("e2",3) ) )
gc <- data.frame( gene=c("a","b","c"), chr=c("c1","c2","c3") )
Ecco come "d" si presenta come:
gene val ind exp
1 a 1.38711902 i1 e1
2 b -0.25578496 i1 e1
3 c 0.49331256 i1 e1
4 a -1.38015272 i1 e2
5 b 1.46779219 i1 e2
6 c -0.84946320 i1 e2
7 a 0.01188061 i2 e1
8 b -0.13225808 i2 e1
9 c 0.16508404 i2 e1
10 a 0.70949804 i2 e2
11 b -0.64950167 i2 e2
12 c 0.12472479 i2 e2
E qui è "gc":
gene chr
1 a c1
2 b c2
3 c c3
Voglio aggiungere un 5 ° colonna "d" incorporando i dati da "GC" quella partita con il 1 ° colonna "d". Per il momento sto utilizzando sapply .
d$chr <- sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
Ma sui dati reali, ci vuole un "molto lungo" tempo (sto facendo funzionare il comando con "system.time ()", in quanto più di 30 minuti e non è ancora finito).
Avete qualche idea di come potrei riscrivere questo in un modo intelligente? O dovrei considerare l'utilizzo di plyr , magari con l'opzione "parallelo" (ho quattro core su Risorse del computer)? In tal caso una, quale sarebbe il migliore della sintassi?
Grazie in anticipo.
Soluzione
Penso che si può semplicemente utilizzare il fattore come indice:
gc[ d[,1], 2]
[1] c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3
Levels: c1 c2 c3
fa la stessa cosa come:
sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
[1] c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3
Levels: c1 c2 c3
Ma è molto più veloce:
> system.time(replicate(1000,sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )))
user system elapsed
5.03 0.00 5.02
>
> system.time(replicate(1000,gc[ d[,1], 2]))
user system elapsed
0.12 0.00 0.13
Modifica:
Per espandere un po 'sul mio commento. Il dataframe gc
richiede una riga per ogni livello di gene
nell'ordine dei livelli di questo lavoro:
d <- data.frame( gene=rep(c("a","b","c"),4), val=rnorm(12), ind=c( rep(rep("i1",3),2), rep(rep("i2",3),2) ), exp=c( rep("e1",3), rep("e2",3), rep("e1",3), rep("e2",3) ) )
gc <- data.frame( gene=c("c","a","b"), chr=c("c1","c2","c3") )
gc[ d[,1], 2]
[1] c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3
Levels: c1 c2 c3
sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
[1] c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1
Levels: c1 c2 c3
Ma non è difficile da risolvere che:
levels(gc$gene) <- levels(d$gene) # Seems redundant as this is done right quite often automatically
gc <- gc[order(gc$gene),]
gc[ d[,1], 2]
[1] c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1
Levels: c1 c2 c3
sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
[1] c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3 c1
Levels: c1 c2 c3
Altri suggerimenti
Una soluzione alternativa che non ha battuto l'approccio di Sasha tempistica-saggio, ma è più generalizzabile e leggibile, è quello di merge
semplicemente i due frame di dati:
d <- merge(d, gc)
Ho un sistema più lento, per cui qui sono i miei tempi:
> system.time(replicate(1000,sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )))
user system elapsed
11.22 0.12 11.86
> system.time(replicate(1000,gc[ d[,1], 2]))
user system elapsed
0.34 0.00 0.35
> system.time(replicate(1000, merge(d, gc, by="gene")))
user system elapsed
3.35 0.02 3.40
Il vantaggio è che si potrebbe avere più chiavi, un controllo preciso gli elementi non corrispondenti, ecc.