Anotaciones

En este capítulo vamos a aprender cómo añadir una anotación en una visualización de ggplot. ¡Empezamos!

Como primer paso, cargaremos los datos que ya hemos utilizado en el capítulo sobre cómo cambiar el estilo de los ejes en ggplot. Se trata de un dataset de Kaggle que contiene todos los metadatos de las películas y series de Netflix hasta el mes de Enero de 2021.

Cargamos también la librería de tidyverse, que incluye ggplot2 y dplyr, así como la librería lubridate, para manipular fechas fácilmente.

library(tidyverse)
library(lubridate)

df <- read.csv("https://raw.githubusercontent.com/elartedeldato/datasets/main/netflix_titles.csv")

df |>
  filter(str_detect(description, "high school")) |>
  mutate(year_added := year(mdy(date_added))) |>
  filter(!is.na(year_added)) |>
  group_by(year_added) |>
  count() -> df_year

No nos olvidemos de inicializar el tema de Netflix que hemos creado en el apartado sobre cómo modificar los ejes tipo fecha en ggplot.

library(ggplot2)

theme_set(theme_netflix())

Para el gráfico, hemos seleccionado el total de películas o shows de instituto por año, filtrando en la descripción del programa ‘high school’.

ggplot(df_year) +
  geom_col(aes(x = year_added, y = n), fill = "red") +
  scale_x_continuous(breaks = df_year$year_added) +
  labs(
    title = "Netflix",
    subtitle = "Películas y series de Instituto",
    caption = "Fuente: Kaggle. elartedeldato.com"
  ) -> p
p

En esta visualización hemos empleado conocimientos de ggplot ya visto anteriormente:

Cómo añadir una anotación en ggplot2

A continuación, remarcaremos que los datos “llegan” hasta enero de 2021, motivo por el cual la última barra del plot es más corta que las anteriores y no sigue la misma tendencia. Para ello, añadimos una anotación de texto sobre el gráfico indicando que se trata sólo de dicho mes.

En ggplot, se emplea la función annotate('text'), donde el argumento label definirá exactamente el texto a añadir.

p + annotate("text",
  label = "Enero",
  x = 2021, y = 10, hjust = 0.5, vjust = 0, family = "Permanent Marker", size = 4, color = "white"
)

Si nos fijamos, las coordenadas x e y definen la posición en el panel donde irá situada la anotación. Por otro lado, hjust se refiere a la justificación horizontal del texto: a la izquierda, centrado o justificado a la derecha. Entonces, hjust=0.5 justifica el texto en el centro y vjust=0 lo justifica verticalmente en la parte superior. El resto de argumentos se refieren a la fuente, tamaño y color del texto respectivamente.

Cómo girar una anotación en ggplot2

Aunque ya podríamos mantener así la anotación, parece que la tipografía y el estilo desenfadado de la visualización piden girar el texto y enlazarlo a la barra de alguna manera. En este punto veremos cómo girar una anotación y en el siguiente, cómo añadir una flecha de enlace y personalizarla.

Girar una anotación es muy sencillo, simplemente basta con especificar angle=45, donde 45 son los grados del giro.

p + 
  annotate(
    geom = "text",
    label = "Enero",
    x = 2021, y = 10, hjust = 0.5, vjust = 0, 
    family = "Permanent Marker", size = 4, color = "white", angle = 45
    ) -> p
p

Cómo añadir una flecha como anotación en ggplot2

Ya por último, vamos a ver cómo añadir una flecha que enlace la anotación con la barra. Para ello, con la misma función annotate() espcificamos 'curve' y generará una curva en la posición del gráfico que le indiquemos.

p + annotate("curve", x = 2021, y = 9, xend = 2021, yend = 5, color = "white")

Sin embargo, la curvatura no es la deseada. Para modificarla, empleamos el argumento curvature=-0.5 que invertirá la curva.

p + annotate("curve", x = 2021, y = 9, xend = 2021, yend = 5, color = "white", curvature = -0.5)

Finalmente, si queremos que la línea termine en forma de flecha, debemos definir el argumento arrow con la función arrow().

p + annotate("curve", x = 2021, y = 9, xend = 2021, yend = 5, color = "white", curvature = -0.5, arrow = arrow(length = unit(0.03, "npc")))

