Valiotti Analytics — построение аналитики для мобильных и digital-стартапов
    DataMarathon.ru — семидневный интенсив в области аналитики для начинающих
13 заметок с тегом

визуализация

Обнаружение статистических выбросов в R

Время чтения текста – 27 минут

Этот материал — перевод статьи «Outliers detection in R». А ещё у нас есть материал про обнаружение выбросов в Python.

Выбросы — значения или наблюдения, отклоняющиеся от других данных. Всегда нужно сравнивать наблюдение с другими значениями, полученными тем же способом, прежде чем называть их выбросами. Действительно, человек с ростом 200 см, скорее всего, будет считаться отклонением по сравнению с остальным населением, но этот же человек не будет считаться статистическим выбросом, если мы измерим рост баскетболистов.

Выбросы могут быть вызваны изменчивостью, присущей наблюдаемому явлению. Например, при сборе данных о заработной плате часто возникают выбросы, поскольку некоторые люди зарабатывают гораздо больше остальных. Выбросы также могут возникать из-за экспериментальной ошибки, ошибки измерения или кодирования. Например, вес человека 786 кг явно является ошибкой при кодировании веса объекта. Её или его вес, скорее всего, составляет 78,6 кг или 7,86 кг в зависимости от того, был измерен вес взрослого человека или ребёнка.

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

В этой статье я представлю несколько подходов к обнаружению выбросов в R от простых методов, таких как описательная статистика (включая минимальные, максимальные значения, гистограмму, прямоугольную диаграмму и процентили), до более формальных методов, таких как фильтр Хэмпеля, тесты Граббса, Диксона и Рознера.

Не существует строгого и однозначного правила насчет того, следует ли удалять выбросы из набора данных перед проведением статистического анализа. Довольно часто переменные значения, вызванные экспериментальной ошибкой или ошибкой измерения удаляются или заменяются на новые значения. Некоторые статистические тесты требуют их отсутствия, чтобы сделать обоснованные выводы, но удаление выбросов рекомендуется не во всех случаях и должно выполняться с осторожностью.

Эта статья поможет обнаружить и проверить выбросы, но вы не узнаете, следует ли удалять, изменять или оставлять такие значения. После проверки вы можете исключить их или включить в свой анализ (а это обычно требует вдумчивого размышления со стороны исследователя). Удаление или сохранение выбросов, в основном, зависит от трех факторов:

  1. Область / контекст вашего анализа и вопрос исследования. В некоторых областях обычно удаляют посторонние значения, поскольку они часто возникают из-за сбоев в процессе. В других областях отклонения сохраняются, потому что они содержат ценную информацию. Также бывает, что анализ выполняется дважды, один раз с посторонними значениями и один раз без них, чтобы оценить их влияние на результаты. Если результаты резко изменятся из-за некоторых определяющих значений, это должно предостеречь исследователя от чрезмерно амбициозных утверждений.
  2. Устойчивость тестов. Например, наклон простой линейной регрессии может значительно варьироваться даже с одним выбросом, тогда как непараметрические тесты, такие как тест Уилкоксона, обычно устойчивы к ним.
  3. Дальность выбросов от других наблюдений. Некоторые наблюдения, рассматриваемые как выбросы, на самом деле не являются экстремальными значениями по сравнению со всеми другими наблюдениями, в то время как другие потенциальные выбросы могут быть действительно отстающими от остальных наблюдений.

Мы будем использовать набор данных mpg из библиотеки ggplot2, чтобы проиллюстрировать различные подходы к обнаружению выбросов в R, и в частности, мы сосредоточимся на работе с переменной hwy (пробег в милях на галлон израсходованного топлива).

Минимальные и максимальные значения

Первое, что необходимо для обнаружения выбросов в R — начать с описательной статистики, и, в частности, с минимальных и максимальных значений.

В R это легко сделать с помощью функции summary():

dat <- ggplot2::mpg
summary(dat$hwy)

##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   12.00   18.00   24.00   23.44   27.00   44.00

Минимум и максимум — первое и последнее значения в выходных данных выше. В качестве альтернативы, их также можно вычислить с помощью функций min() и max():

min(dat$hwy)

## [1] 12

max(dat$hwy)

## [1] 44

Явная ошибка кодирования, такая как, например, человеческий вес в 786 кг уже будет легко обнаружена с помощью этой простой техники.

Гистограмма

Другой базовый способ обнаружения выбросов — построение гистограммы данных.

При помощи внутренних инструментов R:

hist(dat$hwy,
  xlab = "hwy",
  main = "Histogram of hwy",
  breaks = sqrt(nrow(dat))
) # set number of bins

При помощи ggplot2:

library(ggplot2)

ggplot(dat) +
  aes(x = hwy) +
  geom_histogram(bins = 30L, fill = "#0c4c8a") +
  theme_minimal()

Пара полосок справа в отрыве от основного графика — значения, которые больше остальных.

#Box plot
Помимо гистограмм, box plot (ящик с усами) также полезен для обнаружения потенциальных выбросов.

Используя R:

boxplot(dat$hwy,
  ylab = "hwy"
)

или используя ggplot2:

ggplot(dat) +
  aes(x = "", y = hwy) +
  geom_boxplot(fill = "#0c4c8a") +
  theme_minimal()

Box plot помогает визуализировать количественную переменную, отображая пять общих сводных данных (минимальное значение, среднее значение, первый и третий квартили и максимальное значение) и любое значение, которое было классифицировано как предполагаемый выброс с использованием критерия межквартильного размаха (IQR). Критерий межквартильного размаха означает, что все единицы значения больше q₀,₇₅+ 1.5 ⋅ IQR или меньше q₀,₂₅ — 1,5⋅ IQR рассматриваются R, как потенциальные выбросы. Другими словами, все наблюдения за пределами следующего интервала будут рассматриваться как потенциальные выбросы:

I = [q₀,₂₅ — 1.5 * IQR; q₀,₇₅ + 1.5 * IQR]

Выбросы отображаются в виде точек на прямоугольной диаграмме. Исходя из этого критерия, есть 2 потенциальных выброса (смотрите на 2 точки над вертикальной линией в верхней части диаграммы размаха).

Даже если наблюдение рассматривается как потенциальный выброс по критерию IQR, это не означает, что его следует удалять. Удаление или сохранение выброса зависит от контекста вашего анализа, от того, являются ли тесты, которые вы собираетесь проводить с наборами данных, устойчивыми к выбросам или нет, и насколько далеки выбросы от других наблюдений.

Также возможно извлечь потенциальные выбросы на основе критерия IQR благодаря функции boxplot.stats()$out:

boxplot.stats(dat$hwy)$out

## [1] 44 44 41

Как видите, на самом деле есть 3 точки, которые считаются потенциальными выбросами: две со значением 44 и одна со значением 41.

Благодаря функции which() можно извлечь номер строки, соответствующий этим посторонним значениям:

out <- boxplot.stats(dat$hwy)$out
out_ind <- which(dat$hwy %in% c(out))
out_ind

## [1] 213 222 223

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

dat[out_ind, ]

## # A tibble: 3 x 11
##   manufacturer model   displ  year   cyl trans   drv     cty   hwy fl    class  
##   <chr>        <chr>   <dbl> <int> <int> <chr>   <chr> <int> <int> <chr> <chr>  
## 1 volkswagen   jetta     1.9  1999     4 manual… f        33    44 d     compact
## 2 volkswagen   new be…   1.9  1999     4 manual… f        35    44 d     subcom…
## 3 volkswagen   new be…   1.9  1999     4 auto(l… f        29    41 d     subcom…

Ещё можно напечатать выбросы прямо на диаграмме размаха с помощью функции mtext():

boxplot(dat$hwy,
  ylab = "hwy",
  main = "Boxplot of highway miles per gallon"
)
mtext(paste("Outliers: ", paste(out, collapse = ", ")))

Процентили

Этот метод обнаружения посторонних значений основан на процентилях. При использовании метода процентилей все наблюдения, выходящие за пределы интервала, образованного 2,5 и 97,5 процентилями будут рассматриваться как потенциальные выбросы. Другие процентили, такие как 1 и 99 или 5 и 95 процентили, тоже могут быть рассмотрены для построения интервала.

Значения нижнего и верхнего процентилей можно вычислить с помощью функции quantile():

lower_bound <- quantile(dat$hwy, 0.025)
lower_bound

## 2.5% 
##   14

upper_bound <- quantile(dat$hwy, 0.975)
upper_bound

##  97.5% 
## 35.175

В соответствии с этим методом, все наблюдения ниже 14 и выше 35,175 будут рассматриваться как потенциальные выбросы. Номера рядов наблюдений за пределами интервала затем могут быть извлечены с помощью функции which():

outlier_ind <- which(dat$hwy < lower_bound | dat$hwy > upper_bound)
outlier_ind

##  [1]  55  60  66  70 106 107 127 197 213 222 223

Можно вывести значение пробега в милях на галлон израсходованного топлива для таких значений:

dat[outlier_ind, "hwy"]


## # A tibble: 11 x 1
##      hwy
##    <int>
##  1    12
##  2    12
##  3    12
##  4    12
##  5    36
##  6    36
##  7    12
##  8    37
##  9    44
## 10    44
## 11    41

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

dat[outlier_ind, ]

## # A tibble: 11 x 11
##    manufacturer model    displ  year   cyl trans  drv     cty   hwy fl    class 
##    <chr>        <chr>    <dbl> <int> <int> <chr>  <chr> <int> <int> <chr> <chr> 
##  1 dodge        dakota …   4.7  2008     8 auto(… 4         9    12 e     pickup
##  2 dodge        durango…   4.7  2008     8 auto(… 4         9    12 e     suv   
##  3 dodge        ram 150…   4.7  2008     8 auto(… 4         9    12 e     pickup
##  4 dodge        ram 150…   4.7  2008     8 manua… 4         9    12 e     pickup
##  5 honda        civic      1.8  2008     4 auto(… f        25    36 r     subco…
##  6 honda        civic      1.8  2008     4 auto(… f        24    36 c     subco…
##  7 jeep         grand c…   4.7  2008     8 auto(… 4         9    12 e     suv   
##  8 toyota       corolla    1.8  2008     4 manua… f        28    37 r     compa…
##  9 volkswagen   jetta      1.9  1999     4 manua… f        33    44 d     compa…
## 10 volkswagen   new bee…   1.9  1999     4 manua… f        35    44 d     subco…
## 11 volkswagen   new bee…   1.9  1999     4 auto(… f        29    41 d     subco…

Согласно методу процентилей, существует 11 потенциальных выбросов. Чтобы уменьшить это число, вы можете установить процентили от 1 до 99:

lower_bound <- quantile(dat$hwy, 0.01)
upper_bound <- quantile(dat$hwy, 0.99)

outlier_ind <- which(dat$hwy < lower_bound | dat$hwy > upper_bound)

dat[outlier_ind, ]

## # A tibble: 3 x 11
##   manufacturer model   displ  year   cyl trans   drv     cty   hwy fl    class  
##   <chr>        <chr>   <dbl> <int> <int> <chr>   <chr> <int> <int> <chr> <chr>  
## 1 volkswagen   jetta     1.9  1999     4 manual… f        33    44 d     compact
## 2 volkswagen   new be…   1.9  1999     4 manual… f        35    44 d     subcom…
## 3 volkswagen   new be…   1.9  1999     4 auto(l… f        29    41 d     subcom…

Установка процентилей на 1 и 99 дает те же потенциальные выбросы, что и для критерия IQR.

