Бигдата и тепловые карты на примере твитов Байдена и Трампа
medium

Бигдата и тепловые карты на примере твитов Байдена и Трампа

Сразу видно, кто постит сам, а за кого это делает команда

Когда мы рассказывали о том, как устроено бесплатное обучение в «Яндекс Практикуме», то на одном из скриншотов тренажёра показали тепловую карту — она показывала распределение возвратов товара по неделям среди разных компаний. Сегодня мы сделаем нечто подобное сами: используем тепловые карты для анализа постов Байдена и Трампа, чтобы посмотреть, есть ли там что-то интересное.

В чём идея

Стивен Вуд в 2020 году спарсил твиты кандидатов в президенты США — Джозефа Байдена и Дональда Трампа. Спарсил — то есть запустил программу, которая забирает какие-то данные из веб-страницы и укладывает их в понятном нам формате. 

В подборку попали твиты с 1 февраля 2017 года по 11 сентября 2020 года в таком формате:

  • id твита,
  • дата и время поста,
  • количество ретвитов.

Всего получилось около 17 700 твитов, что уже можно считать бигдатой и на чём уже можно проводить математический анализ. Всем стало интересно, а можно ли в этих данных найти какие-то закономерности, и если да — то какие?

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

Что такое тепловая карта

Тепловая карта — это график с распределением значений по обеим осям, где каждому квадрату на карте соответствует какое-то значение. 

Вспомним пример из Практикума: здесь тепловая карта показывает распределение возвратов товара по неделям для разных компаний. Красные и белые квадраты — много возвратов, чёрные — мало. Видно, что меньше всего отказов у Яндекс Муркета, а больше — у Робокотчи и Мурдроида. А ещё по какой-то причине в 14-ю неделю возвратов нет ни у кого, и это странно — нужно разбираться, действительно ли это так.

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

Загружаем данные в программу

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

Заведём программу на Python и подключим сразу все библиотеки, которые нам понадобятся:

# подключаем модуль для обработки данных
import pandas as pd

# # модули для работы с тепловыми картами
import seaborn as sns
import matplotlib.pyplot as plt

Если какой-то библиотеки ещё нет на компьютере, её можно установить командой:

pip install seaborn где seaborn — это название библиотеки.

Проверить, все ли библиотеки на месте, можно командой

pip list

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

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

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

# путь к файлу с датасетом
data_url = "/Users/mike/Downloads/biden_trump_tweets.csv" 
# считываем данные
df = pd.read_csv(data_url, 
                 parse_dates=['date_utc'], 
                 dtype={'hour_utc':int,'minute_utc':int,'id':str}
                )
# проверяем, что данные считались правильно 
# для этого выводим первые 5 строк нашего датасета
print(df.head())
Программа вывела начало нашего датасета — это значит, что данные загрузились нормально и с ними можно дальше работать

Считаем количество твитов с разбивкой по часам и минутам

Чтобы вывести тепловую карту, данные для неё нужно подготовить: сгруппировать данные по какому-то признаку и добавить нули туда, где значений нет. Сначала сгруппируем все записи по часам и минутам — так мы потом сможем посмотреть, кто из кандидатов в какое время активнее всего:

# объединяем события, которые произошли у каждого пользователя в одно и то же время
g = df.groupby(['hour_utc','minute_utc','username'])

# считаем количество твитов в каждой получившейся группе
tweet_cnt = g.id.nunique()
# для проверки выводим первые 5 строк обработанных данных
print(tweet_cnt.head())

Видно, что Байден в 0 часов 0 минут всего сделал 26 твитов, а Трамп — только 6. В 0:01 у Байдена всего 16 твитов, а у Трампа — 11. Кажется, что Байден активнее Трампа, но это только вывод первых 5 значений нашего нового датасета, и делать выводы пока ещё рано.

Формируем данные для тепловой карты

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

Начнём с Джозефа Байдена:

# начинаем обработку данных с Джо Байдена
# получаем доступ к группе строк и столбцов,
# чтобы сформировать новый датасет для тепловой карты 
jb_tweet_cnt = tweet_cnt.loc[:,:,'JoeBiden'].reset_index().pivot(index='hour_utc', columns='minute_utc', values='id')
# выводим фрагмент нового датасета для проверки
print(jb_tweet_cnt.iloc[:10,:9])

В некоторых полях таблицы стоит NaN — неопределённое значение, потому что этих данных не было в исходном датасете. Но для тепловой карты это не подходит — ей нужны точные значения. Заменим их на нули:

