2011-06-06 34 views
5

Les manuels R continuent à promouvoir l'utilisation de lapply au lieu des boucles. C'est facile même pour des fonctions avec des arguments commeUtilisation de lapplication avec des arguments de modification

lapply(somelist, f, a=1, b=2) 

mais que se passe-t-il si les arguments changent en fonction de l'élément de liste? Assumer ma somelist se compose de:

somelist$USA 
somelist$Europe 
somelist$Switzerland 

plus il y a anotherlist avec les mêmes régions et je veux utiliser lapply avec ces arguments changeants? Cela pourrait être utile lorsque f était un calcul de ratio par exemple.

lapply(somelist, f, a= somelist$USA, b=anotherlist$USA) 

Y a-t-il un moyen, à l'exception d'une boucle, de parcourir efficacement ces régions?

EDIT: mon problème semble être que j'ai essayé d'utiliser une fonction précédemment écrite sans index ...

ratio <-function(a,b){ 
z<-(b-a)/a 
return(z) 
} 

qui a conduit à

lapply(data,ratio,names(data)) 

qui ne fonctionne pas. Peut-être que d'autres peuvent aussi apprendre de cette erreur.

Répondre

14

Appliquer sur les noms de liste plutôt que sur les éléments de liste. .: par exemple

somelist <- list('USA'=rnorm(10), 'Europe'=rnorm(10), 'Switzerland'=rnorm(10)) 
anotherlist <- list('USA'=5, 'Europe'=10, 'Switzerland'=4) 
lapply(names(somelist), function(i) somelist[[i]]/anotherlist[[i]]) 

EDIT:

Vous demandez aussi s'il y a un moyen «sauf pour une boucle » pour faire « efficacement ». Vous devriez noter que l'application ne sera pas nécessairement plus efficace. L'efficacité sera probablement déterminée par la rapidité de votre fonction intérieure. Si vous voulez opérer sur chaque élément d'une liste, vous aurez besoin d'une boucle, qu'elle soit cachée dans un appel apply() ou non. Cochez cette question: Is R's apply family more than syntactic sugar?

L'exemple que j'ai donné ci-dessus peuvent être réécrite comme une boucle, et vous pouvez faire quelques repères naïfs:

fun1 <- function(){ 
    lapply(names(somelist), function(i) somelist[[i]]/anotherlist[[i]]) 
} 
fun2 <- function(){ 
    for (i in names(somelist)){ 
     somelist[[i]] <- somelist[[i]]/anotherlist[[i]] 
    } 
    return(somelist) 
} 
library(rbenchmark) 

benchmark(fun1(), fun2(), 
      columns=c("test", "replications", 
      "elapsed", "relative"), 
      order="relative", replications=10000) 

La sortie de la référence sur ma machine était la suivante:

test replications elapsed relative 
1 fun1()  10000 0.145 1.000000 
2 fun2()  10000 0.148 1.020690 

Bien que ce n'est pas une application réelle de travail et les fonctions ne sont pas des tâches réalistes, vous pouvez voir que la différence dans le temps de calcul est tout à fait négligeable.

+0

+1 Je vous me bats pour voir les noms idée –

+0

Ouais, il semblait que la moyen le plus simple pour résoudre le problème. J'ai ajouté une discussion de pour vs appliquer parce qu'il a demandé cela aussi ... – Vincent

7

Vous avez juste besoin de savoir quoi faire lapply() sur. Ici, le names() des listes suffit, après que nous réécrivons f() de prendre différents arguments:

somelist <- list(USA = 1:10, Europe = 21:30, 
       Switzerland = seq(1, 5, length = 10)) 
anotherlist <- list(USA = list(a = 1, b = 2), Europe = list(a = 2, b = 4), 
        Switzerland = list(a = 0.5, b = 1)) 

f <- function(x, some, other) { 
    (some[[x]] + other[[x]][["a"]]) * other[[x]][["b"]] 
} 

lapply(names(somelist), f, some = somelist, other = anotherlist) 

Donner:

R> lapply(names(somelist), f, some = somelist, other = anotherlist) 
[[1]] 
[1] 4 6 8 10 12 14 16 18 20 22 

[[2]] 
[1] 92 96 100 104 108 112 116 120 124 128 

[[3]] 
[1] 1.500000 1.944444 2.388889 2.833333 3.277778 3.722222 4.166667 4.611111 
[9] 5.055556 5.500000 
+0

Dommage, je ne peux pas distribuer un autre +1 ici. Nous avons eu un autre problème, j'ai essayé de demander à SO mais je n'ai pas causé la suggestion a indiqué mon. Vos réponses ont encore aidé! génial. –

Questions connexes