
5 Gráficas con ggplot2
Parte 2: Graficación avanzada en R
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.
Los datos no están en el repositorio; los descargas de Dropbox y los descomprimes en una carpeta datasets/ junto a tu script (ver la presentación).
Si aún no sabes usar ggplot2 te recomiendo revisar la parte 1 de estas notas
5.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.
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 = after_stat(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 = after_stat(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.
5.1.1 Ejercicios

- Para armar el siguiente gráfico se utilizaron
histograma_1,histograma_2y una tercera gráfica decolesterolcontraglucosade esta forma (colordeepskyblue4):

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 relativaReproduce la gráfica
Más información de cowplot la puedes encontrar en sus tutoriales
5.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"
)
5.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:

5.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()
5.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".
Necesitas tanto scale_color_manual como scale_fill_manual.

5.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 ggplot2En 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.
imgPara 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)
5.4.1 Ejercicio
- 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", linewidth = 2
) +
theme_classic() +
coord_fixed() +
labs(
x = "",
y = ""
) +
theme(axis.ticks = element_blank())
5.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.5.1 Ejercicio
- Descarga la fuente
Peraltay replica la siguiente gráfica usando losdatosgenerados 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"),
)
5.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
))
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
weightde libras a kilogramos hay que multiplicarlo por0.453592y para pasar la alturaheighta metros es necesario multiplicarla por0.0254.

5.7 Ejercicio a mano: dibuja el acomodo de plot_grid
A lápiz y papel. Aquí no vas a dibujar las gráficas por dentro, sólo los rectángulos: cuántos hay, de qué tamaño y en qué posición. Es mucho más rápido de lo que parece y te ahorra un montón de prueba y error.
Supón que ya tienes tres gráficas guardadas en las variables A, B y C. Para cada bloque, dibuja en tu hoja el acomodo que resulta (un rectángulo grande dividido en pedazos, con la letra de cada gráfica adentro):
# a)
plot_grid(A, B, ncol = 2)# b)
plot_grid(A, B, ncol = 1)# c)
plot_grid(A, B, C, ncol = 2)# d)
plot_grid(A, B, ncol = 2, rel_widths = c(3, 1))# e)
plot_grid(A, plot_grid(B, C, ncol = 1), ncol = 2)Dos gráficas lado a lado, del mismo ancho:
Aa la izquierda,Ba la derecha.Dos gráficas una encima de la otra:
Aarriba,Babajo. Conncol = 1todo se apila en una sola columna.Cuatro lugares en una rejilla de 2×2, pero sólo tienes tres gráficas:
AyBarriba,Cabajo a la izquierda y el lugar de abajo a la derecha queda vacío. Es el error más común deplot_grid: te queda un hueco blanco y no entiendes por qué.Igual que el
a, peroAocupa tres cuartas partes del ancho yBsólo una cuarta. Los números derel_widthsson proporciones relativas, no centímetros ni pulgadas:c(3, 1)es lo mismo quec(30, 10)o quec(0.75, 0.25).Éste es el truco importante:
plot_gridse puede meter dentro de sí mismo. Del lado izquierdo vaAcompleta (ocupando todo el alto) y del lado derecho vanBarriba yCabajo, apiladas. Así se arman los tableros de indicadores con una gráfica principal grande y dos chiquitas al lado.+-------------+-------------+ | | B | | A +-------------+ | | C | +-------------+-------------+Es exactamente la misma idea que se usa en el ejercicio de cierre de esta sección para pegarle el logo de UNICEF a la gráfica.
5.8 Ejercicio: encuentra el error (graficación avanzada)
Predice el error, córrelo, lee el mensaje completo y arréglalo:
# a) Queremos juntar dos gráficas
plot_grid(histograma_1 + histograma_2, ncol = 2)# b) Queremos el título con una palabra en rojo
ggplot(datos_diabetes) +
geom_point(aes(x = weight, y = height)) +
labs(title = "Peso contra <span style='color:red;'>altura</span>")# c) Queremos resaltar sólo a las personas con colesterol alto
ggplot(datos_diabetes) +
geom_point(aes(x = weight, y = chol)) +
gghighlight(chol > 300)# d) Queremos usar la fuente Manrope
ggplot(datos_diabetes) +
geom_point(aes(x = weight, y = height)) +
theme(text = element_text(family = "Manrope"))Las gráficas se separan con coma, no con
+. El+sirve para pegar capas dentro de una misma gráfica; para juntar gráficas distintas esplot_grid(histograma_1, histograma_2, ncol = 2).El
<span style=...>es HTML, yggplot2por sí solo no lo interpreta: te va a imprimir el código tal cual, con picoparéntesis y todo. Para que lo lea hay que pedirlo explícitamente conggtext, agregandotheme(plot.title = element_markdown()).Éste no falla necesariamente, pero es conceptualmente el error del que hay que cuidarse:
gghighlightno filtra, sólo pinta de gris lo que no cumple la condición. Los puntos con colesterol bajo siguen ahí y siguen contando para las escalas de los ejes. Si lo que querías era quedarte sólo con los de colesterol alto,gghighlightno es la herramienta. (Si ademásRte dicecould not find function "gghighlight", es que te faltólibrary(gghighlight).)Te va a salir un
font family 'Manrope' not found(o simplemente la va a ignorar y usar la fuente de siempre). Las fuentes no se invocan por su nombre nada más: primero hay que registrarlas confont_add_google("Manrope", "Manrope")y activarshowtext_auto(), como se explica en la sección de cambio de fuente.
5.9 Ejercicio con apoyo de una IA (graficación avanzada)
De nuevo: pega código y mensajes de error, nunca datos identificables de personas ni bases confidenciales de tu institución.
El acomodo. Descríbele a una IA el tablero que dibujaste en el inciso
edel ejercicio a mano (“quiero una gráfica grande a la izquierda y dos chiquitas apiladas a la derecha, usandocowplot”) y pídele el código. ¿Le atinó al anidamiento o intentó resolverlo conncolyrel_widthsa la brava? Compara con tu dibujo.Traduce un error de verdad. Corre el inciso
ddel ejercicio anterior (el de la fuenteManrope), copia el mensaje completo y pídele: “Explícame este mensaje deRcomo si nunca hubiera programado y dime exactamente qué renglones tengo que agregar.”. Aplica su solución y verifica que la fuente de veras cambió, comparando la gráfica antes y después. Es fácil creer que se arregló cuando en realidadRestá usando la fuente por defecto en silencio.Cázala inventando (nivel avanzado). Pídele: “Dame la función de
cowplotque agrega automáticamente las letras a), b), c) a cada panel.” Aquí sí existe algo parecido, así que el ejercicio es distinto: verifica con?nombre_de_la_funcionque la función exista y que los argumentos que te dio sean los reales. Es muy común que la función sea correcta pero los argumentos estén inventados o correspondan a una versión vieja del paquete.Pídele lo imposible. Descríbele una gráfica que no se pueda hacer con los datos que tienes (por ejemplo, pedirle una tendencia por mes cuando tu base sólo tiene información anual). ¿Te dijo que no se puede, o te inventó un código que interpola meses de la nada? Escribe en dos renglones qué habrías reportado si no te hubieras dado cuenta.
5.10 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).
Dentro del gghighlight para que respete los facets utiliza calculate_per_facet = TRUE y para eliminar la etiqueta use_direct_label = FALSE
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.
