14 заметок с тегом

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

Неочевидный выбор: количественная или качественная цветовая палитра?

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

Перевод статьи «When to use quantitative and when to use qualitative color scales»

Лиза Шарлотта Мут, дизайнер из Берлина, опубликовала на Datawrapper статью о сложностях выбора между качественной и количественной цветовой палитрой при визуализации данных разных типов. Заинтересовавшись ее мнением и аргументами по данному вопросу, мы решили перевести ее для вас.

Эта статья попытается ответить на следующий вопрос: когда следует использовать количественную цветовую палитру (последовательную или расходящуюся; например, голубой, синий, темно-синий), а когда — качественную палитру (например, красный, желтый, голубой)?

И небольшой дисклеймер: чтобы мне не оперировать длинными терминами количественная цветовая схема и качественная цветовая схема в этой статье, я буду часто употреблять менее корректные, но более удобные для чтения слова «оттенки» и «градиенты» для количественных цветовых схем и «тона» для качественных.

Итак, когда надо использовать оттенки? А когда — тона?

Используйте тона, когда у значений нет изначального порядка

Тона необходимо использовать, когда невозможно упорядочить данные. Например, если в графике представлены промышленности или страны (Иран, Марокко, Пакистан): Марокко ничем не уступает Пакистану, как и наоборот.

Используйте оттенки или градиенты, когда у значений есть порядок

А вот когда вы можете упорядочить данные, представляемые цветом, используйте последовательную или расходящуюся цветовую палитру. Если, например, необходимо передать данные о безработице (4%; 6%; 8%), лучше отдать предпочтение именно количественной цветовой палитре.

Это правило применимо не только к тексту или числам. У шкалы Лайкерта («полностью не согласен», «не согласен», «где-то посередине», «согласен», «полностью согласен») или размеров одежды (XS, S, M, L, XL, XXL) также есть специфический порядок. Они тоже относятся к количественному типу данных, поэтому для их визуализации крайне рекомендуется использовать именно оттенки.

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

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

Здесь разобраться помогут древовидные карты:

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

И тут вы могли бы подумать: «Но я хочу обозначить цветом совершенно новое значение!» Например, если вы отсортируете ячейки по показателю безработицы, возможно, вам захочется покрасить их по ВВП на душу населения. Но такой график было бы крайне тяжело прочитать:

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

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

А если все-таки покрасить подразумеваемые значения в удобочитаемом графике?

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

Давайте посмотрим на один пример, чтобы прокачать ваше интуитивное понимание, когда стоит прибегнуть к такому решению.

Страница с деталями графика «Алгоритм Google» из The Economist от 8 июня, 2019

Сосредоточимся на точечной диаграмме в левом верхнем углу. Точечные диаграммы — это один из немногих видов графиков, где покраска по скрытым данным без их представления каким-либо дополнительным способом (позицией, длиной, и т. д.) удивительно хорошо работает.

Точечную диаграмму проблематично разобрать в частности из-за того, что точки покрашены расходящейся, а не последовательной цветовой палитрой. По сути, The Economist использует цвета для передачи двух различных переменных: тон для презентации политической идеологии («левые»/«правые») и насыщенность и яркость для демонстрации политической крайности. Тот же самый график, который передавал лишь политическую крайность (не важно, в сторону «левых» или «правых») с помощью последовательной цветовой палитры было бы проще читать.

И все же, чтобы понять этот график из The Economist, требуется несколько секунд. Обратите внимание, что точечную диаграмму справа проще прочитать, так как в ней совмещены два приема визуализации: политическая идеология показана позицией («левые» и «правые») и оттенком.

Но я готова поспорить, что диаграмма слева все же работает — именно благодаря диаграмме справа и нормированной линейчатой диаграмме снизу. Они обе как цветовые ключи к тому, как следует читать цвета на первой точечной диаграмме.

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

Итак, мы рассмотрели древовидные карты, столбчатые графики и точечные диаграммы. Теперь, давайте возьмем в качестве примера линейный график:

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

Используйте оттенки для выделения подкатегорий

Есть и другие причины красить качественные данные количественными цветовыми палитрами, а не качественными. Например, чтобы выделить подкатегории. Ниже представлен график из The Economist, который показывает, как это сделать:

Население в занимаемых территориях, сгруппированное по религиозной принадлежности (The Economist, PDF)

Мы видим две категории: «евреи» (голубой) и «арабы» (желтый). Подкатегориями же являются территории, которые представлены оттенками синего и желтого.

Вот похожий пример:

График NZZ