Фильтр Хэмпеля

Другой метод, известный как фильтр Хэмпеля, заключается в том, чтобы рассматривать как выбросы значения вне интервала, которые формируются медианным значением плюс-минус 3 медианы абсолютных отклонений (MAD):

I = [median - 3 * MAD; median + 3 * MAD]

в которых MAD — это медианное абсолютное отклонение и определяется как медиана абсолютных отклонений от медианы данных:

Для этого метода мы сначала устанавливаем пределы интервала с помощью функций median() и mad():

lower_bound <- median(dat$hwy) - 3 * mad(dat$hwy, constant=1)
lower_bound

## [1] 9

upper_bound <- median(dat$hwy) + 3 * mad(dat$hwy, constant=1)
upper_bound

## [1] 39

Все наблюдения меньше 9 и больше 39 будут рассматриваться как потенциальные выбросы. Номера строк наблюдений за пределами интервала затем могут быть извлечены с помощью функции which():

outlier_ind <- which(dat$hwy < lower_bound | dat$hwy > upper_bound)
outlier_ind

## 213 222 223

Согласно фильтру Хэмпеля, для переменной hwy есть 3 потенциальных выброса.

Статистические тесты

В этом разделе мы представим еще 3 формальных метода обнаружения отклонений:

  1. Тест Граббса (Grubbs’s test)
  2. Тест Диксона (Dixon’s test)
  3. Тест Рознера (Rosner’s test)

Эти статистические тесты являются частью формальных методов обнаружения выбросов, поскольку все они включают вычисление с помощью тестовой статистики, которая сравнивается с табличными критическими значениями.

Обратите внимание, что эти тесты подходят только тогда, когда данные распределены нормально. Таким образом, предположение о соответствии нормальности должно быть проверено перед применением этих тестов для выбросов (Как проверить предположение о соответствии нормальному распределению в R).

Тест Граббса (Grubbs’s test)

Тест Граббса позволяет определить, является ли наибольшее или наименьшее значение в наборе данных выбросом. Он обнаруживает по одному выбросу за раз (максимальное или минимальное значение), поэтому нулевая и альтернативная гипотезы проверки максимального значения выглядит так:

  • H₀: Наивысшее значение не является выбросом
  • H₁: Наивысшее значение является выбросом

А минимального — так:

  • H₀: Наименьшее значение не является выбросом
  • H₁: Наименьшее значение является выбросом

Как и в любом статистическом тесте, если значение P меньше порогового уровня статистической значимости (обычно α = 0.05), то нулевая гипотеза отвергается, и мы приходим к выводу, что наименьшее/наибольшее значение является отклонением. Напротив, если значение P больше или равно пороговому уровню значимости, нулевая гипотеза не отвергается, и мы делаем вывод, что на основе данных о том, что наименьшее / наибольшее значение не является выбросом. Обратите внимание на то, что тест Граббса не подходит для выборки объемом 6 или меньше (n <= 6).

Чтобы выполнить тест Граббса в R, используем функцию grubbs.test() из пакетов outliers:

# install.packages("outliers")
library(outliers)
test <- grubbs.test(dat$hwy)
test 

## 
##  Grubbs test for one outlier
## 
## data:  dat$hwy
## G = 3.45274, U = 0.94862, p-value = 0.05555
## alternative hypothesis: highest value 44 is an outlier

Значение P равняется 0,056. На уровне значимости 5% мы не отвергаем гипотезу о том, что наибольшее значение 44 не является выбросом.

По умолчанию тест выполняется на наибольшем значении (как показано в выходных данных R: alternative hypothesis: highest value). Если вы хотите провести тест для наименьшего значения, просто добавьте аргумент opposite = TRUE в функцию grubbs.test():

test <- grubbs.test(dat$hwy, opposite = TRUE)
test

## 
##  Grubbs test for one outlier
## 
## data:  dat$hwy
## G = 1.92122, U = 0.98409, p-value = 1
## alternative hypothesis: lowest value 12 is an outlier

Вывод указывает на то, что тест теперь выполняется при наименьшем значении

Значение P равно 1. На уровне значимости 5% мы не отвергаем гипотезу о том, что наименьшее значение 12 не является выбросом.

Для иллюстрации этого заменим наблюдения более экстремальным значением и выполним тест Граббса для нового набора данных. Давайте заменим 34-ую строку со значением 212:

dat[34, "hwy"] <- 212

Применяем тест Граббса, чтобы проверить, является ли наибольшее значение выбросом:

test <- grubbs.test(dat$hwy)
test

## 
##  Grubbs test for one outlier
## 
## data:  dat$hwy
## G = 13.72240, U = 0.18836, p-value < 2.2e-16
## alternative hypothesis: highest value 212 is an outlier

Значение p < 0,001. На уровне значимости 5% мы делаем вывод, что наивысшее значение 212 является выбросом.

Тест Диксона (Dixon’s test)

Подобно тесту Граббса, тест Диксона используется для того, чтобы проверить, является ли самое высокое или самое низкое значение выбросом. Таким образом, если под сомнением находятся более одного выброса, тест необходимо проводить индивидуально для этих предполагаемых значений.

Обратите внимание на то, что тест Диксона наиболее полезен для выборки небольшого объема (обычно когда n <= 25).

Чтобы выполнить тест Диксона в R, мы используем функцию dixon.test() из пакета outliers. Однако мы ограничиваем наш набор данных 20 первыми наблюдениями, поскольку тест Диксона может быть выполнен только на небольшом размере выборки:

subdat <- dat[1:20, ]
test <- dixon.test(subdat$hwy)
test

## 
##  Dixon test for outliers
## 
## data:  subdat$hwy
## Q = 0.57143, p-value = 0.006508
## alternative hypothesis: lowest value 15 is an outlier

