2011-06-06 18 views
5

Los libros de texto R continúan promoviendo el uso de lapply en lugar de bucles. Esto es fácil incluso para funciones con argumentos comoUso de lapply con cambio de argumentos

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

pero ¿y si los argumentos cambian dependiendo del elemento de la lista? Supongamos mi algunalista consiste en:

somelist$USA 
somelist$Europe 
somelist$Switzerland 

además hay anotherlist con las mismas regiones y quiero utilizar lapply con estos argumentos cambiantes? Esto podría ser útil cuando f era un cálculo de proporción, por ejemplo.

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

¿Hay alguna manera a excepción de un bucle para ejecutar estas regiones de manera eficiente?

EDIT: mi problema parece ser que he tratado de utilizar una función previamente escrito sin índices ...

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

que llevó a

lapply(data,ratio,names(data)) 

que no funciona. Quizás otros también puedan aprender de este error.

Respuesta

14

Aplicar sobre nombres de lista en lugar de elementos de lista. Ej .:

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:

También pregunta si hay una manera "a excepción de un bucle" para hacer esto "eficiente". Debe tener en cuenta que la aplicación no será necesariamente más eficiente. La eficiencia probablemente estará determinada por cuán rápido sea su función interna. Si desea operar en cada elemento de una lista, necesitará un bucle, ya sea que esté oculto en una llamada a apply() o no. Marque esta pregunta: ¿Is R's apply family more than syntactic sugar?

El ejemplo que di anteriormente se puede volver a escribir como un bucle, y usted puede hacer algunos puntos de referencia ingenuos:

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 salida del índice de referencia en mi máquina era la siguiente:

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

Aunque esta no es una aplicación de trabajo real y las funciones no son tareas realistas, se puede ver que la diferencia en el tiempo de cálculo es bastante insignificante.

+0

+1 veo que se me adelantó a la idea nombres –

+0

Sí, parecía que la forma más directa de solucionar el problema. Agregué un poco de discusión de para vs aplicar porque él también lo pidió ... – Vincent

7

Solo necesita averiguar qué lapply() más. Aquí el names() de las listas basta, después volvemos a escribir f() tomar diferentes argumentos:

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) 

Dar:

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

Lástima, no puedo repartir otro +1 aquí. Tuve otro problema, traté de preguntar por SO, pero no causó que la sugerencia apuntara a esto. ¡Tus respuestas ayudaron de nuevo! estupendo. –

Cuestiones relacionadas