2014-06-06

Agregar datos en R con el paquete reshape2

Title El paquete reshape2, creado por Hadley Wickham permite transformar y agregar datos con dos funciones: melt y cast. En esta entrada nos vamos a centrar en la agregación de datos para obtener resultados similares a los obtenidos con las tablas dinámicas en Excel. El objetivo de la agregación es ordenar, reducir y mostrar la información resumida de manera que nos facilite responder a preguntas planteadas sobre nuestros datos.

Partimos de la base de datos Northwind. Exportamos la consulta Análisis de ventas a Excel y creamos una tabla dinámica con la que iremos comparando los resultados que deseamos obtener en R. Desde Excel guardamos la hoja de datos como fichero CSV que importaremos en R.

Puedes descargar el fichero Análisis de ventas.csv y copiarlo en tu directorio de trabajo.

Importación y formato

1. Importamos el fichero CSV. Es importante observar el formato de la columna con valores. El símbolo de moneda si lo hubiera, los separadores decimal y de miles. Previamente instalamos los paquetes necesarios.

require(plyr)  # Para usar la función . de plyr
require(reshape2)
data <- read.csv("Análisis de ventas.csv", sep = ";") 
2. Formateamos los datos importados.

names(data) <- tolower(names(data))  # Nombres de columnas en minúsculas
data$ventas <- sub("([.])", "", data$ventas)  # Separador de miles la coma
data$ventas <- sub(",", ".", data$ventas)  # Separador decimal el punto
data$ventas <- as.numeric(data$ventas)  # Ventas tipo de variable numérica
data$fecha.de.pedido <- as.Date(data$fecha.de.pedido, "%d/%m/%Y") # Fechas

reshape2

3. Definimos identifiers (id.vars) y measured variables (measure.vars) con melt. Identificadores y valores, el equivalente en las tablas dinámicas de Excel a etiquetas o rótulos de filas y valores.

data.m <- melt(data, id.vars = c(1:19), measure.vars = 6)
data.m # Resultado: 
      cliente mesdetrimestre variable value
1 Compañía AA              1   ventas  1400
2 Compañía AA              1   ventas   105
3  Compañía D              1   ventas   300
4  Compañía D              1   ventas   530
5  Compañía D              1   ventas    35
6  Compañía L              1   ventas   270
A la derecha de la última id.vars (mesdetrimestre en este caso) crea dos columnas, con los nombres variable y value. Bajo variable sólo incluirá la variable ventas pues es la única definida como measured variable.

4. Comenzamos a agregar datos usando dcast.

a. Agrupando por empleado. Queremos el siguiente resultado.

data.c <- dcast(data.m, empleado ~ variable, sum, margins = "empleado")
data.c # Resultado:
               empleado   ventas
1      Francisco Chaves 19974.25
2      Humberto Acevedo  3786.50
3         Jesús Escolar  2617.50
4     Juan Carlos Rivas  6378.00
5          Luis Bonifaz   680.00
6        María González  6561.00
7    María Jesús Cuesta  6278.00
8 Pilar Pinilla Gallego  5787.50
9                 (all) 52062.75 

Argumentos de dcast:

data: molten data frame. El data frame obtenido con melt anteriormente.
formula: especificamos las variables que deseamos incluir, en este caso empleado. Hay un par de caracteres especiales: "..." representa todas las otras variables no usadas en la fórmula y "." representa a ninguna variable.
fun.aggregate: la función de agregación de la función, en ese caso sum.
margins: para añadir los subtotales, margins = TRUE devolverá todos los subtotales disponibles.
fill: valor que sustituirá a aquellos que faltan cuando fun.aggregate se aplica sobre un vector de longitud 0.

Más ejemplos:

# Total de ventas
data.c <- dcast(data.m, . ~ variable, sum) 
# Agrupar por otra variable, nombre de cliente
data.c <- dcast(data.m, nombre.del.cliente ~ variable, sum, margins = "nombre.del.cliente") 

Función . de plyr para filtrar

5. Agrupamos por nombre de cliente y filtramos por el empleado Francisco Chaves. Queremos el siguiente resultado.

data.c <- dcast(data.m, nombre.del.cliente ~ variable, sum, margins = "nombre.del.cliente", subset = .(empleado == "Francisco Chaves")) 
data.c
  nombre.del.cliente   ventas
1        Compañía AA  1505.00
2        Compañía BB 13800.00
3         Compañía D   184.00
4         Compañía Y   860.00
5         Compañía Z  3625.25
6              (all) 19974.25

Más consultas

Jugamos con los elementos anteriores, tomados de reshape2, plyr, y los operadores lógicos. Practicamos la sintaxis e interrogamos nuestros datos hasta que confiesen.1

6. Queremos saber las ventas de los empleados Francisco Chaves y Humberto Acevedo, agrupadas por categoría con su correspondiente subtotal por empleado. El resultado sería:

Añadimos dos identifiers (rótulos de fila), agrupamos por el total de categoría de cada empleado y filtramos por dos empleados, Francisco Chaves o Humberto Acevedo.

