Gráficas con ggplot2

Parte 2: Graficación avanzada en R

Autores

Rodrigo Zepeda-Tello

Última actualización

October 7, 2022

Resumen
Mostramos algunas funciones avanzadas de ggplot2: cómo formatear el texto con otros colores usando gridtext, cómo agregar en la misma gráfica datos de dos fuentes distintas, cómo juntar gráficas usando cowplot y cómo agregar logotipos a gráficas.
Note

Los datos están disponibles en el Github y en Dropbox

Warning

Si aún no sabes usar ggplot2 te recomiendo revisar la parte 1 de estas notas

1 Unión de gráficos con cowplot

Muchas veces interesa tener una gráfica que conjunte dos imágenes distintas. Por ejemplo la siguiente imagen muestra dos histogramas lado a lado en una sola gráfica.

Dos gráficas en una sola

Para poder juntarlos es necesario: 1) hacer cada histograma por separado y 2) usar el paquete cowplot para juntarlos.

Empecemos elaborando el primer histograma a partir de los datos de diabetes:

datos_diabetes <- read_csv("datasets/diabetes.csv")

histograma_1 <- ggplot(datos_diabetes) +
  geom_histogram(aes(x = chol, y = ..density..), 
                 bins = 50, color = "white", fill = "deepskyblue3") +
  labs(
    y = "",
    x = "Niveles de colesterol"
  ) +
  scale_y_continuous(labels = scales::percent) +
  theme_classic()

continuemos con el segundo:

histograma_2 <- ggplot(datos_diabetes) +
  geom_histogram(aes(x = stab.glu, y = ..density..), 
                 bins = 50, color = "white", fill = "deepskyblue3") +
  labs(
    y = "",
    x = "Niveles de glucosa"
  ) +
  scale_y_continuous(labels = scales::percent) +
  theme_classic()

Finalmente con plot_grid dentro de cowplot las juntamos:

library(cowplot) #para usar plot_grid
plot_grid(histograma_1, histograma_2, ncol = 2) #ncol indica cuántas columnas

El paquete cowplot permite incluso combinar más de dos gráficos y establecer las alturas.

1.1 Ejercicios

Ejemplo avanzado de combinación de gráficos

  1. Para armar el siguiente gráfico se utilizaron histograma_1, histograma_2 y una tercera gráfica de colesterol contra glucosa de esta forma (color deepskyblue4):

La gráfica de colesterol contra glucosa se almacenó en la variable col_glu la cual luego se utilizó en el plot_grid con la siguiente instrucción:

plot_grid(histograma_1,            #Primer histograma
          ggplot() + theme_void(), #Genera un gráfico vacío 
          col_glu,                 #Gráfica de glucosa vs colesterol
          histograma_2 + coord_flip(), #coord_flip voltea el histograma
          ncol = 2,                #dos columnas
          rel_widths  = c(1, 0.5), #Establece el ancho relativo
          rel_heights = c(0.5, 1)) #Establece la altura relativa

Reproduce la gráfica

Tip

Más información de cowplot la puedes encontrar en sus tutoriales

2 Formateo de títulos con ggtext

La librería ggtext sirve para poner negritas, cursivas y cambiar colores en los textos. Probemos con la base de datos datasets/BCG_Sin_Son_2020_IMSS.csv que contiene información de las dosis de BCG en Sinaloa y Sonora aplicadas en el IMSS.

#Lectura de datos
bcg_ss <- read_csv("datasets/BCG_Sin_Son_2020_IMSS.csv")

#Gráfico de tendencias
ggplot(bcg_ss) +
  geom_line(aes(x = Año, y = Dosis, color = Entidad)) +
  scale_color_manual("Entidad", values = c("Sinaloa" = "deepskyblue4",
                                           "Sonora" = "deeppink3")) +
  theme_minimal() +
  labs(
    x = "Año",
    y = "Dosis de B.C.G. (Bacillus Calmette–Guérin)",
    title = "Dosis de B.C.G aplicadas en el IMSS en Sonora y Sinaloa",
    caption = "Fuente: Datos abiertos del IMSS"
  )

