Alcance

Este post me surge tras las preguntas que me hacen los alumnos del Curso Introducción al software estadístico R que imparto en la AEC:

  1. ¿Se puede cambiar el criterio para decidir el umbral de observaciones atípicas?
  2. ¿Cómo puedo identificar las observaciones atípicas en un gráfico de cajas con ggplot?

La segunda de las preguntas es algo muy recurrente, que todo el mundo necesita. La segunda es más específica.

Cargamos aquí los paquetes que utilizaremos:

library(ggplot2)
library(dplyr)

Datos

Vamos a simular unos datos con los que podremos ilustrar las respuestas a estas preguntas. Simulamos 100 valores de una distribución log-normal, que es muy asimétrica (con una columna más de letras aleatorias que luego usaremos):

set.seed(1)
datos <- tibble(x = rlnorm(100, meanlog = 2),
                letra = sample(LETTERS, 100, TRUE))
datos
## # A tibble: 100 x 2
##        x letra
##    <dbl> <chr>
##  1  3.95 Z    
##  2  8.88 M    
##  3  3.20 Z    
##  4 36.4  B    
##  5 10.3  B    
##  6  3.25 S    
##  7 12.0  Q    
##  8 15.5  B    
##  9 13.1  K    
## 10  5.44 C    
## # … with 90 more rows

Su histograma:

datos %>% 
  ggplot(aes(x)) +
  geom_histogram(bins = 10,
                 fill = "white", 
                 col = "gray")

Definición de atípicos

El gráfico de cajas representa lo que se llama el resumen de cinco números:

fivenum(datos$x)
## [1]  0.8067835  4.4379067  8.2869579 14.7812264 81.5827433

Es decir, el mínimo, el primer cuartil, la mediana, el tercer cuartil y el máximo.

Los valores que van más allá de 1.5 veces el rango intercuartílico desde Q1 o Q3 (bordes de las cajas) son candidatos a ser valores atípicos, y se representan con un punto.

datos %>% 
  ggplot(aes(y = x)) +
  geom_boxplot()

La definición de atípicos se puede cambiar con el argumento coef, que por defecto es 1.5 pero se puede cambiar, por ejemplo, a 3, que ya no sería candidato a ser atípico sino claramente atípico:

datos %>% 
  ggplot(aes(y = x)) +
  geom_boxplot(coef = 3) 

Dando así respuesta a la primera de las preguntas.

Identificación de atípicos

Una vez vemos en el gráfico de cajas los puntos como valores atípicos, querremos saber a qué observaciones se refieren.

La forma más fácil de encontrarlos es ordenando la tabla en orden descendente, generalmente si sin pocos los tendremos a la vista:

datos %>% 
  arrange(desc(x))
## # A tibble: 100 x 2
##        x letra
##    <dbl> <chr>
##  1  81.6 U    
##  2  64.9 D    
##  3  53.5 J    
##  4  36.4 B    
##  5  36.1 Y    
##  6  33.5 C    
##  7  32.0 H    
##  8  31.0 T    
##  9  28.8 N    
## 10  24.7 C    
## # … with 90 more rows

Como en el gráfico se veían 3 puntos, claramente son los tres primeros. Si hubiera outliers por la parte inferior, bastaría quitar desc.

Podemos también filtrarlos exactamente en el conjunto de datos con la ayuda de la función de R base boxplot.stats, que nos devuelve una lista cuyo elemento out contiene los valores atípicos, y por tanto podemos filtrar la tabla de datos.

boxplot.stats(datos$x, coef = 3)
## $stats
## [1]  0.8067835  4.4379067  8.2869579 14.7812264 36.4259270
## 
## $n
## [1] 100
## 
## $conf
## [1] 6.652713 9.921202
## 
## $out
## [1] 53.53844 81.58274 64.88469
datos %>% 
  filter(x %in% boxplot.stats(datos$x, coef = 3)$out)
## # A tibble: 3 x 2
##       x letra
##   <dbl> <chr>
## 1  53.5 J    
## 2  81.6 U    
## 3  64.9 D

Para identificarlos en el gráfico, podemos guardar los valores anteriores y representarlos con etiquetas, mapeando a cualquier otra columna (si queremos el número de fila, lo podemos añadir a los datos).

atipicos <- datos %>% 
  filter(x %in% boxplot.stats(datos$x, coef = 3)$out)
datos %>% 
  ggplot(aes(y = x)) +
  geom_boxplot(coef = 3) +
  geom_label(data = atipicos,
             aes(x = 0.1, y = x,
                 label = letra), 
             col = "red")

Otro ejemplo cambiando los puntos por el número de fila (si hay muchos atípicos, se puede usar ggrepel o geom_jitter)

atipicos <- datos %>% 
  mutate(fila = 1:nrow(datos)) %>% 
  filter(x %in% boxplot.stats(datos$x, coef = 3)$out)
datos %>% 
  ggplot(aes(y = x)) +
  geom_boxplot(coef = 3,
               outlier.shape = NA) +
  geom_text(data = atipicos,
             aes(x = 0, y = x,
                 label = fila), 
             col = "blue")