Themes y customización tipográfica
¿Qué es un theme?
En la gramática de Wilkinson, el theme es la capa que controla todo lo que no depende de los datos: tipografía, fondo, leyendas, márgenes, colores del eje, grosor de las líneas no-datos.
Datos y theme son ortogonales. Puedes cambiar el theme entero sin tocar el resto del gráfico. Es lo que permite tener un “estilo de la casa”, tu marca visual, tus tipos, tu paleta de neutrales, aplicado consistentemente a treinta gráficos distintos.
Themes built-in
ggplot2 viene con varios themes pre-hechos:
library(ggplot2)
p <- ggplot(diamonds, aes(carat, price)) + geom_point(alpha = 0.1)
p + theme_grey() # default — fondo gris, líneas blancas
p + theme_bw() # fondo blanco, ejes negros, gridlines grises
p + theme_minimal() # como bw pero sin marco
p + theme_classic() # ejes negros estilo libro, sin grid
p + theme_void() # sin nada (solo el gráfico, útil para mapas)
p + theme_dark() # fondo oscuroRecomendación práctica: theme_minimal() para uso editorial moderno. theme_classic() para algo más austero, estilo libro científico. theme_void() cuando los ejes son redundantes (mapas, infografías).
Para que se aplique globalmente sin escribirlo en cada gráfico:
theme_set(theme_minimal())A partir de esa línea, todos los ggplot() siguientes usan theme_minimal por defecto.
theme() y los tres tipos de elementos
Para customizar más allá del preset, usa theme(). Cada componente del gráfico es un elemento que se ajusta con una de estas tres funciones:
| Función | Para qué elementos |
|---|---|
element_text() |
Cualquier texto (títulos, ejes, leyendas) |
element_line() |
Líneas (ejes, gridlines) |
element_rect() |
Rectángulos (fondo del panel, fondo total) |
Ejemplo completo:
ggplot(diamonds, aes(carat, price)) +
geom_point(alpha = 0.1) +
theme_minimal() +
theme(
plot.title = element_text(size = 16, face = "bold"),
axis.title = element_text(size = 12, color = "grey20"),
axis.text = element_text(size = 10),
panel.grid.minor = element_blank(),
panel.grid.major = element_line(color = "grey90"),
plot.background = element_rect(fill = "white", color = NA)
) +
labs(title = "Precio vs quilates")La estructura: <nombre_del_elemento> = element_<tipo>(<parámetros>).
Los nombres siguen el patrón <dónde>.<qué>:
plot.*: del gráfico entero (título, subtítulo, caption, background).panel.*: del área del gráfico (background, grid lines).axis.*: de los ejes (text, title, ticks, line).legend.*: de las leyendas (text, title, position, key).strip.*: de los títulos de los facets.
Para “no mostrar” un elemento: element_blank().
Tipografía editorial: showtext
ggplot2 por defecto usa la fuente de sistema. Si quieres tipografía editorial (serif para títulos, sans humanista para texto), showtext es la solución más robusta.
install.packages("showtext")
library(showtext)
# Cargar fuentes desde Google Fonts
font_add_google("Newsreader", "newsreader")
font_add_google("Inter Tight", "inter")
# Activar showtext (necesario para que las fuentes se rendericen)
showtext_auto()
# Usar en el theme
ggplot(diamonds, aes(carat, price)) +
geom_point(alpha = 0.1) +
theme_minimal(base_family = "inter") +
theme(
plot.title = element_text(family = "newsreader", size = 18, face = "plain"),
plot.subtitle = element_text(family = "inter", size = 11, color = "grey40")
) +
labs(
title = "Precio vs quilates",
subtitle = "Dataset diamonds (ggplot2)"
)base_family en el theme aplica la fuente a todo el texto base. El theme() posterior la sobrescribe para elementos específicos.
Importante: showtext_auto() debe estar activo cuando renderizas. Si exportas con ggsave() y las fuentes salen como las del sistema en lugar de las cargadas, has olvidado activar showtext_auto() o estás en un dispositivo gráfico que no lo soporta. Casi siempre la solución es añadir al script:
showtext_opts(dpi = 300) # asegura buena resolución al exportarConstruir un theme reutilizable
El paso que separa scripts ad-hoc de un sistema visual: define tu theme una vez, úsalo en todos tus gráficos.
theme_rmori <- function(base_size = 12) {
theme_minimal(base_size = base_size, base_family = "inter") +
theme(
plot.title = element_text(family = "newsreader", size = rel(1.5),
face = "plain", color = "#2A2A2A"),
plot.subtitle = element_text(size = rel(1.0), color = "#6B6B6B",
margin = margin(b = 12)),
plot.caption = element_text(size = rel(0.75), color = "#6B6B6B",
hjust = 0, margin = margin(t = 12)),
axis.title = element_text(size = rel(0.9), color = "#3D3D3D"),
axis.text = element_text(size = rel(0.85), color = "#6B6B6B"),
panel.grid.minor = element_blank(),
panel.grid.major = element_line(color = "#E5E1DA", linewidth = 0.3),
plot.background = element_rect(fill = "#FAF9F6", color = NA),
legend.position = "bottom"
)
}Uso:
ggplot(diamonds, aes(carat, price)) +
geom_point(alpha = 0.1, color = "#5F8575") +
theme_rmori() +
labs(
title = "Precio vs quilates",
subtitle = "Relación log-lineal en diamantes",
caption = "Fuente: dataset diamonds (ggplot2)"
)Ahora tienes identidad visual. Cualquier gráfico nuevo que añadas con theme_rmori() aplicará el mismo lenguaje. Cuando quieras evolucionarlo, cambias una función y todos los gráficos se actualizan.
Trampas habituales
- Olvidar
showtext_auto()al exportar conggsave(). Las fuentes salen genéricas y no entiendes por qué. Solución:showtext_auto()antes deggsave()o configurarshowtext_opts(dpi = ...). element_blank()vs no especificar nada. Si no defines un elemento entheme(), se hereda del theme base. Si quieres eliminarlo expresamente,element_blank(). No es lo mismo.theme()antes quetheme_minimal(). El orden importa: si ponestheme(panel.grid = ...)antes detheme_minimal(), el preset sobrescribe tu customización. Convención:theme_minimal()primero,theme(...)después.- Tamaños absolutos en lugar de
rel(). Si ponessize = 14para el título y luego cambiasbase_size = 16, el título no escala. Usarsize = rel(1.5)mantiene proporciones cuando varías el tamaño base.
En la siguiente entrega
Has aprendido a controlar la apariencia. Queda una pieza para que tus gráficos cuenten una historia: anotar. Etiquetas, líneas de referencia, marcadores destacados, todo lo que dirige la atención del lector al hallazgo. Vemos geom_text, annotate() y ggrepel en el siguiente tutorial.