Позднее Ctrl + ↑

Граф телеграм-каналов по теме аналитики

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

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

В рамках текущего курса моей учебы, я изучаю много современных подходов к анализу и визуализации данных. В самом начале курса было разминочное упражнение: объектно-ориентированное программирование на Python для сбора и итеративного построения графа с TMDB API. В задаче этот метод применяется для построения графа связи актеров, где связь — игра в одном и том же фильме. Но я решил, что можно применить его и к другой задаче: построению графа связей аналитического сообщества.

Поскольку последнее время мой временной ресурс особенно ограничен, а аналогичную задачу для курса я уже выполнил, то я решил передать эти знания кому-то еще, кто интересуется аналитикой. К счастью, в этот момент, ко мне в личку постучался кандидат на вакансию младшего аналитика данных — Андрей. Он сейчас находится в процессе постижения всех тонкостей аналитики, поэтому мы договорились на стажировку, в рамках которой Андрей спарсил данные с telegram-каналов.

Основной задачей Андрея был сбор всех текстов с телеграм-канала Интернет-аналитика, выделение каналов, на которые ссылался Алексей Никушин, сбор текстов из этих телеграм-каналов и ссылок на этих каналах. Под “ссылкой” подразумевается любое упоминание канала: через @, через ссылку или репостом. В результате парсинга, у Андрея получилось два файла: nodes и edges.
Теперь я представлю вам граф, который получился у меня на основе этих данных и прокомментирую результаты.

Пользуясь случаем, хочу выразить мое почтение команде karpov.courses, поскольку у Андрея отличное знание языка Python!

В результате топ-10 каналов по показателю degree (количество связей) выглядит так:

  1. Интернет-аналитика
  2. Reveal The Data
  3. Инжиниринг Данных
  4. Data Events
  5. Datalytics
  6. Чартомойка
  7. LEFT JOIN
  8. Epic Growth
  9. RTD: ссылки и репосты
  10. Дашбордец

По-моему, получилось супер-круто и визуально интересно, а Андрей — большой молодец! Кстати, он тоже начал свой канал ”Это разве аналитика?”, где публикуются новости аналитики.

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

Принципы построения bubble-charts: площадь VS радиус

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

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

Суть построения bubble-чарта

Немного скучной теории перед тем, как мы приступим к анализу данных. Bubble-chart — удобный способ показать три параметра наблюдения без построения трехмерной модели. По привычным осям X и Y указываются значения двух параметров, а третий показан размером круга, который соответствует каждому наблюдению. Именно это позволяет избежать необходимости построения сложного 3D графика, то есть любой, кто видит bubble-chart, гораздо быстрее сможет сделать выводы о данных изображенных на одной плоскости.

Ошибка, которую может допустить дизайнер, но не аналитик данных

С метриками, которые отображены на осях графика не возникает никаких вопросов, это привычный способ их визуализации, а вот с размерами возникает некоторая трудность: как грамотно и точно отобразить изменения в значениях переменной, если управление идет не точкой на оси, а размером этой точки?
Дело в том, что при построении такого графика без использования аналитических средств, например, в графическом редакторе, автор может нарисовать круги, принимая радиус круга за его размер. На первый взгляд, все кажется абсолютно корректным — чем больше значение переменной, тем больше радиус круга. Однако, в таком случае, площадь круга будет увеличиваться не как линейная, а как степенная функция, ведь S = π × r2. Например, на рисунке ниже показано, что, если увеличить радиус круга в два раза, то площадь увеличится в 4 раза.


Построение круга в Matplotlib

fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(1, 1, 1)
s = 4*10e3


ax.scatter(100, 100, s=s, c='r')
ax.scatter(100, 100, s=s/4 ,c='b')
ax.scatter(100, 100, s=10, c='g')
plt.axvline(99, c='black')
plt.axvline(101, c='black')
plt.axvline(98, c='black')
plt.axvline(102, c='black')


ax.set_xticks(np.arange(95, 106, 1))
ax.grid(alpha=1)

plt.show()

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

Как построить такой график правильно?

К счастью, если строить bubble-charts с помощью библиотек Python (Matplotlib и Seaborn), то размер круга будет определяться именно площадью, что абсолютно корректно и грамотно с точки зрения визуализации.
Сейчас на примере реальных данных, найденных на Kaggle, покажем, как построить bubble-chart правильно. В данных присутствуют следующие переменные: страна, численность населения, процент грамотного населения. Для того чтобы диаграмма была читаемой, возьмем подвыборку из 10 первых стран после сортировки всех данных по возрастанию ВВП.

