Comment réécrire une commande « sapply » pour augmenter la performance?
-
24-10-2019 - |
Question
J'ai un data.frame nommé « d » de lignes et 1.300.000 ~ 4 colonnes et une autre data.frame nommée « gc » de lignes et 12.000 ~ 2 colonnes (mais voir l'exemple ci-dessous plus petit).
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") )
Voici comment ressemble à "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
Et voici "gc":
gene chr
1 a c1
2 b c2
3 c c3
Je veux ajouter une 5ème colonne « d » en incorporant des données de « gc » qui correspondent à la 1ère colonne de « d ». Pour le moment je suis en utilisant sapply .
d$chr <- sapply( 1:nrow(d), function(x) gc[ gc$gene==d[x,1], ]$chr )
Mais sur les données réelles, il faut un temps « très long » (je suis en cours d'exécution avec la commande « system.time () » depuis plus de 30 minutes et il est toujours pas fini).
Avez-vous une idée de la façon dont je pourrais réécrire cela d'une manière intelligente? Ou devrais-je envisager d'utiliser l'option plyr , peut-être avec le « parallèle » (j'ai quatre cœurs sur mon ordinateur)? Dans ce cas, quelle serait la meilleure syntaxe?
Merci à l'avance.
La solution
Je pense que vous pouvez simplement utiliser le facteur comme indice:
gc[ d[,1], 2]
[1] c1 c2 c3 c1 c2 c3 c1 c2 c3 c1 c2 c3
Levels: c1 c2 c3
fait la même chose 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
Mais est beaucoup plus rapide:
> 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
Edit:
Pour développer un peu sur mon commentaire. La trame de données de gc
exige une ligne pour chaque niveau de gene
dans l'ordre des niveaux pour que cela fonctionne:
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
Mais il est difficile de ne pas fixer que:
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
Autres conseils
Une solution alternative qui ne merge
pas battu le calendrier sage approche de Sasha, mais il est plus généralisables et lisible, est simplement les deux trames de données:
d <- merge(d, gc)
J'ai un système plus lent, donc voici mes horaires:
> 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
L'avantage est que vous pouvez avoir plusieurs clés, contrôle fin sur les éléments qui ne correspondent pas, etc.