NZZ использует оттенки (зеленый и синий), чтобы помочь читателям заметить разницу между двумя категориями: Ältere («пожилые») и Jüngere («молодые»). Оттенки помогают различать подкатегории: какие слова пожилые люди и молодежь используют, чтобы сказать «сердцевина яблока».

(Кстати, и тому и другому графику цвета, в целом-то, и не нужны: все секции могут быть серыми с разделяющими их белыми линиями, так как каждая из подкатегорий объяснена текстом. Слова Gaza, Israel, Ältere, Gräutschi и так далее показаны на самих графиках, делая цветовое решение лишним.)

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

Используйте оттенки, чтобы разграничить первостепенные и второстепенные категории

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

График Лизы Шарлотты Рост

По сути, этот график превращает категории («в браке», «не замужем/не женат», «разведен/а», «вдова/вдовец») в подкатегории, так как они все поделены на две большие группы («первостепенные» и «второстепенные»). Если покрасить подкатегории градиентом, наш читатель все отлично поймет.

Используйте оттенки, чтобы сделать отдельные категории менее яркими
и более удобными для чтения для людей, страдающих дальтонизмом

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

Ну, допустим. Тогда какой же цвет темнее, этот или этот ? И как насчет и ? Сюрприз! Два первых превратятся в  в серой градации. А два последних будут выглядеть как .

Для простых смертных судить о светлоте двух цветов, если у них разные тона, не так-то и просто. Поэтому «сделать так, чтобы визуализация работала в черно-белом исполнении» проще, если специалист по дата визуализации использует градиенты только одного тона.

Две причины — доступность и строгость — могут убедить вас выбрать оттенки, а не тона, при работе с категориями. Вот пример из The Financial Times:

График из The Financial Times

Но имейте в виду следующие особенности: во-первых, скорее всего, некоторые читатели будут пытаться придать цветам какой-то смысл. Даже если это не входило в ваши планы, они могут попытаться найти объяснение тому, почему использован градиент. «США изображены более темным цветом, так как имеют самые высокие показатели». Или: «... так как их данные имеют большую значимость в этой статье». Красить наобум нельзя.
Синтия Брюэр отмечала то же самое, когда говорила о представлении бинарных данных («да»/«нет», «общественный»/«частный», «присутствует»/«отсутствует») на картах:

Синтия Брюэр: «Рекомендации по выбору цвета для карт и визуализации», с. 136 по цитате в: «Визуализация в современной картографии», том 2, 1994. Курсив автора статьи.

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

Во-вторых, опыт показывает, что чем больше оттенков одного тона для передачи категорий, тем сложнее прочитать такой график. Вполне реально различить между двумя, тремя оттенками. Но если их будет четыре, пять или шесть, ваши читатели полезут на стенку. Особенно если цветовые индикаторы не упорядочены и/или прокомментированы и/или выполнены лишь в одном тоне (от светло-голубого до темно-синего) вместо нескольких (от светло-желтого до темно-синего).

В-третьих, если хочется использовать градиент, старайтесь использовать оттенки только одного тона, если нет необходимости вводить еще один.

Спросите себя, какое ключевое послание вы хотите передать, и выделите участки, которые нужно сделать более заметными, с помощью другого тона и его оттенков, как «Europe» и «US» в этом графике:

График The Financial Times, который я слегка отредактировала

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

До этого момента, мы в основном смотрели на причины использовать оттенки вместо тонов. А вот причина использовать тона вместо оттенков:

Используйте тона, чтобы лучше обособить категории

В 2020 году, The Financial Times опубликовали график, в котором отобранные страны Европы были сгруппированы в зависимости от значительного/умеренного/небольшого роста чисел новых случаев заболевания Covid-19.

График The Financial Times

Стивен Бернард и Джон Берн-Мердок, создатели этого графика, решили использовать тона для разных стран в каждой группе.

Я спросила Стивена Бернарда, почему он предпочел тона вместо оттенков. И вот что он ответил:

«Логика в такой покраске в том, чтобы сделать линии максимально отличными друг от друга на каждом графике, но, в то же время, сохранить сортировку во всех четырех графиках. [...] Тот подход, который я применил, позволяет зрителю понять из первого графика, что темно-синий представляет собой первое место, розовый — второе, голубой — третье, а зеленый — четвертое. Так, зритель может легко считать данные с других трех графиков».

Когда я посмотрела на график Стивена, я не поняла, что темно-синяя линяя расценивается как первая, как и то, что розовая линия идет второй, и так далее. Читатели (как и я) не ожидают, что категории, которые отображаются тонами, могут обыгрывать еще и порядок. Если бы у категорий был порядок, их стоило бы покрасить градиентом.