A través de ggtext podemos colorear sólo secciones del título poniéndolas entre las etiquetas <span style = 'color:nombredelcolor;'> y </span>. Podemos poner negritas usando dos asteriscos como en whatsapp, **lo que va en negritas**, y cursivas con guiones bajos, _lo que va en cursivas_. Para poner espacios verticales basta con escribir un <br> y eso de inmediato da un espacio. Finalmente para que los temas apliquen hay que cambiar a element_markdown las opciones donde queremos aplicar el tema dentro del theme (nuestro caso plot.title, axis.title.y y plot.capiton pero también se puede en axis.title.x y plot.subtitle).

library(ggtext)

#Gráfico de tendencias
ggplot(bcg_ss) +
  geom_line(aes(x = Año, y = Dosis, color = Entidad)) +
  scale_color_manual("Entidad", values = c("Sinaloa" = "deepskyblue4",
                                           "Sonora" = "deeppink3")) +
  theme_minimal() +
  labs(
    x = "Año",
    y = "Dosis de B.C.G. (_Bacillus Calmette-Guérin_)",
    title = 
      "**Dosis de B.C.G aplicadas en el IMSS en 
    <span style = 'color:deeppink3;'>Sonora</span> y 
    <span style = 'color:deepskyblue4;'>Sinaloa</span>.**",
    caption = "**Fuente:** Datos abiertos del IMSS"
  ) +
  theme(
    plot.title   = element_markdown(),
    axis.title.y = element_markdown(),
    plot.caption = element_markdown()
  )

dentro de los element_markdown se pueden cambiar los tamaños de fuentes como lo hicimos en la sección pasada con element_text: son lo mismo. También podemos esconder la leyenda con legend.position = "none" dentro del theme:

library(ggtext)

#Gráfico de tendencias
ggplot(bcg_ss) +
  geom_line(aes(x = Año, y = Dosis, color = Entidad)) +
  scale_color_manual("Entidad", values = c("Sinaloa" = "deepskyblue4",
                                           "Sonora" = "deeppink3")) +
  theme_minimal() +
  labs(
    x = "Año",
    y = "Dosis de B.C.G.<br>(_Bacillus Calmette-Guérin_)",
    title = 
      "Dosis de B.C.G aplicadas en 
    <span style = 'color:deeppink3;'>**Sonora**</span> y 
    <span style = 'color:deepskyblue4;'>**Sinaloa**</span><br>",
    caption = "**Fuente:** Datos abiertos del IMSS"
  ) +
  theme(
    plot.title   = element_markdown(size = 18),
    axis.title.y = element_markdown(),
    plot.caption = element_markdown(),
    legend.position = "none"
  )

2.1 Ejercicio

La base de datos datasets/hpv_unicef.csv contiene información del objetivo target de vacunación (primera dosis en mujeres) para el papiloma humano y las dosis que en realidad implementaron doses en 2021 para los países que se tiene registro.

hpv <- read_csv("datasets/hpv_unicef.csv")

Podemos notar en la base que México tenía un objetivo de 1094757 y aplicó 13512. La siguiente gráfica subraya a México dentro de los datos:

ggplot(hpv) + 
  geom_point(aes(x = target, y = doses), color = "gray25", 
             size = 1) +
  geom_point(aes(x = 1094757, y = 13512), color = "tomato3", 
             size = 2, data = NULL) +
  scale_x_continuous(labels = scales::comma) +
  scale_y_continuous(labels = scales::comma) +
  theme_minimal() +
  labs(
    x = "Dosis objetivo",
    y = "Dosis aplicadas",
  )

Modifica la gráfica para que el resultado sea el siguiente:

3 Uso de múltiples bases de datos en la misma gráfica

La base de datos datasets/hpv_unicef_ultima_dosis.csv contiene la información de cobertura doses y cobertura objetivo target de la última dosis de la vacuna de VPH en mujeres. La base datasets/hpv_unicef.csv contiene la misma información pero para la primera dosis. Hagamos una nube de puntos con ambas bases en conjunto. Para ello podemos usar ggplot pero ahora especificaremos dentro del geom_point el parámetro data

