Ejes

Hasta el momento hablamos de algunos de los aspectos esenciales de un gráfico como son el título, la leyenda, el color o el tema del gráfico, pero se nos escapa uno de los principales asuntos en visualización de datos: los ejes.

En este capítulo veremos cómo reordenar una variable asociada a un eje, cómo trabajar con el formato fecha en los ejes y cómo añadir unidades a los ejes.

Cómo reordenar una variable

Primero, partimos del gráfico de barras que representa el número medio de horas de sueño de un mamífero clasificado por el tipo de alimentación.

library(dplyr)
library(ggplot2)

theme_set(theme_elartedeldato())

msleep$vore[is.na(msleep$vore)] <- "desconocido"
msleep$vore <- as.factor(msleep$vore)
levels(msleep$vore) <- c("carnívoro", "desconocido", "herbívoro", "insectívoro", "omnívoro")

msleep |>
  group_by(vore) |>
  summarise(avg_sleep := mean(sleep_total)) |>
  
  ggplot(aes(x = vore, y = avg_sleep)) +
    geom_col(fill = "#222222") +
    theme_minimal() +
    labs(
      title = "El sueño de los mamíferos",
      subtitle = "Media de horas de sueño por tipo de mamífero según alimentación",
      caption = "Proceedings of the National Academy of Sciences, 104 (3):1051-1056, 2007. elartedeldato.com",
      x = "", y = ""
    ) 

Por defecto, el eje x está ordenado de forma alfabética, siguiendo el orden del factor. En caso, de trabajar con una variable de tipo character, ggplot también seguiría el orden alfabético.

Sin embargo, en un gráfico de barras la información se suele ordenar según la variable cuantitativa representada, en este caso, las horas de sueño medio que el tipo de animal duerme. Por lo que podríamos:

  • Bien reordenar el factor en el propio dataset a través de la función factor() y el argumento levels.

  • O bien reordenar la variable dentro de ggplot directamente con la función reorder().

La segunda opción es la más aconsejable ya que no modifica el dataset original y afecta sólo al gráfico, lo cuál es la intención última de la ordenación.

msleep |>
  group_by(vore) |>
  summarise(avg_sleep := mean(sleep_total)) |>
  
  ggplot(aes(x = reorder(vore, desc(avg_sleep)), y = avg_sleep)) +
    geom_col(fill = "#222222") +
    theme_minimal() +
    labs(
      title = "El sueño de los mamíferos",
      subtitle = "Media de horas de sueño por tipo de mamífero según alimentación",
      caption = "Proceedings of the National Academy of Sciences, 104 (3):1051-1056, 2007. el artedeldato.com",
      x = "", y = ""
    ) 

Como vemos, el eje x quedaría ordenado y la información queda mejor representada.

Nota

Si nos fijamos, al principio hemos cambiado el valor desconocido, NA, por un valor concreto. El motivo del cambio es que ggplot representa los NA al final del gráfico siempre, sea cual sea la ordenación indicada. Esto puede interesarnos o no, dependiendo del caso. Para el ejemplo, consideramos más apropiado añadirlo como una categoría más, pero tengamos en cuenta que si ocurre, no hay que alarmarse!

Cómo modificar los ejes tipo fecha en ggplot

Trabajar con variables de tipo fecha es una de las tareas más recurrentes en el manejo de datos. Son variables muy comunes que pueden aparecer de mil maneras diferentes, por eso es interesante tener claro cómo trabajar con ellas a la hora de añadirlas en un gráfico. En esta sección hablaremos sobre cómo modificar el formato de los ejes tipo fecha en ggplot.

De aquí en adelante, trabajaremos con un nuevo dataset. Se trata de un csv que contiene información sobre las películas y series de Netflix desde 2008. Incluye títulos, una breve descripción y la fecha de lanzamiento, entre otros. Nosotros nos centraremos concretamente en la fecha en la que las películas de Netflix fueron añadidas a la plataforma.

library(reactable)

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

reactable(head(df), resizable = TRUE, wrap = FALSE)
df |>
  filter(type == "Movie") |>
  group_by(date_added) |>
  count() -> df_netflix_movies

Por un lado, la variable date_added es de tipo character, por lo que debemos convertirla a formato fecha, date. Para ello recurrimos a la función as.Date() y especificamos el formato en el que aparece en el dataset, '%B %d, %Y'.

Si no sabes cómo especificar un formato fecha en R, puedes consultarlo aquí.

head(df_netflix_movies$date_added)
[1] "April 1, 2016"  "April 1, 2017"  "April 1, 2018"  "April 1, 2019" 
[5] "April 1, 2020"  "April 10, 2017"
df_netflix_movies$date_added <- as.Date(df_netflix_movies$date_added, format = "%B %d, %Y")
head(df_netflix_movies$date_added)
[1] "2016-04-01" "2017-04-01" "2018-04-01" "2019-04-01" "2020-04-01"
[6] "2017-04-10"

