Gráficos con ggplot2 (B): más elementos

Programación y manejo de datos con R (Slides 06-B)

Pedro J. Pérez

Más elementos de un ggplot()

  • Ya hemos presentado los principales elementos de los gráficos hechos con ggplot2, pero es evidente que un gráfico tiene más elementos

  • Lógicamente, hay que conocerlos un poco para poder ajustar los gráficos a nuestras necesidades y mejorar su calidad


Veremos algo de los siguientes elementos:

  • Títulos del gráfico
  • Themes
  • Small multiples (o Facetting)
  • Anotaciones
  • Ejes
  • Escalas
  • Stats (transformaciones estadísticas)
  • Position adjustments
  • Coordenadas

Añadiendo títulos, leyenda, etc …a un ggplot

p <- ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) +
        geom_point()
p +
p +
labs(title = "Gráfico 1: Longitud del sépalo frente al pétalo") +
p +
labs(title = "Gráfico 1: Longitud del sépalo frente al pétalo") +
labs(subtitle = "(diferenciando por especie de lirio)") +
p +
labs(title = "Gráfico 1: Longitud del sépalo frente al pétalo") +
labs(subtitle = "(diferenciando por especie de lirio)") +
labs(caption = "Datos provenientes del Iris dataset") +
p +
labs(title = "Gráfico 1: Longitud del sépalo frente al pétalo") +
labs(subtitle = "(diferenciando por especie de lirio)") +
labs(caption = "Datos provenientes del Iris dataset") +
labs(x = "Longitud del sépalo") +
p +
labs(title = "Gráfico 1: Longitud del sépalo frente al pétalo") +
labs(subtitle = "(diferenciando por especie de lirio)") +
labs(caption = "Datos provenientes del Iris dataset") +
labs(x = "Longitud del sépalo") +
labs(y = "Longitud del pétalo") +
p +
labs(title = "Gráfico 1: Longitud del sépalo frente al pétalo") +
labs(subtitle = "(diferenciando por especie de lirio)") +
labs(caption = "Datos provenientes del Iris dataset") +
labs(x = "Longitud del sépalo") +
labs(y = "Longitud del pétalo") +
labs(color = "Especie de lirio") +
p +
labs(title = "Gráfico 1: Longitud del sépalo frente al pétalo") +
labs(subtitle = "(diferenciando por especie de lirio)") +
labs(caption = "Datos provenientes del Iris dataset") +
labs(x = "Longitud del sépalo") +
labs(y = "Longitud del pétalo") +
labs(color = "Especie de lirio") +
labs(tag = "Plot 1")

El chunk no se suele escribir de esa forma, sino así. Lo guardamos como p_t: a partir de ahora usaremos p (sin títulos) y p_t (con títulos)

p_t <- p +
labs(title = "Gráfico 1: Longitud del sépalo frente al pétalo",
    subtitle = "(diferenciando por especie de lirio)",
    caption = "Datos provenientes del Iris dataset",
    x = "Longitud del sépalo",
    y = "Longitud del pétalo",
    color = "Especie de lirio",
    tag = "Plot 1")

A veces interesa eliminar elementos de los labels/títulos

p_t +
p_t +
labs(color = NULL) + #- borra el título de la leyenda
p_t +
labs(color = NULL) + #- borra el título de la leyenda
labs(x = NULL) + #- borra el título del eje X
p_t +
labs(color = NULL) + #- borra el título de la leyenda
labs(x = NULL) + #- borra el título del eje X
labs(subtitle = NULL) +
p_t +
labs(color = NULL) + #- borra el título de la leyenda
labs(x = NULL) + #- borra el título del eje X
labs(subtitle = NULL) +
labs(caption = NULL) +
p_t +
labs(color = NULL) + #- borra el título de la leyenda
labs(x = NULL) + #- borra el título del eje X
labs(subtitle = NULL) +
labs(caption = NULL) +
labs(title = NULL) +
p_t +
labs(color = NULL) + #- borra el título de la leyenda
labs(x = NULL) + #- borra el título del eje X
labs(subtitle = NULL) +
labs(caption = NULL) +
labs(title = NULL) +
labs(y = NULL)  + #- borra el título del eje Y
p_t +
labs(color = NULL) + #- borra el título de la leyenda
labs(x = NULL) + #- borra el título del eje X
labs(subtitle = NULL) +
labs(caption = NULL) +
labs(title = NULL) +
labs(y = NULL)  + #- borra el título del eje Y
labs(tag = NULL)

  • Para los labels de los ejes X e Y es muy frecuente usar: xlab(NULL) y ylab(NULL)

  • Queda la leyenda. La leyenda no es un elemento de los labels/título, sino del theme. Se puede eliminar con: theme(legend.position = "none")

Theme: cambiando la apariencia del gráfico

  • Podemos cambiar los detalles de la apariencia del gráfico; por ejemplo, el tamaño, fuente y color de los títulos, pero también de los puntos, las líneas, el fondo del gráfico, la apariencia de las grid-lines, el lugar para las leyendas, etc … etc …. Para hacerlo hay que usar las “funciones de tema”. Todas ellas comienzan con theme_()


  • En general con las funciones theme_() podemos cambiar/ajustar cualquier elemento del gráfico, con la excepción de la propia representación de los datos (ya sabemos que esto se hacen con las funciones geom_()). Estos elementos afectan a la apariencia y detalles del gráfico, pero no a la relación entre variables que se muestra realmente en el gráfico