primera_dosis <- read_csv("datasets/hpv_unicef.csv")
ultima_dosis  <- read_csv("datasets/hpv_unicef_ultima_dosis.csv")

Realizamos la gráfica nota que hay que poner dos geom_points uno para cada fuente de datos data:

#Aquí puedes poner la base que quieras o dejar vacío
ggplot() +
  geom_point(aes(x = target, y = doses), data = primera_dosis) +
  geom_point(aes(x = target, y = doses), data = ultima_dosis) +
  scale_x_continuous(labels = scales::comma) +
  scale_y_continuous(labels = scales::comma)

Podemos agregar una leyenda para que R distinga las primeras de las últimas. Si bien eso no depende de la base de datos podemos meterlo dentro del aes junto con el nombre a asignar:

#Aquí puedes poner la base que quieras o dejar vacío
ggplot() +
  geom_point(aes(x = target, y = doses, color = "Primera"), 
             data = primera_dosis, alpha = 0.5) +
  geom_point(aes(x = target, y = doses, color = "Última"), 
             data = ultima_dosis, alpha = 0.5) +
  scale_x_continuous(labels = scales::comma) +
  scale_y_continuous(labels = scales::comma)

Para visualizar mehor la información cambiaré la escala a escala logarítmica:

#Aquí puedes poner la base que quieras o dejar vacío
ggplot() +
  geom_point(aes(x = target, y = doses, color = "Primera"), 
             data = primera_dosis, alpha = 0.5) +
  geom_point(aes(x = target, y = doses, color = "Última"), 
             data = ultima_dosis, alpha = 0.5) +
  scale_x_log10(labels = scales::comma) +
  scale_y_log10(labels = scales::comma)

Finalmente terminamos la personalización para tener la gráfica final:

#Aquí puedes poner la base que quieras o dejar vacío
ggplot() +
  geom_point(aes(x = target, y = doses, color = "Primera"), 
             data = primera_dosis, alpha = 0.5, size = 2) +
  geom_point(aes(x = target, y = doses, color = "Última"), 
             data = ultima_dosis, alpha = 0.5, size = 2) +
  scale_x_log10(labels = scales::comma) +
  scale_y_log10(labels = scales::comma) +
  labs(
    x = "Dosis objetivo",
    y = "Dosis aplicadas",
    title = "Dosis objetivo y aplicadas para la vacuna contra el VPH",
    subtitle = "Primera y última dosis aplicadas en mujeres",
    caption = "Fuente: Datos de inmunización de UNICEF"
  ) +
  scale_color_manual("Dosis", values = c("Primera" = "#FAA916",
                                         "Última" = "#96031A")) +
  theme_linedraw()

3.1 Ejercicio

Las bases de datos unicef_dtp_1.csv y unicef_dtp_3.csv contienen la covertura de inmunización (DTP1 y DTP3 respectivamente) por país y año. Reproduce la siguiente gráfica donde la leyenda se pasó abajo mediante el comando legend.position = "bottom" dentro de theme. Los colores son: "DPT-1" = "#214F4B" y "DPT-3" = "#16C172".

Tip

Necesitas tanto scale_color_manual como scale_fill_manual.

4 Agregar logotipos

Los logotipos usualmente vienen en dos formatos distintos. Muchas veces vienen como svg. Las svg son los formatos preferidos pues son imágenes que no se pixelean aunque les hagas zoom. En ocasiones el svg no está disponible en cuyo caso el logo puede venir en cualquier formato de imagen png, jpeg, etc. Las instrucciones para trabajar con uno o con otro son muy similares y haremos ambos casos usando los logos del IMSS IMSS_Logo.png y Logotipo_del_IMSS.svg en la carpeta images.

En ambos casos usaremos las librerías magick y ggimage:

library(magick)  #Leer la imagen 
library(cowplot) #Poner la imagen en ggplot2

En el caso de la imagen en svg podemos leerla como:

img <- image_read_svg("imagenes/Logotipo_del_IMSS.svg", width = 1000)

