R Notebook: Gráfico barras animado (Un primer intento)
Objetivo
Se ha viralizado los gráfico de barras horizontales que van mostrando un ranking (sea de ventas, valor de acciones, goles, etc.), y se muestra una animación de cómo cambia ese ranking por año o mes.
Preliminares
Datos
Vamos a crear una estructura básica en empresas comerciales:
ALMACEN | PRODUCTO | PERIODO | VENTA.
library(tidyverse)
library(lubridate)
library(ggplot2)
library(gganimate)
set.seed(123)
df_1 <- tbl_df( data.frame(
ALMACEN= rep(c("Mall del Sol", "Riocentro"), each= 15),
PRODUCTO= paste("Prod", LETTERS[1:15], sep="_"),
PERIODO= rep(seq.Date(from=as.Date("2015-01-01"), to=as.Date("2017-06-01"), by="month"), each=30),
VTA= round(runif(n = 900, min = 1000, max= 7000),0),
stringsAsFactors = FALSE
) )
glimpse(df_1)
## Observations: 900 ## Variables: 4 ## $ ALMACEN <chr> "Mall del Sol", "Mall del Sol", "Mall del Sol", "Mall... ## $ PRODUCTO <chr> "Prod_A", "Prod_B", "Prod_C", "Prod_D", "Prod_E", "Pr... ## $ PERIODO <date> 2015-01-01, 2015-01-01, 2015-01-01, 2015-01-01, 2015... ## $ VTA <dbl> 2725, 5730, 3454, 6298, 6643, 1273, 4169, 6355, 4309,...
Agrupamiento
Se va a realizar un ranking de los productos por mes, eso implica que necesitamos
- Calcular el total de venta mensual de cada producto (lo que implica hacer un arupamiento por Producto, periodo)
- Obtener el ranking de los productos por mes (para ello debemos hacer un agrupamiento por periodo)
Usando el tidyverse sería:
df_resumen <- df_1 %>%
filter(year(PERIODO)== 2015) %>%
group_by( PRODUCTO, PERIODO) %>% ## Agrupar por Producto, Periodo
summarise(VENTA= sum(VTA)) %>% ## Sumar la venta por Producto, Periodo
group_by( PERIODO) %>% ## Agrupar por Periodo
mutate( RANKING= min_rank(-VENTA)) ## Obtener el ranking de prod por Periodo
glimpse(df_resumen)
## Observations: 180
## Variables: 4
## $ PRODUCTO <chr> "Prod_A", "Prod_A", "Prod_A", "Prod_A", "Prod_A", "Pr...
## $ PERIODO <date> 2015-01-01, 2015-02-01, 2015-03-01, 2015-04-01, 2015...
## $ VENTA <dbl> 9124, 8611, 7312, 8126, 9846, 10287, 9838, 7263, 9993...
## $ RANKING <int> 8, 6, 10, 9, 2, 1, 3, 9, 4, 4, 1, 9, 10, 5, 15, 1, 6,...
Animación
Un primer gráfico
Vamos a escoger un mes en específico y armar un gráfico que será el que animaremos, entendiendo la lógica de ggplot2, requerimos:
1. En un eje el ranking (pero como el uno debe estar arriba, entonces lo ponemos en negativo), 2. En el otro eje la venta 3. Con esto se hace un gráfico de barras 4. Los labels se los agrega con geom_text 5. El gráfico de barras por default es vertical, pero lo queremos horizontal por lo que se hace un flip.
ggplot(df_resumen %>% filter(PERIODO==ymd("2015-01-01")), aes(x = -RANKING, y = VENTA, fill = PRODUCTO)) +
geom_bar(stat = "identity") +
labs(title = "Ranking de Productos", subtitle = "Mes: 2015-01-01") +
geom_text(aes(label = PRODUCTO, y = VENTA + 750 ),
position = position_dodge(0.9), vjust = 0.35) +
coord_flip()
Lo pulimos un poco más y con esto tenemos nuestra base para la animación
ggplot(df_resumen %>% filter(PERIODO==ymd("2015-01-01")), aes(x = -RANKING, y = VENTA, fill = PRODUCTO)) +
geom_bar(stat = "identity") +
labs(title = "Ranking de Productos", subtitle = "Mes: 2015-01-01") +
geom_text(aes(label = PRODUCTO, y = VENTA + 750 ),
position = position_stack(vjust = 1.01)) +
coord_flip() +
theme(axis.ticks.y=element_blank(),
axis.text.y=element_blank())
Animación
La idea es generar un gráfico por periodo, por lo que se define que los estados de la transición sea Periodo, nótese la animación en el subtitulo.
gbar <- ggplot(df_resumen, aes(x = -RANKING, y = VENTA, fill = PRODUCTO)) +
geom_bar(stat = "identity") +
labs(title = "Ranking de Productos", subtitle = "Mes: {closest_state}") +
geom_text(aes(label = PRODUCTO, y = VENTA + 750 ),
position = position_stack(vjust = 1.01)) +
coord_flip() +
theme(axis.ticks.y=element_blank(),
axis.text.y=element_blank()) +
transition_states(states = PERIODO, transition_length = 2, state_length = 1) +
enter_fade() +
exit_shrink() +
ease_aes('sine-in-out')
gbar_gif <- animate(gbar, width = 940, height = 480)
gbar_gif
El resultado es:
En esta entrada se ha usado geom_bar() para la animación, pero podemos utilizar otra geometría, eso se revisará en nuestra segunda aproximación.