Для начала, загрузим все нужные библиотеки:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

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

data = pd.read_csv('countries of the world.csv', sep = ',')
data = data.dropna()
data = data.sort_values(by = 'Population', ascending = False)
data = data.head(10)
data['Population'] = data['Population'].apply(lambda x: x/1000000)

Теперь, когда все подготовка завершена, можно построить bubble-chart:

sns.set(style="darkgrid")    
fig, ax = plt.subplots(figsize=(10, 10))    
g = sns.scatterplot(data=data, x="Literacy (%)", y="GDP ($ per capita)", size = "Population", sizes=(10,1500), alpha=0.5)
plt.xlabel("Literacy (Percentage of literate citizens)")
plt.ylabel("GDP per Capita")
plt.title('Chart with bubbles as area', fontdict= {'fontsize': 'x-large'})

def label_point(x, y, val, ax):
    a = pd.concat({'x': x, 'y': y, 'val': val}, axis=1)
    for i, point in a.iterrows():
        ax.text(point['x'], point['y']+500, str(point['val']))

label_point(data['Literacy (%)'], data['GDP ($ per capita)'], data['Country'], plt.gca()) 

ax.legend(loc='upper left', fontsize = 'medium', title = 'Population (in mln)', title_fontsize = 'large', labelspacing = 1)

plt.show()

На этом графике получилось понятным образом отобразить три метрики: уровень ВВП на душу населения по оси Y, процент грамотного населения по оси X и численность населения — площадью круга.

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

Различия между медианой и средним арифметическим как целевым показателем анализа данных

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

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

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

Исследовательские задачи

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

import pandas as pd
city = pd.read_csv('city.csv')

Затем, нужно посчитать среднее и медиану выборки.

mean_pop = round(city.population_2020.mean(), 0)
median_pop = round(city.population_2020.median(), 0)

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

import matplotlib.pyplot as plt
import seaborn as sns

sns.set_palette('rainbow')
fig = plt.figure(figsize = (20, 15))
ax = fig.add_subplot(1, 1, 1)
g = sns.histplot(data = city, x= 'population_2020', alpha=0.6, bins = 100, ax=ax)

g.axvline(mean_pop, linewidth=2, color='r', alpha=0.9, linestyle='--', label = 'Среднее = {:,.0f}'.format(mean_pop).replace(',', ' '))
g.axvline(median_pop, linewidth=2, color='darkgreen', alpha=0.9, linestyle='--', label = 'Медиана = {:,.0f}'.format(median_pop).replace(',', ' '))

plt.ticklabel_format(axis='x', style='plain')
plt.xlabel("Численность населения", fontsize=25)
plt.ylabel("Количество городов", fontsize=25)
plt.title("Распределение численности населения российских городов", fontsize=25)
plt.legend(fontsize="xx-large")
plt.show()

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

fig = plt.figure(figsize = (10, 10))
sns.set_theme(style="whitegrid")
sns.set_palette(palette="pastel")

sns.boxplot(y = city['population_2020'], showfliers = False)

plt.scatter(0, 550100, marker='*', s=100, color = 'black', label = 'Выбросы')
plt.scatter(0, 560200, marker='*', s=100, color = 'black')
plt.scatter(0, 570300, marker='*', s=100, color = 'black')
plt.scatter(0, mean_pop, marker='o', s=100, color = 'red', edgecolors = 'black', label = 'Среднее')
plt.legend()

plt.ylabel("Численность населения", fontsize=15)
plt.ticklabel_format(axis='y', style='plain')
plt.title("Боксплот численности населения", fontsize=15)
plt.show()

Из графиков следует, что медиана существенно меньше среднего, а также, ясно, что это следствие наличия больших выбросов — Москвы и Санкт-Петербурга. Поскольку среднее арифметическое — метрика крайне чувствительная к выбросам — при их наличии в выборке опираться на выводы относительно среднего не стоит. Рост или снижение численности населения Москвы может сильно смещать среднюю численность по России, однако это не будет влиять на настоящий общерегиональный тренд.
Используя среднее арифметическое мы скажем, что численность типичного (среднего) города в РФ — 268 тысяч человек. Однако, это вводит нас в заблуждение, так как среднее значительно превышает медиану исключительно из-за численности населения Москвы и Санкт-Петербурга. На самом деле, численность типичного российского города существенно меньше (аж в 2 раза!) и составляет 104 тысячи жителей.