Themes/Temas predefinidos

p_t +
p_t +
theme_gray()  + #- tema por defecto
p_t +
theme_gray()  + #- tema por defecto
theme_light() +
p_t +
theme_gray()  + #- tema por defecto
theme_light() +
theme_dark() +
p_t +
theme_gray()  + #- tema por defecto
theme_light() +
theme_dark() +
theme_classic() +
p_t +
theme_gray()  + #- tema por defecto
theme_light() +
theme_dark() +
theme_classic() +
theme_minimal() +
p_t +
theme_gray()  + #- tema por defecto
theme_light() +
theme_dark() +
theme_classic() +
theme_minimal() +
theme_void()

En el pkg ggthemes hay muchos themes

library(ggthemes)
library(ggthemes)
p_t +
library(ggthemes)
p_t +
theme_economist() +
library(ggthemes)
p_t +
theme_economist() +
theme_fivethirtyeight() +
library(ggthemes)
p_t +
theme_economist() +
theme_fivethirtyeight() +
theme_stata() +
library(ggthemes)
p_t +
theme_economist() +
theme_fivethirtyeight() +
theme_stata() +
theme_solarized() +
library(ggthemes)
p_t +
theme_economist() +
theme_fivethirtyeight() +
theme_stata() +
theme_solarized() +
theme_excel()

Si te interesa el tema de los themes. [Aquí] tienes el repo de ggthemes y [aquí] puedes ver más ejemplos de themes.

¿Puedo definir mi propio theme? Sí, absolutely.

my_theme <- theme(
  axis.text.x = element_text(colour = "red", angle = 45, hjust = 1) ,
  axis.text.y = element_text(colour = "#7700AA",  size = 14) ,
  text = element_text(size = 16))

p_t + my_theme



Podemos fijar el theme (para los siguientes gráficos)

#-podemos fijar un theme concreto
theme_set(theme_minimal())

# podemos fijar un theme y variar algún elemento (p.ej: eje X sin ticks ni escalas)
theme_set(theme_minimal() + theme(axis.text.x = element_blank()))

# si queremos volver al default theme
theme_set(theme_gray())

theme(): 3 tipos de elementos [ 🌟 ]

En ggplot2 se puede cambiar TODO de un gráfico con theme() (todo menos lo que tiene que ver con los datos),
pero para hacerlo hay que entender que en el fondo solo hay 3 tipos de elementos: textos, líneas y rectángulos,
y que cada uno se cambia con su función: element_text(), element_line() y element_rect()

  • La sintaxis es siempre la misma: theme(elemento = element_xxx(...))
p_t + theme(plot.title = element_text(colour = "blue"),
            axis.text.x = element_text(angle = 45))
  • text afecta a todos los textos a la vez:
    theme(text = element_text(size = 16))

  • Y con rel() cambias un texto en proporción al tamaño base de la letra,
    que por defecto es de 11 puntos (base_size = 11):
    plot.title = element_text(size = rel(1.5))
    pone el título a 16,5 puntos (1,5 veces 11)

p + theme(panel.grid =
            element_line(colour = "purple",
                         linetype = "dashed"))

p + theme(panel.background =
            element_rect(fill = "lightyellow",
                         colour = "black"),
          plot.background =
            element_rect(fill = "grey90"))

p_t + theme(panel.background = element_blank(),
            panel.grid.minor = element_blank(),
            axis.ticks = element_blank(),
            plot.title = element_blank())

La leyenda

  • La leyenda la crea ggplot2 al mapear una variable (color = Species). Lo que más se suele hacer con ella:
#- quitarla
p + theme(legend.position = "none")
p + guides(color = "none")

#- quitarle el título
p + labs(color = NULL)
p + theme(legend.title = element_blank())

#- cambiarla de sitio
p + theme(legend.position = "bottom")
p + theme(legend.position = "inside",
          legend.position.inside = c(0.85, 0.2))

  • El título de la leyenda se cambia con labs() (labs(color = "Especie")); su aspecto, con theme() (legend.title, legend.text, …)

¿Cómo se llaman todos los elementos del gráfico?

Colores ( 🖥 🎨 )

Sé poquito de colores, pero claro, si quieres cambiar la apariencia de los gráficos, has de saber los colores de R, así que:

  • R tiene 657 colores con nombre. Para ver la lista ejecuta: aa <- as.data.frame(colours())

  • Si sabes el nombre del color que quieres, [aquí], podrás buscarlo y ver su color y su codificación en RGB y Hex

  • También es interesante el [paquete paleteer] que agrupa un conjunto amplio de paletas de colores para usar en R

Paletas de colores (y daltonismo)

  • Alrededor de un 8% de los hombres tiene algún tipo de daltonismo: el rojo y el verde de la paleta por defecto les cuesta distinguirlos

  • Mejor usar paletas pensadas para eso: viridis (viene con ggplot2) o algunas de ColorBrewer como "Dark2"