Результаты показывают, что самое наименьшее значение 15 является выбросом (p-значение = 0,007).

Чтобы проверить максимальное значение, просто добавьте аргумент opposite = TRUE к функции dixon.test():

test <- dixon.test(subdat$hwy,
  opposite = TRUE
)
test

## 
##  Dixon test for outliers
## 
## data:  subdat$hwy
## Q = 0.25, p-value = 0.8582
## alternative hypothesis: highest value 31 is an outlier

Результаты показывают, что максимальное значение 31 не является выбросом (p-значение = 0,858).

Рекомендуется всегда сверять результаты статистического теста на выбросы с диаграммой, чтобы убедиться, что мы проверили все потенциальные выбросы:

out <- boxplot.stats(subdat$hwy)$out
boxplot(subdat$hwy,
  ylab = "hwy"
)
mtext(paste("Outliers: ", paste(out, collapse = ", ")))

По box plot заметно, что мы можем применить тест Диксона к значению 20 в дополнение к значению 15, выполненному ранее. Это можно сделать, найдя номер строки минимального значения, исключив этот номер строки из набора данных, а затем применив тест Диксона к этому новому набору данных:

# find and exclude lowest value
remove_ind <- which.min(subdat$hwy)
subsubdat <- subdat[-remove_ind, ]

# Dixon test on dataset without the minimum
test <- dixon.test(subsubdat$hwy)
test

## 
##  Dixon test for outliers
## 
## data:  subsubdat$hwy
## Q = 0.44444, p-value = 0.1297
## alternative hypothesis: lowest value 20 is an outlier

Результаты показывают, что второе наименьшее значение 20 не является выбросом (p-значение = 0,13).

Тест Рознера (Rosner’s test)

  1. Тест Рознера на выбросы имеет следующие преимущества:
  2. Он используется для одновременного обнаружения нескольких выбросов (в отличие от теста Граббса и Диксона, которые должны выполняться итеративно для выявления нескольких выбросов)
  3. Он разработан чтобы избежать проблемы, когда выброс, близкий по значению к другому выбросу, может остаться незамеченным.

Обратите внимание, что в отличие от теста Диксона, тест Рознера подходит к большому объему выборки (n≥20). Поэтому мы снова используем исходный набор данных dat, который включает 234 наблюдения.

Для выполнения теста Рознера мы используем функцию rosnerTest() из пакета EnvStats. Для этой функции требуется как минимум 2 аргумента: данные и количество предполагаемых выбросов k.

library(EnvStats)
test <- rosnerTest(dat$hwy,
  k = 3
)
test

## $distribution
## [1] "Normal"
## 
## $statistic
##       R.1       R.2       R.3 
## 13.722399  3.459098  3.559936 
## 
## $sample.size
## [1] 234
## 
## $parameters
## k 
## 3 
## 
## $alpha
## [1] 0.05
## 
## $crit.value
## lambda.1 lambda.2 lambda.3 
## 3.652091 3.650836 3.649575 
## 
## $n.outliers
## [1] 1
## 
## $alternative
## [1] "Up to 3 observations are not\n                                 from the same Distribution."
## 
## $method
## [1] "Rosner's Test for Outliers"
## 
## $data
##   [1]  29  29  31  30  26  26  27  26  25  28  27  25  25  25  25  24  25  23
##  [19]  20  15  20  17  17  26  23  26  25  24  19  14  15  17  27 212  26  29
##  [37]  26  24  24  22  22  24  24  17  22  21  23  23  19  18  17  17  19  19
##  [55]  12  17  15  17  17  12  17  16  18  15  16  12  17  17  16  12  15  16
##  [73]  17  15  17  17  18  17  19  17  19  19  17  17  17  16  16  17  15  17
##  [91]  26  25  26  24  21  22  23  22  20  33  32  32  29  32  34  36  36  29
## [109]  26  27  30  31  26  26  28  26  29  28  27  24  24  24  22  19  20  17
## [127]  12  19  18  14  15  18  18  15  17  16  18  17  19  19  17  29  27  31
## [145]  32  27  26  26  25  25  17  17  20  18  26  26  27  28  25  25  24  27
## [163]  25  26  23  26  26  26  26  25  27  25  27  20  20  19  17  20  17  29
## [181]  27  31  31  26  26  28  27  29  31  31  26  26  27  30  33  35  37  35
## [199]  15  18  20  20  22  17  19  18  20  29  26  29  29  24  44  29  26  29
## [217]  29  29  29  23  24  44  41  29  26  28  29  29  29  28  29  26  26  26
## 
## $data.name
## [1] "dat$hwy"
## 
## $bad.obs
## [1] 0
## 
## $all.stats
##   i   Mean.i      SD.i Value Obs.Num     R.i+1 lambda.i+1 Outlier
## 1 0 24.21795 13.684345   212      34 13.722399   3.652091    TRUE
## 2 1 23.41202  5.951835    44     213  3.459098   3.650836   FALSE
## 3 2 23.32328  5.808172    44     222  3.559936   3.649575   FALSE
## 
## attr(,"class")
## [1] "gofOutlier"

Результаты представлены в таблице $all.stats:

test$all.stats

##   i   Mean.i      SD.i Value Obs.Num     R.i+1 lambda.i+1 Outlier
## 1 0 24.21795 13.684345   212      34 13.722399   3.652091    TRUE
## 2 1 23.41202  5.951835    44     213  3.459098   3.650836   FALSE
## 3 2 23.32328  5.808172    44     222  3.559936   3.649575   FALSE

Основываясь на тесте Рознера, мы видим, что существует только один выброс (см. Столбец Outlier), и что это наблюдение 34 (см. Obs.Num) со значением 212 (см. Value).

Итоги