И все же мне кажется, что тона неплохо работают в графике Стивена. Ведь, как всегда, стоит вопрос: чем оправдан ваш выбор типа палитры? В данном случае, The Financial Times выбрали качественную палитру, чтобы помочь читателям лучше различать линии, переплетающиеся на графике, вместо того, чтобы подчеркнуть их порядок.

Выводы

Итак, благодаря Лизе Шарлотте Мут, мы можем подвести следующие итоги:
• Используйте тона, когда у значений нет изначального порядка
• Используйте оттенки или градиенты, когда у значений есть порядок
• Используйте оттенки, чтобы подчеркнуть подразумеваемый порядок
• Используйте оттенки для выделения подкатегорий
• Используйте оттенки, чтобы разграничить первостепенные и второстепенные категории
• Используйте оттенки, чтобы сделать отдельные категории менее яркими и более удобными для чтения для людей, страдающих дальтонизмом
• Используйте тона, чтобы лучше обособить категории

Список литературы

  1. Синтия Брюэр: «Рекомендации по выбору цвета для карт и визуализации», с. 136 по цитате в: «Визуализация в современной картографии», том 2, 1994.
  2. Adobe Spectrum: Цвет для визуализации данных, 2019.
  3. Юусо Копонен, Джонатан Хильден: «Руководство по визуализации данных», 2019, с. 73-79.
 Нет комментариев    157   2022   визуализация   Дизайн

Обнаружение статистических выбросов в 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 с помощью нескольких методов описательной статистики (включая минимум, максимум, гистограмму, диаграмму размаха и процентили) или благодаря более формальным методам обнаружения выбросов (включая фильтр Хампеля, тест Граббса, Диксона и Рознера). Следующим этапом проверьте эти значения, и если они действительно являются выбросами — решите, как с ними поступить (сохранить, удалить или изменить), прежде чем проводить анализ.

 Нет комментариев    1358   2021   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.
 Нет комментариев    37   2020   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 — это закрепит ваши навыки и придаст уверенности при работе с данными.

 Нет комментариев    101   2020   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

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

 Нет комментариев    1210   2020   визуализация   шрифты

Как построить красивый 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()

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

 Нет комментариев    129   2020   Data Analytics   plotly   python   визуализация

Красивая визуализация в Python. Диаграмма Градусник

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

Очень часто диаграммы, построенные стандартными средствами Matplotlib, выглядят некрасиво и неинформативно. В 2011ом году для целей одного из отчетов телеком-компании в Excel мы построили полезную симпатичную диаграмму «Градусник», рецепт которой стал известен из популярного в тот момент блога Chandoo про приемы визуализации в Excel.
Вот как она выглядела в Excel:

Времена меняются, и мы попробуем восстановить знание о построении этой полезной диаграммы, используя штатные средства библиотеки matplotlib в Python.

Для каких случаев подойдет диаграмма «Градусник»?
Лучше всего использовать данный тип для сравнения плановых и фактических значений, таким образом наглядно можно увидеть недовыполнение и перевыполнение показателей. При этом план / факт может быть как в процентах, так и в фактических значениях. Мы рассмотрим пример с фактическими значениями в условных единицах.

В этот раз возьмем данные из excel-файла. Используем типичный состав библиотек для работы с данными (и соответствующие им типичные alias):

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

И считываем в DataFrame таблицу:

df = pd.read_excel('data.xlsx')

Посмотрим, как она выглядит:

Начнем извлекать из таблицы колонки. Первый столбец «Продажи» будет вертикальной подписью к каждому столбцу на графике. «План» — статичный столбец, относительно которого измеряется «Факт». Некоторые данные могут приходить в виде вещественных чисел — такие столбцы будут считаны как тип str, если в данных будет встречена запятая. Чтобы работать с такими числами, будем сначала менять в них запятую на точку, а затем переводить в тип float.

xticks = df.iloc[:,0]
try:
    bars2 = df.iloc[:,1].str.replace(',','.').astype('float')
except AttributeError:
    bars2 = df.iloc[:,1].astype('float')
try:
    bars1 = df.iloc[:,2].str.replace(',','.').astype('float')
except AttributeError:
    bars1 = df.iloc[:,2].astype('float')

Так как мы не знаем наверняка, будут ли в данных такие числа, можем словить AttributeError в случае их отсутствия, ведь будем обращаться к методу str, который есть только у строк. Поэтому напишем обработчик исключений try — except, который будет на всякий случай переводить данные в тип float.

