Ignorar los valores atípicos en ggplot2 boxplot

132

¿Cómo ignoraría los valores atípicos en ggplot2 boxplot? No solo quiero que desaparezcan (es decir, outlier.size = 0), pero quiero que se ignoren de modo que el eje y escale para mostrar el percentil 1 ° / 3 °. Mis valores atípicos están causando que la "caja" se encoja tanto que prácticamente es una línea. ¿Hay algunas técnicas para lidiar con esto?

Editar Aquí hay un ejemplo:

y = c(.01, .02, .03, .04, .05, .06, .07, .08, .09, .5, -.6)
qplot(1, y, geom="boxplot")

ingrese la descripción de la imagen aquí

SFun28
fuente
Algunos datos de muestra y un ejemplo reproducible facilitarán su ayuda.
Andrie
3
mi archivo es de 200 meg! Simplemente tome cualquier conjunto de datos donde haya muchos puntos de datos entre el primer y tercer cuantil y algunos valores atípicos (solo necesita 1). Si el valor atípico está muy lejos de la primera / tercera entonces necesariamente las cajas van a encogerse para acomodar el valor atípico
SFun28
Sí, eso es lo que tenía en mente. Cree tal conjunto de datos y use dput () para publicarlo aquí junto con la declaración ggplot () que usa. Ayúdanos a ayudarte.
Andrie
¿No puede simplemente alterar los límites del eje y para "acercar" la parte del eje y que le interesa?
Gavin Simpson
2
déjame mirar ... Oh sí, lo siento. Simplemente haga fivenum()en los datos para extraer lo que, IIRC, se usa para las bisagras superiores e inferiores en los diagramas de caja y use esa salida en la scale_y_continuous()llamada que mostró @Ritchie. Esto se puede automatizar muy fácilmente utilizando las herramientas que proporcionan R y ggplot. Si también necesita incluir los bigotes, considere usarlos boxplot.stats()para obtener los límites superior e inferior de los bigotes y luego usarlos scale_y_continuous().
Gavin Simpson

Respuestas:

141

Aquí hay una solución usando boxplot.stats

# create a dummy data frame with outliers
df = data.frame(y = c(-100, rnorm(100), 100))

# create boxplot that includes outliers
p0 = ggplot(df, aes(y = y)) + geom_boxplot(aes(x = factor(1)))


# compute lower and upper whiskers
ylim1 = boxplot.stats(df$y)$stats[c(1, 5)]

# scale y limits based on ylim1
p1 = p0 + coord_cartesian(ylim = ylim1*1.05)
Ramnath
fuente
15
+1 para el cálculo automático, +1 para usar coord_cartesian para hacer zoom en lugar de excluir datos
Ben Bolker
2
@Ben: ¿tienes dos cuentas? =) @Ramnath - esta es una solución realmente elegante
SFun28
77
Usando el método anterior, los límites pueden estar sesgados por un extremo pequeño en un lado y un extremo grande en el otro, por ejemplo, ylim <- c(-0.1, 1000) * 1.05da [1] 0.105 1050. Para obtener límites iguales alrededor de la media que podría usar ylim + c(-0.05, 0.05) * diff(ylim) / 2. Más bonita en mi opinión.
Bram Visser
2
@Ramnath ¿qué hace el $ stats [c (1,5)]?
lukeg
3
El no funciona si lo usas facet_grid(). Entonces tienes plotsbox multible en lugar de uno. Por lo tanto, no obtienes los límites correctos.
WitheShadow
204

Use geom_boxplot(outlier.shape = NA)para no mostrar los valores atípicos y scale_y_continuous(limits = c(lower, upper))para cambiar los límites del eje.

Un ejemplo.

n <- 1e4L
dfr <- data.frame(
  y = exp(rlnorm(n)),  #really right-skewed variable
  f = gl(2, n / 2)
)

p <- ggplot(dfr, aes(f, y)) + 
  geom_boxplot()
p   # big outlier causes quartiles to look too slim

p2 <- ggplot(dfr, aes(f, y)) + 
  geom_boxplot(outlier.shape = NA) +
  scale_y_continuous(limits = quantile(dfr$y, c(0.1, 0.9)))
p2  # no outliers plotted, range shifted

En realidad, como Ramnath mostró en su respuesta (y Andrie también en los comentarios), tiene más sentido recortar las escalas después de calcular la estadística, a través de coord_cartesian.

coord_cartesian(ylim = quantile(dfr$y, c(0.1, 0.9)))

(Probablemente aún necesite usar scale_y_continuouspara arreglar los saltos del eje).

