domingo, 31 de enero de 2010

Aplicar una función a una matriz o array

La función apply

Para aplicar una función a una matriz o array, R dispone de la función apply que se utiliza con tres parámetros: el objeto array (una matriz es un array de dos dimensiones), la dimensión sobre la que actuaremos y la función que se aplicará. Como con la función sapply, es posible añadir al final todos los argumentos que precise la función que aplicaremos. En el caso de las matrices, si el segundo argumento es un 1, significa que la función se aplicará a las filas, mientras que si es un 2, se aplicará a las columnas.
En muchos casos se puede utilizar una instrucción apply en vez de un bucle. Además, el resultado es un vector o una matriz con nombres (si los había) y dicen que a menudo es más eficiente que un bucle.
Si queremos aplicar un procedimiento, primero definiremos la función a aplicar. Por ejemplo, si queremos saber el número de datos en cada una de las variables (columnas) que forman una base de datos (matriz), primero definiremos la función

n.datos <- function(x) n <- sum(!is.na(x))

y la aplicaremos sobre la base de datos

apply(base.de.datos, 2, n.datos)

La función apply se puede aplicar sobre elementos que no son arrays, pero debemos saber que entonces trata de convertirlos mediante un as.matrix o un as.array, dependiendo de las dimensiones.
Supongamos ahora que queremos estandarizar cada una de las variables de una base de datos, pero utilizando la mediana como estadístico de centralidad y la MAD como medida de dispersión.
El truco consiste en utilizar la función scale pero con los parámetros adecuados:

datos.estand <- scale(datos, center=apply(datos, 2, median), scale=apply(datos, 2, mad))

Sin embargo, si lo que queremos es calcular la suma o la media, es mejor utilizar funciones como rowSums, colSums, rowMeans o colMeans que son más eficientes. Además tienen un argumento na.rm= que permite prescindir de los valores faltantes.

La función sweep

Otra situación se presenta cuando queremos procesar una matriz por filas o por columnas, pero cada fila o columna de forma distinta, dependiendo de los valores de un vector dado. En estos casos utilizaremos la función sweep que tiene básicamente 4 argumentos. Como en la función apply, los dos primeros son la matriz y la dimensión sobre la que aplicaremos la función. El tercero es el vector de valores para procesar cada fila o columna de forma distinta y finalmente, el cuarto argumento es la función a aplicar.
Como funciones a aplicar podemos utilizar los operadores binarios como la suma "+", la resta "-" (por defecto), el producto "*" y el cociente "/", siempre con comillas. Por ejemplo, para dividir cada columna de unos datos por su máximo haremos

maximos <- apply(datos, 2, max)

sweep(datos, 2, maximos, "/")


Ahora bien, para otro tipo de funciones deberemos asegurarnos que la función trabaje correctamente con sweep. No todas lo hacen.
Si la función a aplicar calcula correctamente lo que queremos para cada vector de la matriz, pero no lo hace con sweep, podemos pensar en la función mapply.
La función mapply es la versión multivariante de sapply.
mapply aplica la función (primer argumento) sobre los primeros elementos de cada argumento, los segundos elementos, los terceros y así sucesivamente. El resultado se trata de simplificar, normalmente es una lista o vector.

mapply(rep, 1:4, 4:1)

Si la función necesita más argumentos, se los podemos pasar así:

mapply(rep, 1:4, MoreArgs=list(x=5))

Con un data.frame datos podemos hacer

mapply("/", datos, maximos)

y obtendremos un resultado similar al de sweep.