Построим из этого классический barchart — график со столбцами. Зададим массив положения на оси Х для bars1 функцией np.arange и bars2, смещённый на ширину столбца:

barWidth = 0.2
r1 = np.arange(len(bars1))
r2 = [x + barWidth for x in r1]
 
plt.bar(r1, bars1, width=barWidth)
plt.bar(r2, bars2, width=barWidth)

И посмотрим, что получилось:

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

barWidth1 = 0.065
barWidth2 = 0.032
x_range = np.arange(len(bars1) / 8, step=0.125)

А теперь отобразим столбцы на графике, задав им положение, ширину, значения, цвет, легенду и подписи к диаграммам bars2:

plt.bar(x_range, bars1, color='#dce6f2', width=barWidth1/2, edgecolor='#c3d5e8', label='План')
plt.bar(x_range, bars2, color='#ffc001', width=barWidth2/2, edgecolor='#c3d5e8', label='Факт')
for i, bar in enumerate(bars2):
    plt.text(i / 8 - 0.015, bar + 1, bar, fontsize=14)

Наконец, сделаем несколько визуальных штрихов — уберём лишние рамки, чёрточки, добавим серую линию под столбцами, поправим размер и шрифт легенде, сделаем диаграмму шире, выведем её на экран и сохраним как plt.png в директории скрипта:

plt.xticks(x_range, xticks)
plt.tick_params(
    bottom=False,
    left=False,
    labelsize=15
)
plt.rcParams['figure.figsize'] = [25, 7]
plt.axhline(y=0, color='gray')
plt.legend(frameon=False, loc='lower center', bbox_to_anchor=(0.25, -0.3, 0.5, 0.5), prop={'size':20})
plt.box(False)
plt.savefig('plt', bbox_inches = "tight")
plt.show()

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

 Нет комментариев    182   2020   Data Analytics   matplotlib   python   визуализация

Когортный анализ в Redash

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

В одной из прошлых заметок мы рассматривали построение Retention-отчета и в нем частично затрагивали понятие когорт.
Под когортой обычно подразумевают группу пользователей продукта или компании. Чаще всего группы выделяют на основе времени установки приложения / появления пользователя в системе.
Выходит, что используя когортный анализ, можно отследить как повлияли изменения в продукте на поведение пользователей (например, на старых и новых пользователей).

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

Мы разберемся с тем, как сравнить Retention пользователей недельных когорт в Redash, поскольку у Redash имеется специальный тип визуализации для построения такого отчета.
Определимся для начала c SQL-запросом. У нас как и прежде две таблицы — user (id пользователя и время установки приложения) и client_session — таймстемпы (created_at) активности каждого пользователя (user_id). Будем считать Retention первых семи дней для недельных когорт за последние 60 дней.
Запрос написан в Cloudera Impala, рассмотрим его.

Для начала построим общую численность когорт:

select trunc(from_unixtime(user.installed_at), "WW") AS cohort_week, 
	ndv(distinct user.id) as cohort_size //считаем количество пользователей в когорте
	from user 
	where from_unixtime(user.installed_at) between date_add(now(), -60) and now() //берем зарегистрированных за последние 60 дней
group by trunc(from_unixtime(user.installed_at), "WW")

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

select trunc(from_unixtime(user.installed_at), "WW") AS cohort_week, 
        datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) as days,
	ndv(distinct user.id) as value  //считаем количество активных пользователей на каждый день
		from user 
		left join client_session cs on user.id=cs.user_id
where from_unixtime(user.installed_at) between date_add(now(), -60) and now()
and from_unixtime(cs.created_at) >= date_add(now(), -60) //берем сессии за последние 60 дней
and datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) between 0 and 30 //отрезаем только первые 30 дней активности
group by 1,2

Итого запрос целиком:

select size.cohort_week, size.cohort_size, ret.days, ret.value from
(select trunc(from_unixtime(user.installed_at), "WW") AS cohort_week, 
		ndv(distinct user.id) as cohort_size 
	from user 
	where from_unixtime(user.installed_at) between date_add(now(), -60) and now()
group by trunc(from_unixtime(user.installed_at), "WW")) size
left join (select trunc(from_unixtime(user.installed_at), "WW") AS cohort_week, 
        datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) as days,
		ndv(distinct user.id) as value 
		from user 
		left join client_session cs on user.id=cs.user_id