# меняем NaN на 0, чтобы у нас в данных остались только численные значения
jb_tweet_cnt.fillna(0, inplace=True)
# убеждаемся, что это сработало и вместо NaN стоят нули
print(jb_tweet_cnt.iloc[:10,:9])

Нули появились, но нужно решить ещё одну проблему: у нас нет минут 7, 8 и 9, а ещё, может быть, нет минут, которые не вошли в тестовый вывод. Всё потому, что в исходном датасете не было записи о том, что в это время публиковались твиты. Это может исказить тепловую карту, поэтому добавим недостающие минуты и часы, а также приведём всё к целым числам:

# Добавляем отсутствующие часы, если их нет в нашем датасете
jb_tweet_cnt = jb_tweet_cnt.reindex(range(0,24), axis=0, fill_value=0)
# Добавляем отсутствующие минуты и приводим всё к целым числам
jb_tweet_cnt = jb_tweet_cnt.reindex(range(0,60), axis=1, fill_value=0).astype(int) 
# проверяем, что у нас появились 7, 8 и 9-я минуты
print(jb_tweet_cnt.iloc[:10,:9])
Сделаем всё то же самое для Дональда Трампа:
# делаем то же самое для данных Дональда Трампа
dt_tweet_cnt = tweet_cnt.loc[:,:,'realDonaldTrump'].reset_index().pivot(index='hour_utc', columns='minute_utc', values='id')
dt_tweet_cnt.fillna(0, inplace=True)
dt_tweet_cnt = dt_tweet_cnt.reindex(range(0,24), axis=0, fill_value=0)
dt_tweet_cnt = dt_tweet_cnt.reindex(range(0,60), axis=1, fill_value=0).astype(int)

Рисуем тепловую карту

У нас всё готово к тому, чтобы вывести тепловую карту твитов. Логика формирования карты будет такая:

  1. Обе карты сделаем для наглядности в одном файле.
  2. В цикле переберём все элементы из нашего подготовленного датасета и отправим данные из них в массив тепловой карты.
  3. Сразу зададим минимальное и максимальное значение на шкале (мы заранее запустили и выяснили, что максимум было 40 твитов в одно и то же время).
  4. Подпишем оси и их условные обозначения.
  5. Сохраним результат в файл.

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

# готовимся показать несколько графиков в одном фрейме
fig, ax = plt.subplots(2, 1, figsize=(24,12))

#  перебираем оба датасета по одному элементу
for i,d in enumerate([jb_tweet_cnt,dt_tweet_cnt]):
    # получаем значения минут и часов для разметки осей
    labels = d.applymap(lambda v: str(v) if v == d.values.max() else '')
    # формируем тепловую карту
    sns.heatmap(d,
                cmap="viridis",  # тема оформления
                annot=labels, # разметку осей берём из переменной labels
                annot_kws={'fontsize':11},  # размер шрифта для подписей
                fmt='',          # говорим, что с метками надо работать как со строками
                square=True,     # квадратные ячейки
                vmax=40,         # максимальное
                vmin=0,          # и минимальное значение твитов в ячейке
                linewidth=0.01,  # добавляем разлиновку сеткой
                linecolor="#222",# цвет сетки
                ax=ax[i],        # значение каждой клетки в тепловой карте берём в соответствии с датасетом
               )
# подписываем оси
ax[0].set_title('@JoeBiden')
ax[1].set_title('@realDonaldTrump')
ax[0].set_ylabel('Распределение по часам')
ax[1].set_ylabel('Распределение по часам')
ax[0].set_xlabel('')
ax[1].set_xlabel('Распределение по минутам')

# сохраняем результат в файл final.png
plt.tight_layout()
plt.savefig('final.png', dpi=120)
# подключаем модуль для обработки данных
import pandas as pd

# модули для работы с тепловыми картами
import seaborn as sns
import matplotlib.pyplot as plt

# путь к файлу с датасетом
data_url = "/Users/mike/Downloads/biden_trump_tweets.csv" 
# считываем данные
df = pd.read_csv(data_url, 
                 parse_dates=['date_utc'], 
                 dtype={'hour_utc':int,'minute_utc':int,'id':str}
                )
# проверяем, что данные считались правильно 
# для этого выводим первые 5 строк нашего датасета
print(df.head())

# объединяем события, которые произошли у каждого пользователя в одно и то же время
g = df.groupby(['hour_utc','minute_utc','username'])

# считаем количество твитов в каждой получившейся группе
tweet_cnt = g.id.nunique()
# для проверки выводим первые 5 строк обработанных данных
print(tweet_cnt.head())