Обратите внимание, что некоторые преобразования могут «естественным образом» устранить выбросы. Например, если взять натуральный логарифм или квадратный корень из значения, отклонение станет меньше. Я надеюсь, статья помогла вам обнаружить выбросы в R с помощью нескольких методов описательной статистики (включая минимум, максимум, гистограмму, диаграмму размаха и процентили) или благодаря более формальным методам обнаружения выбросов (включая фильтр Хампеля, тест Граббса, Диксона и Рознера). Следующим этапом проверьте эти значения, и если они действительно являются выбросами — решите, как с ними поступить (сохранить, удалить или изменить), прежде чем проводить анализ.

 Нет комментариев    581   2 мес   r   визуализация   выбросы   статистика

Анимируем теннисные мячики в Tableau

Время чтения текста – 1 минута

В прошлом видео мы научились визуализировать теннисные мячики из Swing Vision на графике в Tableau с кастомным фоном и кастомными фигурами. Сегодня будем анимировать дашборд, чтобы посмотреть, как менялись удары и получим видео с результатами игры, которое можно экспортировать и кому-нибудь показать.

Краткое резюме:

  1. Создаём элемент Pages. Он позволяет управлять движением анимации: нажимая на кнопку Play, Title страницы будет меняться.
  2. Добавим историю: ставим галочку на Show History ниже и выберем длину в 7 ударов.
  3. Вернёмся на дашборд. Переходим во вкладку Worksheet — Show Cards и выбираем Current Page.
  4. Для захвата видео с экрана добавим новый контейнер и перенесём в него панель с ударами.
  5. Утилитой записи экрана выбираем область с дашбордом и жмём на Play. Для macOS можно воспользоваться встроенной: достаточно нажать комбинацию клавиш ⌘ + Shift + 5.
 Нет комментариев    38   8 мес   bi   BI-инструменты   tableau   визуализация

Обзор библиотеки pandas-profiling на примере датасета Superstore Sales

Время чтения текста – 10 минут

Перед тем как работать с данными, необходимо составить представление, с чем мы имеем дело. В материале будем рассматривать датасет SuperStore Sales, а именно его лист Orders. В нём собраны данные о покупках клиентов канадского интернет-супермаркета: идентификаторы заказа, товаров, клиента, тип доставки, цены, категории и названия продуктов и прочее. Подробнее с датасетом можно ознакомиться на GitHub. Например, если мы создадим из датасета DataFrame, можем воспользоваться стандартным методом describe() библиотеки pandas для описания данных:

import pandas as pd

df = pd.read_csv('superstore_sales_orders.csv', decimal=',')
df.describe(include='all')

И во многих случаях получим такую кашу:

Код библиотеки доступен на GitHub

Если постараться и потратить время, можно извлечь полезную информацию. Например, можем узнать, что люди чаще выбирают «Regular air» в качестве доставки или что большинство заказов поступило из провинции Онтарио. Тем не менее, есть и другое решение, которое подробнее и качественнее описывает датасет — библиотека pandas-profiling. Вы отдаёте ей DataFrame, а она генерирует html-страницу с подробным описанием сета данных:

import pandas_profiling
profile = pandas_profiling.ProfileReport(df)
profile.to_file("output.html")

Всего Pandas Profiling возвращает 6 разделов: обзор датасета, переменные, отношения и корреляцию между ними, количество пропущенных значений и примеры из датасета.

Web-версия отчёта доступна по ссылке

Обзор данных

Рассмотрим первый подраздел — «Overview». Библиотека собрала следующую статистику: количество переменных, наблюдений, пропущенных ячеек, дубликатов и общий вес файла. В колонке Variable types описаны типы переменных: здесь 12 качественных и 9 числовых.

В подразделе «Reproduction» собрана техническая информация библиотеки: сколько времени занял анализ сета данных, версия библиотеки и прочее.

А подраздел «Warnings» сообщает о возможных проблемах в структуре датасета: сейчас он, например, предупреждает, что у поля «Order Date» — слишком большое количество уникальных значений.

Переменные

Двигаемся ниже. В этом разделе содержится подробное описание каждой переменной: сколько возможных уникальных значений она принимает, сколько значений пропущено, сколько памяти занимает поле. Справа от статистики присутствует гистограмма с распределением значений поля.

При нажатии на Toggle details откроется расширенная информация: квартили, медиана и прочая полезная описательная статистика. В остальных вкладках находятся гистограмма из основного экрана, топ-10 значений по частоте и экстремальные значения.

Отношения переменных

В этом разделе визуализированы отношения переменных при помощи hexbin plot: выглядит это не очень очевидно и понятно. Особенно усугубляет положение отсутствие легенды к графику.

Корреляция переменных

В этом разделе представлена по-разному посчитананя корреляция переменных: например, первым указано r-value Пирсона. Заметно, что переменная Profit положительно коррелирует с переменной Sales. При нажатии на Toggle correlation descriptions открывается подробное пояснение к каждому коэффициенту.

Пропущенные значения

Тут всё просто — bar chart, матрица и дендрограмма с количеством заполненных полей в каждой переменной. Заметно, что в колонке Product Base Margin отсутствуют три значения.

Примеры

И, наконец, последний раздел представляет первые и последние 10 значений в качестве примера кусков сета данных — аналог метода head() из pandas.

Что в итоге?

Библиотека уделяет больше внимания статистике, чем pandas: можно получить подробную описательную статистику по каждой переменной, посмотреть, как коррелируют между собой столбцы датасета. В совокупности с генерацией простого и удобного интерфейса библиотека строит полноценный отчёт по датасету, уже на основании которого можно делать выводы и сформировать представление о данных.
И всё же, у библиотеки есть и минусы. На генерацию отчётов к громадным датасетам может уйти много времени вплоть до нескольких часов. Это безусловно хороший инструмент для автоматического проектирования, но он не может сделать полноценный анализ за вас и добавить больше деталей в графики. Кроме того, если вы только начали практиковаться с анализом данных лучше будет начать с pandas — это закрепит ваши навыки и придаст уверенности при работе с данными.

 Нет комментариев    80   9 мес   BI-инструменты   pandas   pandas-profiling   python   визуализация