p + scale_color_viridis_d()

p + scale_color_brewer(palette = "Dark2")

  • Otro paquete con paletas pensadas para daltónicos: scico (scico::scale_color_scico_d(palette = "batlow"))

Para practicar: themes

Tarea: partiendo del gráfico p, ponle el tema theme_minimal(), lleva la leyenda abajo y haz que todos los textos del gráfico sean más grandes (tamaño 16)

p + theme_minimal() +
  theme(legend.position = "bottom",
        text = element_text(size = 16))

  • Ojo con el orden: theme_minimal() va antes que theme(). Si lo pones después, el tema completo “pisa” tus cambios

Small multiples con facet_grid() y facet_wrap()

  • Veámoslo en acción con el conjunto de datos iris. En iris, hay una variable: Species que es un factor, es una variable categórica
p +
p +
facet_grid(cols = vars(Species)) +   # gráficos x columnas
p +
facet_grid(cols = vars(Species)) +   # gráficos x columnas
facet_grid(rows = vars(Species)) +   # gráficos x filas
p +
facet_grid(cols = vars(Species)) +   # gráficos x columnas
facet_grid(rows = vars(Species)) +   # gráficos x filas
facet_wrap(vars(Species), nrow = 2, ncol = 2) # filas y columnas

En muchos ejemplos (y en internet) verás los facets escritos con una fórmula (~). Es equivalente:

p + facet_grid(. ~ Species)                     #- igual que facet_grid(cols = vars(Species))
p + facet_grid(Species ~ .)                     #- igual que facet_grid(rows = vars(Species))
p + facet_wrap(~ Species, nrow = 2, ncol = 2)   #- igual que facet_wrap(vars(Species), nrow = 2, ncol = 2)

Small multiples con 2 variables categóricas

  • Iris sólo tiene un factor, así que vamos a discretizar una variable del dataset:
iris_2 <- iris |>
  mutate(Petal_2 = if_else(ntile(Petal.Width, 2) == 1, "pétalo estrecho", "pétalo ancho"))
iris_2  |> ggplot() +
geom_point(aes(Sepal.Length, Petal.Length, color = Species)) +
facet_grid(rows = vars(Petal_2), cols = vars(Species))

Ejes de los small multiples (!!)

  • Por defecto las escalas de los ejes de los small multiple son comunes, pero podemos cambiarlo:
p + facet_grid(rows = vars(Species))    #- escalas comunes

p + facet_grid(rows = vars(Species), scales = "free")   #- las escalas de cada small pueden variar

p + facet_grid(rows = vars(Species), scales = "free_y") #- solo dejamos libre/variar la escala del eje y

Para practicar: small multiples

Tarea: con los datos penguins, haz un gráfico de puntos con la longitud del pico (bill_len) en el eje X y su profundidad (bill_dep) en el eje Y, con un panel para cada especie (species)

  • Después, prueba con scales = "free". ¿Qué se gana y qué se pierde?
ggplot(penguins, aes(bill_len, bill_dep)) +
  geom_point() +
  facet_wrap(vars(species))

  • Con los ejes comunes es fácil comparar las especies. Con scales = "free" se ve mejor cada panel, pero ya no puedes comparar los paneles “a ojo”: hay que mirar los números de los ejes

Anotaciones (!!)

  • Las anotaciones permiten resaltar algún fenómeno o observación de interés y son importantes a la hora de contar historias (storytelling) con los gráficos y visualizaciones

  • En el entorno ggplot podemos hacer anotaciones en nuestros gráficos de varias maneras: veremos un poco de 3:

    • con la función annotate()

    • con geom_text()

    • con geom_vline() , geom_hline() y geom_abline()

Anotaciones con annotate()

p +
p +
annotate(geom = "text",
  x = 6,
  y = 2,
  label = "Una anotación",
  size = 5) +
p +
annotate(geom = "text",
  x = 6,
  y = 2,
  label = "Una anotación",
  size = 5) +
annotate("rect",
  xmin = 6,
  xmax = 7,
  ymin = -Inf,
  ymax = Inf,
  alpha = 0.2,
  fill = "pink") +
p +
annotate(geom = "text",
  x = 6,
  y = 2,
  label = "Una anotación",
  size = 5) +
annotate("rect",
  xmin = 6,
  xmax = 7,
  ymin = -Inf,
  ymax = Inf,
  alpha = 0.2,
  fill = "pink") +
annotate("segment",
  x = 5,
  xend = 7,
  y = 6,
  yend = 8,
  colour = "blue")

La forma habitual de escribir ese código es:

p + annotate(geom = "text", x = 6, y = 2, label = "Una anotación", size = 5) +
    annotate("rect", xmin = 6, xmax = 7,ymin = -Inf, ymax = Inf, alpha = 0.2, fill = "pink") +
    annotate("segment", x = 5, xend = 7, y = 6, yend = 8, colour = "blue")

Anotaciones con geom_text()

