Визуализация поездок на общественном транспорте

Начну с постановки задачи – в связи с продажей автомобиля решил провести анализ трат на общественный транспорт начиная с февраля 2022 года и заодно применить/закрепить имеющиеся начальные знания python.

Для того, чтобы анализировать информацию, ее нужно сначала получить. Источником информации по поездкам может служить личный кабинет MosMetro (на Android), где хранятся сведения об истории проходов по привязанным картам Тройка. Увы, но способа выгрузить оттуда информацию автоматически я не нашел, поэтому переносил вручную в гуглтаблицу. Плюсом является то, что можно переносить информацию спустя некоторое время, она никуда не денется из личного кабинета. Все было бы хорошо, но у меня были и поездки, которые я оплачивал не картой Тройка, а банковской картой Мир. Так что эту информацию также заносил вручную в ту же самую гуглтаблицу, информацию же брал из банковского приложения.

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

Сама форма выглядит так:

Итак, теперь пара слов о формате данных гуглтаблицы, в которой хранится информация о поездках. Состоит она из четырех столбцов: дата и время поездки, вид общественного транспорта (Метро, Автобус, Трамвай), номер трамвая или автобуса и стоимость поездки. Назовем их Timestamp, Type, Comment, Price. Две последние колонки заполнять не обязательно, например, если поездка была на метро и по ранее купленному Единому (цена 32,33 рубля в данном случае это средняя цена поездки по месячному проездному – стоимость проездного делится на количество поездок за месяц).

Гуглтаблица получается такая:

Да, конечно можно сделать аналитику используя возможности Google Sheets – pivot table, диаграмы и графики, но так не интересно. Есть же Colab и python и те же самые инструменты )))

Итак, для начала надо определиться какую аналитику надо сделать. Решил остановиться на красивом – heatmap, для чего использовал библиотеку python july (можно и calmap).

pip install july #устанавливаем пакет July для создания календарей с heatmaps

Сначала надо прописать библиотеки, которые буду использовать:

import numpy as np
import matplotlib.pyplot as plt
import july
from july.utils import date_range
import pandas as pd
import plotly.graph_objects as go

import gspread
from google.colab import auth
auth.authenticate_user()
from google.auth import default
creds, _ = default()

Затем импортируем данные из Google Sheets:

gc = gspread.authorize(creds) #поключаемся к аккаунту Гугла
file='Поездки на общественном транспорте (Responses)' #имя файла с гуглтаблицей, расширение можно не указывать
ws = gc.open(file).sheet1 #открываем гуглтаблицу, все нужные данные на первом листе
rows = ws.get_all_values() #считываем данные по строкам
clnd = pd.DataFrame.from_records(rows[1:], columns = ['Timestamp','Type','Comment','Price']) # в первой строке таблицы названия колонок из Гугл формы, поэтому при импорте мы первую строку просто не берем

Преобразуем индексную колонку в Timeseries:

clnd.index = pd.DatetimeIndex(clnd['Timestamp'], dayfirst=True) #парсим колонку Timestamp в формат даты и время и определяем ее как индекс

Делаем перегруппировку по дням, считаем число поездок за день, записываем в новый датафрейм для передачи данных в july:

clnd_july = clnd.resample('D')['Timestamp'].count()

Еще немного магии:

dates = date_range(clnd_july.index.min(), clnd_july.index.max()) #определяем интервал дат иходном датафрейме
july.heatmap(dates, clnd_july, title='Поездки на общественном транспорте 2022', cmap="PuBu", colorbar=True, fontfamily="monospace", date_label = True, year_label = False, value_label = False, month_grid = True, dpi = 300) #рисуем heatmap, интересно поиграться с цветовыми схемами
#plt.savefig('saved_figure.jpg', transparent=True) #опционально - можно сохранить тепловую карту в виде картинки. Сохраняется в папку на гуглдиске

Конечный результат:

Но это еще не все. В библиотеке Plotly есть очень классный тип диаграмм – Диаграмма Санки. Наверняка встречали. Мне диаграмма понравилась за наглядность и интерактивность. Но для подготовки данных придется немного видоизменить датасет.

clnd_sankey = clnd #создаем новую копию датасета для диаграммы Санки
clnd_sankey['Timestamp'] = 'Поездка' # заполняем колонку названием, которое будет исходным для диаграммы
clnd_sankey.rename(columns = {'Timestamp': 'Trip'}, inplace = True) # переименовываем колонку

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

def get_sankey(data,path,value_col): #функция для автоматического заполнения словарей (в моем случае - 4 шт) для создания диаграммы Санки
    sankey_data = {
    'label':[],
    'source':[],
    'target':[],
    'value':[]
    }
    counter = 0
    while (counter < len(path) - 1): #в цикле перебираем столбцы на предмет уникальных значений и создаем иерархию для Диаграммы Санки
        for parent in data[path[counter]].unique():
            sankey_data['label'].append(parent)
            for sub in data[data[path[counter]] == parent][path[counter+1]].unique():
                sankey_data['source'].append(sankey_data['label'].index(parent))
                sankey_data['label'].append(sub)
                sankey_data['target'].append(sankey_data['label'].index(sub))
                sankey_data['value'].append(data[data[path[counter+1]] == sub][value_col].count())
                
        counter +=1
    return sankey_data

Запускаем функцию:

my_sankey = get_sankey (clnd_sankey, ['Trip','Type','Comment'],'Price') #вспомогательный датафрейм для построения диаграммы

И, наконец, рисуем саму Диаграмму Санки:

fig = go.Figure(data=[go.Sankey(
    node = dict(
      pad = 15,
      thickness = 30,
      line = dict(color = "grey", width = 0.5),
      label = my_sankey['label'],
      color = ['orange', 'blue', 'green', 'red']
    ),
    link = dict(
      source = my_sankey['source'],
      target = my_sankey['target'],
      value = my_sankey['value']
  ))])
  
#fig.write_html('test.html') #опционально - результат сохранится в виде интерактивной HTML странички, где можно поиграться связями
fig.show()

Вот результат:

Диаграмма Санки

Пока все.

Leave a Reply

Your email address will not be published. Required fields are marked *