Выбор шрифтов для визуализации данных

Время чтения текста – 22 минуты

Данная статья — перевод оригинала: «Choosing Fonts for Your Data Visualization»

Цель визуализации данных — сделать макет, который быстро передаст большое количество информации. Хорошая визуализация помогает пользователю понять сложные данные. Любой текст, сопровождающий график, должен читаться настолько легко, будто он отсутствует совсем.
В этой статье я расскажу, как выбрать читаемый и эстетичный шрифт для вашего проекта. Я сосредоточусь прежде всего на небольшом тексте, предназначенном для пояснения графика, включая метки, сноски и источники. Чтобы угодить более широкой аудитории, я буду рекомендовать только бесплатные шрифты Google, хотя есть и множество хороших платных вариантов.

Хорошо читаемые шрифты

Хорошо читаемые шрифты требуют меньше напряжения для понимания. Визуализации с читаемыми шрифтами имеют постоянный ритм и выглядят как единое целое. Хорошая типографика не является показной — она объясняет содержание, не мешая работе пользователя.
Чтобы понять, что это за тип «хорошо читаемых» шрифтов, давайте рассмотрим несколько ключевых элементов.

X-высота

X-высота — это высота строчных букв. Так как строчные буквы иногда имеют разную высоту, этот параметр измеряется с помощью буквы «х».

Х-высота напрямую влияет на то, насколько шрифт читаем при небольших размерах. Взгляните на изображение выше. Все эти шрифты имеют размер в 10 пунктов. Какой шрифт вы считаете наиболее читабельным? Gill Sans и Athelas имеют меньшую Х-высоту, что затрудняет чтение текста. Open Sans, Noto Sans и Lato имеют большую Х-высоту, что обеспечивает удобство чтения при небольших размерах. При выборе шрифта для визуализации данных выберите шрифт с большой Х-высотой.

Обратите внимание: у Lato хорошая высота по оси Х, но укорочена длина линии.
Если ваша визуализация имеет ось Х с ограниченным пространством, вы можете рассмотреть более сжатую гарнитуру, как, например, Lato.

Апертура

Апертура — это пустая область в буквах, как «р» и «о».

Форма апертуры напрямую влияет на читаемость при небольших размерах. Глаза должны легко идти по буквам, человек не должен тратить время на выяснение, буква «о» это или «е». Шрифты с искаженной апертурой плохо отображаются при небольших размерах, поэтому следует избегать причудливых шрифтов вроде Marker Felt. Обратите внимание, как трудно читать плотные шрифты, такие как League Gothic и Futura Condensed при небольших размерах: это связано с тем, что при сжатии шрифта апертура удлиняется. При выборе шрифта для визуализации данных используйте шрифт со стабильной открытой апертурой.

Засечки

Засечки — это маленькие галочки вокруг буквы.

При маркировке данных часто избегают засечек, потому что дополнительные элементы иногда запутывают представление о форме буквы. Первые три строки выше — шрифты с засечками. Сравните их с двумя последними без засечек: в этом примере слова без засечек легче расшифровать. И хотя некоторые шрифты с засечками можно использовать при меньших размерах, если вы не уверены, выберите что-нибудь без засечек.

Использование чисел

Числа гарнитуры бывают либо пропорциональными, либо табличными.

Пропорциональные числа выглядят хорошо при использовании в тексте. Табличные числа предназначены для работы с данными и занимают одинаковое количество ширины на символ. Если вы выстроите цифры в линию, увидите, что они попадают в одинаково расположенные столбцы. Табличные числа легче читать в визуализациях.
Open Sans — табличный шрифт, центрирующий числа в пространстве столбца. А Lato, например, немного смещает число в таком пространстве. Почему это важно?

Потому что это маленькое пространство требует от вашего мозга работы для осознания того, что это непрерывная цепочка цифр. Мы можем исключить эту работу, выбрав гарнитуру с табличными числами или даже с пробелами.

Посмотрите на старомодные числа. Raleway — красивый шрифт, который обычно используется для текстов и логотипов, однако он плохо работает для маркировки и визуализации данных. Это потому, что он использует старомодные числа вместо ровных чисел, идущих в одну линию. Взгляните, как 3, 5 и 4 опускаются ниже базовой линии. Такие числа трудно воспринимать не только в визуализации, но и в обычном тексте.

Системы типографского дизайна

Системы типографского дизайна представляют собой набор из размеров шрифта, веса и высоты строк, которые будут использоваться в макете. Системы помогают обеспечить визуальную согласованность проекта. Ниже представлено пять систем, которые можно опробовать в своем дизайне. Все они используют бесплатные шрифты Google с открытым исходным кодом.

Система 1: один шрифт / один размер

Такая система использует одну гарнитуру с одним заданным размером, что приводит к универсальной визуальной текстуре. Такой тонкий подход хорош для дашбордов или для добавления графиков в бизнес-отчёт. Важная информация может быть отмечена при использовании более тяжелого веса шрифта или оттенков серого.
Пример из жизни: примером этой техники является панель инструментов Google Trends. Этот дашборд использует шрифт Roboto как для заголовка, так и для надписей. Панель инструментов разделяет контент с помощью макета на основе карты. Эта карта использует одну гарнитуру и один размер шрифта по всей композиции.

В следующей таблице используется техника «Один шрифт / один размер» с Lato в 14 пунктов и высотой строки в 18 пунктов:

Используемый шрифт: Lato — хороший шрифт для визуализации данных, потому что хорошо читается при небольших размерах. Шрифт имеет чистую апертуру, умеренную X-высоту и узкий, но неискаженный интервал между буквами. Для чисел он использует табличные значения, они равномерно распределены для удобства чтения. Жирный шрифт легко отличается от обычного, однако полужирный не имеет достаточных отличий от обычного или жирного, так что его следует избегать. Lato был выпущен в Google Fonts в 2015 году и в настоящее время является третьим по популярности шрифтом на их сайте.

Система 2: Один шрифт / большой заголовок

Типографская система «один шрифт / большой заголовок» использует один шрифт с заголовком, который больше и жирнее прочего контента. Больший заголовок выделяется и позволяет пользователю быстро понять, о чём идёт речь.

В визуализации The Washington Post использован шрифт ITC Franklin Pro

Пример из жизни: The Washington Post использует такую систему в приведённой ниже визуализации. Дизайнер сохраняет семейство и размер шрифта в соответствии с тем, чтобы навигация ощущалась как часть макета.

Ниже приведён пример использования такой системы. В нём используется шрифт Assistant размером в 24 пункта для заголовка и в 14 пунктов для прочего содержимого.

Используемый шрифт: Assistant — чёткий современный шрифт, который хорошо работает в дизайне благодаря простому, открытому эстетичному размеру букв. Числа хорошо сбалансированы и не имеют пробелов. Assistant — семейство шрифтов с открытым исходным кодом, основанное на Source Sans Pro. Проект открыт для сотрудничество и принимает участие в репозитории GitHub.

Система 3. Два шрифта / тяжёлый & лёгкий

Такая система использует два шрифта без засечек с дополнительной X-высотой и межбуквенными интервалами. Заголовок имеет большой вес, содержание — лёгкий.
Пример из жизни: для визуализации данных Reuter в материале «The Rohingya Crisis: Life in the camps» используется комбинация тяжелых и легких шрифтов. Дизайнер использовал фирменную гарнитуру Reuter «Knowledge» для заголовка. Прочая информация использует Source Sans Pro, доступный в библиотеке Google Fonts. Обратите внимание, как «Negative for E. Coli» выделено серым цветом, чтобы привлечь внимание к первым трём показателям.

В следующей таблице используется эта система. Заголовки большие и полужирные, остальное содержимое имеет небольшой вес. Сочетание тяжелых и тонких шрифтов обеспечивает контрастное разделение, помогая пользователю определить важные разделы композиции.

Используемые шрифты: PT Sans Bold — сильный округлый шрифт с большой X-высотой. PT Sans и PT Serif были разработаны для русского алфавита как всеобъемлющий шрифт для поощрения печати и чтения на национальной языке. В дополнение к кириллице, PT Sans поддерживает латиницу, греческий, арабский и другие формы. Семейство также включает стиль PT Sans Caption, который был разработан специально для мелкого шрифта и хорошо подходит для длинных заметок или разделов источников.

Содержимое в примере использует шрифт Noto Sans: это чистый шрифт со слегка сжатым межбуквенным интервалом, что компенсируется чёткой круглой апертурой и большой X-высотой. Табличные числа включают в себя прочное число «1» с широкой ножкой, придающую структуре чисел чёткий вид. Noto Sans является частью большого семейства шрифтов Noto. В группе более 100 различных шрифтов с целью обеспечения «визуальной гармонии» на нескольких языках. Это делает Noto Sans особенно хорошим вариантом для многоязычных визуализаций.

Система 4: Один с засечками / один без засечек.

Такая система использует два шрифта, один из которых с засечками, а другой — без.
Пример из жизни: ниже приведён скриншот статьи New York Times «Coronavirus in US». Название использует шрифт NYT Cheltenham, а метки данных — NYT Franklin, оба шрифта созданы специально для New York Times. Метки имеют два компонента: заметное название штата и менее заметное число. Использование этих двух стилей создает шаблон, который помогает зрителю быстро декодировать информацию. Что бросается в глаза? Название штата или номер?

В следующем примере такой системы используется шрифт Merriweather в 22 пункта с высотой строки в 26 пунктов для заголовка. Source Sans Pro используется для остального контента.

Используемые шрифты: Merriweather — слегка округлый шрифт с толстым и тонким штрихом средней контрастности, что делает его подходящим для заголовков, но не для мелкого текста. Вес шрифта, использованный выше, самый тяжелый. Source Sans Pro используется для содержания. Он был разработан специально для пользовательских интерфейсов.
Серая шкала текста может использоваться для создания шаблонов (как в приведённом выше фрагменте NYT), для снижения визуальной значимости элемента (как это было во фрагменте Reuter) или для выцветания больших фрагментов менее заметной информации, таких как источники или раздел заметок.

Система 5: С засечками для чтения / Без засечек для маркировки

В такой системе заголовок, подзаголовок и прочая информация имеет шрифт с засечками. Эта идея основана на давней дискуссии в теории дизайна о том, что шрифты с засечками для длинных отрывков текста, возможно, позволяют быстрее усваивать информацию. Для максимальной читабельности оставьте длину строки не более 60 символов.

Пример из жизни: New York Times особенно хорошо смешивает шрифт NYT Cheltenham с засечками для чтения и шрифт NYT Franklin без засечек для марикровки данных. Несмотря на то, что это два совершенно разных шрифта, они работаю вместе, потому что буквы имеют дополнительную апертуру, большую X-высоту и одинаковую ширину штриха при соответствующих весах.

На следующем графике отображена такая система с использованием шрифта Lora для заголовка и подзаголовка и шрифта Libre Franklin для содержимого маркировки.

Используемые шрифты: Lora — шрифт Google, оптимизированный для экрана, но также хорошо подходящий и для печатных проектов. Текст диаграммы — Libre Franklin, это шрифт на основе Franklin. Он относится к группе шрифтов, вдохновленных оригинальным шрифтом Franklin Gothic, созданным примерно в 1910 году.