p +
p +
geom_text(aes(label = Petal.Length)) +
p +
geom_text(aes(label = Petal.Length)) +
geom_text(aes(label = Species))

Así no parece muy útil, pero podemos mejorarlo: ponemos una etiqueta sólo en el lirio con el pétalo más largo de cada especie

iris_max <- iris |> group_by(Species) |>
  slice_max(Petal.Length, n = 1, with_ties = FALSE)

p_text <- p +
 geom_text(data = iris_max, aes(label = Species),
           color = "black", size = 4)

Una flecha y un texto: para contar una historia

  • Con annotate() también podemos dibujar una flecha (geom = "curve" o "segment", con arrow) que lleve del texto al punto del que queremos hablar
p + annotate(geom = "text", x = 5.8, y = 6.6,
             label = "el pétalo\nmás largo") +
    annotate(geom = "curve",
1             x = 6.2, y = 6.6,
2             xend = 7.6, yend = 6.9,
             curvature = -0.3,
             arrow = arrow(length = unit(2, "mm")))
1
dónde empieza la flecha (al lado del texto)
2
dónde acaba (el punto: 7,7 y 6,9)

  • Las coordenadas x e y son las de los datos. Hay que probar un poco hasta que quede bien … o usar ggannotate, que veremos en los asistentes

Etiquetas que no se solapan: ggrepel

  • Con geom_text() las etiquetas se ponen justo encima del punto y muchas veces se solapan. El paquete ggrepel las “aparta” automáticamente
iris_extremos <- iris |> group_by(Species) |>
  filter(Petal.Length %in% range(Petal.Length)) |>
  slice_head(n = 2)

p_repel <- p +
  ggrepel::geom_label_repel(data = iris_extremos,
      aes(label = paste(Species, Petal.Length)),
      size = 3.5, show.legend = FALSE)

Anotaciones con geom_xx_line()

p +
p +
geom_vline(xintercept = 6) +
p +
geom_vline(xintercept = 6) +
geom_hline(yintercept = 5,
  linewidth = 1.7,
  colour = "purple",
  linetype = "dashed") +
p +
geom_vline(xintercept = 6) +
geom_hline(yintercept = 5,
  linewidth = 1.7,
  colour = "purple",
  linetype = "dashed") +
geom_abline(intercept = 0.7,
  slope = 0.4,
  linewidth = 2,
  colour = "steelblue")

La forma habitual de escribir ese código es:

p + geom_vline(xintercept = 6)
p + geom_hline(yintercept = 5, linewidth = 1.7, colour = "purple", linetype = "dashed")
p + geom_abline(intercept = 0.7, slope = 0.4, linewidth = 2, colour = "steelblue")

Escalas (!!)

  • Las escalas permiten leer/interpretar un gráfico

  • A veces es importante pararse a pensar cuál es la mejor escala para representar las variables. Quizás sea conveniente cambiar la escala de un eje para distribuir mejor las observaciones en el espacio, o para interpretar mejor las variaciones entre observaciones; por ejemplo la escala logarítmica o en porcentajes son a veces más apropiadas que las escalas originales


  • Las escalas en ggplot2 se crean/controlan con una familia de funciones: scale_xx(). Estas funciones también construyen los elementos que permiten leer/interpretar los gráficos: los ejes y las leyendas


  • Como ggplot2 genera las escalas y leyendas automáticamente, en la práctica podemos hacer gráficos sin saber cómo funcionan las scale_xx(). PERO, en realidad, para cada par variable/estética representada en un gráfico ggplot es necesaria una escala, y tendría que fijarse con una de las funciones de la familia scale_xx()

Escalas: ggplot2 las pone por nosotros

  • ggplot2 genera las escalas automáticamente. Cuando hacemos este gráfico:
ggplot(iris, aes(Sepal.Length, Petal.Length)) +
  geom_point(aes (color = Species))


  • … “en realidad” se está haciendo:
ggplot(iris, aes(Sepal.Length, Petal.Length)) +
  geom_point(aes (color = Species)) +
  scale_x_continuous() +
  scale_y_continuous() +
  scale_color_discrete()
  • Cada escala depende del tipo de variable:

    • Sepal.Length es continua → scale_x_continuous()
    • Species es categórica → scale_color_discrete()

Escalas: breaks, labels y limits [ 🌟 ]

  • De una escala, lo que más se cambia es:
    • breaks: dónde van las marcas del eje
    • labels: qué pone en cada marca
    • limits: el rango del eje
p + scale_x_continuous(name = "Longitud del sépalo",
                       breaks = seq(4, 8, by = 1),
                       labels = paste0(4:8, " cm"),
                       limits = c(4, 8))

  • Hay escalas ya “hechas”: scale_y_reverse(), scale_x_log10(), scale_colour_grey(), …

Escalas para fechas (y números): el paquete scales

  • Con series temporales casi siempre hay que arreglar el eje de las fechas: con scale_x_date(). Y para los números (miles, %, …), el paquete scales tiene funciones para los breaks y los labels
ggplot(economics, aes(x = date, y = unemploy)) +
  geom_line() +
