Box plot en R
Los box plots, también conocidos como diagramas de cajas y bigotes, son una representación gráfica que permite resumir las características principales de los datos (posición, dispersión, asimetría, …) e identificar la presencia de valores atípicos. En este tutorial revisaremos cómo hacer box plots en R base y en ggplot2.
¿Cómo interpretar un diagrama de caja y bigotes en R?
La caja de un boxplot comienza en el primer cuartil (25%) y termina en el tercero (75%). Por lo tanto, la caja representa el 50% de los datos centrales, con una línea dentro que representa la mediana. A cada lado de la caja se dibuja un segmento con los datos más lejanos sin contar los valores atípicos (outliers) del box plot, que en caso de existir, se representarán con círculos.
Un dato atípico es aquella observación que está muy distante del resto de los datos. Se dice que un valor es un valor atípico si es mayor que \(Q_3\) + 1.5 \(\cdot IQR\) (valor atípico a la derecha), o es menor que \(Q_1\) – 1.5 \(\cdot IQR\) (valor atípico a la izquierda), siendo \(Q_1\) el primer cuartil, \(Q_3\) el tercer cuartil y \(IQR\) el rango intercuartil (\(Q_3\) – \(Q_1\)) que representa el ancho de la caja para diagramas de caja horizontales.
La función boxplot en R
En R base se puede crear un gráfico caja y bigotes con la función boxplot
. Puedes dibujar este tipo de gráfico a partir de diferentes inputs, como vectores o data frames, como revisaremos en las siguientes subsecciones. En caso de dibujar diagramas de caja para múltiples grupos en el mismo gráfico también puedes especificar una fórmula como entrada. Además, es posible personalizar el gráfico de cajas resultante con varios argumentos, como veremos a continuación.
Boxplot de un vector
Si te preguntas cómo hacer un boxplot en R a partir de un vector, solo necesitas pasar el vector a la función boxplot
. Por defecto, el diagrama de caja será vertical, pero puedes cambiar la orientación estableciendo el argumento horizontal
como TRUE
.
x <- c(8, 5, 14, -9, 19, 12, 3, 9, 7, 4,
4, 6, 8, 12, -8, 2, 0, -1, 5, 3)
boxplot(x, horizontal = TRUE)
Ten en cuenta que los diagramas de caja ocultan la distribución subyacente de los datos. Para resolver este problema, puedes agregar puntos al diagrama de cajas con la función stripchart
(el método jitter
evitará dibujar sobre los datos atípicos) de la siguiente manera:
stripchart(x, method = "jitter", pch = 19, add = TRUE, col = "blue")
Desde R 4.0.0 los gráficos de caja son grises por defecto en lugar de blancos.
Intervalo de confianza para la mediana del boxplot
Puedes representar los intervalos de confianza al 95% para la mediana en un diagrama de caja de R estableciendo el argumento notch
como TRUE
.
boxplot(x, notch = TRUE)
Si los intervalos de confianza de dos o más diagramas de caja no se superponen significa que hay una fuerte evidencia estadística de que las medianas son diferentes.
Gráfico de cajas por grupo
Si tu conjunto de datos tiene una variable categórica que contiene grupos, puedes crear un diagrama de caja especificando una fórmula (variable_continua ~ variable_categorica
). Para este ejemplo, vamos a utilizar el conjunto de datos chickwts
de R base.
head(chickwts)
weight feed
1 179 horsebean
2 160 horsebean
3 136 horsebean
4 227 horsebean
5 217 horsebean
6 168 horsebean
Ahora, puedes crear un diagrama de caja que muestre la variable weight
(peso) contra el tipo de alimentación (feed
). Ten en cuenta que al trabajar con datasets puedes llamar directamente a los nombres de las variables si especificas el nombre del data frame en el argumento data
.
boxplot(chickwts$weight ~ chickwts$feed)
boxplot(weight ~ feed, data = chickwts) # Equivalente
Además, en este ejemplo podrías agregar puntos a cada diagrama de caja de la siguiente manera:
stripchart(chickwts$weight ~ chickwts$feed, vertical = TRUE, method = "jitter",
pch = 19, add = TRUE, col = 1:length(levels(chickwts$feed)))
Múltiples boxplots
En caso de que todas las variables de tu conjunto de datos sean variables numéricas, puedes crear directamente un diagrama de caja desde un data frame. Para ilustrarlo vamos a utilizar el conjunto de datos trees
.
head(trees)
Girth Height Volume
1 8.3 70 10.3
2 8.6 65 10.3
3 8.8 63 10.2
4 10.5 72 16.4
5 10.7 81 18.8
6 10.8 83 19.7
Observa la diferencia en la estructura con respecto al conjunto de datos chickwts
. Sin embargo, puedes convertir este conjunto de datos como uno del mismo formato que el conjunto de datos chickwts
con la función stack
.
stacked_df <- stack(trees)
head(stacked_df)
values ind
1 8.3 Girth
2 8.6 Girth
3 8.8 Girth
4 10.5 Girth
5 10.7 Girth
6 10.8 Girth
Ahora puedes dibujar el diagrama de caja con el data frame original o con el apilado como lo hicimos en la sección anterior. Ten en cuenta que puedes cambiar el color del diagrama de caja por grupo con un vector de colores como parámetro del argumento col
. Así, cada diagrama de caja tendrá un color diferente.
# Boxplot del conjunto de datos 'trees'
boxplot(trees, col = rainbow(ncol(trees)))
# Equivalente a:
boxplot(stacked_df$values ~ stacked_df$ind,
col = rainbow(ncol(trees)))
Puedes apilar columnas de un data frame con la función stack
.
En caso de que necesites dibujar un diagrama de caja diferente para cada columna de un data frame, puedes usar la función lapply
e iterar sobre cada columna. En este caso, dividiremos el panel gráfico en una fila y tantas columnas como tenga el conjunto de datos. Otra alternativa sería dibujar boxplots de forma individual. La función invisible
evita mostrar el texto de salida de la función lapply
.
par(mfrow = c(1, ncol(trees)))
invisible(lapply(1:ncol(trees), function(i) boxplot(trees[, i])))
Reordenar boxplot en R
Por defecto, los diagramas de caja se dibujarán con el orden de los factores en los datos. Sin embargo, puedes reordenar u ordenar un diagrama de caja en R reordenando los datos basándote en cualquier medida, como la mediana o la media, con la función reorder
.
par(mfrow = c(1, 2))
# De más bajo a más alto
medianas <- reorder(chickwts$feed, chickwts$weight, median)
# medianas <- with(chickwts, reorder(feed, weight, median)) # Equivalente
boxplot(chickwts$weight ~ medianas, las = 2, xlab = "", ylab = "")
# De más alto a más bajo
medianas <- reorder(chickwts$feed, -chickwts$weight, median)
# medianas <- with(chickwts, reorder(feed, -weight, median)) # Equivalente
boxplot(chickwts$weight ~ medianas, las = 2, xlab = "", ylab = "")
par(mfrow = c(1, 1))
Si quieres ordenar el diagrama de caja con otra métrica, simplemente cambia la función median
por la que prefieras.
Personalizar un boxplot en R
Un diagrama de caja se puede personalizar completamente. En el siguiente bloque de código mostramos un ejemplo extenso de cómo personalizar un diagrama de caja en R.
Ten en cuenta que hay incluso más argumentos que los del siguiente ejemplo para personalizar el diagrama de caja, como boxlty
, boxlwd
, medlty
o staplelwd
. Revisa la lista completa de parámetros gráficos de diagrama de caja en el argumento pars
de la ayuda de ?bxp
o help(bxp)
.
plot.new()
set.seed(1)
# Fondo gris claro
rect(par("usr")[1], par("usr")[3], par("usr")[2], par("usr")[4],
col = "#ebebeb")
# Añadimos un grid blanco
grid(nx = NULL, ny = NULL, col = "white", lty = 1,
lwd = par("lwd"), equilogs = TRUE)
# Boxplot
par(new = TRUE)
boxplot(rnorm(500), # Datos
horizontal = FALSE, # Horizontal o vertical
lwd = 2, # Lines width
col = rgb(1, 0, 0, alpha = 0.4), # Color
xlab = "Etiqueta eje X", # Etiqueta eje X
ylab = "Etiqueta eje Y", # Etiqueta eje Y
main = "Boxplot personalizado en R base", # Título
notch = TRUE, # Añade intervalos de confianza para la mediana
border = "black", # Color del borde del boxplot
outpch = 25, # Símbolo para los outliers
outbg = "green", # Color de los datos atípicos
whiskcol = "blue", # Color de los bigotes
whisklty = 2, # Tipo de línea para los bigotes
lty = 1) # Tipo de línea (caja y mediana)
# Agregamos una leyenda
legend("topright", legend = "Boxplot", # Posición y título
fill = rgb(1, 0, 0, alpha = 0.4), # Color
inset = c(0.03, 0.05), # Cambiamos los márgenes
bg = "white") # Color de fondo de la leyenda
Añadir la media a un boxplot en R
Por defecto, cuando creas un diagrama de caja, se muestra la mediana. Sin embargo, es posible que también quieras mostrar la media u otra característica de los datos. Para ese propósito, puedes usar la función segments
si quieres mostrar una línea como la mediana, o la función points
para agregar puntos. Ten en cuenta que el código es ligeramente diferente si creas un diagrama de caja vertical o un diagrama de caja horizontal.
En el siguiente bloque de código, mostramos cómo agregar puntos y segmentos con la media a ambos tipos de diagramas de caja, cuando trabajes con un solo diagrama de caja.
par(mfrow = c(1, 2))
#-----------------
# Boxplot vertical
#-----------------
boxplot(x)
# Añadir línea con la media
segments(x0 = 0.8, y0 = mean(x),
x1 = 1.2, y1 = mean(x),
col = "red", lwd = 2)
# abline(h = mean(x), col = 2, lwd = 2) # Línea completa
# Añadir punto con la media
points(mean(x), col = 3, pch = 19)
#-------------------
# Boxplot horizontal
#-------------------
boxplot(x, horizontal = TRUE)
# Añadir línea con la media
segments(x0 = mean(x), y0 = 0.8,
x1 = mean(x), y1 = 1.2,
col = "red", lwd = 2)
# abline(v = mean(x), col = 2, lwd = 2) # Línea completa
# Añadir punto con la media
points(mean(x), 1, col = 3, pch = 19)
par(mfrow = c(1, 1))
Nótese que en este caso la media y la mediana son casi iguales, ya que la distribución es simétrica.
Puedes cambiar la función mean
del código anterior por otra función para mostrar otra medida.
También puedes agregar la media en boxplots por grupo. En este caso, puedes hacer uso de la función lapply
y así evitar bucles. Para calcular la media de cada grupo se puede usar la función apply
por columnas o la función colMeans
, ya que es más eficiente. Puedes seguir el siguiente código para añadir los puntos y las líneas en boxplots horizontales y verticales.
par(mfrow = c(1, 2))
mi_df <- trees
#----------------------------
# Boxplot vertical por grupo
#----------------------------
boxplot(mi_df, col = rgb(0, 1, 1, alpha = 0.25))
# Añadir líneas con las medias
invisible(lapply(1:ncol(mi_df),
function(i) segments(x0 = i - 0.4,
y0 = mean(mi_df[, i]),
x1 = i + 0.4,
y1 = mean(mi_df[, i]),
col = "red", lwd = 2)))
# Añadir puntos con la medias
medias <- apply(mi_df, 2, mean)
medias <- colMeans(mi_df) # Equivalente (más eficiente)
points(medias, col = "red", pch = 19)
#-----------------------------
# Boxplot horizontal por grupo
#-----------------------------
boxplot(mi_df, col = rgb(0, 1, 1, alpha = 0.25),
horizontal = TRUE)
# Añadir líneas con la medias
invisible(lapply(1:ncol(mi_df),
function(i) segments(x0 = mean(mi_df[, i]),
y0 = i - 0.4,
x1 = mean(mi_df[, i]),
y1 = i + 0.4,
col = "red", lwd = 2)))
# Añadir puntos con la medias
medias <- apply(mi_df, 2, mean)
medias <- colMeans(mi_df) # Equivalente (más eficiente)
points(medias, 1:ncol(mi_df), col = "red", pch = 19)
par(mfrow = c(1, 1))
Devolver valores de un boxplot
Si asignas el diagrama de caja a una variable puedes devolver una lista con diferentes componentes. Crea un diagrama de caja con el conjunto de datos trees
y almacénalo en una variable:
res <- boxplot(trees)
res
$`stats`
[, 1] [, 2] [, 3]
[1, ] 8.30 63 10.2
[2, ] 11.05 72 19.4
[3, ] 12.90 76 24.2
[4, ] 15.25 80 37.3
[5, ] 20.60 87 58.3
$n
[1] 31 31 31
$conf
[, 1] [, 2] [, 3]
[1, ] 11.70814 73.72979 19.1204
[2, ] 14.09186 78.27021 29.2796
$out
[1] 77
$group
[1] 3
$names
[1] "Girth" "Height" "Volume"
La salida contendrá seis elementos, descritos a continuación:
- stats: cada columna representa el bigote inferior, el primer cuartil, la mediana, el tercer cuartil y el bigote superior de cada grupo.
- n: número de observaciones de cada grupo.
- conf: cada columna representa los extremos inferior y superior del intervalo de confianza de la mediana.
- out: número total de valores atípicos.
- group: número total de grupos.
- names: nombres de cada grupo.
Es interesante destacar que puedes recrear el boxplot de la variable que hemos creado (res
) con la función bxp
.
bxp(res)
Boxplot e histograma en R
Una limitación de los gráficos de caja y bigotes es que no están diseñados para detectar multimodalidad. Por esa razón, es recomendable dibujar diagramas de caja combinados con un histograma o una curva de densidad.
par(mfrow = c(1, 1))
# Datos multimodales
n <- 20000
ii <- rbinom(n, 1, 0.5)
dat <- rnorm(n, mean = 110, sd = 11) * ii +
rnorm(n, mean = 70, sd = 5) * (1 - ii)
# Histograma
hist(dat, probability = TRUE, ylab = "", col = "grey",
axes = FALSE, main = "")
# Eje
axis(1)
# Densidad
lines(density(dat), col = "red", lwd = 2)
# Boxplot
par(new = TRUE)
boxplot(dat, horizontal = TRUE, axes = FALSE,
lwd = 2, col = rgb(0, 1, 1, alpha = 0.15))
Los boxplots no están diseñados para detectar multimodalidad en los datos.
Como alternativas ante este problema puedes usar violin plots o beanplots.
Boxplot en ggplot2
Los diagramas de caja que creamos en las secciones anteriores también se pueden realizar con el paquete ggplot2
.
Boxplot en ggplot2 a partir de un vector
El tipo de dato de entrada para usar la función ggplot
tiene que ser un data frame, por lo que tendrás que convertir tu vector en clase data.frame
. Una vez hecho podrás usar la función geom_boxplot
para crear y personalizar la caja y la función stat_boxplot
para añadir los bigotes.
# install.packages("ggplot2")
library(ggplot2)
# Datos
x <- c(8, 5, 14, -9, 19, 12, 3, 9, 7, 4,
4, 6, 8, 12, -8, 2, 0, -1, 5, 3)
# Transformar x en un data frame
x <- data.frame(x)
# Boxplot a partir de un vector
ggplot(data = x, aes(x = "", y = x)) +
stat_boxplot(geom = "errorbar", # Bigotes
width = 0.2) +
geom_boxplot(fill = "#4271AE", # Color de la caja
outlier.colour = "red", # Color de los valores atípicos
alpha = 0.9) + # Transparencia del color de la caja
ggtitle("Boxplot a partir de un vector") + # Título del plot
xlab("") + # Etiqueta del eje x
coord_flip() # Boxplot horizontal
Boxplot en ggplot2 por grupo
Si quieres crear un diagrama de caja con ggplot2 por grupo, deberás especificar las variables en el argumento aes
de la siguiente manera:
# Boxplot por grupo
ggplot(data = chickwts, aes(x = feed, y = weight)) +
stat_boxplot(geom = "errorbar", # Bigotes
width = 0.2) +
geom_boxplot(fill = "#4271AE", colour = "#1F3552", # Colores
alpha = 0.9, outlier.colour = "red") +
scale_y_continuous(name = "Peso") + # Etiqueta de la variable continua
scale_x_discrete(name = "Alimentación") + # Etiqueta de los grupos
ggtitle("Boxplot por grupos en ggplot2") + # Título del plot
theme(axis.line = element_line(colour = "black", # Personalización del tema
size = 0.25))
Boxplot en ggplot2 desde un data frame wide format
Finalmente, para crear un boxplot con ggplot2
directamente desde un conjunto de datos como el de trees
deberás apilar primero los datos con la función stack
:
# Boxplot a partir de un data frame
ggplot(data = stack(trees), aes(x = ind, y = values)) +
stat_boxplot(geom = "errorbar", # Bigotes
width = 0.2) +
geom_boxplot(fill = "#4271AE", colour = "#1F3552", # Colores
alpha = 0.9, outlier.colour = "red") +
scale_y_continuous(name = "Peso") + # Etiqueta de la variable continua
scale_x_discrete(name = "Alimentación") + # Etiqueta de los grupos
ggtitle("Boxplot a partir de un data frame") + # Título del plot
theme(axis.line = element_line(colour = "black", # Personalización del tema
size = 0.25))