donde el parámetro width controla qué tan bien se ve la imagen: a mayor width menor pixeleo del logo (pero también más tiempo de procesamiento).

Ojo la imagen no se va a ver aún pero ya la tenemos almacenada en img.

El logo del IMSS que guardamos en img

Para agregar el logo necesitamos una gráfica en la cual nos interese poner el logotipo. Yo trabajaré con el histograma que hicimos antes que utiliza la base de datos de diabetes:

#Si ya tenías los datos no es necesario volver a leerlos
datos_diabetes <- read_csv("datasets/diabetes.csv")

#Grafico el histograma. No le estoy poniendo formato porque
#en lo que quiero enfocarme es en el logotipo
ggplot(datos_diabetes) + 
  geom_histogram(aes(x = chol)) 

Para poner la imagen es suficiente con usar draw_image y especificar las coordenadas x y y donde queremos que vaya. El parámetro scale controla qué tan grande o pequeña la queremos:

ggplot(datos_diabetes) + 
  geom_histogram(aes(x = chol)) +
  draw_image(img, x = 400, y= 40, scale=50)

exactamente el mismo proceso puede replicarse si el logo viene en png:

#Aquí no se pone width pues el png ya tiene un width predefinido
img_png <- image_read("imagenes/IMSS_Logo.png")

#Realizamos la gráfica
ggplot(datos_diabetes) + 
  geom_histogram(aes(x = chol)) +
  draw_image(img_png, x = 400, y= 40, scale=50)

4.1 Ejercicio

  1. Agrega el logo del Instituto Nacional de Salud Pública (link de descarga) a la siguiente gráfica en el centro del corazón:
#Armamos la base de datos
heartdf = tibble(
    t = seq(0, 2*pi, pi/60),
    x = 16*sin(t)^3,
    y = 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) - cos(4*t)
)

#Generamos la gráfica
ggplot(data = heartdf, aes(x, y)) + 
    geom_polygon(aes(group = 1), color = "#DDCCAA", 
                 fill = "white", size = 2) +
  theme_classic() + 
  coord_fixed() +
  labs(
    x = "",
    y = ""
  ) + 
  theme(axis.ticks = element_blank())

5 Cambio de fuente

La librería showtext es bastante útil para cambiar el tipo de fuente en los textos de un ggplot. Para ello es necesario llamar a la librería:

library(showtext)

Para agregar una fuente elige el nombre de la fuente de Google. La lista completa de fuentes puedes hallarla en https://fonts.google.com/:

font_add_google("Schoolbell", "Schoolbell")

para que R sepa que hay que usar showtext agrega esta línea:

showtext_auto()

Finalmente en tu gráfica agrega dentro del element_text de theme la fuente con su nombre. Por ejemplo a la siguiente base de datos:

#Genera una base de datos inventada
set.seed(34659)
datos <- data.frame(x = rnorm(100))

#Grafica
ggplot(datos) +
  geom_histogram(aes(x = x)) +
  labs(
    title = "Histograma"
  )

le podemos cambiar el texto sólo del título:

#Grafica
ggplot(datos) +
  geom_histogram(aes(x = x), bins = 10, color = "white") +
  labs(
    title = "Histograma"
  ) +
  theme(
    plot.title = element_text(family = "Schoolbell", size = 20)
  )

Los cambios pueden hacerse de manera idéntica con plot.subtitle, plot.caption, axis.text y axis.title.

5.1 Ejercicio

  1. Descarga la fuente Peralta y replica la siguiente gráfica usando los datos generados anteriormente.
#Genera una base de datos inventada
set.seed(34659)
datos <- data.frame(x = rnorm(100))

font_add_google("Peralta","Peralta")

ggplot(datos) +
  geom_histogram(aes(x = x), bins = 10, color = "white") +
  theme_classic() +
  labs(
    title = "Título",
    y = "Eje y",
    x = "Eje x",
    caption = "Pie de figura",
    subtitle = "Subtítulo"
  ) +
  theme(
    plot.title    = element_text(family = "Peralta", size = 20),
    plot.subtitle = element_text(family = "Peralta", size = 14),
    plot.caption  = element_text(family = "Peralta", size = 10),
    axis.text = element_text(family = "Peralta"),
    axis.title = element_text(family = "Peralta"),
  )