1  scale_x_date(breaks = scales::breaks_width("10 years"),
2               labels = scales::label_date("%Y")) +
3  scale_y_continuous(labels = scales::label_number(big.mark = ".")) +
  labs(x = NULL, y = "Parados en EE.UU. (miles)")
1
una marca cada 10 años
2
en cada marca, solo el año
3
los miles, con punto: 12.000 y no 12000

  • Otras funciones útiles de scales: label_percent() (para tantos por uno), label_dollar() y label_date("%b %Y") (mes y año)

Escalas para el color

p + scale_color_manual(
      values = c("purple", "orange", "steelblue"),
      breaks = c("setosa", "versicolor",
                 "virginica"),
      labels = c("my_Setosa", "my_Versicolor",
                 "my_Virginica"),
      name = "Especie")
  • Con values eliges los colores; con breaks y labels, qué pone en la leyenda (sin tocar los datos)

  • Las paletas que vimos antes (scale_color_viridis_d(), scale_color_brewer()) también son escalas de color

Escalas de color: ¿continua o por tramos?

  • La tasa de paro por provincias (INE), con un color continuo o por tramos: en los mapas, por tramos suele leerse mejor
ggplot(mapa_prov) +
  geom_sf(aes(fill = total), color = "white") +
  scale_fill_distiller(palette = "YlOrRd",
                       direction = 1)

ggplot(mapa_prov) +
  geom_sf(aes(fill = total), color = "black") +
  scale_fill_fermenter(palette = "YlOrRd",
                       direction = 1)

  • distiller y fermenter: las paletas de ColorBrewer, en continuo y por tramos. Con viridis: scale_fill_viridis_c() y _b()

Escalas: ¿por qué dan error?

  • Tarea: ¿por qué dan error estas dos expresiones?
p + scale_colour_continuous()
#> Error in `scale_colour_continuous()`:
#> ! Discrete value supplied to a continuous scale.
#> ℹ Example values: setosa, versicolor, and virginica.

pp <- ggplot(iris, aes(Sepal.Length, Petal.Length)) +
  geom_point(aes(color = Petal.Width))

pp + scale_colour_discrete()
#> Error in `scale_colour_discrete()`:
#> ! Continuous value supplied to a discrete scale.
#> ℹ Example values: 0.2, 0.4, 0.3, 0.1, and 0.5.
  • La escala tiene que ir con el tipo de variable: Species es categórica, así que su escala tiene que ser discreta; Petal.Width es continua, así que su escala tiene que ser continua
pp + scale_colour_gradient(low = "blue",
                           high = "red")

Para practicar: escalas

Tarea: partiendo del gráfico p:

  • haz que las etiquetas del eje X lleven " cm" detrás
  • elige tú los colores de las especies
p + scale_x_continuous(labels = scales::label_number(suffix = " cm")) +
  scale_color_manual(values = c("darkorange", "purple", "cyan4"))

Límites de los ejes

  • Los límites o dominio del gráfico suelen obtenerse automáticamente de los datos, pero, según Hadley Wickham, hay dos razones por las que podemos estar interesados en cambiar los límites del gráfico:

    • centrarnos en una región específica del gráfico

    • aumentar los límites para que varios gráficos ajusten sus escalas

  • Si quieres modificar el recorrido de los ejes, los “límites” de los ejes, puedes usar lims(). Para los ejes X e Y hay dos funciones auxiliares: xlim() e ylim()

En realidad-realidad los límites de los ejes se controlan con scale_xx()

  • . . . como vimos en la sección anterior: el argumento limits de scale_x_continuous() y scale_y_continuous()

Límites de los gráficos: lims()

#limites: x[4-8] y[1-7]
p +
#limites: x[4-8] y[1-7]
p +
lims(x = c(NA,6)) +
#limites: x[4-8] y[1-7]
p +
lims(x = c(NA,6)) +
lims(y = c(1,8)) +
#limites: x[4-8] y[1-7]
p +
lims(x = c(NA,6)) +
lims(y = c(1,8)) +
lims(color = c("setosa")) +
#limites: x[4-8] y[1-7]
p +
lims(x = c(NA,6)) +
lims(y = c(1,8)) +
lims(color = c("setosa")) +
xlim(c(5, 6)) +
#limites: x[4-8] y[1-7]
p +
lims(x = c(NA,6)) +
lims(y = c(1,8)) +
lims(color = c("setosa")) +
xlim(c(5, 6)) +
ylim(c(NA, 5)) +
#limites: x[4-8] y[1-7]
p +
lims(x = c(NA,6)) +
lims(y = c(1,8)) +
lims(color = c("setosa")) +
xlim(c(5, 6)) +
ylim(c(NA, 5)) +
#- podemos dar la vuelta a los ejes
xlim(c (7, 3)) +
#limites: x[4-8] y[1-7]
p +
lims(x = c(NA,6)) +
lims(y = c(1,8)) +
lims(color = c("setosa")) +
xlim(c(5, 6)) +
ylim(c(NA, 5)) +
#- podemos dar la vuelta a los ejes
xlim(c (7, 3)) +
ylim(c(NA, 5))

