ggplot(mpg, aes(displ, hwy)) +
geom_point(aes(colour = factor(cyl))) +
labs(
x = "Cilindrada del motor (litros)",
y = "Millas de carretera por galón",
colour = "Número de cilindros",
title = "Kilometraje por tamaño de motor y cilindros.",
subtitle = "Fuente: https://fueleconomy.gov"
)
8 Anotaciones
You are reading the work-in-progress third edition of the ggplot2 book. This chapter should be readable but is currently undergoing final polishing.
Al construir una visualización de datos, a menudo es necesario hacer anotaciones en los datos mostrados. Conceptualmente, una anotación proporciona metadatos para el gráfico: es decir, proporciona información adicional sobre los datos que se muestran. Sin embargo, desde un punto de vista práctico, los metadatos son sólo otra forma de datos. Debido a esto, las herramientas de anotación en ggplot2 reutilizan las mismas geoms que se usan para crear otros gráficos. Sin embargo, para satisfacer las necesidades específicas que los usuarios suelen tener al anotar gráficos, existen algunas funciones auxiliares en ggplot2 y varios otros paquetes han ampliado ggplot2 de maneras que pueden resultarle útiles.
8.1 Títulos de trama y eje
Al personalizar un gráfico, suele ser útil modificar los títulos asociados con el gráfico, los ejes y las leyendas. Para ayudar con esta tarea, ggplot2 proporciona la función auxiliar labs()
, que le permite configurar los diversos títulos usando pares de nombre-valor como título = "Título de mi gráfico
, x = "Eje X"
o fill = "fill legend"
:
Los valores proporcionados a labs()
suelen ser cadenas de texto, y \n
se usa para especificar saltos de línea, pero también puede proporcionar expresiones matemáticas envueltas en quote()
. Las reglas mediante las cuales se interpretan estas expresiones se pueden encontrar escribiendo ?plotmath
.
values <- seq(from = -2, to = 2, by = .01)
df <- data.frame(x = values, y = values ^ 3)
ggplot(df, aes(x, y)) +
geom_path() +
labs(y = quote(f(x) == x^3))
También es posible incluir (algunas) rebajas en los títulos de ejes y leyendas con la ayuda del paquete ggtext (Wilke 2020) y el sistema de temas ggplot2 (ver ?sec-polish). Para habilitar la reducción, debe configurar el elemento del tema relevante en ggtext::element_markdown()
, como se demuestra a continuación:
df <- data.frame(x = 1:3, y = 1:3)
base <- ggplot(df, aes(x, y)) +
geom_point() +
labs(x = "Título del eje con *cursiva* y **negrita**")
base
base + theme(axis.title.x = ggtext::element_markdown())
Hay dos formas de eliminar la etiqueta del eje. Configurar labs(x = "")
omite la etiqueta pero aún asigna espacio; configurar labs(x = NULL)
elimina la etiqueta y su espacio.
Para obtener más información sobre cómo se relaciona labs()
con las escalas en ggplot2, consulte Sección 14.2.
8.2 Etiquetas de texto
Agregar texto a un gráfico es una de las formas más comunes de anotación. La mayoría de los gráficos no se beneficiarán al agregar texto a cada observación en el gráfico, pero etiquetar los valores atípicos y otros puntos importantes es muy útil. Sin embargo, la anotación de texto puede resultar complicada debido a la forma en que R maneja las fuentes. El paquete ggplot2 no tiene todas las respuestas, pero proporciona algunas herramientas para hacerle la vida un poco más fácil. La herramienta principal para etiquetar gráficos es geom_text()
, que agrega un etiqueta de texto, label
, en las posiciones x
e y
especificadas. geom_text()
tiene la mayor estética de cualquier geom, porque hay muchas maneras de controlar la apariencia de un texto:
-
La estética
family
proporciona el nombre de una fuente. Esta estética sí le permite usar el nombre de una fuente del sistema, pero se requiere algo de cuidado. Sólo hay tres fuentes que garantizan su funcionamiento en todas partes: “sans” (la predeterminada), “serif” o “mono”. Para ilustrar estos:df <- data.frame(x = 1, y = 3:1, family = c("sans", "serif", "mono")) ggplot(df, aes(x, y)) + geom_text(aes(label = family, family = family))
La razón por la que puede resultar complicado utilizar fuentes del sistema en un gráfico es que cada dispositivo gráfico (GD) maneja el dibujo de texto de forma diferente. Hay dos grupos de GD: dispositivos de pantalla como
windows()
(para Windows),quartz()
(para Mac),x11()
(principalmente para Linux) yRStudioGD()
(dentro de RStudio)) dibujan el gráfico en la pantalla, mientras que los dispositivos de archivos comopng()
ypdf()
escriben el gráfico en un archivo. Desafortunadamente, los dispositivos no especifican las fuentes de la misma manera, por lo que si desea que una fuente funcione en todas partes, deberá configurar los dispositivos de diferentes maneras. Dos paquetes simplifican un poco el dilema:showtext, https://github.com/yixuan/showtext, de Yixuan Qiu, crea gráficos independientes de GD al representar todo el texto como polígonos.
extrafont, https://github.com/wch/extrafont, de Winston Chang, convierte fuentes a un formato estándar que todos los dispositivos pueden usar.
Ambos enfoques tienen ventajas y desventajas, por lo que tendrás que probar ambos y ver cuál funciona mejor para tus necesidades.
-
La estética
fontface
especifica el formato y puede tomar tres valores: “plain” (el valor predeterminado), “bold” para negrita o “italic” para cursiva. Por ejemplo:df <- data.frame(x = 1, y = 3:1, face = c("plain", "bold", "italic")) ggplot(df, aes(x, y)) + geom_text(aes(label = face, fontface = face))
-
Puede ajustar la alineación del texto con las estéticas
hjust
(“left”, “center”, “right”, “inward”, “outward”) yvjust
(“bottom”, “middle”, “top” , “inward”, “outward”). La alineación está centrada de forma predeterminada, pero a menudo hay buenas razones para anularla. Una de las alineaciones más útiles es la “inward”. Alinea el texto hacia el centro de la trama, lo que garantiza que las etiquetas permanezcan dentro de los límites de la trama:df <- data.frame( x = c(1, 1, 2, 2, 1.5), y = c(1, 2, 1, 2, 1.5), text = c( "bottom-left", "top-left", "bottom-right", "top-right", "center" ) ) ggplot(df, aes(x, y)) + geom_text(aes(label = text)) ggplot(df, aes(x, y)) + geom_text(aes(label = text), vjust = "inward", hjust = "inward")
El tamaño de fuente está controlado por la estética del
size
. A diferencia de la mayoría de herramientas, ggplot2 especifica el tamaño en milímetros (mm), en lugar de los puntos habituales (pts). El motivo de esta elección es que hace que las unidades para tamaños de fuente sean consistentes con la forma en que se especifican otros tamaños en ggplot2. (Hay 72,27 puntos en una pulgada, por lo que para convertir de puntos a mm, simplemente multiplique por 25,4/72,27).angle
especifica la rotación del texto en grados.
El paquete ggplot2 le permite asignar valores de datos a la estética utilizada por geom_text()
, pero debe usar moderación: es difícil percibir la relación entre las variables asignadas a esta estética, y rara vez es útil hacerlo.
Además de las diversas estéticas, geom_text()
tiene tres parámetros que puedes especificar. A diferencia de la estética, estos sólo toman valores únicos, por lo que deben ser los mismos para todas las etiquetas:
-
A menudo desea etiquetar puntos existentes en el gráfico, pero no desea que el texto se superponga con los puntos (o barras, etc.). En esta situación es útil desplazar un poco el texto, lo que puedes hacer con los parámetros
nudge_x
ynudge_y
:df <- data.frame( treatment = c("a", "b", "c"), response = c(1.2, 3.4, 2.5) ) ggplot(df, aes(treatment, response)) + geom_point() + geom_text( mapping = aes(label = paste0("(", response, ")")), nudge_x = -0.3 ) + ylim(1.1, 3.6)
(Tenga en cuenta que modificamos manualmente los límites del eje y para darles a las etiquetas un poco más de espacio.)
-
El tercer parámetro es
check_overlap
. Sicheck_overlap = TRUE
, las etiquetas superpuestas se eliminarán automáticamente del gráfico. El algoritmo es simple: las etiquetas se trazan en el orden en que aparecen en el marco de datos; si una etiqueta se superpone con un punto existente, se omite.ggplot(mpg, aes(displ, hwy)) + geom_text(aes(label = model)) + xlim(1, 8) ggplot(mpg, aes(displ, hwy)) + geom_text(aes(label = model), check_overlap = TRUE) + xlim(1, 8)
A primera vista, esta característica no parece muy útil, pero la simplicidad del algoritmo resulta útil. Si ordena los datos de entrada por orden de prioridad, el resultado es un gráfico con etiquetas que enfatizan puntos de datos importantes.
Una variación de geom_text()
es geom_label()
: dibuja un rectángulo redondeado detrás del texto. Esto lo hace útil para agregar etiquetas a gráficos con fondos ocupados:
label <- data.frame(
waiting = c(55, 80),
eruptions = c(2, 4.3),
label = c("peak one", "peak two")
)
ggplot(faithfuld, aes(waiting, eruptions)) +
geom_tile(aes(fill = density)) +
geom_label(data = label, aes(label = label))
Etiquetar bien los datos plantea algunos desafíos:
El texto no afecta los límites de la trama. Desafortunadamente, no hay forma de hacer que esto funcione ya que una etiqueta tiene un tamaño absoluto (por ejemplo, 3 cm), independientemente del tamaño de la trama. Esto significa que los límites de un gráfico tendrían que ser diferentes dependiendo del tamaño del mismo; simplemente no hay forma de que eso suceda con ggplot2. En su lugar, necesitarás modificar
xlim()
yylim()
según tus datos y el tamaño del gráfico.-
Si desea etiquetar muchos puntos, es difícil evitar superposiciones.
check_overlap = TRUE
es útil, pero ofrece poco control sobre qué etiquetas se eliminan. Una técnica popular para solucionar este problema es utilizar el paquete ggrepel https://github.com/slowkow/ggrepel de Kamil Slowikowski. El paquete proporcionageom_text_repel()
, que optimiza la posición de la etiqueta para evitar la superposición. Funciona bastante bien siempre que el número de etiquetas no sea excesivo:mini_mpg <- mpg[sample(nrow(mpg), 20), ] ggplot(mpg, aes(displ, hwy)) + geom_point(colour = "red") + ggrepel::geom_text_repel(data = mini_mpg, aes(label = class))
A veces puede resultar difícil garantizar que las etiquetas de texto encajen en el espacio deseado. El paquete ggfittext https://github.com/wilkox/ggfittext de Claus Wilke contiene herramientas útiles que pueden ayudar con esto, incluidas funciones que le permiten colocar etiquetas de texto dentro de las columnas en un gráfico de barras.
8.3 Crear anotaciones personalizadas
Etiquetar puntos individuales con texto es un tipo importante de anotación, pero no es la única técnica útil. El paquete ggplot2 proporciona varias otras herramientas para anotar trazados usando las mismas geoms que usaría para mostrar datos. Por ejemplo, puedes utilizar:
geom_text()
ygeom_label()
para agregar texto, como se ilustró anteriormente.geom_rect()
para resaltar interesantes regiones rectangulares de la trama.geom_rect()
tiene estéticaxmin
,xmax
,ymin
yymax
.geom_line()
,geom_path()
ygeom_segment()
para agregar líneas. Todas estas geoms tienen unaarrow
parámetro, que le permite colocar una punta de flecha en la línea. Crea puntas de flecha conarrow()
, que tiene argumentosangle
,length
,ends
ytype
.geom_vline()
,geom_hline()
ygeom_abline()
le permiten agregar líneas de referencia (a veces llamadas reglas), que abarcan todo el rango de la trama.
Normalmente, puede colocar anotaciones en primer plano (usando alpha
si es necesario para poder seguir viendo los datos) o en segundo plano. Con el fondo predeterminado, una línea blanca gruesa es una referencia útil: es fácil de ver pero no llama la atención. Para ilustrar cómo se pueden utilizar las herramientas ggplot2 para anotar gráficos, comenzaremos con una serie temporal que representa el desempleo en EE. UU. a lo largo del tiempo:
ggplot(economics, aes(date, unemploy)) +
geom_line()
Una forma útil de anotar este gráfico es utilizar sombreado para indicar qué presidente estaba en el poder en ese momento. Para hacer esto, usamos geom_rect()
para introducir sombreado, geom_vline()
para introducir separadores, geom_text()
para agregar etiquetas y luego usamos geom_line()
para superponer los datos sobre estos fondos. elementos:
presidential <- subset(presidential, start > economics$date[1])
ggplot(economics) +
geom_rect(
aes(xmin = start, xmax = end, fill = party),
ymin = -Inf, ymax = Inf, alpha = 0.2,
data = presidential
) +
geom_vline(
aes(xintercept = as.numeric(start)),
data = presidential,
colour = "grey50", alpha = 0.5
) +
geom_text(
aes(x = start, y = 2500, label = name),
data = presidential,
size = 3, vjust = 0, hjust = 0, nudge_x = 50
) +
geom_line(aes(date, unemploy)) +
scale_fill_manual(values = c("blue", "red")) +
xlab("Fecha") +
ylab("Deseempleo")
Tenga en cuenta que hay pocas novedades aquí: en su mayor parte, anotar gráficos en ggplot2
es una manipulación sencilla de geoms existentes. Dicho esto, hay una cosa especial a tener en cuenta en este código: el uso de -Inf
e Inf
como posiciones. Estos se refieren a los límites superior e inferior (o izquierdo y derecho) del gráfico.
Esta técnica también se puede aplicar de otras formas. Por ejemplo, puedes usarlo para agregar una sola anotación a un gráfico, pero es un poco complicado porque tienes que crear un marco de datos de una fila:
yrng <- range(economics$unemploy)
xrng <- range(economics$date)
caption <- paste(strwrap("Las tasas de desempleo en EE.UU. han
varió mucho a lo largo de los años", 40), collapse = "\n")
ggplot(economics, aes(date, unemploy)) +
geom_line() +
geom_text(
aes(x, y, label = caption),
data = data.frame(x = xrng[1], y = yrng[2], caption = caption),
hjust = 0, vjust = 1, size = 4
)
Este código funciona y genera la trama deseada, pero es muy engorroso. Sería molesto tener que hacer esto cada vez que quieras agregar una sola anotación, por lo que ggplot2 incluye la función auxiliar annotate()
que crea el marco de datos por ti:
ggplot(economics, aes(date, unemploy)) +
geom_line() +
annotate(
geom = "text", x = xrng[1], y = yrng[2],
label = caption, hjust = 0, vjust = 1, size = 4
)
La conveniencia de la función annotate()
resulta útil en otras situaciones. Por ejemplo, una forma común de anotación es resaltar un subconjunto de puntos dibujando puntos más grandes en un color diferente debajo del conjunto de datos principal. Para resaltar los vehículos fabricados por Subaru, puedes usar esto para crear la trama básica:
p <- ggplot(mpg, aes(displ, hwy)) +
geom_point(
data = filter(mpg, manufacturer == "subaru"),
colour = "orange",
size = 3
) +
geom_point()
El problema con esto es que la categoría resaltada no estaría etiquetada. Esto se soluciona fácilmente usando annotate()
p +
annotate(geom = "point", x = 5.5, y = 40, colour = "orange", size = 3) +
annotate(geom = "point", x = 5.5, y = 40) +
annotate(geom = "text", x = 5.6, y = 40, label = "subaru", hjust = "left")
Este enfoque tiene la ventaja de crear una etiqueta dentro de la región del trazado, pero el inconveniente es que la etiqueta está distante de los puntos que selecciona (de lo contrario, el punto naranja y negro adyacente a la etiqueta podría confundirse con datos reales). Un enfoque alternativo es utilizar una geom diferente para hacer el trabajo. geom_curve()
y geom_segment()
se pueden usar para dibujar curvas y líneas que conectan puntos con etiquetas, y se pueden usar junto con annotate()
como se ilustra a continuación:
p +
annotate(
geom = "curve", x = 4, y = 35, xend = 2.65, yend = 27,
curvature = .3, arrow = arrow(length = unit(2, "mm"))
) +
annotate(geom = "text", x = 4.1, y = 35, label = "subaru", hjust = "left")
8.4 Etiquetado directo
Los gráficos de Subaru anteriores proporcionan ejemplos de “etiquetado directo”, en los que la propia región del trazado contiene las etiquetas para grupos de puntos en lugar de utilizar una leyenda. Esto generalmente hace que el gráfico sea más fácil de leer porque acerca las etiquetas a los datos. El ecosistema más amplio de ggplot2 contiene una variedad de otras herramientas para lograr esto de una manera más automatizada. El paquete directlabels, de Toby Dylan Hocking, proporciona una serie de herramientas para facilitar esto:
ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point()
ggplot(mpg, aes(displ, hwy, colour = class)) +
geom_point(show.legend = FALSE) +
directlabels::geom_dl(aes(label = class), method = "smart.grid")
Directlabels proporciona varios métodos de posición. smart.grid
es un lugar razonable para comenzar con los diagramas de dispersión, pero existen otros métodos que son más útiles para los polígonos de frecuencia y los diagramas de líneas. Consulte el sitio web de directlabels, http://directlabels.r-forge.r-project.org, para conocer otras técnicas.
Otra versión de esta idea proviene del paquete ggforce de Thomas Lin Pedersen https://github.com/thomasp85/ggforce. El paquete ggforce contiene muchas herramientas útiles para ampliar la funcionalidad de ggplot2, incluidas funciones como geom_mark_ellipse()
que superpone un gráfico con marcas circulares “resaltadas”. Por ejemplo:
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
ggforce::geom_mark_ellipse(aes(label = cyl, group = cyl))
Un tercer enfoque para el etiquetado directo lo proporciona el paquete gghighlight de Hiroaki Yutani https://github.com/yutannihilation/gghighlight. En muchas situaciones es útil para resaltar puntos o líneas (o incluso una variedad de geoms diferentes) dentro de un gráfico, particularmente para datos longitudinales:
data(Oxboys, package = "nlme")
ggplot(Oxboys, aes(age, height, group = Subject)) +
geom_line() +
geom_point() +
gghighlight::gghighlight(Subject %in% 1:3)
#> Warning: Tried to calculate with group_by(), but the calculation failed.
#> Falling back to ungrouped filter operation...
#> Tried to calculate with group_by(), but the calculation failed.
#> Falling back to ungrouped filter operation...
#> label_key: Subject
8.5 Anotación entre facetas
Cuando se usan bien, las anotaciones pueden ser una herramienta poderosa para ayudar al lector a entender sus datos. Un ejemplo de esto es cuando desea que el lector compare grupos entre facetas. Por ejemplo, en el gráfico siguiente es fácil ver la relación dentro de cada faceta, pero las diferencias sutiles entre las facetas no resaltan:
Es mucho más fácil ver estas sutiles diferencias si añadimos una línea de referencia:
mod_coef <- coef(lm(log10(price) ~ log10(carat), data = diamonds))
ggplot(diamonds, aes(log10(carat), log10(price))) +
geom_bin2d() +
geom_abline(intercept = mod_coef[1], slope = mod_coef[2],
colour = "white", linewidth = 1) +
facet_wrap(vars(cut), nrow = 1)
En este gráfico, cada faceta muestra los datos de una categoría frente a la misma línea de regresión. Esto facilita la comparación de las facetas entre sí porque existe una línea de referencia compartida para ayudar en la comparación visual.
Una variación de este tema surge cuando desea que cada faceta de un gráfico muestre datos de un solo grupo, con el conjunto de datos completo trazado discretamente en cada panel para ayudar a la comparación visual. El paquete gghighlight es particularmente útil en este contexto:
ggplot(mpg, aes(displ, hwy, colour = factor(cyl))) +
geom_point() +
gghighlight::gghighlight() +
facet_wrap(vars(cyl))