Указанные в тексте шрифты и системы являются взаимозаменяемыми. Ниже приведён список упомянутых гарнитур:

  1. Lato
  2. Assistant
  3. Noto Sans
  4. Source Sans Pro
  5. Libre Franklin

Это шрифты, подходящие для заголовков:

  1. PT Sans
  2. Merriweather
  3. Lora

Хороший выбор шрифтов для системы начинается с понимания того, как элементы форм букв обеспечивают высокую читаемость. Я надеюсь, что этот подход поможет вам найти свою систему дизайна шрифтов. Какие ваши любимые шрифты для визуализации данных? Дайте мне знать ниже и спасибо за чтение!

 Нет комментариев    421   12 мес   визуализация   шрифты

Как построить красивый waterfall chart в Python?

Время чтения текста – 6 минут

Когда-то давно в 2014ом году для одной из презентаций о рынке e-commerce в Юлмарте мы строили широко известную во всем мире консалтинга диаграмму Waterfall, средствами Excel. В этом материале построим Waterfall chart средствами Python — она наглядно демонстрирует изменения с появлением нового положительного или отрицательного фактора. Для построения диаграммы будем использовать библиотеку plotly.
Для тех, кто пропустил: в цикле материалов о визуализации данных на Python мы уже пробовали строить диаграмму Градусник — она полезна, когда мы хотим сравнить, как соотносятся ожидаемые и реальные данные.

В качестве данных используем сведенную в Юлмарте информацию об изменении объёма рынка e-commerce с 2013 по 2014 год. Данные по оси X — подписи к каждому столбцу, по Y — начальные, итоговые значения и их изменения. Функцией sum() посчитаем итог и добавим его в конец списка. Тег <br> в списке x_list означает перенос строки.

import plotly.graph_objects as go

x_list = ['2013','Макроэкономика РФ','Сокращение численности<br>трудоспобного населения','Проникновение интернета','Развитие трансграничной<br>торговли', 'Федеральные компании', '2014']
y_list = [738.5, 48.7, -7.4, 68.7, 99.7, 48.0]
total = round(sum(y_list))
y_list.append(total)

Создадим список text_list — это те самые значения столбцов. Они берутся из списка y_list, но сперва их нужно немного обработать: переведём все числа в строки и если это столбец с изменением, то есть любой столбец, кроме первого и последнего, добавим к строке знак «плюс» для наглядности. А ещё в случае положительного изменения поменяем цвет на зелёный и на красный в обратном случае. Первому и последнему значению прибавим жирности к шрифту тегом <b>.

text_list = []
for index, item in enumerate(y_list):
    if item > 0 and index != 0 and index != len(y_list) - 1:
        text_list.append(f'+{str(y_list[index])}')
    else:
        text_list.append(str(y_list[index]))
for index, item in enumerate(text_list):
    if item[0] == '+' and index != 0 and index != len(text_list) - 1:
        text_list[index] = '<span style="color:#2ca02c">' + text_list[index] + '</span>'
    elif item[0] == '-' and index != 0 and index != len(text_list) - 1:
        text_list[index] = '<span style="color:#d62728">' + text_list[index] + '</span>'
    if index == 0 or index == len(text_list) - 1:
        text_list[index] = '<b>' + text_list[index] + '</b>'

Для того, чтобы поместить на фон пунктирные линии, необходимо задать их параметры. Сделаем список словарей и положим в него пунктирные линии светло-серого цвета с положением по Y от 0 до 1000 с шагом в 200.

dict_list = []
for i in range(0, 1200, 200):
    dict_list.append(dict(
            type="line",
            line=dict(
                 color="#666666",
                 dash="dot"
            ),
            x0=-0.5,
            y0=i,
            x1=6,
            y1=i,
            line_width=1,
            layer="below"))

Теперь зададим диаграмму — она лежит в методе Waterfall(). У каждого столбца есть тип — total, absolute или relative. Колонки с итоговыми значениями получают тип total или absolute, с промежуточными — relative. Кроме того, задаём цвета: делаем соединяющую линию прозрачной, положительные изменения — зелёными, отрицательные — красными, а итоговые колонки — фиолетовыми. Для текста выберем шрифт Open Sans.

О том, как подобрать хорошие шрифты для своей визуализации данных, можно узнать в материале «Choosing Fonts for Your Data Visualization»

fig = go.Figure(go.Waterfall(
    name = "e-commerce", orientation = "v",
    measure = ["absolute", "relative", "relative", "relative", "relative", "relative", "total"],
    x = x_list,
    y = y_list,
    text = text_list,
    textposition = "outside",
    connector = {"line":{"color":'rgba(0,0,0,0)'}},
    increasing = {"marker":{"color":"#2ca02c"}},
    decreasing = {"marker":{"color":"#d62728"}},
    totals={'marker':{"color":"#9467bd"}},
    textfont={"family":"Open Sans, light",
              "color":"black"
             }
))

Наконец, добавим заголовок и описание графика, уберём легенду, подпишем ось Y и внесём пунктирные линии на график.

fig.update_layout(
    title = 
        {'text':'<b>Waterfall chart</b><br><span style="color:#666666">Изменение объема рынка e-commerce с 2013 по 2014 год</span>'},
    showlegend = False,
    height=650,
    font={
        'family':'Open Sans, light',
        'color':'black',
        'size':14
    },
    plot_bgcolor='rgba(0,0,0,0)',
    yaxis_title="млрд руб.",
    shapes=dict_list
)
fig.update_xaxes(tickangle=-45, tickfont=dict(family='Open Sans, light', color='black', size=14))
fig.update_yaxes(tickangle=0, tickfont=dict(family='Open Sans, light', color='black', size=14))

fig.show()

Получим такую диаграмму:

 Нет комментариев    47   12 мес   Data Analytics   plotly   python   визуализация
Ранее Ctrl + ↓