where from_unixtime(user.installed_at) between date_add(now(), -60) and now()
and from_unixtime(cs.created_at) >= date_add(now(), -60)
and datediff(cast(cs.created_at as timestamp),cast(user.installed_at as timestamp)) between 0 and 30
group by 1,2) ret on size.cohort_week=ret.cohort_week

Отлично, теперь нам доступны правильно посчитанные данные.

Данные когорт в табличном виде

Создадим новую визуализацию в Redash и правильно укажем параметры:

Важно правильно указать параметры — им соответствуют колонки результирующего запроса

Обязательно отметим, что у нас недельные когорты:

Вуа-ля, наша визуализация когорт готова:

К ней можно добавить фильтры и параметры и использовать в дашборде

Материалы по теме

 Нет комментариев    96   2020   BI-инструменты   redash   sql   визуализация

Нововведения в Redash v8

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

Две недели назад произошел финальный релиз Redash версии 8. Полный список усовершенствований представлен на странице релиза beta версии v8.

В своем обзоре новшеств остановлюсь только на тех, которые максимально значимо меняют пользовательский опыт использования Redash, то есть наиболее наглядны для нас с вами:

  • Support for multi-select in dropdown (and query dropdown) parameters.
  • Support for dynamic values in date and date-range parameters.
  • Search dropdown parameter values.
  • Pivot Table: support hiding totals.
  • New Visualization: Details View.

Support for multi-select in dropdown (and query dropdown) parameters.

В 8-ой версии Redash появилась долгожданная поддержка выбора нескольких значений в параметре типа «выпадающий список» или «выпадающий список на основе запроса»:

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

Для нас это означает возможность использования в запросах условия типа IN с параметром:

При использовании таких значений параметра, мы получим следующий URL в строке браузера:

Support for dynamic values in date and date-range parameters.

Максимально удобная и полезная фича для выбора дат, которая часто используется в Tableau, но до сих пор не была реализована в Redash: использование динамических значений для дат:

При нажатии на молнию, можно выбрать произвольный период и Redash самостоятельно подставит нужные значения в запрос. Так, например, можно быстро посмотреть статистику за последнюю неделю или последний месяц.

Search dropdown parameter values.

Допустим, мы используем параметр типа «выпадающий список на основе запроса», и в выбранном запросе более 300 результирующих строчек, по которым нам надо найти интересующее нас значение. В 8-ой версии эта проблема решена автокомплитом и поиском по значениям параметра. В примере ниже я ввожу строку «Orga» и получаю все значения, в которых данная строка встречается, удобно.

Pivot Table: support hiding totals.

Достаточно долгожданная фича, позволяющая убрать итоги по строкам / столбцам.

New Visualization: Details View

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

 Нет комментариев    59   2019   BI-инструменты   redash   визуализация

Диаграмма матрицы BCG (Boston Consulting Group)

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

Разбавлю блог интересным отчетом, который в свое время был построен для компании Yota в ноябре 2011го года. Построить данный отчет нас вдохновила матрица BCG.

У нас было: один пакет Excel, 75 VBA макросов, ODBC подключение к Oracle, SQL-запросы к БД всех сортов и расцветок. На таком стеке и рассмотрим построение отчета, но в начале немного о самой идее отчета.

Матрица BCG — это матрица размером 2х2, на которой сегменты клиентов изображаются окружностями с центрами на пересечении координат, образуемых соответствующими темпами двух выбранных показателей.

Если упростить, то нам надо было поделить всех клиентов компании на 4 сегмента: ARPU выше среднего / ниже среднего, потребление трафика (основной услуги) выше среднего / ниже среднего. Таким образом получалось, что возникает 4 квадранта, в каждый из которых необходимо поместить пузырьковую диаграмму, где размер пузырька обозначает общее количество пользователей в сегменте. Дополнительно к этому добавляется еще один пузырек в каждом квадранте (более мелкий), который показывает отток в каждом сегменте (авторское усовершенствование).

Что хотелось получить на выходе?
График подобного вида:

Представление матрицы BCG на данных компании Yota

Постановка задачи более-менее ясна, перейдем к реализации отчёта.
Предположим, что мы уже собрали нужные данные (то есть научились определять средний ARPU и среднее потребление трафика, в данном посте не будем разбирать SQL-запрос), тогда первостепенная основная задача — понять как отобразить средствами Excel пузырьки в нужных местах.

Для этого на помощь приходит базовая пузырьковая диаграмма:

Вставка — Диаграмма — Пузырьковая

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

Отлично, выходит, если предположить, что наша диаграмма будет расположена в координатах по X от -1 до 1, а по Y от -1 до 1, то центр правого верхнего пузырька это точка (0.5; 0.5) на диаграмме. Аналогичным образом, расположим все остальные основные пузырьки.