A continuación, agrupamos la información mensualmente utilizando format() y realizamos un gráfico de líneas en ggplot con geom_line().

df_netflix_movies$month <- as.Date(format(df_netflix_movies$date_added, "%Y-%m-01"))

df_netflix_movies |>
  filter(month < "2021-01-01", month >= "2020-01-01") |>
  group_by(month) |>
  summarise(n := sum(n)) |>
  
  ggplot(aes(month, n)) +
    geom_line(color = "red") +
    geom_point(color = "red") +
    labs(
      title = "Netflix 2020",
      subtitle = "Total de películas añadidas a la plataforma durante el año 2020",
      x = "", y = ""
    ) -> p
p

Pero este gráfico no tiene un estilo muy netflix, ¿verdad? Vamos a crear un tema de Netflix en ggplot con R. ¿Recuerdas cómo crear temas?

library(showtext)

font_add_google("Bebas Neue")
font_add_google("Permanent Marker")

showtext_auto()

theme_netflix <- function() {
  main_font <- "Bebas Neue"
  secondary_font <- "Permanent Marker"
  
  theme_bw() +
  theme(
    text = element_text(family = main_font),
    
    panel.border = element_blank(),
    panel.background = element_rect(fill = "black"),
    panel.grid = element_blank(),
    panel.grid.major.y = element_line(size = 0.05, color = "white"),
    
    axis.title = element_blank(),
    axis.text = element_text(family = secondary_font, size = 10, color = "white"),
    axis.ticks = element_blank(),

    plot.title = element_text(family = main_font, color = "red", size = 45, 
                              margin = margin(15, 0, 5, 0)),
    plot.subtitle = element_text(family = secondary_font, size = 10, color = "white"),
    plot.caption = element_text(family = secondary_font, color = "white", size = 7, 
                                margin = margin(30, 0, 0, 0)),
    plot.background = element_rect(fill = "black", color = "black"),
                                   
    legend.position = "none"
    ) 
}

theme_set(theme_netflix())

Cómo modificar los breaks del eje x

Una vez realizado el gráfico base, revisamos los breaks de los ejes. Como vemos, el eje x consta de cuatro cortes, cuando tal vez tendría más sentido que las marcas fueran mes a mes.

Para modificar los breaks del eje tipo fecha, se emplea el argumento date_breaks.

Este argumento se puede indicar en lenguaje natural la distancia temporal entre marcas del eje como 2 months o 1 year, eso sí, en inglés.

p + scale_x_date(date_breaks = "2 months")

Cómo cambiar el formato fecha el eje x

Por defecto, el formato de la fecha es el nombre abreviado del mes y el año, y al modificar los breaks, la fecha con el formato original en el dataset. Entonces, si quisiéramos modificar el formato del eje, aplicamos la función scale_x_date() especificando en el argumento date_labels el formato que deseamos.

Por ejemplo, para añadir el formato Mes/Año, tal que mm/yyyy:

p + scale_x_date(date_labels = "%m/%Y", date_breaks = "2 months")

Otra opción, sería indicar el nombre del mes completo y año, véase "%B %Y".

p + scale_x_date(date_labels = "%B %Y", date_breaks = "2 months")

Si se solapan los nombres y creemos conveniente mantener los breaks de forma mensual, no hay problema. En ggplot, se puede evitar el solapamiento con guide = guide_axis(n.dodge = 2).

p + scale_x_date(date_labels = "%B %Y", date_breaks = "1 month", guide = guide_axis(n.dodge = 2))

Cómo cambiar el color de las etiquetas de los ejes

Hemos visto cómo modificar el formato de los ejes tipo fecha en ggplot, utilizando los datos sobre el total de películas añadidas a Netflix durante el año 2020. Ahora veremos, cómo modificar el estilo del texto de los ejes, que como ya sabemos de antemano, haremos uso de la función theme().

Para cambiar el color del texto de las etiquetas de los ejes, se emplea el argumento axis.text. En este argumento, indicamos con la función element_text() el color deseado.

p + theme(axis.text = element_text(color = "red"))

Advertencia

No confundir con el color del título del eje, que se modificaría con axis.title.

Si sólo quisiéramos modificar uno de los ejes, axis.text.x o bien, axis.text.y.

Cómo cambiar la fuente de las etiquetas de los ejes

La fuente se modifica a través del argumento family. Por tanto, al tratarse de la fuente de las etiquetas de los ejes, basta con indicarlo de nuevo en la función element_text() del argumento axis.text.

