Función aggregate: Ejemplos

Función <code>aggregate</code>: Ejemplos y trucos.

En R es fácil resumir información a partir de apliación de funciones sobre los subgrupos creados según las variables de agrupación (factor o numérica) por las cuales se desean hacer las agrupaciones. Es posible hacer esto con una gran variedad de funciones (aggregate, split, by, tapply y con otras funciones del paquete plyr), sin embargo este post sólo trata de la función aggregate porque está en el paquete base de R y es muy manejable.

Según la documentación de la función aggregate, esta función divide la muestra en subconjuntos, calcula los estadísticos deseados por cada subconjunto y devuelve el resultado en una forma conveniente, esto significa que el resutado puede ser un vector, un data.frame o una lista según sea más conveniente.

A continuación algunos ejemplos usando la base datos iris.

data(iris)  # cargando la base de datos `iris`
head(iris)  # así lucen los datos.
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa

Nota En todo este post se usará la notación de fórmula dentro de la función aggregate donde la lógica es escribir al lado izquierdo del símbolo ~ la variable que se quiere agrupar y al lado derecho, la variable de agrupación: variable_a_ser_agrupada ~ variable_de_agrupación. Así la sintaxis entera será:

aggregate(variable_a_ser_agrupada ~ variable_de_agrupación, FUN=función_deseada, data=base_de_datos)

Ejemplo 1

Calculando la suma de Sepal.Length por Species, como ha de esperarse, el resultado final será un data.frame con la suma total de la variable Sepal.Length por cada especie contenida en la variable Species.

aggregate(Sepal.Length ~ Species, FUN = sum, data = iris)
##      Species Sepal.Length
## 1     setosa        250.3
## 2 versicolor        296.8
## 3  virginica        329.4

Ejemplo 2

Una facilidad que permite la función aggregate es que se pueden resumir más de una variable, veamos la suma de todas las demás varibles según la especie.

aggregate(cbind(Sepal.Length, Sepal.Width, Petal.Length, Petal.Width) ~ Species, 
    FUN = sum, data = iris)
##      Species Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1     setosa        250.3       171.4         73.1        12.3
## 2 versicolor        296.8       138.5        213.0        66.3
## 3  virginica        329.4       148.7        277.6       101.3

Nótese que cuando se quieren “agregar” muchas variables es necesario usar la función cbind para que funcione. Sin embargo, no es muy práctico escribir el nombre de cada variable como se muestra en el ejemplo anterior, pero como se está utilizando el estilo de “fórmula” para escribir dentro de la función aggregate esta permite reemplazar lo anterior por este nuevo código:

aggregate(. ~ Species, FUN = sum, data = iris)
##      Species Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1     setosa        250.3       171.4         73.1        12.3
## 2 versicolor        296.8       138.5        213.0        66.3
## 3  virginica        329.4       148.7        277.6       101.3

y como ha de esperarse, los resultados son los mismos.

Ejemplo 3

¿Qué tal si tuviéramos más de 1 variable de agregación, por ejemplo Species y Petal.Size?, Esto no representa ningún problema para aggregate, veamos un ejemplo.

Primero se ha de crear la nueva variable Petal.Size que indica si la longitud del pétalo (Petal.Length) es mayor que su valor mediano (4.350 cm) tendrá el valor Big y si es menor o igual, será Small.

iris$Petal.Size <- with(iris, ifelse(Petal.Length > median(Petal.Length), "Big", 
    "Small"))
aggregate(Petal.Length ~ Species + Petal.Size, FUN = sum, data = iris)  # resultado sólo para Petal.Length
##      Species Petal.Size Petal.Length
## 1 versicolor        Big        115.9
## 2  virginica        Big        277.6
## 3     setosa      Small         73.1
## 4 versicolor      Small         97.1
aggregate(. ~ Species + Petal.Size, FUN = sum, data = iris)  # resultado para todos
##      Species Petal.Size Sepal.Length Sepal.Width Petal.Length Petal.Width
## 1 versicolor        Big        156.4        72.8        115.9        36.5
## 2  virginica        Big        329.4       148.7        277.6       101.3
## 3     setosa      Small        250.3       171.4         73.1        12.3
## 4 versicolor      Small        140.4        65.7         97.1        29.8

Ejemplo 4 (un truco sencillo)

¿Y si en lugar de sólo querer la suma, quiero además la media y desviación estándar de los datos de Petal.Length agrupados por Species?, Esto se puede hacer sin ningún problema.

resultado <- aggregate(Petal.Length ~ Species, FUN = function(x) c(Suma = sum(x), 
    Media = mean(x), SD = sd(x)), data = iris)
resultado
##      Species Petal.Length.Suma Petal.Length.Media Petal.Length.SD
## 1     setosa           73.1000             1.4620          0.1737
## 2 versicolor          213.0000             4.2600          0.4699
## 3  virginica          277.6000             5.5520          0.5519

A pesar que el resultado mostrado es el adecuado, en realidad, no es muy útil si se desea guardar para luego volverlo a usar, porque en realidad el output de este último ejemplo es un data.frame que debería tener la información calculada, pero si intentamos acceder a esa info a través de su nombre o su posición nos dice que no existe tal información!!! :(

resultado[, "Petal.Length.Suma"]  # Error en `[.data.frame`(resultado, , 3) : undefined columns selected  :(
resultado[, "Petal.Length.Media"]  # Error en `[.data.frame`(resultado, , 3) : undefined columns selected  :(
resultado[, 3]  # Error en `[.data.frame`(resultado, , 3) : undefined columns selected  :(

Pero tranquilos,“calma! que no panda el cúnico”, hay una sencilla solución para ello, basta con convertir esto a un data.frame usado la función do.call

resultado2 <- do.call(data.frame, resultado)

Aparentemente no ha habido ningún cambio respecto a lo anterior, sin embargo, ahora sí se pueden acceder a los datos de las columnas, porque ahora sí existen!!! :D

resultado2[, "Petal.Length.Suma", drop = FALSE]  # Sí funciona!!!
resultado2[, "Petal.Length.Media", drop = FALSE]  # Sí funciona!!!
resultado2[, 3, drop = FALSE]  # Sí funciona!!!

No hay comentarios:

Publicar un comentario