2011-12-20 18 views
16

Así que quieren aplicar una función a través de una matriz en R. Esto funciona muy intuitiva para funciones simples:R: La función de la aplicación sobre la matriz y manteniendo dimensiones de la matriz

> (function(x)x*x)(matrix(1:10, nrow=2)) 
[,1] [,2] [,3] [,4] [,5] 
[1,] 1 9 25 49 81 
[2,] 4 16 36 64 100 

... pero está claro que no lo hacen entender todo de su funcionamiento:

> m = (matrix(1:10, nrow=2)) 
> (function(x) if (x %% 3 == 0) { return(NA) } else { return(x+1) })(m) 
    [,1] [,2] [,3] [,4] [,5] 
[1,] 2 4 6 8 10 
[2,] 3 5 7 9 11 
Warning message: 
In if (x == 3) { : 
    the condition has length > 1 and only the first element will be used 

que leer sobre esto y se enteraron de Vectorize y sapply, que ambos parecían grandes y al igual que lo que quería, excepto que ambos convertir mi matriz en una lista:

> y = (function(x) if (x %% 3 == 0) { return(NA) } else { return(x+1) }) 
> sapply(m, y) 
[1] 2 3 NA 5 6 NA 8 9 NA 11 
> Vectorize(y)(m) 
[1] 2 3 NA 5 6 NA 8 9 NA 11 

... mientras que me gustaría mantenerlo en una matriz con sus dimensiones actuales. ¿Cómo podría hacer esto? ¡Gracias!

+0

También puedes ver este post útil en las diferentes versiones de aplicar: http://nsaunders.wordpress.com/2010/08/20/a-brief-introduction-to-apply-in-r/ – patrickmdnet

Respuesta

15

@Joshua Ulrich (y Dason) tiene una gran respuesta. Y hacerlo directamente sin la función y es la mejor solución. Pero si realmente necesita para llamar a una función, puede hacerlo más rápido usando vapply. Produce un vector sin dimensiones (como sapply, pero más rápido), pero luego se puede añadir de nuevo usando structure:

# Your function (optimized) 
y = function(x) if (x %% 3) x+1 else NA 

m <- matrix(1:1e6,1e3) 
system.time(r1 <- apply(m,1:2,y)) # 4.89 secs 
system.time(r2 <- structure(sapply(m, y), dim=dim(m))) # 2.89 secs 
system.time(r3 <- structure(vapply(m, y, numeric(1)), dim=dim(m))) # 1.66 secs 
identical(r1, r2) # TRUE 
identical(r1, r3) # TRUE 

... Como se puede ver, el enfoque es sobre vapply 3x más rápido que apply ... Y el motivo vapply es más rápido que sapply es que sapply debe analizar el resultado para descubrir que se puede simplificar a un vector numérico. Con vapply, que especifica el tipo de resultado (numeric(1)), por lo que no tiene que adivinar ...

ACTUALIZACIÓN me di cuenta de otra manera (más corto) de preservar la estructura de la matriz:

m <- matrix(1:10, nrow=2) 
m[] <- vapply(m, y, numeric(1)) 

Simplemente asigne los nuevos valores al objeto usando m[] <-. Luego se conservan todos los demás atributos (como dim, dimnames, class, etc.).

+0

Muchas gracias por esto.En retrospectiva, estoy de acuerdo en que con el ejemplo de juguete que proporcioné omitiendo la función real fue la decisión correcta, pero realmente quería saber cómo debería manejarse esto cuando en realidad es una función. (Traté de idear uno para ese efecto que fuera más simple que el que realmente estaba tratando, pero evidentemente me perdí la marca.) De todos modos, tener la información de tiempo es muy útil porque también estoy tratando de optimizar esto, ¡gracias! –

12

Una forma es utilizar apply en ambas filas y columnas:

apply(m,1:2,y) 
    [,1] [,2] [,3] [,4] [,5] 
[1,] 2 NA 6 8 NA 
[2,] 3 5 NA 9 11 

También puede hacerlo con subíndices debido == ya está vectorizada:

m[m %% 3 == 0] <- NA 
m <- m+1 
m 
    [,1] [,2] [,3] [,4] [,5] 
[1,] 2 NA 6 8 NA 
[2,] 3 5 NA 9 11 
7

Para este ejemplo específico que sólo puede hacer algo como esto

> # Create some fake data 
> mat <- matrix(1:16, 4, 4) 
> # Set all elements divisible by 3 to NA 
> mat[mat %% 3 == 0] <- NA 
> # Add 1 to all non NA elements 
> mat <- mat + 1 
> mat 
    [,1] [,2] [,3] [,4] 
[1,] 2 6 NA 14 
[2,] 3 NA 11 15 
[3,] NA 8 12 NA 
[4,] 5 9 NA 17 
+0

Un compañero de trabajo me señaló este enfoque. Satisface mi necesidad y la aprecio, pero realmente parece que debería haber alguna forma de aplicar una función preexistente sobre una matriz. –

6

Hay un ligero refinamiento de la solución de Dason y Josh usando ifelse.

mat <- matrix(1:16, 4, 4) 
ifelse(mat %% 3 == 0, NA, mat + 1) 
    [,1] [,2] [,3] [,4] 
[1,] 2 6 NA 14 
[2,] 3 NA 11 15 
[3,] NA 8 12 NA 
[4,] 5 9 NA 17 
+0

Bueno. este sería el más rápido. una comprobación rápida con 'rbenchmark' muestra que es alrededor de 8 veces más rápido que la solución' vapply'. ¡La vectorización siempre triunfa! – Ramnath

Cuestiones relacionadas