Ojo: lims(), xlim() e ylim() eliminan (convierten en NA) los datos que quedan fuera de los límites. Por eso R avisa: Removed n rows containing missing values

Haciendo zoom: cuidado!!

  • Si cambias los límites de un gráfico para hacer un zoom, hay que tener cuidado ya que si después utilizas alguna transformación estadística como por ejemplo geom_smooth(), las observaciones eliminadas al ajustar los límites no entrarán en el cálculo estadístico
p + geom_smooth(color = "purple")
p + geom_smooth(color = "purple") +
  xlim(c(4, 5.7)) + ylim(c(1.5, 5))   #- ¡elimina puntos!

Solución: coord_cartesian()

p + geom_smooth(color = "purple") +
  coord_cartesian(xlim = c(4, 5.7),
                  ylim = c(1.5, 5))

Stats (transformaciones estadísticas)

  • Algunos gráficos, como los de puntos, no requieren transformaciones estadísticas: lo que se grafica coincide con los valores de las observaciones. Ciertos gráficos, por ejemplo los boxplots, no se representan las variables originales, sino una transformación de estas

  • Podemos usar transformaciones estadísticas en gráficos ggplot con la familia de funciones stat_xx()

  • Cada geom_xx() que utilicemos, en realidad necesita de un stat_xx(), entonces ¿por qué nunca lo hemos usado nosotros? Con ggplot2 la razón es casi siempre la misma: porque under the hood ggplot2 hace muchas cosas por nosotros. En concreto, cada vez que usamos un geom_xx() ggplot2 está fijando una transformación estadística por defecto por nosotros

  • Cuando hacíamos:

ggplot(iris, aes(Petal.Length, Sepal.Length)) + geom_point()
  • En realidad, estamos haciendo:
ggplot(iris, aes(Petal.Length, Sepal.Length)) +
  geom_point(stat = "identity")

Stats: el ejemplo de geom_bar()

  • geom_bar() no dibuja los datos tal cual: primero cuenta cuántas filas hay de cada categoría. Ese recuento lo hace su stat por defecto, stat = "count". Estas 3 expresiones hacen el mismo gráfico:
ggplot(mpg, aes(class)) + geom_bar()
ggplot(mpg, aes(class)) + geom_bar(stat = "count")
ggplot(mpg, aes(class)) + stat_count(geom = "bar")

  • Por eso, si ya tienes calculado el valor de cada barra, no usas geom_bar() sino geom_col(), que usa stat = "identity": dibuja los valores tal cual. Lo veremos en las slides C

Stats: un ejemplo (!!!)

ggplot(iris,
 aes(Species, Sepal.Length)) +
ggplot(iris,
 aes(Species, Sepal.Length)) +
geom_boxplot() +
ggplot(iris,
 aes(Species, Sepal.Length)) +
geom_boxplot() +
geom_point(
  stat = "summary",
  fun = "mean",
  colour = "red", size = 4)
  • Es raro que tengamos que usar stat_xx() directamente

  • La forma habitual de escribir ese código es:

ggplot(iris, aes(Species, Sepal.Length)) +
  geom_boxplot() +
  geom_point(stat = "summary",
             fun = "mean",
             colour = "red", size = 4)

Stats: otro ejemplo (!!!)

ggplot(iris, aes(Sepal.Length)) + geom_histogram()

ggplot(iris, aes(Sepal.Length)) +
   geom_histogram(aes(y = after_stat(count / max(count))))

Position adjustments (un ejemplo)

p_pos <- ggplot(iris, aes(Sepal.Length, Petal.Length)) + geom_point()

p_pos + geom_point(position = "jitter", color = "pink")

También se puede usar geom_jitter()

p_pos + geom_jitter(color = "pink")

Position adjustments (otro ejemplo)

p_bar <- ggplot(mtcars, aes(factor(cyl), fill = factor(vs)))
p1 <- p_bar + geom_bar()                                  #- apiladas (por defecto)
p2 <- p_bar + geom_bar(position = "fill")                 #- apiladas al 100%
p3 <- p_bar + geom_bar(position = "dodge")                #- una al lado de otra
p4 <- p_bar + geom_bar(position = position_dodge2(preserve = "single"))

4. Combinando gráficos

A veces interesa crear una figura compuesta de varios gráficos diferentes

Combinando gráficos

library(patchwork)
p1 <- ggplot(iris, aes(Sepal.Length, Petal.Length, color = Species)) + geom_point()
p2 <- ggplot(iris)+ aes(Species, Sepal.Length) + geom_boxplot()
p3 <- ggplot(iris, aes(Sepal.Length, fill = Species)) + geom_density(position = "stack", alpha = 0.5)
p1 + p2 / p3

5. Exportando gráficos

A veces necesitaremos guardar el gráfico en un fichero


Piensa que estamos guardando la imagen, la representación del gráfico, no el objeto R

Exportando gráficos

Si queremos guardar (la representación gráfica de) un gráfico en un archivo (.jpg, png, …)

  • Podemos usar la “Export Tab” en el Plot Pane de Rstudio. Podemos cambiar las dimensiones (anchura y altura) del gráfico

  • Podemos usar la función ggsave(). Nos permite cambiar el tamaño y la resolución del gráfico. Si no le decimos qué gráfico (plot =), guarda el último que hemos dibujado