Algodón Richie
fuente
1
Entonces, ¿tendría que calcular el valor inferior / superior, tal vez calculando el percentil 1 ° / 3 °? ¿Significa que no hay una forma mágica automática de decirle a gg-plot2 que ignore los valores atípicos y escale de manera inteligente?
SFun28
38
Tenga cuidado con scale_y_continuous (limites = ...) Esto eliminará los datos que quedan fuera de los límites y luego realizará los cálculos estadísticos. En otras palabras, la media y otros resúmenes se verán afectados. Si esto es lo que quieres, entonces genial. La alternativa es usar coord_cartesian (límites = ...): esto 'amplía' sin eliminar datos ni afectar los resúmenes.
Andrie
@Andrie - ¡gracias! No quiero que los resúmenes malos y otros se vean afectados.
SFun28
1
coord_cartesian()No juega bien con coord_flip(), en mi experiencia, así que prefiero scale_y_continuous().
PatrickT
1
Esta es la mejor solución. La razón por la que quiero ocultar los valores atípicos es porque también estoy trazando puntos nerviosos con geom_jitter. En este caso, los valores atípicos simplemente se interponen y hacen que parezca que hay más puntos de los que debería haber.
williamsurles
14

Tuve el mismo problema y precalculé los valores para Q1, Q2, mediana, ymin, ymax usando boxplot.stats:

# Load package and generate data
library(ggplot2)
data <- rnorm(100)

# Compute boxplot statistics
stats <- boxplot.stats(data)$stats
df <- data.frame(x="label1", ymin=stats[1], lower=stats[2], middle=stats[3], 
                 upper=stats[4], ymax=stats[5])

# Create plot
p <- ggplot(df, aes(x=x, lower=lower, upper=upper, middle=middle, ymin=ymin, 
                    ymax=ymax)) + 
    geom_boxplot(stat="identity")
p

El resultado es un diagrama de caja sin valores atípicos. ingrese la descripción de la imagen aquí

Matthias Munz
fuente
9

Una idea sería ganar la clasificación de los datos en un procedimiento de dos pasos:

  1. ejecuta una primera pasada, aprende cuáles son los límites, por ejemplo, corte en un percentil dado, o N desviación estándar por encima de la media, o ...

  2. en una segunda pasada, establezca los valores más allá del límite dado al valor de ese límite

Debo enfatizar que este es un método anticuado que debería estar dominado por técnicas más modernas y robustas, pero todavía se lo encuentra mucho.

Dirk Eddelbuettel
fuente
1
Quien acaba de votar en silencio : deja un comentario para explicar el por qué .
Dirk Eddelbuettel
No fui yo. Solo quería agregar que tener bigotes que se detienen en los percentiles (generalmente 10 y 90) parece ser muy común con los datos ambientales.
Richie Cotton
Era un silencioso +1 , y desearía tener otro para ofrecer. Winsorizing casi siempre se realiza en econ + finance. Si SFun tiene valores atípicos que arruinan la visualización de datos, me pregunto cuál es su efecto en el análisis de datos.
Richard Herron
estaba releyendo esta publicación, mencionaste que la windsorización es una técnica más antigua ... ¿cuáles serían algunas técnicas más modernas?
SFun28
1
En general, métodos robustos como un desarrollo de los últimos 30 años.
Dirk Eddelbuettel
2

La opción "coef" de la función geom_boxplot permite cambiar el límite de valores atípicos en términos de rangos intercuartiles. Esta opción está documentada para la función stat_boxplot. Para desactivar los valores atípicos (en otras palabras, se tratan como datos regulares), en lugar de usar el valor predeterminado de 1.5 se puede especificar un valor de corte muy alto:

library(ggplot2)
# generate data with outliers:
df = data.frame(x=1, y = c(-10, rnorm(100), 10)) 
# generate plot with increased cutoff for outliers:
ggplot(df, aes(x, y)) + geom_boxplot(coef=1e30)
eckart
fuente
3
Simplemente extiende los bigotes, no reescalar el gráfico en absoluto
Moody_Mudskipper
2

Si desea obligar a los bigotes a extenderse a los valores máximo y mínimo, puede modificar el coefargumento. El valor predeterminado para coefes 1.5 (es decir, la longitud predeterminada de los bigotes es 1.5 veces el IQR).

# Load package and create a dummy data frame with outliers 
#(using example from Ramnath's answer above)
library(ggplot2)
df = data.frame(y = c(-100, rnorm(100), 100))

# create boxplot that includes outliers
p0 = ggplot(df, aes(y = y)) + geom_boxplot(aes(x = factor(1)))

# create boxplot where whiskers extend to max and min values
p1 = ggplot(df, aes(y = y)) + geom_boxplot(aes(x = factor(1)), coef = 500)

imagen de p0

imagen de p1

IggyM
fuente
2

Ipaper :: geom_boxplot2 es justo lo que quieres.

# devtools::install_github('kongdd/Ipaper')
library(Ipaper)
library(ggplot2)
p <- ggplot(mpg, aes(class, hwy))
p + geom_boxplot2(width = 0.8, width.errorbar = 0.5)

ingrese la descripción de la imagen aquí

Dongdong Kong
fuente
¡¡Gracias!! Probado con mis datos, funciona perfectamente! Recomendaría esta solución, aunque no estoy seguro de la estabilidad / soporte a largo plazo de las cosas de Github.
Gildas