Маркетинговые задачи

В контексте бизнеса разница между средним арифметическим и медианой также важна, так как использование неверной метрики может серьезно сказаться на результатах проведения акции или затруднить достижение цели. Давайте посмотрим на реальном примере, с какими трудностями может столкнуться предприниматель в ритейле, если неверно выберет целевую метрику.
Для начала, как и в предыдущем примере, загрузим датасет о покупках в супермаркете. Выберем необходимые для анализа столбцы датасета и переименуем их, для упрощения кода в дальнейшем. Поскольку эти данные не так хорошо подготовлены, как предыдущие, необходимо сгруппировать все купленные товары по чекам. В этом случае необходима группировка по двум переменным: по id покупателя и по дате покупки (дата и время определяется моментом закрытия чека, поэтому все покупки в рамках одного чека совпадают по дате). Затем, назовем полученный столбец «total_bill», то есть сумма чека и посчитаем среднее и медиану.

df = pd.read_excel('invoice_data.xlsx')
df_nes = df[['Номер КПП', 'Сумма', 'Дата продажи']]
df_nes.columns = ['user','total_price', 'date']
groupped_df = pd.DataFrame(df_nes.groupby(['user', 'date']).total_price.sum())
groupped_df.columns = ['total_bill']
mean_bill = groupped_df.total_bill.mean()
median_bill = groupped_df.total_bill.median()

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

sns.set_palette('rainbow')
fig = plt.figure(figsize = (20, 15))
ax = fig.add_subplot(1, 1, 1)
sns.histplot(groupped_df, x = 'total_bill', binwidth=200, alpha=0.6, ax=ax)
plt.xlabel("Покупки", fontsize=25)
plt.ylabel("Суммы чеков", fontsize=25)
plt.title("Распределение суммы чеков", fontsize=25)
plt.axvline(mean_bill, linewidth=2, color='r', alpha=1, linestyle='--', label = 'Среднее = {:.0f}'.format(mean_bill))
plt.axvline(median_bill, linewidth=2, color='darkgreen', alpha=1, linestyle='--', label = 'Медиана = {:.0f}'.format(median_bill))
plt.legend(fontsize="xx-large")
plt.show()
fig = plt.figure(figsize = (10, 10))
sns.set_theme(style="whitegrid")
sns.set_palette(palette="pastel")

sns.boxplot(y = groupped_df['total_bill'], showfliers = False)

plt.scatter(0, 1800, marker='*', s=100, color = 'black', label = 'Выбросы')
plt.scatter(0, 1850, marker='*', s=100, color = 'black')
plt.scatter(0, 1900, marker='*', s=100, color = 'black')
plt.scatter(0, mean_bill, marker='o', s=100, color = 'red', edgecolors = 'black', label = 'Среднее')
plt.legend()

plt.ticklabel_format(axis='y', style='plain')
plt.ylabel("Сумма чека", fontsize=15)
plt.title("Боксплот суммы чеков", fontsize=15)
plt.show()

Из графиков следует, что распределение смещено к началу координат (отличное от нормального), а значит медиана и среднее не равны. Медианное значение меньше среднего примерно на 220 рублей.
Теперь представим, что у маркетологов есть задача повысить средний чек покупателя. Маркетолог может решить, что поскольку средний чек равен 601 рублю, то можно предложить следующую акцию: «Всем покупателям, кто совершит покупку на 600 рублей, мы предоставляем скидку 20% на товар за 100 рублей». В целом, резонное предложение, однако, в реальности, средний чек ниже — 378 рублей. То есть большая часть покупателей не заинтересуется в предложении, поскольку их покупка обычно не достигает предложенного порога. Это значит. что они не воспользуются предложением и не получат скидку, а компания не сможет достичь поставленной цели и увеличить прибыль супермаркета. Все дело в том, что исходные предпосылки были ошибочны.

Выводы

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

Дашборд первых 8 месяцев жизни малыша

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

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

Сбор данных