ggsave("my_plot.png", plot = p_t, width = 8, height = 6, units = "in", dpi = "retina")

Si queremos guardar una copia completa del gráfico, no de su representación visual, sino del objeto R, podemos hacerlo con saveRDS()

saveRDS(p_t, "my_plot.rds")

Exportando: ¿por qué el gráfico guardado se ve tan distinto? [ 🌟 ]

  • Los textos y los puntos tienen un tamaño fijo, pero el gráfico no: si lo guardas pequeño, la letra parece enorme; si lo guardas grande, diminuta. El mismo p_t, guardado con dos tamaños:

Guardado a 9 cm de ancho

Guardado a 25 cm de ancho

  • Solución: decide antes el tamaño final (para un Word, unos 16 cm de ancho) y guárdalo con ese tamaño. Si aun así la letra no se ve bien, cambia su tamaño base:
ggsave("my_plot.png", plot = p_t + theme_gray(base_size = 9),
       width = 16, height = 10, units = "cm")
  • El panel Plots de RStudio (Export → Save as Image) también deja elegir el tamaño y ver cómo quedará. Y el paquete ggview lo enseña con código: p_t + ggview::canvas(width = 16, height = 10, units = "cm")

6. Asistentes para ggplot2

Hay varios asistentes para crear o modificar gráficos ggplot a través de interfaces gráficas

Asistentes para ggplot2

Hay varios asistentes para crear o modificar gráficos ggplot a través de interfaces gráficas. Por ejemplo: [ggThemeAssist], [esquisse] y [ggannotate]


  • El paquete ggThemeAssist (sólo funciona en RStudio, no en Positron) facilita, mediante un addin de RStudio, la edición de los detalles de un gráfico; es decir, puedes comenzar haciendo un gráfico básico en RStudio, para después usar ggThemeAssist para modificar los elementos estéticos de gráfico como títulos, leyendas, colores etc… etc…
  • El paquete esquisse permite crear gráficos ggplot desde cero con una interfaz gráfica: ejecuta esquisse::esquisser(iris), haz el gráfico arrastrando variables y copia el código que genera
  • El paquete ggannotate permite crear anotaciones en gráficos ggplot

Un gráfico con datos reales

  • Hasta ahora casi todo ha sido con iris. Vamos a hacer un gráfico con datos de verdad: la tasa de paro de España y de otros países de la UE, con los datos de Eurostat que vimos en las slides 04B
paro <- eurostat::get_eurostat("une_rt_a",
          filters = list(geo = c("ES", "DE", "FR", "IT", "PT", "EU27_2020"),
                         sex = "T", age = "Y15-74", unit = "PC_ACT"),
          time_format = "num")
  • Primero, el gráfico “de andar por casa”, el que haríamos para explorar los datos:
ggplot(paro, aes(time, values, color = geo)) + geom_line()

Y ahora lo pulimos para presentarlo

paro |>
paro |>
ggplot(aes(time, values, group = geo)) +
paro |>
ggplot(aes(time, values, group = geo)) +
geom_line(color = "grey75") +
paro |>
ggplot(aes(time, values, group = geo)) +
geom_line(color = "grey75") +
geom_line(data = paro |> filter(geo == "ES"),
          color = "firebrick", linewidth = 1.2) +
paro |>
ggplot(aes(time, values, group = geo)) +
geom_line(color = "grey75") +
geom_line(data = paro |> filter(geo == "ES"),
          color = "firebrick", linewidth = 1.2) +
annotate("text", x = 2013, y = 27.5,
         label = "España", color = "firebrick") +
paro |>
ggplot(aes(time, values, group = geo)) +
geom_line(color = "grey75") +
geom_line(data = paro |> filter(geo == "ES"),
          color = "firebrick", linewidth = 1.2) +
annotate("text", x = 2013, y = 27.5,
         label = "España", color = "firebrick") +
scale_y_continuous(
  labels = scales::label_percent(scale = 1)) +
paro |>
ggplot(aes(time, values, group = geo)) +
geom_line(color = "grey75") +
geom_line(data = paro |> filter(geo == "ES"),
          color = "firebrick", linewidth = 1.2) +
annotate("text", x = 2013, y = 27.5,
         label = "España", color = "firebrick") +
scale_y_continuous(
  labels = scales::label_percent(scale = 1)) +
labs(title = "El paro en España, muy por encima del resto",
     subtitle = "En gris: DE, FR, IT, PT y la UE-27",
     x = NULL, y = NULL,
     caption = "Fuente: Eurostat (une_rt_a)") +
paro |>
ggplot(aes(time, values, group = geo)) +
geom_line(color = "grey75") +
geom_line(data = paro |> filter(geo == "ES"),
          color = "firebrick", linewidth = 1.2) +
annotate("text", x = 2013, y = 27.5,
         label = "España", color = "firebrick") +
scale_y_continuous(
  labels = scales::label_percent(scale = 1)) +