Отдельно следует подумать о пузырьках типа Churn (для отображения оттока), они расположены правее и ниже основного пузырька и могут с ним пересекаться, поэтому правый верхний пузырек разместим в эмпирически полученных координатах (0.65; 0.35).

Таким образом, для четырех основных и четырех дополнительных пузырьков мы можем организовать данные в следующем виде:

Рассмотрим подробнее, как будем их использовать:

Итак, мы задаем по X — горизонтальные координаты центра наших пузырьков, которые лежат в ячейках A9:A12, по Y — вертикальные координаты центра наших пузырьков, которые лежат в ячейках B9:B12, а размеры пузырьков мы храним в ячейках E9:E12.
Далее, добавляем еще один ряд данных для Оттока и снова указываем все необходимые параметры.

Мы получим следующий график:

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

Добавив необходимые подписи данных, получим то, что требовалось в задаче.

Делитесь в комментариях — приходилось ли строить подобные графики, каким образом решали задачу?

 Нет комментариев    158   2019   analysis   Data Analytics   excel   marketing   sql   strategy   визуализация

Как посчитать Retention?

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

В этой заметке разберём как правильно построить отчет по Retention с использованием Redash и языка SQL.
Для начала, в двух словах, что это за метрика Retention rate, почему она важна, какой бывает и каким образом считается.

Retention rate

Метрика Retention rate довольно широко распространена и особенно известна в мобильной индустрии, поскольку позволяет понять насколько хорошо продукт вовлекает пользователей в ежедневное использование. Вспомним (или узнаем) как рассчитывается Retention:
Retention дня X — это N% процентов пользователей, которые вернутся к продукту в день X. Иными словами, если в какой-то конкретный день (день 0) пришло 100 новых пользователей, а на 1-ый день вернулось 15, то Retention 1-го дня составит 15 / 100 = 15%.
Чаще всего выделяют Retention дней 1, 3, 7 и 30 как наиболее описательные метрики продукта, однако полезно в целом рассматривать кривую Retention и делать выводы исходя из нее.

Кривая retention

В конечном итоге нас интересует построение такой кривой, которая показывает удержание пользователей с 0-го дня до 30-го.

Кривая Retention rate c 0-го по 30-ый день

Rolling Retention (RR)

Кроме классического Retention rate выделяют также Rolling Retention (далее RR). При расчете Rolling Retention помимо дня X учитываются также все последующие дни. Таким образом RR 1-го дня — количество пользователей, которые вернулись в 1-ый и последующие дни.

Сравним Retention и Rolling Retention 10-го дня:
Retention10 — количество пользователей, вернувшихся в 10-ый день / количество пользователей, установивших приложение 10 дней назад * 100%.
Rolling Retention10 — количество пользователей, вернувшихся в 10-ый день или позже / количество пользователей, установивших приложение 10 дней назад * 100%.

Гранулярность (retention временных отрезков)

В некоторых отраслях и соответствующих задачах полезно понимать Retention конкретного дня (чаще всего в мобильной индустрии), в других случаях полезно понимать удержание пользователя на разных временных интервалах: например, недельные отрезки или месячные (часто полезно в e-commerce, ретейле).

Пример когорт по месяцам и соответствующий им месячный Retention

Как построить Retention отчет на языке SQL?

Выше мы разобрали как посчитать Retention в формулах. Теперь приложим это к языку SQL.
Допустим, что у нас есть две таблицы: user — храним данные об идентификаторах пользователей и мета-информацию, client_session — информация о посещениях пользователями мобильного приложения.
В запросе будут фигурировать только две эти таблицы, поэтому вы с легкостью сможете адаптировать запрос под себя.
примечание: в рамках данного кода я использую Impala в качестве СУБД.

Собираем размер когорт

SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
          ndv(user.id) AS users
   FROM USER
   WHERE from_unixtime(user.installed_at)>=date_add(now(), -60)
     AND from_unixtime(user.installed_at)<=date_add(now(), -31)
   GROUP BY 1

Разберем этот довольно несложный запрос: по каждому дню мы считаем количество уникальных пользователей для отрезка [60 дней назад; 31 день назад].
Чтобы не лезть в документацию: команда ndv() в Impala аналог команды count(distinct).

Считаем количество вернувшихся пользователей по каждой когорте

SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
          datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) AS date_diff,
          ndv(user.id) AS ret_base
   FROM USER
   LEFT JOIN client_session cs ON cs.user_id=user.id
   WHERE 1=1
     AND datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) between 0 and 30
     AND from_unixtime(user.installed_at)>=date_add(now(), -60)
     AND from_unixtime(user.installed_at)<=date_add(now(), -31)
   GROUP BY 1, 2

В этом запросе ключевая часть содержится в команде datediff: теперь мы считаем для каждой когорты и для каждого datediff количество уникальных пользователей все той же командой ndv() (фактически, количество пользователей которые вернулись в дни от 0-го до 30-го).

Отлично, теперь у нас есть размер когорт и количество вернувшихся пользователей

Собираем все вместе

SELECT reg.reg_date AS date_registration,
       reg.users AS cohort_size,
       cohort.date_diff AS day_difference,
       cohort.ret_base AS retention_base,
       cohort.ret_base/reg.users AS retention_rate
FROM
  (SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
          ndv(user.id) AS users
   FROM USER
   WHERE from_unixtime(user.installed_at)>=date_add(now(), -60)
     AND from_unixtime(user.installed_at)<=date_add(now(), -31)
   GROUP BY 1) reg
LEFT JOIN
  (SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
          datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) AS date_diff,
          ndv(user.id) AS ret_base
   FROM USER
   LEFT JOIN client_session cs ON cs.user_id=user.id
   WHERE 1=1
     AND datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) between 0 and 30
     AND from_unixtime(user.installed_at)>=date_add(now(), -60)
     AND from_unixtime(user.installed_at)<=date_add(now(), -31)
   GROUP BY 1, 2) cohort ON reg.reg_date=cohort.reg_date
    ORDER BY 1,3

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

Retention rate, посчитанный для каждой когорты пользователей

Построение единственной кривой Retention

Несколько модифицируем наш запрос и получим требуемые данные для построения одной кривой Retention:

SELECT 
       cohort.date_diff AS day_difference,
       avg(reg.users) AS cohort_size,
       avg(cohort.ret_base) AS retention_base,
       avg(cohort.ret_base)/avg(reg.users)*100 AS retention_rate
FROM
  (SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
          ndv(user.id) AS users
   FROM USER
   WHERE from_unixtime(user.installed_at)>=date_add(now(), -60)
     AND from_unixtime(user.installed_at)<=date_add(now(), -31)
   GROUP BY 1) reg
LEFT JOIN
  (SELECT from_unixtime(user.installed_at, "yyyy-MM-dd") AS reg_date,
          datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) AS date_diff,
          ndv(user.id) AS ret_base
   FROM USER
   LEFT JOIN client_session cs ON cs.user_id=user.id
   WHERE 1=1
     AND datediff(cast(cs.created_at AS TIMESTAMP), cast(installed_at AS TIMESTAMP)) between 0 and 30
     AND from_unixtime(user.installed_at)>=date_add(now(), -60)
     AND from_unixtime(user.installed_at)<=date_add(now(), -31)
   GROUP BY 1,2) cohort ON reg.reg_date=cohort.reg_date
    GROUP BY 1        
    ORDER BY 1

Теперь у нас для каждого дня посчитан средний по всем когортам Retention rate.

Больше по теме

 2 комментария    2798   2019   analysis   Analytics Engineering   BI-инструменты   Data Analytics   redash   sql   визуализация

Обзор Yandex DataLens

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

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

Сегодня обсудим новый сервис от Яндекса — DataLens (доступ к демо мне любезно предоставил мой большой друг Василий Озеров и команда Fevlake / Rebrain). Сервис находится в режиме Preview и по сути является облачным BI. Ключевая фишка сервиса в том, что он легко и удобно работает с кликхаусом (Yandex Clickhouse).

Подключение источников данных

Рассмотрим основные вещи: подключение источника данных и настройку датасета.
Выбор СУБД не велик, но некоторые основные вещи присутствуют. Для целей нашего тестирования возьмем MySQL.

Выбор источников данных DataLens

На основе созданного подключения предлагается создать датасет:

Интерфейс настройки датасета, определение измерений и метрик

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

Визуализация данных

Что касается самого интерфейса для визуализации — все довольно легко и удобно. Напоминает облачную версию Tableau. А в сравнении с Redash, который чаще всего используется в связке с Clickhouse, возможности визаулизации — просто потрясают.
Чего стоят сводные таблицы, в которых можно использовать Measure Names в качестве названия столбцов:

Настройка сводных таблиц в DataLens

Разумеется, в DataLens от Яндекса есть возможность собрать и базовые графики:

Построение линейного графика в DataLens

