¿Cómo reescribir un comando "SAPPLY" para aumentar el rendimiento?
-
24-10-2019 - |
Pregunta
Tengo un datos de datos llamado "D" de ~ 1,300,000 líneas y 4 columnas y otro Data.Frame llamado "GC" de ~ 12,000 líneas y 2 columnas (pero vea el ejemplo más pequeño a continuación).
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") )
Así es como se ve "D":
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
Y aquí está "GC":
gene chr
1 a c1
2 b c2
3 c c3
Quiero agregar una quinta columna a "D" incorporando datos de "GC" que coincidan con la primera columna de "D". Por el momento que estoy usando zarza.
d$chr <- sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
Pero en los datos reales, lleva un tiempo "muy largo" (estoy ejecutando el comando con "System.time ()" ya que más de 30 minutos y todavía no está terminado).
¿Tienes alguna idea de cómo podría reescribir esto de una manera inteligente? O debería considerar usar plyr, ¿tal vez con la opción "paralela" (tengo cuatro núcleos en mi computadora)? En tal caso, ¿cuál sería la mejor sintaxis?
Gracias por adelantado.
Solución
Creo que puedes usar el factor como índice:
gc[ d[,1], 2]
[1] c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3
Levels: c1 c2 c3
hace lo mismo que:
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
Pero es mucho más rápido:
> 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
Editar:
Para expandirse un poco en mi comentario. los gc
DataFrame requiere una fila para cada nivel de gene
En el orden de los niveles para que esto funcione:
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
Pero no es difícil arreglar eso:
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
Otros consejos
Una solución alternativa que no supere el enfoque de Sasha en cuanto a tiempo, pero es más generalizable y legible, es simplemente para simplemente merge
Los dos marcos de datos:
d <- merge(d, gc)
Tengo un sistema más lento, así que aquí están mis tiempos:
> 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
El beneficio es que puede tener múltiples claves, control fino sobre elementos no coincidentes, etc.