Как мне легко найти выбросы на коробчатой ​​диаграмме

Ниже приведен пример использования набора данных mtcars. Есть один выброс со значением 33,9, но мне нужна функция, которая находит их все для данного столбца.

library(dplyr)
library(ggplot2)

mtcars %>%
  ggplot(aes(x = "", y = mpg)) +
  geom_boxplot(fill = "#2645df")

Я не знаю формулы для определения пределов усов коробчатой ​​диаграммы, поэтому я использовал приведенный выше график, чтобы найти это значение, а затем изменил его вручную:

res = ifelse(mtcars$mpg > 33, "outlier", "not outlier")
res = ifelse(mtcars$mpg < 10, "outlier", "not outlier")

Этот подход одновременно неэффективен и неверен: 33 не являются нижним пределом для выбросов, как и 10.


1
56
3

Ответы:

Если вы хотите увидеть выбросы для каждого столбца mtcars, вы можете нормализовать их значения (без этого вы ничего не увидите), а затем Pivot_wider и построить график с выбросами = TRUE.

mtcars %>%
  mutate(across(everything(), scale)) %>%
  pivot_longer(cols = everything()) %>%
  ggplot(aes(x = name, y = value)) +
  geom_boxplot(
    outliers = TRUE,
    fill = "#2645df")  

Мне удалось добиться желаемого результата. Используя формулу для выбросов коробчатой ​​диаграммы, я смог создать две аккуратные функции, которые не только служат желаемой цели, но и работают в рамках семантики tidyverse:

# smaller function to find the boxplot wisker limits:

outlierLimits = function(x, extreme = F){
  qts = quantile(x, c(.25, .75), names = F)
  
  IQR = qts[2] - qts[1]
  
  ret = c(
    
    lower = qts[1] - IQR*1.5,
    upper = qts[2] + IQR*1.5,
    lower.extreme = qts[1] - IQR*3,
    upper.extreme = qts[2] + IQR*3
    
  )[c(T, T, extreme, extreme)]
  
  return(ret)
}
# The function I was looking for:

outlierClassify = function(x, extreme = F,
                           labels = c("regular", "outlier",
                                      "extreme")[c(T,T,extreme)]){
  lims = outlierLimits( x, extreme )
  
  ret = ifelse(x > lims[1] & x < lims[2],
               labels[1], labels[2])
  
  if (extreme){
    ret[ ret != labels[1] ] = ifelse(
      
      x[ ret != labels[1] ] > lims[3] & 
        x[ ret != labels[1] ] < lims[4],
      
      labels[2], labels[3]
    )
  }
  return(ret)
}

Таким образом, функция outlierClassify возвращает вектор символов, который относится к входному вектору x.

Вот несколько замечательных примеров использования:

# simply obtaining the resulted vector

outlierClassify(mtcars$mpg, F)
# using it with mutate()
library(dplyr)

test = mtcars %>%
  select(mpg, cyl) %>%
  mutate(car = rownames(mtcars),
         .before = 1) %>% 

  # added an 'extreme' oulier for examplification
  rbind(data.frame(
    car = "UNO Mille", mpg = 34, cyl = 6
  )) %>% 
  group_by(cyl) %>% 
  mutate(outliers = outlierClassify(mpg, T),
         .after = mpg)
# using it with ggplot
library(ggplot2)

test %>% 
  ggplot(aes(x = as.factor(cyl), y = mpg))+
  geom_boxplot(outlier.shape = NA, fill = "#2645df", alpha = .6)+
  geom_jitter(aes(color = outliers), width = .1)+
  #making it pretty
  scale_color_manual(values = c("red", "darkorange", "black"))+
  theme_minimal()+
  theme(plot.background = element_rect(fill = "wheat3"))

Решено

Вы можете использовать boxplot.stats:

my_outliers <- function(x, coef = 1.5) boxplot.stats(x, coef = coef)$out

Это то, что graphics::boxplot использует. Это работает немного иначе, чем ggplot, что, я думаю, эквивалентно:

my_outliers2 <- function(x, coef = 1.5) {
  x[x > quantile(x, 0.75) + IQR(x) * coef | x < quantile(x, 0.25) - IQR(x) * coef]
}