Al añadir texto, es común que algunas palabras se solapen entre sí, llevando, dificultando la comprensión del gráfico. En esta sección, aprenderemos a evitar el solapamiento de texto en ggplot, algo muy habitual cuando se trabaja con etiquetas en un gráfico. ¡Vamos a ello!

Utilizamos el dataset de la última sección con nuevas variables: el año de estreno de la película y su duración en minutos. Así, representaremos una variable frente a la otra y, a modo de ejemplo, añadiremos las etiquetas del top 3 de títulos de mayor y menor duración.

df <- read.csv("https://raw.githubusercontent.com/elartedeldato/datasets/main/netflix_titles.csv")

df |>
  filter(str_detect(description, "high school"), type == "Movie") |>
  mutate(duration := as.numeric(str_remove(duration, " min"))) |>
  select(release_year, duration, title) -> df_title

df_title |>
  mutate(top_duration := rank(desc(duration))) -> df_title

ggplot(df_title) +
  geom_point(aes(x = release_year, y = duration), color = "red") +
  labs(
    title = "Netflix",
    subtitle = "Duración de las películas sobre High School por año de estreno",
    caption = "Fuente: Kaggle. elartedeldato.com",
    x = "Año de estreno", y = "Duración en min"
  ) -> p

p

Cómo añadir etiquetas de texto

Existen dos geometrías en ggplot que permiten añadir texto. Una es, geom_text() y la otra geom_label(). Ambas están pensadas para visualizar variables de tipo carácter de un dataset, aunque la segunda dibuja a mayores una caja rectangular que contiene el texto, facilitando así su lectura.

p + geom_text(
  data = subset(df_title, top_duration <= 3 | top_duration >= 78),
  aes(x = release_year, y = duration, label = title),
  family = "Permanent Marker", size = 4, color = "white", nudge_y = 3
)

Como vemos, es complicado discernir los títulos entre sí y entender el texto. Además, en las etiquetas situadas muy cerca de los límites del panel se meustran cortadas. Ésto nos obliga a expandir los límites del gráfico y mover manualmente cada etiqueta, lo cuál es posible, pero sin duda muy engorroso.

También hemos incluido el argumento nudge_y = 3, para “subir” el texto 3 puntos verticalmente, y no solaparse con los putnos del gráfico, pero aún así los problemas de lectura permanecen.

Cómo evitar solapamiento del texto con ggrepel

El solapamiento del texto se puede evitar utilizando una de las librerías extra de ggplot más conocidas: ggrepel. Esta librería proporciona dos geometrías equivalentes a las anteriormente nombradas, véase geom_text_repel() y geom_label_repel() con las mismas diferencias y similitudes que las originales de ggplot.

La salvedad, es que automáticamente ajustan el espacio entre elementos del gráfico y sus márgenes y sitúan generalmente en posiciones visibles las diferentes etiquetas. Decimos generalmente porque realmente hay situaciones en las que es difícil evitar un reajuste manual de la posiciones de los labels.

library(ggrepel)

p + geom_text_repel(
  data = subset(df_title, top_duration <= 3 | top_duration >= 78),
  aes(x = release_year, y = duration, label = title),
  family = "Permanent Marker", size = 4, color = "white"
)

Nota

Recuerda añadir la fuente de Google con

library(showtext)
font_add_google("Permanent Marker")
showtext_auto()

Cómo alejar las etiquetas de texto

Otra idea para que quede más claro a qué punto se refiere cada etiqueta, es expandir el tamaño de la caja contenedora (invisible) de la geometría geom_text_repel, aumentando el box.padding.

p + geom_text_repel(
  data = subset(
    df_title,
    top_duration <= 3 | top_duration >= 78
  ),
  aes(x = release_year, y = duration, label = title),
  family = "Permanent Marker", size = 4, color = "white", box.padding = 0.6
)

En resumen…
  • Añadimos una anotación de texto con annotate('text')
  • Giramos una anotación con annotate(angle = ...)
  • Añadimos una flecha como anotación con annotate('curve', arrow = arrow(...), curvature=...)
  • Añadimos etiquetas de texto con geom_text() o geom_label()
  • Evitamos solapamiento del texto con geom_text_repel(data = ...)
  • Alejamos las etiquetas con geom_text_repel(box.padding = ...)