labs(title = "El paro en España, muy por encima del resto",
     subtitle = "En gris: DE, FR, IT, PT y la UE-27",
     x = NULL, y = NULL,
     caption = "Fuente: Eurostat (une_rt_a)") +
theme_minimal(base_size = 14)
#> # A tibble: 6 × 7
#>   freq  age    unit   sex   geo        time values
#>   <chr> <chr>  <chr>  <chr> <chr>     <dbl>  <dbl>
#> 1 A     Y15-74 PC_ACT T     EU27_2020  2003     NA
#> 2 A     Y15-74 PC_ACT T     EU27_2020  2004     NA
#> 3 A     Y15-74 PC_ACT T     EU27_2020  2005     NA
#> 4 A     Y15-74 PC_ACT T     EU27_2020  2006     NA
#> 5 A     Y15-74 PC_ACT T     EU27_2020  2007     NA
#> 6 A     Y15-74 PC_ACT T     EU27_2020  2008     NA

Fíjate en lo que hemos cambiado: el color, solo para lo que importa (España), y el resto en gris, como contexto; un título que cuenta el mensaje del gráfico; y fuera lo que sobra (los nombres de los ejes, el fondo gris), con la letra más grande

Juntándolo todo: un gráfico “de verdad”

Tarea: con los datos penguins (vienen con R desde la versión 4.5) haz un gráfico que:

  • represente la longitud de la aleta (flipper_len) frente al peso (body_mass), con el color según la especie (species)
  • tenga título, subtítulo, nombres de los ejes en castellano y la fuente de los datos
  • tenga un panel para cada isla (island)
  • use una paleta apta para daltónicos y theme_minimal(), con la leyenda abajo
ggplot(penguins, aes(flipper_len, body_mass, color = species)) +
  geom_point(alpha = 0.7) +
  facet_wrap(vars(island)) +
  scale_color_viridis_d() +
  labs(title = "Pingüinos del archipiélago Palmer",
       subtitle = "Los pingüinos con aletas más largas pesan más",
       x = "Longitud de la aleta (mm)", y = "Peso (g)", color = "Especie",
       caption = "Datos: paquete datasets (R >= 4.5)") +
  theme_minimal() +
  theme(legend.position = "bottom")

Extensiones

Algunas novedades de ggplot2 4.0 (2025). No hacen falta para el curso, pero te pueden ahorrar trabajo

Extensión: los nombres en castellano, una sola vez

  • Para poner en castellano el nombre de una variable hay que escribirlo en cada gráfico y en cada estética (x, y, color…). Con labs(dictionary = ...) se traduce por el nombre de la variable, salga donde salga

  • El diccionario se define una vez (un vector con nombres) y luego se usa en todos los gráficos:

dicc <- c(Sepal.Length = "Longitud del sépalo",
          Sepal.Width  = "Anchura del sépalo",
          Petal.Length = "Longitud del pétalo",
          Species      = "Especie")

p + labs(dictionary = dicc)

ggplot(iris, aes(Species, Sepal.Width)) + geom_boxplot() + labs(dictionary = dicc)

Extensión: los colores de todo el gráfico [ 🌶 ]

  • Los temas tienen 3 argumentos nuevos: ink (el color de los textos y las líneas), paper (el fondo) y accent (el color de la curva de geom_smooth() y de algún otro geom parecido)
p + geom_smooth(aes(group = 1)) +
  theme_minimal(ink = "white", paper = "grey20", accent = "orange")

  • La paleta de colores también se puede fijar en el theme. Con theme_set() sirve para todos los gráficos de la sesión. “Okabe-Ito” es una paleta pensada para daltónicos
p + theme(palette.colour.discrete = "Okabe-Ito")

Extensión: coordenadas

  • Es el sistema por defecto. Ya lo hemos usado para hacer zoom sin eliminar datos
p + coord_cartesian(xlim = c(4, 5.7), ylim = c(1.5, 5))

  • Intercambia los ejes X e Y (útil, p.ej., en gráficos de barras con etiquetas largas). Hoy en día suele bastar con intercambiar las variables en aes()
ggplot(iris, aes(Species, Sepal.Length)) + geom_boxplot() + coord_flip()

  • Fija la relación entre las unidades de los ejes: con ratio = 1, una unidad en X mide lo mismo que una unidad en Y
p + coord_fixed(ratio = 1)

  • Coordenadas polares. Así se hacen, p.ej., los gráficos de tarta (que no suelen ser buena idea!)
ggplot(mtcars, aes(x = "", fill = factor(cyl))) + geom_bar() + coord_polar(theta = "y")

Extensión: texto alternativo (gráficos accesibles)

  • Las personas que usan lectores de pantalla no “ven” el gráfico. Con labs(alt = ...) podemos añadir un texto alternativo que describa lo que muestra:
p + labs(alt = "Gráfico de puntos: la longitud del pétalo crece con
                la del sépalo; los lirios setosa forman un grupo aparte,
                con pétalos mucho más cortos")
  • En Quarto también se puede poner con la opción de chunk #| fig-alt: "..."

  • Un buen texto alternativo no describe el código, sino el mensaje del gráfico