Animar distintos ordenamientos para un gráfico de barras con gganimate

En este post vamos a ver cómo animar un gráfico de barras, de tal forma que en cada cambio de la animación se muestre un orden distinto de las barras: alfabético, por conteo (absoluto) y por una categoría y conteo.

Para el ejemplo vamos a utilizar el conjunto de datos mpg de ggplot2, y vamos a jugar con las variables class y drv para realizar los cálculos.

Preparar los datos

Necesitamos crear una estructura de datos que permita que cada elemento de nuestro interés tenga tres ordenamientos distintos. Podemos hacer esto de dos formas:

  • Creando una variable para cada orden
  • Triplicando las filas y almacenando los tres valores de cada barra en una única variable.

Para conseguir una animación en la que las barras se desplacen a la posición correspondiente tenemos que optar por el segundo tipo de estructura, ya que el tipo de transición que generará la animación que nos interesa es transition_states(), que genera un estado de la animación por cada valor distinto de una variable.

Si cruzamos las variables class y drv obtenemos 12 clases distintas, que son las que vamos a ordenar de tres formas distintas. Para poder hacerlo, tenemos que realizar las siguientes transformaciones:

  • Agrupar el conjunto de datos original por esas dos variables
    • Creamos una variable textual que concatena los valores de las dos variables
    • Realizamos el recuento de casos
  • Desagrupamos el conjunto de datos
  • Lo ordenamos por la nueva variable que hemos creado (al ser textual, se ordenará alfabéticamente).
  • Creamos una nueva variable orden que almacene el número de fila (que es el que usaremos para ordenar las barras).

Para cada nuevo ordenamiento que queramos crear hay que crear un nuevo conjunto de datos (basado en el primero que ya hemos creado), para lo que realizaremos los mismos dos pasos por cada conjunto ordenado que queramos crear:

  • Ordenar según el criterio de interés
  • Almacenar el número de fila en la variable orden.

Finalmente, una vez que tengamos todos los conjuntos de datos preparados, no nos queda más que combinar todos los (sub)conjuntos de datos con la función dplyr::bind_rows(). Usamos el argumento .id para identificar cada subconjunto de datos y transformamos los valores numéricos en textos explicativos que luego usaremos a modo de título en la animación.

datos1 <- mpg %>% 
  group_by(drv, class) %>% 
  mutate(class_drv = paste0(class, " - ", drv)) %>% 
  summarise(count = n(),
            class_drv = unique(class_drv)) %>% 
  ungroup() %>% 
  arrange(class_drv) %>% 
  mutate(orden = row_number())

datos2 <- datos1 %>% 
  arrange(count) %>% 
  mutate(orden = row_number())

datos3 <- datos1 %>% 
  arrange(drv, count) %>% 
  mutate(orden = row_number())

datos <- datos1 %>% 
  bind_rows(datos2, datos3, .id = "estado") %>% 
  mutate(estado = case_when(
    estado == 1 ~ "Alfabético",
    estado == 2 ~ "Total",
    TRUE ~ "drv"
  ))

Diseñar el gráfico

A la hora de crear el gráfico vamos a limitarnos a la configuración por defecto de ggplot2, aunque vamos a tener que realizar algunas modificaciones para que las barras estén debidamente identificadas.

La idea es que para ordenar las barras vamos a usar la variable orden, que tiene 12 valores distintos para un total de 36 barras (que en realidad son en todo momento las mismas barras, los datos no cambian, únicamente el orden).

Al usar orden como variable para posicionar las barras, obtenemos etiquetas incorrectas para su eje; queremos sustituir las cifras de 1 a 12 por el texto correspondiente a cada barra… y ese texto tendrá que desplazarse con la barra si ésta cambia de posición, por lo que no pueden ser etiquetas del eje.

Para poder solventar esta cuestión, vamos a eliminar las etiquetas del eje, y añadir los textos que necesitamos como un capa geom_text() (al ser datos, la transición les afectará de la misma forma que a las barras). Para que tengan aspecto de etiquetas de eje, modificamos la escala para poder alinearlos a la izquierda de las barras.

Antes de animar el gráfico, las barras y los textos se verán solapados.

(barras <- ggplot(datos, aes(desc(orden), count, fill = drv)) +
  geom_col() +
  geom_text(aes(label = class_drv, y = -1), hjust = 1) +
  coord_flip() +
  scale_y_continuous(limits = c(-12, max(datos$count))) +
  theme(axis.text.y = element_blank())
)
## Warning: Removed 10 rows containing missing values (geom_col).

La animación

Una vez tenemos el gráfico base, la animación en si es bastante simple: como ya se ha dicho, usaremos transition_states(), que crea los estados de la animación a partir de los niveles de una variable (y afecta a todas las capas por igual).

anim <- barras + 
  labs(title = "Orden: {closest_state}") +
  transition_states(estado, transition_length = 1, state_length = 3)

animate(anim, nframes = 100)


Más posts