p + theme(axis.text = element_text(family = "mono"))

Cómo aumentar el tamaño de letra de los ejes

Por otro lado, el tamaño de letra de los ejes se puede aumentar o disminuir con el argumento size.

p + theme(axis.text = element_text(size = 20))

Cómo girar las etiquetas de los ejes

Finalmente, uno de los recursos más útiles es saber cómo indicar un ángulo de texto diferente al estándar. Esto de hace a través del argumento angle, véase, ángulo en inglés. Los ángulos más habituales son 45 o 90 grados.

p + theme(axis.text = element_text(angle = 45))

p + theme(axis.text = element_text(angle = 90))

Cómo añadir unidades en los ejes en ggplot

Tras haber visto cómo modificar los ejes, ahora descubriremos cómo añadir unidades en los ejes en ggplot.

Principalmente, existen dos maneras de añadir unidades en un gráfico de ggplot:

  1. Una, utilizando directamente las funciones del paquete base, “pegando” la unidad en sí.

  2. Otra, a través de la librería scales. Con esta librería podremos añadir formatos estándar habituales como son los porcentajes, notación matemática, bytes, símbolo de moneda, entre otros.

La última opción es más recomendada, ya que aunque depende de una librería externa, es “más limpia”. Veamos en detalle cada una de ellas.

Añadiendo manualmente la unidad

Esta vez, generamos 100 datos aleatorios y los representamos sobre un gráfico de líneas.

df <- data.frame(x = 1:100, y = abs(rnorm(100) * 1000))

ggplot(df, aes(x, y)) +
  geom_line() +
  theme_minimal() -> p
p

Imaginemos que el eje y se refiere a cantidad de dólares y queremos añadir el símbolo del dólar a cada una de las etiquetas del eje. Crear una función que “pegue” el correspondiente símbolo sería la solución: function(x) paste0('$', x)

Añadir símbolo del dólar

p + scale_y_continuous(label = function(x) paste0("$", x))

Añadir el símbolo del porcentaje

La misma técnica se aplicaría si trabajamos con porcentajes.

p + scale_y_continuous(label = function(x) paste0(x, "%"))

Añadir prefijos del Sistema Internacional

Ahora bien. Si por ejemplo, queremos abreviar las etiquetas siguiendo el Sistema Internacional de prefijos, debemos dividir entre 1000 y añadir el sufijo k.

p + scale_y_continuous(label = function(x) paste0(x / 1000, "K"))

Sin embargo, necesitamos nosotros manualmente redondear la unidad. Con la siguiente solución veremos que este punto nos lo podemos ahorrar.

Utilizando la librería scales

Ya hemos utilizado la librería scales en otras ocasiones. Por ejemplo para añadir expresiones en la leyenda del gráfico. En esta ocasión, la utilizaremos para indicar las unidades al eje y.

Añadir símbolo del dólar

library(scales)
p + scale_y_continuous(labels = scales::label_dollar())

Para los euros, deberemos crear una función específica.

euro <- label_dollar(
  prefix = "",
  suffix = "\u20ac",
  big.mark = ".",
  decimal.mark = ","
)

p + scale_y_continuous(labels = euro)

Añadir el símbolo del porcentaje

p + scale_y_continuous(labels = label_percent(scale = 1))

Nótese que el valor queda multiplicado por 100 si no se indica scale=1.

Añadir prefijos del Sistema Internacional

p + scale_y_continuous(labels = label_number_si())

Otras posibilidades son, si se trata de gramos, label_number_si('g'), o bien, de metros, label_number_si('m').

En resumen…
  • Modificamos el formato fecha con el argumento date_labels de la función scale_x_date()
  • Los breaks se pueden cambiar en lenguaje natural con el argumento date_breaks
  • Para evitar el solapamiento del texto en el eje, guide = guide_axis(n.dodge = ...)
  • Cambiamos el color de las etiquetas de los ejes con theme(axis.text = element_text(color='red'))
  • Modificamos el estilo de fuente de los ejes con theme(axis.text = element_text(family=...))
  • Aumentamos el tamaño de letra de los ejes con theme(axis.text = element_text(size=...))
  • Giramos las etiquetas de los ejes con theme(axis.text = element_text(angle = ...))
  • Añadimos símbolo del dólar con scale_y_continuous(label = function(x) paste0('$', x)) o bien con scale_y_continuous(labels = scales::label_dollar())
  • Añadimos el símbolo del porcentaje con scale_y_continuous(label = function(x) paste0(x,'%')) o bien con scale_y_continuous(labels = label_percent(scale=1))
  • Añadimos prefijos del Sistema Internacional con scale_y_continuous(labels = label_number_si())