Ignorar outliers no ggplot2 boxplot

132

Como eu ignoraria os outliers no boxgot do ggplot2? Eu simplesmente não quero que eles desapareçam (ou seja, outlier.size = 0), mas quero que eles sejam ignorados de forma que o eixo y seja escalado para mostrar o percentil 1/3. Meus outliers estão fazendo com que a "caixa" encolha tão pequena que é praticamente uma linha. Existem algumas técnicas para lidar com isso?

Editar Aqui está um exemplo:

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

insira a descrição da imagem aqui

SFun28
fonte
Alguns dados de amostra e um exemplo reproduzível tornarão mais fácil ajudá-lo.
22711 Andrie
3
meu arquivo tem 200 meg! Basta pegar qualquer conjunto de dados em que haja muitos pontos de dados entre o 1º e o 3º quantil e alguns outliers (você só precisa de 1). Se o outlier é longe do 1º / 3º então necessariamente as caixas vão encolher para acomodar o outlier
SFun28
Sim, era isso que eu tinha em mente. Crie esse conjunto de dados e use dput () para publicá-lo aqui junto com a instrução ggplot () usada. Ajude-nos a ajudá-lo.
22711 Andrie
Você não pode simplesmente alterar os limites do eixo y para "ampliar" a parte do eixo y em que está interessado?
Gavin Simpson
2
deixe-me olhar .... Oh sim, desculpe. Basta fazer fivenum()os dados para extrair o que, IIRC, é usado para as dobradiças superior e inferior dos boxplots e usar essa saída na scale_y_continuous()chamada que o @Ritchie mostrou. Isso pode ser automatizado com muita facilidade usando as ferramentas que R e ggplot fornecem. Se você precisar incluir os bigodes, considere usar boxplot.stats()para obter os limites superior e inferior para os bigodes e use-os então scale_y_continuous().
Gavin Simpson

Respostas:

141

Aqui está uma solução 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
fonte
15
+1 para computação automática, +1 para usar coord_cartesian para fazer zoom em vez de excluir dados
Ben Bolker
2
@ Ben - você tem duas contas? =) @Ramnath - esta é uma solução realmente elegante
SFun28
7
Usando o método acima, os limites podem ser influenciados por um pequeno extremo de um lado e um grande extremo do outro, por exemplo, ylim <- c(-0.1, 1000) * 1.05[1] 0.105 1050. Para obter limites iguais em relação ao meio que você poderia usar ylim + c(-0.05, 0.05) * diff(ylim) / 2. Mais bonito na minha opinião.
Bram Visser
2
@ Ramnath, o que o $ stats [c (1,5)] faz?
Lukeg
3
O não está funcionando se você usar facet_grid(). Então você tem boxplots multible em vez de um. Assim, você não obtém os limites certos.
WitheShadow
204

Use geom_boxplot(outlier.shape = NA)para não exibir os valores extremos e scale_y_continuous(limits = c(lower, upper))alterar os limites do eixo.

Um exemplo.

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

Na verdade, como Ramnath mostrou em sua resposta (e Andrie também nos comentários), faz mais sentido cortar as escalas depois de calcular a estatística, via coord_cartesian.

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

(Você provavelmente ainda precisará usar scale_y_continuouspara corrigir as quebras do eixo.)

Richie Cotton
fonte
1
Então eu teria que calcular o menor / o mais alto - talvez calculando o percentil 1/3? Significando que não há uma maneira auto-mágica de dizer ao gg-plot2 para ignorar valores extremos e escalar inteligentemente?
SFun28
38
Cuidado com scale_y_continuous (limits = ...) Isso removerá os dados que estão fora dos limites e, em seguida, executará os cálculos estatísticos. Em outras palavras, a média e outros resumos serão afetados. Se é isso que você quer, então ótimo. A alternativa é usar coord_cartesian (limits = ...) - isso 'amplia' sem remover dados ou afetar os resumos.
Andrie
@Andrie - obrigado! Não quero que a média e outros resumos sejam afetados.
SFun28
1
coord_cartesian()não joga bem com coord_flip(), na minha experiência, então eu prefiro scale_y_continuous().
PatrickT
1
essa é a melhor solução. A razão pela qual eu quero ocultar os outliers é porque também estou plotando pontos com jittered com geom_jitter. Nesse caso, os discrepantes apenas atrapalham e fazem parecer que há mais pontos do que deveriam.
williamsurles
14

Eu tive o mesmo problema e pré-calculei os 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

O resultado é um boxplot sem discrepâncias. insira a descrição da imagem aqui

Matthias Munz
fonte
9

Uma idéia seria otimizar os dados em um procedimento de duas passagens:

  1. faça uma primeira passagem, aprenda quais são os limites, por exemplo, corte em determinado percentil ou desvio padrão de N acima da média ou ...

  2. em uma segunda passagem, defina os valores além do limite especificado para o valor desse limite

Devo enfatizar que este é um método antiquado que deve ser dominado por técnicas robustas mais modernas, mas você ainda o encontra muito.

Dirk Eddelbuettel
fonte
1
Quem acabou de votar em silêncio : deixe um comentário para explicar o porquê .
Dirk Eddelbuettel
Não fui eu. Só queria acrescentar que ter bigodes que param em percentis (geralmente 10 e 90) parece ser muito comum com dados ambientais.
Richie Algodão
Eu era um +1 silencioso e gostaria de ter outro a oferecer. Winsorizing é quase sempre feito em econ + finance. Se SFun tem discrepâncias que arruinam a visualização de dados, pergunto-me qual é o efeito delas na análise de dados.
precisa
estava relendo este post, você mencionou que o windsorizing é uma técnica mais antiga .... quais seriam algumas técnicas mais modernas?
SFun28
1
Em geral, métodos robustos como um desenvolvimento dos últimos 30 anos ou mais.
Dirk Eddelbuettel
2

A opção "coef" da função geom_boxplot permite alterar o ponto de corte externo em termos de intervalos interquartis. Esta opção está documentada para a função stat_boxplot. Para desativar os valores discrepantes (em outras palavras, eles são tratados como dados regulares), em vez de usar o valor padrão de 1,5, especifique um valor de corte muito 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
fonte
3
Ele só alarga os bigodes, não redimensionar o gráfico que seja
Moody_Mudskipper
2

Se você deseja forçar os bigodes a se estenderem aos valores máximo e mínimo, pode ajustar o coefargumento. O valor padrão para coefé 1,5 (ou seja, o comprimento padrão dos bigodes é 1,5 vezes o 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)

imagem de p0

imagem de p1

IggyM
fonte
2

Ipaper :: geom_boxplot2 é exatamente o que você deseja.

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

insira a descrição da imagem aqui

Dongdong Kong
fonte
Obrigado!! Testado com meus dados, funcionando perfeitamente! Eu recomendaria esta solução, embora não tenha certeza sobre a estabilidade / suporte de longo tempo das coisas do github.
Gildas