Исходные данные: трекинг основных элементов заботы о малыше в первые 8 месяцев: сон, кормление, смена подгузника. Данные были собраны с помощью приложения BabyTracker.
Моя жена — большая молодец, потому что в течение первых 7 месяцев она очень тщательно и исправно отслеживала все важные моменты. Она забыла отключить время кормления малыша ночью всего пару раз, но я достаточно быстро увидел в данных заметные выбросы, и датасет был от них очищен.
Изначально у меня в голове было несколько форматов визуализации данных, и я попробовал их сразу же внедрить в проектируемый дашборд. Мне хотелось показать интервалы сна малыша в виде вертикальной диаграммы Гантта, однако ночной сон переходил через сутки (0:00), и было совершенно непонятно как это можно исправить в Tableau. После ряда самостоятельных безуспешных попыток найти решение этой проблемы, я решил посоветоваться с Ромой Буниным. К сожалению, мы вместе пришли к заключению, что это никак не решить. Тогда пришлось написать небольшой код на Python, который дробил такие временные отрезки и добавлял новые строки в датасет.
Однако, пока мы переписывались, Рома прислал идентичный моей идее пример! В этом примере утверждается, что женщина собирала данные о сне и бодрствовании своего ребенка в первый год его жизни, а затем написала код, с помощью которого получилось вышить полотенце с датавизом паттернов сна малыша. Для меня это оказалось удивительным, так как выяснилось, что подобный способ визуализации — основной метод, который позволяет показать, как непроста жизнь и сон родителей в первые месяцы появления ребенка.
В моем дашборде на Tableau Public получилось три смысловых блока и несколько “KPI”, про которые я хотел бы рассказать детально и поделиться основными житейскими мудростями. В верхней части дашборда можно увидеть ключевые средние показатели часов дневного и ночного сна, часов и частоты кормлений малыша, а также число смен подгузника в первые три месяца. Я выделил именно три месяца, поскольку я считаю это самым непростым периодом, ведь в вашей жизни происходят существенные изменения, к которым нужно адаптироваться.

Сон

Левая диаграмма — “Полотенце” — иллюстрирует сон малыша. На этой диаграмме важно обратить внимание на белые пропуски, особенно ночью. Это те часы, когда малыш бодрствует, а это значит бодрствуют и родители. Посмотрите, как меняется диаграмма, особенно в первые месяцы, когда мы отказывались от привычки ложиться спать в 1-2 часа ночи и засыпали пораньше. Грубо говоря, в первые три месяца (до марта 2021) ребенок мог заснуть в 2 или 3 часа ночи, но нам повезло, что ночной сон нашего ребенка оказался довольно длинным.
Правый график наглядно иллюстрирует как меняется длина сна малыша ночью и днем со временем, а боксплоты под ним показывают распределение часов дневного и ночного сна. График подтверждает вывод: “Это временно и скоро точно станет лучше!”

Кормление

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

Смена подгузника

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

Выводы

Мне кажется, что использование настоящих личных данных и подобная визуализация иногда куда показательнее, чем множество видео или прочитанных книг о том, каким будет этот период. Именно поэтому я решил поделиться здесь своими выводами и наблюдениями с вами. Главный вывод, который я хотел, чтобы вы вынесли из датавиза: дети — это прекрасно! ❤️

Обзор дашборда в Metabase

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

В новом обзоре BI-систем мы посмотрим на Metabase — open-source инструмент для бизнес-аналитики, в котором можно писать запросы к данным нескольких видов и визуализировать результаты на дашбордах. Ещё Metabase может сам описать ваш датасорс и построить множество разных графиков, а ещё инструмент позволяет создавать «пульсы» благодаря встроенной системе оповещений, которые будет отправлять вам в Slack или на почту уведомления об изменениях в данных.

В видео говорим про интерфейс BI-инструмента, про виды запросов и визуализаций в Metabase, про подключение датасорсов, про реализацию переменных и фильтров, про сводные таблицы (сделаны очень грамотно!), вёрстку дашборда в системе и публикацию в сети.

Внутри команды мы оценили дашборд в Metabase и получили следующие средние оценки (1 — худшая оценка, 10 — лучшая):
Отвечает ли заданным вопросам — 8,6
Порог входа в инструмент — 6,0
Функциональность инструмента — 7,2
Удобство пользования — 7,4
Соответствие результата макету — 7,0
Визуальная составляющая — 6,6

Итог: дашборд получает 7,1 баллов из 10. Посмотрите на полученный результат.

Автор дашборда, член команды Valiotti Analytics — Мария Авдеева

Ранее Ctrl + ↓