Есть и диаграммы с областями:

Построение диаграммы с областями в DataLens

Однако мне не удалось обнаружить каким образом осуществляется группировка дат по месяцам / кварталам / неделям. Судя по примеру данных, доступному в пробной версии, разработчики пока решают этот вопрос созданием дополнительных атрибутов (DayMonth, DayWeek, etc).

Дашборды

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

Не совсем очевидное окно настройки параметров дашборда

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

Пример работающего дашборда с параметрами и вкладками в DataLens

Ждем исправления интерфейсных недочетов, улучшения DataLens и готовимся к использованию в паре с Clickhouse!

 Нет комментариев    182   2019   analysis   BI-инструменты   Data Analytics   data science   datalens   визуализация

Строим funnel-репорт в redash

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

Итак, мы планировали разобрать Funnel-визуализацию отчета в redash.
В первую очередь, построим запрос к подключенному нами источнику данных — google analytics.

Прямо вот такой текст необходимо положить в консоль запроса:

{
    "ids": "ga:128886640",
    "start_date": "30daysAgo",
    "end_date": "yesterday",
    "metrics": "ga:users,ga:goal1Completions,ga:goal2Completions,ga:goal3Completions"
}

В данном запросе мы просим API Google Analytics отдать данные за последние 30 дней по аккаунту GA: 128886640. Мы хотим увидеть число пользователей и число выполнения целей 1, 2 и 3.

В итоге получаем таблицу следующего вида:

ga:users ga:goal1Completions ga:goal2Completions ga:goal3Completions
3,926 105 41 32

Отлично, это именно то, что нам нужно для построения воронки.
Расскажу об одной очень полезной фиче Redash: query-results. Чтобы подключить таблицы с результатами выполнения запросов, идем в Data Sources и ищем query-results (beta). Подключаем новый источник данных.
Теперь у нас появилась возможность обращаться к результатам запросов redash. Так, например, мы можем воспользоваться результатами запроса к Google Analytics.

Как это сделать?
Необходимо выбрать слева источник данных query-results:

Выпадающее меню с выбором источников данных (в консоли — слева)

Теперь научимся делать funnel-визуализацию. Для этого пишем следующий SQL-запрос:

select 'Добавление товара в корзину' as step_name, ga_goal1Completions as goalCompletion from query_8
union all
select 'Просмотр корзины' as step_name, ga_goal2Completions from query_8
union all
select 'Оформление заказа' as step_name, ga_goal3Completions from query_8

В данном случае query_8 — это как раз таблица с результатами запроса к источнику данных Google Analytics.

Настроим визуализацию:

Аккуратно выбираем параметры, чтобы получить желаемый результат

В итоге мы получаем воронку конверсий из одной цели в другую:

Данную воронку можно отобразить в дашборде и добавить к ней фильтры / параметры
 Нет комментариев    79   2018   BI-инструменты   google analytics   redash   визуализация

Визуализация данных в Redash

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

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

Поскольку я про аналитику, то вместе с графиками мы будем изучать полезные показатели бизнеса. Начнем с довольной традиционной метрики для ритейла/e-commerce AOV (Average Order Value) — средняя сумма заказа (в данном случае за месяц). Показатель позволяет отслеживать изменения, связанные с покупательским поведением (стали ли в среднем больше или меньше покупать).

Пример столбиковой диаграммы в Redash на основе показателя AOV (Average Order Value)

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

Зачастую динамика гораздо отчетливее, если посмотреть либо на традиционной график, либо на так называемую area-диаграмму. В данном случае мы исследуем новых пользователей, а также какую долю MAU (Monthly Active Users) занимают новые пользователи.

В диаграмме используется принцип stacked — это когда данные двух рядов суммируются и показываются один над другим.

В этом примере наша диаграмма максимально информативна — мы даем понять какую долю занимают новые пользователи, а используя принцип stacked показываем сколько всего было активных пользователей за месяц (по сути, убиваем двух зайцев сразу).

А вообще мы могли бы представить данные и в несколько ином виде. Например, довольно популярно смешение разных типов диаграмм. Представим, что MAU представлено столбиковой диаграммой (зеленым на графике), а доля новых пользователей от MAU красной линией, которая отложена по вспомогательной (правой оси).

Два типа диаграмм на одном графике

C redash можно строить сводные таблицы, отображать воронки и когорты, а также использовать карт для отображения гео-данных.
В следующих постах я расскажу о диаграмме для построения воронки (но перед этим научимся подключать google analytics).

 Нет комментариев    155   2018   BI-инструменты   redash   визуализация