# начинаем обработку данных с Джо Байдена
# получаем доступ к группе строк и столбцов,
# чтобы сформировать новый датасет для тепловой карты 
jb_tweet_cnt = tweet_cnt.loc[:,:,'JoeBiden'].reset_index().pivot(index='hour_utc', columns='minute_utc', values='id')
# выводим фрагмент нового датасета для проверки
print(jb_tweet_cnt.iloc[:10,:9])
# меняем NaN на 0, чтобы у нас в данных остались только численные значения
jb_tweet_cnt.fillna(0, inplace=True)
# убеждаемся, что это сработало и вместо NaN стоят нули
print(jb_tweet_cnt.iloc[:10,:9])

# Добавляем отсуствующие часы, если их нет в нашем датасете
jb_tweet_cnt = jb_tweet_cnt.reindex(range(0,24), axis=0, fill_value=0)
# Добавляем отсутствующие минуты и приводим всё к целым числам
jb_tweet_cnt = jb_tweet_cnt.reindex(range(0,60), axis=1, fill_value=0).astype(int) 
# проверяем, что у нас появились 7, 8 и 9-я минуты
print(jb_tweet_cnt.iloc[:10,:9])

# делаем то же самое для данных Дональда Трампа
dt_tweet_cnt = tweet_cnt.loc[:,:,'realDonaldTrump'].reset_index().pivot(index='hour_utc', columns='minute_utc', values='id')
dt_tweet_cnt.fillna(0, inplace=True)
dt_tweet_cnt = dt_tweet_cnt.reindex(range(0,24), axis=0, fill_value=0)
dt_tweet_cnt = dt_tweet_cnt.reindex(range(0,60), axis=1, fill_value=0).astype(int)

# готовимся показать несколько графиков в одном фрейме
fig, ax = plt.subplots(2, 1, figsize=(24,12))

#  перебираем оба датасета по одному элементу
for i,d in enumerate([jb_tweet_cnt,dt_tweet_cnt]):
    # получаем значения минут и часов для разметки осей
    labels = d.applymap(lambda v: str(v) if v == d.values.max() else '')
    # формируем тепловую карту
    sns.heatmap(d,
                cmap="viridis",  # тема оформления
                annot=labels, # разметку осей берём из переменной labels
                annot_kws={'fontsize':11},  # размер шрифта для подписей
                fmt='',          # говорим, что с метками надо работать как со строками
                square=True,     # квадратные ячейки
                vmax=40,         # максимальное
                vmin=0,          # и минимальное значение твитов в ячейке
                linewidth=0.01,  # добавляем разлиновку сеткой
                linecolor="#222",# цвет сетки
                ax=ax[i],        # значение каждой клетки в тепловой карте берём в соответствии с датасетом
               )
# подписываем оси
ax[0].set_title('@JoeBiden')
ax[1].set_title('@realDonaldTrump')
ax[0].set_ylabel('Распределение по часам')
ax[1].set_ylabel('Распределение по часам')
ax[0].set_xlabel('')
ax[1].set_xlabel('Распределение по минутам')

# сохраняем результат в файл final.png
plt.tight_layout()
plt.savefig('final.png', dpi=120)

Смотрим на результат

Картинка с картами лежит в той же папке, что и наша программа. Чем светлее — тем больше твитов было в это время. Вот какие выводы можно сделать, глядя на эти две карты:

  1. У Байдена явно видно, что большинство твитов делается в интервалах по 15 и 5 минут, причём в 30 и 45 минут твитов больше всего. Скорее всего, это значит, что за его твиттером следит команда, которая по расписанию выкладывает нужные записи (или ставит их на отложенную публикацию). 
  2. Трамп не привязывался ко времени и публиковал новые посты когда захочет. Судя по карте, чаще всего он это делал с утра на свежую голову, а потом в течение дня добавлял новые записи, как только возникали новые мысли.
  3. Байден (или его команда) отдыхает явно больше Трампа — чёрный интервал, когда твитов почти нет, у Байдена в два раза больше.

Общий вывод напрашивается такой: Трамп вёл свой твиттер сам, а за Байдена это делает команда с чётким графиком публикаций.

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

Художник:

Алексей Сухов

Корректор:

Ирина Михеева

Вёрстка:

Кирилл Климентьев

Соцсети:

Алина Грызлова

Получите ИТ-профессию
В «Яндекс Практикуме» можно стать разработчиком, тестировщиком, аналитиком и менеджером цифровых продуктов. Первая часть обучения всегда бесплатная, чтобы попробовать и найти то, что вам по душе. Дальше — программы трудоустройства.
Вам может быть интересно
medium
[anycomment]
Exit mobile version