data.c <- dcast(data.m, empleado + categoría ~ variable, sum, margins = "categoría", subset = .(empleado == "Francisco Chaves" | empleado == "Humberto Acevedo")) 
                 empleado                   categoría   ventas
1       Francisco Chaves                      Aceite   533.75
2       Francisco Chaves                     Bebidas 15200.00
3       Francisco Chaves              Carne enlatada   552.00
4       Francisco Chaves                 Condimentos   660.00
5       Francisco Chaves                Frutos secos   105.00
6       Francisco Chaves     Mermeladas y confituras  2250.00
7       Francisco Chaves         Productos horneados   384.00
8       Francisco Chaves                       Sopas   289.50
9       Francisco Chaves                       (all) 19974.25
10      Humberto Acevedo                       Pasta  1950.00
11      Humberto Acevedo           Productos lácteos  1740.00
12      Humberto Acevedo                       Sopas    96.50
13      Humberto Acevedo                       (all)  3786.50
Más ejemplos:

# Por empleado y categoría, excluyendo la Compañía H de los clientes
data.c <- dcast(data.m, empleado + categoría ~ variable, sum, margins = c("categoría", "empleado"), subset = .(nombre.del.cliente != "Compañía H")) 

# Otras funciones de agregación: mínimo, media y máximo
# Ventas por nombre del cliente
data.c <- dcast(data.m, nombre.del.cliente ~ variable, min, fill = 0) 
data.c <- dcast(data.m, nombre.del.cliente ~ variable, mean, fill = 0)
data.c <- dcast(data.m, nombre.del.cliente ~ variable, max, fill = 0) 

Introducimos el argumento fill para evitar el mensaje de advertencia.

Fechas

7. La venta del empleado María Jesús Cuesta por producto entre dos fechas.

Opción 1

# Genera un error
data.c <- dcast(data.m, fecha.de.pedido + producto ~ variable, margins = c("fecha.de.pedido", "producto"), sum, subset = .(fecha.de.pedido > "2006-03-01" & fecha.de.pedido < "2006-05-01" & empleado == "María Jesús Cuesta")) 
str(data.c) # Estructura de un objeto
La inclusión de la fecha para los subtotales (margins) y del filtro por fecha genera un error. Y transforma fecha.de.pedido de Date a Factor. Se comprueba con la función str que muestra la estructura interna del objeto.

Warning messages:
1: In Ops.factor(fecha.de.pedido, "2006-03-01") :
  > not meaningful for factors
2: In Ops.factor(fecha.de.pedido, "2006-05-01") :
  < not meaningful for factors 

> str(data.c)
'data.frame': 1 obs. of  3 variables:
 $ fecha.de.pedido: Factor w/ 24 levels "2006-01-15","2006-01-20",..: NA
 $ producto       : Factor w/ 24 levels "Aceite de oliva Northwind Traders",..: NA
 $ NA             : num NA
Opción 2

# Subtotales incompletos
data.c <- dcast(data.m, as.Date(fecha.de.pedido) + producto ~ variable, sum, margins =c("fecha.de.pedido", "producto"), subset = .(fecha.de.pedido > "2006-03-01" & fecha.de.pedido < "2006-05-01" & empleado == "María Jesús Cuesta"))
data.c 
No genera un error pero presenta uno subtotales incompletos, solamente por producto.

  as.Date(fecha.de.pedido)                                 producto ventas
1       2006-03-24                       Té verde Northwind Traders    598
2       2006-03-24                                            (all)    598
3       2006-04-05 Galletas de chocolate surtidas Northwind Traders    230
4       2006-04-05                    Salsa curry Northwind Traders   1000
5       2006-04-05                                            (all)   1230
6       2006-04-25                        Almíbar Northwind Traders    500
7       2006-04-25                    Salsa curry Northwind Traders    120
8       2006-04-25                                            (all)    620
Opción 3

# Resultado correcto
fecha <- data.m$fecha.de.pedido
data.d <- data.m[fecha > "2006-03-01" & fecha < "2006-05-01", ]
data.c <- dcast(data.d, fecha.de.pedido + producto ~ variable, sum, margins = c("fecha.de.pedido", "producto"), subset = .(empleado == "María Jesús Cuesta"))
data.c
Filtramos previamente el molten data frame creado anteriormente con melt (punto 4).

  fecha.de.pedido                                         producto ventas
1      2006-03-24                       Té verde Northwind Traders    598
2      2006-03-24                                            (all)    598
3      2006-04-05 Galletas de chocolate surtidas Northwind Traders    230
4      2006-04-05                    Salsa curry Northwind Traders   1000
5      2006-04-05                                            (all)   1230
6      2006-04-25                        Almíbar Northwind Traders    500
7      2006-04-25                    Salsa curry Northwind Traders    120
8      2006-04-25                                            (all)    620
9           (all)                                            (all)   2448
Referencias:
Introducción y manual


1 Ronald Coase: "If you torture the data enough, nature will always confess."

No hay comentarios:

Publicar un comentario

Nube de datos