Пользователь
0,0
рейтинг
12 января в 14:23

Разработка → График счастья с python, pandas и matplotlib из песочницы

image

Зима — это по истине прекрасное время года. Но именно зимой я всегда задумываюсь о том, что встаю и ухожу на работу, а затем и возвращаюсь с работы, не видя солнечного света. Сегодня мне захотелось визуализировать данные о восходе и заходе солнца и соотнести их со столь привычным для многих распорядком дня (рабочие часы и время бодрствования). Для работы мы будем использовать Python (pandas + matplotlib). Посмотрим, что из этого получилось.

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

Для удобства создадим папку проекта /sumerki, внутри неё создадим папку /input и скрипт приложения sumerki.py. В папку input мы положим 2 файла sumerki_1.txt и sumerki_2.txt, куда просто скопируем таблицы с сайта. Первые строки таблиц выглядят так:

1 января	09:00:39	12:33:54	16:07:09	07:06:29	+1:16
08:14:08	07:25:39	06:40:27	18:27:20	17:42:09	16:53:40


Данных из внешних источников нам вполне достаточно, теперь осталось обозначить время бодрствования и рабочее время. Не мудрствуя лукаво я решил взять следующие временные интервалы: 07:00:00 — 20:00:00 для бодрствования и 09:00:00 — 18:00:00 рабочий день.

Со структурой всё понятно. Давайте теперь немного кода на Python.

Сначала сделаем все необходимые импорты и осуществим небольшие настройки.

import os
import datetime
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib import rc


# Создадим небольшую служебную функцию для преобразования строкового времени с число
def stn(dstr):
    return mdates.datestr2num(dstr.tolist())


# Простой рецепт для решения проблемы отображения кириллицы в matplotlib
font = {'family': 'Verdana', 'weight': 'normal'}
rc('font', **font)

DIR = os.path.dirname('__File__')

Сформируем данные в pandas (на выходе получим 2 датафрейма, с которыми будем работать — S, W):

# Считаем данные из текстовых файлов и сохраним их в датафрейм с заранее известными столбцами
s1 = open(os.path.join(DIR, 'input', 'sumerki_1.txt'), 'r').read().split('\n')
s2 = open(os.path.join(DIR, 'input', 'sumerki_2.txt'), 'r').read().split('\n')
oday = datetime.datetime.strptime('01.01.2016', '%d.%m.%Y')
dates = [oday + datetime.timedelta(days=dt) for dt in range(len(s1))]

# От себя мы добавим столбцы с временем начала и окончания дня (0, 24)
s = [[dates[i[0]]] + s1[i[0]].split('\t') + s2[i[0]].split('\t') + ['00:00:01', '23:59:59'] for i in enumerate(s1)]
columns = ['datetime', 'date', 'voshod', 'zenit', 'zahod', 'dolgota', 'cng',
           'sum1_from', 'sum2_from', 'sum3_from', 'sum3_to', 'sum2_to', 'sum1_to',
           '0', '24']
S = pd.DataFrame(s, columns=columns)

# Сформироуем датафрейм, содержащий данные о дне и начале/конце рабочего дня (бодрствования)
w = [[dt, '07:00:00', '09:00:00', '18:00:00', '20:00:00'] for dt in dates]
columns = ['datetime', 'life_from', 'work_from', 'work_to', 'life_to']
W = pd.DataFrame(w, columns=columns)

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

# Ну а теперь построим график
fig, ax = plt.subplots()

plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.gca().xaxis.set_major_locator(mdates.MonthLocator())
plt.gcf().autofmt_xdate()

# Отобразим нужные данные на графике
l1, = ax.plot(S['datetime'], stn(S['voshod']), 'r-', label='восход')
l2, = ax.plot(S['datetime'], stn(S['zahod']), 'b-', label='заход')
l3, = ax.plot(S['datetime'], stn(S['sum1_from']), 'g-', label='сумерки (от)')
l4, = ax.plot(S['datetime'], stn(S['sum1_to']), 'm-', label='сумерки (до)')
l5, = ax.plot(W['datetime'], stn(W['work_from']), 'k-', label='рабочее время')
l6, = ax.plot(S['datetime'], stn(W['work_to']), 'k-', label='рабочее время')
l7, = ax.plot(S['datetime'], stn(W['life_from']), 'k-', label='время бодрствования')
l8, = ax.plot(S['datetime'], stn(W['life_to']), 'k-', label='время бодрствования')


# Заполним пространство между
# Восход-заход
plt.fill_between(S['datetime'].tolist(), stn(S['voshod']), stn(S['zahod']), alpha=0.4, color='yellow', hatch='.')
# Cумерки
plt.fill_between(S['datetime'].tolist(), stn(S['sum1_from']), stn(S['voshod']), alpha=0.4, color='orange', hatch='.')
plt.fill_between(S['datetime'].tolist(), stn(S['zahod']), stn(S['sum1_to']), alpha=0.4, color='orange', hatch='.')
# Ночь
plt.fill_between(S['datetime'].tolist(),  stn(S['0']), stn(S['sum1_from']), alpha=0.4, color='blue', hatch='*')
plt.fill_between(S['datetime'].tolist(), stn(S['sum1_to']),  stn(S['24']), alpha=0.4, color='blue', hatch='*')
# Рабочее время и время бодрствования
plt.fill_between(W['datetime'].tolist(), stn(W['work_from']), stn(W['work_to']), alpha=0.1, color='blue', hatch='/')
plt.fill_between(W['datetime'].tolist(), stn(W['life_from']), stn(W['life_to']), alpha=0.1, color='blue', hatch='/')

# Форматирование, заголовок и легенда
ax.yaxis_date()
ax.xaxis_date()
ax.set_xlabel("Дата")
ax.set_ylabel("Время")
plt.title('График зависимости рабочего времени (времени бодрствования) от светового дня.')
plt.legend(handles=[l1, l2, l3, l4, l6, l8], loc=1, fontsize=11)
fig.autofmt_xdate()

# Ну и покажем график уже наконец
plt.show()


UPD (13.01.2016)
По просьбе myxo привожу несколько графиков для разных широт. В исходной статье график Московский (UTC +3).
Калининград (UTC+2)
image

Санкт-Петербург(UTC+3)
image

Екатеринбург(UTC+5)
image

Новосибирск(UTC+6)
image


Просматривая эти графики, выводы напрашиваются сами:
  • Чем восточнее находится город, тем раньше светает и раньше темнеет
  • Чем севернее находится город, тем более растянут световой день

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

Спасибо за внимание и комментарии!
Alexey Samoylov @tibhar940
карма
9,0
рейтинг 0,0
Пользователь
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (5)

  • +1
    Чтобы статья была хоть чуточку интереснее, могли бы привести графики для разных широт.
    • 0
      Спасибо за комментарий! Добавил несколько графиков для разных широт в конец статьи.
  • +3
    Не мудрствуя лукаво я решил взять следующие временные интервалы: 07:00:00 — 20:00:00 для бодрствования

    Я вот даже как-то не знаю что и сказать. Вы правда 11 часов ежедневно спите :)?
    • +1
      Спасибо, что заметили. Здесь я имел в виду временной интервал с пробуждения до возвращения домой. Моя ошибка.
  • 0
    А вот мои расчёты для разных режимов работы

    http://geektimes.ru/post/243255/

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