Distribuciones: histogram, density, boxplot, violin
Cuatro formas de ver la misma variable
Cuando tienes una variable continua y quieres ver “cómo se distribuye”, tienes cuatro herramientas básicas:
| Geom | Qué muestra | Cuándo es la mejor |
|---|---|---|
geom_histogram |
Conteos en bins discretos | Datos abundantes y forma simple |
geom_density |
Estimación suavizada de la densidad | Comparar formas entre grupos |
geom_boxplot |
Resumen en 5 números + outliers | Comparar muchos grupos |
geom_violin |
Densidad simétrica | Cuando boxplot oculta multimodalidad |
Cada una responde una pregunta ligeramente distinta. Elegir mal no rompe el gráfico, pero esconde información que sí estaba en los datos.
geom_histogram: bins como decisión, no detalle
library(ggplot2)
ggplot(diamonds, aes(price)) +
geom_histogram()ggplot2 va a quejarse:
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Esto no es ruido, es una decisión que tienes que tomar tú. El binwidth controla cuánto se “promedia” en cada barra:
# binwidth pequeño: muestra el detalle (incluyendo ruido)
ggplot(diamonds, aes(price)) +
geom_histogram(binwidth = 50)
# binwidth medio: suaviza pero conserva forma
ggplot(diamonds, aes(price)) +
geom_histogram(binwidth = 500)
# binwidth grande: caricaturiza
ggplot(diamonds, aes(price)) +
geom_histogram(binwidth = 5000)No hay un binwidth “correcto”, hay uno apropiado a la pregunta. Para EDA, prueba 2-3 valores y compara. Si la forma cambia drásticamente, los datos son sensibles al bineado y conviene ser explícito sobre la elección en el código final.
Una alternativa a binwidth es bins, que controla el número de divisiones:
geom_histogram(bins = 50)Da igual la unidad de medida, útil cuando quieres consistencia visual entre gráficos comparables.
geom_density: la versión continua
ggplot(diamonds, aes(price)) +
geom_density()geom_density estima la densidad con un kernel y la dibuja como una curva suave. Tres ventajas frente a histogram:
- No depende de binwidth (depende del bandwidth, ajustable con
bw =, pero el default suele estar bien). - Permite superponer grupos sin amontonarse:
ggplot(diamonds, aes(price, fill = cut)) +
geom_density(alpha = 0.4)- Es continua: útil cuando los datos son intrínsecamente continuos (no conteos).
Cuándo evitarla:
- Con pocos datos (<50 puntos), el kernel exagera la forma y mientes con confianza.
- Cuando los datos tienen un límite físico (precios ≥ 0), el kernel “se derrama” más allá del límite y dibuja cola en valores que no existen.
geom_boxplot: cinco números y un convenio
ggplot(diamonds, aes(cut, price)) +
geom_boxplot()Un boxplot muestra cinco estadísticos por grupo:
- Caja: cuartiles 1 (Q1), 2 (mediana) y 3 (Q3).
- Bigotes: hasta 1.5 × IQR (rango intercuartílico) más allá de Q1 y Q3.
- Puntos: cualquier valor fuera de los bigotes, lo que el método llama outliers.
El boxplot es muy útil para comparar muchos grupos en poco espacio. Pero tiene una limitación importante que conviene conocer.
Cuándo el boxplot miente
Mira estos dos vectores:
set.seed(1)
unimodal <- rnorm(1000, mean = 50, sd = 5)
bimodal <- c(rnorm(500, 40, 2), rnorm(500, 60, 2))
mean(unimodal); mean(bimodal) # ambos ~50
median(unimodal); median(bimodal) # ambos ~50
IQR(unimodal); IQR(bimodal) # IQRs muy parecidosTienen la misma mediana, la misma media y casi el mismo IQR. Sus boxplots se ven prácticamente idénticos. Pero unimodal tiene una sola moda (campana centrada en 50) y bimodal tiene dos modas en 40 y 60 sin nadie en el centro.
El boxplot pierde esa información. Si vas a comparar distribuciones donde la multimodalidad importa (subgrupos ocultos, mezclas de poblaciones), necesitas algo distinto.
geom_violin: cuando boxplot no basta
ggplot(diamonds, aes(cut, price)) +
geom_violin()Un violin es una densidad simétrica girada 90°. Te muestra dónde se acumulan los datos dentro de cada grupo. Si hay multimodalidad, se ve. Si los datos están sesgados a un lado, también.
El patrón clásico es combinar violin + boxplot superpuesto:
ggplot(diamonds, aes(cut, price)) +
geom_violin(fill = "grey80", alpha = 0.5) +
geom_boxplot(width = 0.15, fill = "white", outlier.shape = NA)Lo mejor de los dos mundos: la forma de la distribución (violin) y los cinco números (boxplot).
Cuando los grupos son pocos pero los datos no son muchos
Si tienes 4 grupos y 30 observaciones por grupo, violin no funciona, la densidad estimada es ruido. Para muestras pequeñas, muestra los puntos directamente:
ggplot(diamonds[sample(nrow(diamonds), 200), ], aes(cut, price)) +
geom_jitter(width = 0.2, alpha = 0.5) +
stat_summary(fun = mean, geom = "point", size = 3, color = "red")Puntos + un marcador para la media. Honesto y sin pretensiones.
Trampas habituales
- Aceptar el binwidth por defecto sin pensar. El warning de
bins = 30es información, no ruido. Decide tu binwidth en función de la escala de tus datos. - Boxplot con pocos datos. Con n < 10 por grupo, el boxplot es engañoso (Q1 y Q3 son inestables). Muestra los puntos directamente.
- Densidad sin alpha cuando hay grupos. Si superpones varias densidades sin
alpha, las últimas tapan a las primeras. Casi siempre quieresgeom_density(alpha = 0.4). - No declarar el límite inferior cuando la variable es ≥ 0. Una densidad de “edades” puede mostrar una cola que se mete en valores negativos. Para esto: histogramas,
ggridgescon kernel limitado, o simplementecoord_cartesian(xlim = c(0, ...))para no enseñar la cola fantasma.
En la siguiente entrega
Has cerrado el bloque 1, los fundamentos de ggplot2. Ya sabes la gramática, sabes mapear con aes(), conoces los geoms básicos y los de distribución. El siguiente bloque entra en composición y escala: cómo facetar para small multiples, cómo cambiar escalas (log, fechas, percentiles) y cómo aplicar themes con tipografía editorial. Empieza por el facetado.