Box plot en R

Crea gráficos de cajas con la función boxplot 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.

Interpretación del diagrama de caja y bigotes

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)

Boxplot básico en R horizontal

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")

Boxplot con datos con la función stripchart de R

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)

Boxplot en R con intervalo de confianza para la mediana

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

Gráfico de cajas y bigotes por grupos en R

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)))

Boxplot por grupos y datos con la función stripchart

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)))

Boxplots múltiples en R

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])))

Gráficos de caja múltiples separados

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))

Boxplots reordenados en R

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

Boxplot personalizado en R base

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))

Añadir la media a un gráfico de cajas y bigotes

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))

Añadir media a boxplots por grupos en R

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))

Superponer boxplot a un histograma un otro gráfico en R

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

Box plot en ggplot2 a partir de un vector

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 por grupos

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))

Boxplot en R a partir de un data frame