6 Uso de gghighlight para resaltar valores

Considera la misma base de datos IMSS_BCG_2020.csv que utilizamos la vez pasada donde graficamos las Dosis BCG aplicadas en el IMSS.

bcg <- read_csv("datasets/IMSS_BCG_2020.csv")

ggplot(bcg) +
  geom_col(aes(x = Del, y = `Dosis BCG`), 
           fill = "deepskyblue4") +
  theme_minimal() +
  labs(
    x = "Delegación",
    y = "Dosis aplicadas",
    subtitle = "Año 2020",
    title = "Dosis B.C.G. aplicadas en el IMSS"
  ) +
  theme(axis.text.x = element_text(angle = 90, 
                                   hjust = 1, vjust = 0.5))

Supongamos que nos interesa resaltar aquellas delegaciones con más de 20000 dosis aplicadas. El paquete de gghighlight ayuda con esto. Basta con poner gghighlight después del geom para hacerlo:

library(gghighlight)

ggplot(bcg) +
  geom_col(aes(x = Del, y = `Dosis BCG`), 
           fill = "deepskyblue4") +
  gghighlight(`Dosis BCG` > 20000) + 
  theme_minimal() +
  labs(
    x = "Delegación",
    y = "Dosis aplicadas",
    subtitle = "Año 2020",
    title = "Dosis B.C.G. aplicadas en el IMSS"
  ) +
  theme(axis.text.x = element_text(angle = 90, 
                                   hjust = 1, vjust = 0.5))

Hay distintas opciones dentro de gghighlight en particular para cambiar el color y la transparencia de los no subrayados:

#alpha es la transparencia (entre 0 y 1)
#fill es el relleno puede ser NULL si no deseas que cambie el color
ggplot(bcg) +
  geom_col(aes(x = Del, y = `Dosis BCG`), 
           fill = "tomato3") +
  gghighlight(`Dosis BCG` > 20000,  
              unhighlighted_params = list(fill = "deepskyblue4", alpha = 1)) + 
  theme_minimal() +
  labs(
    x = "Delegación",
    y = "Dosis aplicadas",
    subtitle = "Año 2020",
    title = "Dosis B.C.G. aplicadas en el IMSS"
  ) +
  theme(axis.text.x = element_text(angle = 90, 
                                   hjust = 1, vjust = 0.5))

6.1 Ejercicio

En la siguiente gráfica a partir de los datos de diabetes colorea aquellos puntos donde el índice de masa corporal dado por (weight * 0.453592) /(height * 0.0254)^2 sea mayor a 25 (exceso de peso) para obtener una gráfica similar a la siguiente con tus colores favoritos.

Recuerda que para pasar el peso weight de libras a kilogramos hay que multiplicarlo por 0.453592 y para pasar la altura height a metros es necesario multiplicarla por 0.0254.

7 Ejercicios de cierre de la sección

El archivo datasets/vacuna_unicef_regional.csv contiene la información de la cobertura de las inmunizaciones a nivel región desde 1980 por tipo de vacuna. Utiliza todo lo que sabes para reproducir la siguiente gráfica que utiliza la fuente Manrope. Los colores utilizados son:

"LACR" = "#2c525c", 
"EAPR" = "#0a6569",
"ECAR" = "#00786a", 
"ESAR" = "#1b8a5e",
"MENA" = "#509947", 
"ROSA" = "#84a428",
"WCAR" = "#bfa900", 
"Non-programme" = "#ffa600",
"Global" = "black"

el color de fondo de la gráfica es #fffff8, el tema es classic y el relleno de los strip.background es #2c525c.

Nota La región de México es LACR (Latinoamérica y el Caribe).

Tip

Dentro del gghighlight para que respete los facets utiliza calculate_per_facet = TRUE y para eliminar la etiqueta use_direct_label = FALSE

Tip

Necesitas usar plot_grid para juntar el gráfico que sólo contiene el logo (en imagenes) con el gráfico que contiene la info de vacunas.