Escalas: log, fechas, percentiles
¿Qué hace una escala?
En la gramática de Wilkinson, una escala controla cómo se traducen los valores de los datos a propiedades visuales. Si tu data frame tiene price en USD, la escala del eje Y decide:
- Si los números se muestran como
1000,1,000o$1k. - Si la escala es lineal, logarítmica o de percentiles.
- Dónde aparecen las marcas (breaks) y qué etiqueta llevan.
- Qué rango se muestra (limits).
ggplot2 elige una escala razonable por defecto. Customizar la escala es lo que separa un gráfico funcional de uno legible al instante.
Escalas logarítmicas: cuándo y por qué
Cuando una variable abarca varios órdenes de magnitud (precios de viviendas, población de ciudades, citas de papers), una escala lineal aplasta los valores bajos contra el eje. Una escala logarítmica reparte el espacio proporcional al cambio relativo.
library(ggplot2)
# Lineal — el grueso se aplasta en el lado bajo
ggplot(diamonds, aes(carat, price)) +
geom_point(alpha = 0.05)
# Log10 — la relación carat-price se vuelve casi lineal
ggplot(diamonds, aes(carat, price)) +
geom_point(alpha = 0.05) +
scale_x_log10() +
scale_y_log10()Pista visual: si tus puntos forman una nube curva pero al log se vuelve recta, la relación es multiplicativa, no aditiva. Eso es información sustantiva, no estética.
Variantes:
scale_x_log10() # log base 10
scale_x_log10(labels = scales::label_dollar()) # con formateo de moneda
scale_x_continuous(trans = "log2") # log base 2
scale_x_sqrt() # raíz cuadrada (útil con conteos)Escalas de fecha
Cuando una variable es de tipo Date o POSIXct, ggplot2 usa por defecto scale_x_date o scale_x_datetime. Para personalizar:
library(scales)
ggplot(economics, aes(date, unemploy)) +
geom_line() +
scale_x_date(
date_breaks = "5 years",
date_labels = "%Y"
)date_breaks controla cada cuánto aparece una marca. date_labels el formato (códigos strftime: %Y año, %m mes, %b mes abreviado, etc.).
Para fechas con locale español:
Sys.setlocale("LC_TIME", "es_ES.UTF-8")
ggplot(economics, aes(date, unemploy)) +
geom_line() +
scale_x_date(date_breaks = "5 years", date_labels = "%b %Y")
# "ene 2010", "jun 2015"...Breaks y labels personalizados
Para cualquier escala continua, breaks controla dónde aparecen las marcas y labels qué texto las acompaña:
ggplot(diamonds, aes(carat, price)) +
geom_point(alpha = 0.05) +
scale_y_continuous(
breaks = c(0, 5000, 10000, 15000, 20000),
labels = c("0 €", "5k", "10k", "15k", "20k")
)Una opción más limpia: usar funciones de scales que generan breaks y labels coherentes:
library(scales)
scale_y_continuous(
breaks = breaks_pretty(n = 5),
labels = label_number(big.mark = ".", suffix = " €")
)El paquete scales: formatters listos
scales es un paquete auxiliar (instalado automáticamente con ggplot2) que provee funciones de formateo muy útiles:
| Función | Ejemplo de output |
|---|---|
label_number() |
1,234.5 (genérico) |
label_comma() |
1,234,567 |
label_dollar() |
$1,234.50 |
label_percent() |
12.5% |
label_scientific() |
1.23e+04 |
label_log() |
10^4 (para ejes log) |
label_date(format = "%b %Y") |
Mar 2024 |
Y para breaks:
breaks_pretty(n): divisiones “humanas”.breaks_log(): divisiones logarítmicas (10, 100, 1000…).breaks_width(width): divisiones de ancho fijo.
Combinación típica:
scale_y_continuous(
breaks = breaks_pretty(n = 6),
labels = label_number(scale = 1e-3, suffix = "k")
)scale = 1e-3 divide los valores entre 1000 antes de mostrarlos, con sufijo "k". Útil para no llenar el eje con muchos ceros.
limits vs coord_cartesian: la trampa silenciosa
Hay dos formas de “recortar” el rango visible de un gráfico. No son equivalentes.
# Forma 1: limits en la escala
ggplot(diamonds, aes(carat, price)) +
geom_point() +
scale_x_continuous(limits = c(0, 2)) # ¡filtra los datos!
# Forma 2: zoom con coord_cartesian
ggplot(diamonds, aes(carat, price)) +
geom_point() +
coord_cartesian(xlim = c(0, 2)) # zoom sin filtrarLa diferencia es crítica:
limitsen la escala → los puntos fuera del rango son eliminados antes de pasar a los siguientes pasos. Si tienes ungeom_smoothdespués, lo calcula solo con los puntos dentro del rango.coord_cartesian(xlim = ...)→ los puntos siguen ahí, los cálculos siguen usando todos los datos. Solo se cambia el rango visible.
Regla práctica: para “ver una zona del gráfico”, usa coord_cartesian. Para “filtrar los datos antes de plotear”, filtra explícitamente con dplyr::filter(), no escondas el filtrado dentro de la escala.
Trampas habituales
limitsenscale_*cuando quieres zoom. El gráfico se ve igual perogeom_smoothy otras estadísticas se han recalculado con menos datos. Diagnóstico: la línea de tendencia cambia al “ajustar el eje”. Solución:coord_cartesianpara zoom.- Olvidar el
scale_para ejes log y aplicarlog()a la columna.aes(y = log(price))funciona, pero el eje muestra valores logarítmicos (-2,0,2), no los originales. Usarscale_y_log10()mantiene las etiquetas legibles (1,10,100). - Mezclar locale del SO con locale del gráfico. Si fijas
Sys.setlocale("LC_TIME", "es_ES.UTF-8")y olvidas resetearlo, puede afectar a otros análisis de la misma sesión. Mejor documentarlo en el script o usarwithr::with_locale()para encapsularlo. breakscon valores fuera del rango de los datos. Funciona, pero deja huecos visuales. Si especificasbreaksmanualmente, asegúrate de que tienen sentido para el rango efectivo de los datos.
En la siguiente entrega
Has aprendido a controlar las escalas. Los datos ya están bien representados. Falta hacer que el gráfico se vea bien como pieza editorial. Eso es el séptimo componente de la gramática: el theme. Vemos cómo elegir un theme base, cómo customizar tipografía y cómo construir un theme reutilizable que dé identidad visual a tus gráficos. Es lo siguiente.