2010-04-17 18 views
12

a menudo quieren hacer esencialmente lo siguiente:puede lapply no modificar las variables en un ámbito superior

mat <- matrix(0,nrow=10,ncol=1) 
lapply(1:10, function(i) { mat[i,] <- rnorm(1,mean=i)}) 

Pero, yo esperaría que la estera tendría 10 números aleatorios en ella, sino que tiene 0. (No estoy preocupado por la parte del informe. Claramente hay una manera correcta de hacerlo. Me preocupa afectar el tapete desde dentro de una función anónima de lapply) ¿No puedo afectar la matrix mat desde adentro? Por qué no? ¿Hay una regla de alcance de R que está bloqueando esto?

Respuesta

26

Discutí este problema en esta pregunta relacionada: "Is R’s apply family more than syntactic sugar". Observará que si mira la firma de función para for y apply, tienen una diferencia crítica: un bucle for evalúa una expresión, mientras que un bucle apply evalúa una función .

Si quiere modificar cosas fuera del alcance de una función de aplicar, entonces necesita usar <<- o assign. O más al punto, use algo como un bucle for en su lugar. Pero realmente debe tener cuidado cuando trabaja con cosas fuera de una función, ya que puede provocar un comportamiento inesperado.

En mi opinión, una de las principales razones para usar una función apply es explícitamente porque no altera las cosas que están fuera de ella. Este es un concepto central en la programación funcional, en el que las funciones evitan tener side effects. Esta es también una razón por la cual la familia de funciones apply se puede usar en procesamiento paralelo (y existen funciones similares en los diversos paquetes paralelos, como la nieve).

Por último, la manera correcta de ejecutar el ejemplo de código es pasar también en los parámetros a su función como tal, y la asignación de vuelta la salida:

mat <- matrix(0,nrow=10,ncol=1) 
mat <- matrix(lapply(1:10, function(i, mat) { mat[i,] <- rnorm(1,mean=i)}, mat=mat)) 

Siempre es mejor ser explícito acerca de un parámetro cuando sea posible (de ahí el mat=mat) en lugar de inferirlo.

1

En lugar de alterar realmente la estera, lapply simplemente devuelve la versión modificada de mat (como una lista). Solo tiene que asignarlo a la carpeta y volverlo a una matriz usando as.matrix().

5

Una de las principales ventajas de las funciones de orden superior como lapply() o sapply() es que no tiene que inicializar su "contenedor" (matriz en este caso).

Como Fojtasek sugiere:

as.matrix(lapply(1:10,function(i) rnorm(1,mean=i))) 

alternativa:

do.call(rbind,lapply(1:10,function(i) rnorm(1,mean=i))) 

O, simplemente como un vector numérico:

sapply(1:10,function(i) rnorm(1,mean=i)) 

Si realmente desea modificar una variable por encima de la alcance de su función anónima (generador de números aleatorios en esta instancia), use <<-

> mat <- matrix(0,nrow=10,ncol=1) 
> invisible(lapply(1:10, function(i) { mat[i,] <<- rnorm(1,mean=i)})) 
> mat 
      [,1] 
[1,] 1.6780866 
[2,] 0.8591515 
[3,] 2.2693493 
[4,] 2.6093988 
[5,] 6.6216346 
[6,] 5.3469690 
[7,] 7.3558518 
[8,] 8.3354715 
[9,] 9.5993111 
[10,] 7.7545249 

Ver this post sobre <<-. Sin embargo, en este ejemplo particular, un bucle para apenas tendría más sentido:

mat <- matrix(0,nrow=10,ncol=1) 
for(i in 1:10) mat[i,] <- rnorm(1,mean=i) 

con el menor costo de crear una variable de indexación, i, en el espacio de trabajo global.

Cuestiones relacionadas