Как подсветить временные отрезки на графиках

от автора

Вывести временной интервал на график временного ряда с помощь Python

Часто для анализа временных рядов нужно перебрать много факторов в поисках возможных связей. И обычно, факторы — это просто какие-то события происходившие в некоторый период времени и на прямую не влиявшие на целевой показатель. То есть хочется «подсветить» диапазон времени на графике временного ряда, а ещё так чтобы каждый тип события имел свой цвет.
Как оказалось, найти решение гораздо сложнее, чем его реализовать. Код базируется на статье с geeksforgeeks.org, однако моё решение представлено в виде функции, пусть и не очень изящной, позволяет автоматически генерировать разные цвета для разных диапазонов времени, а также собирает легенду.
Для иллюстрации посмотрим, есть ли какие-то взаимосвязи между извержениями вулканов России и средним отклонением мировой температуры от базовой. Кривую отклонения температур берём от сюда, а базу с извержением вулканов можно скачать здесь. Чтобы не загружать график выберем только извержения с VEI от 4 и больше (Volcanic Explosivity Index — метрика силы извержения).

Последовательность действий

В целом, всё что нужно сделать укладывается в 4 пункта:

  1. объявляем subplots

  2. «строим» scatter или plot с целевым значением

  3. с помощью axvspan добавляем нужные временные отрезки

  4. plt.show() — ура, временные интервалы подсветились на графике

Строим график сами

Библиотеки и загрузка данных

Для сборки графика потребуется функции subplots и axvspan из библиотеки matplotlib.pyplot. Данные удобнее всего хранить в датафреймах, поэтому также подгружаем pandas и для генерации цветов, учитывая возможную потребность в их большом количестве воспользуемся методом random из numpy.

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

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

Загрузка и предобработка данных
# загружаем данные в датафремы df_temp = pd.read_csv('annual_temp.csv') df_eruptions = pd.read_excel('eruptions.xlsx') # приводим данные в datetime для температурных данных df_temp['Year'] = pd.to_datetime(df_temp['Year'], format='%Y') df_temp.dropna(inplace=True) # убираем пропуски df_temp = df_temp.groupby('Year').mean() # данные за каждый год представлены несколькими источниками, группируем и берем среднее df_temp['Year'] = df_temp.index # для удобства создадим столбец с годами # данные о изврежения нужно фильтровать df_eruptions = df_eruptions[['Volcano Name', 'VEI', 'Start Year', 'Start Month', 'Start Day', 'End Year','End Month', 'End Day']] # нужные столбцы df_eruptions.dropna(inplace=True) # убираем пропуски df_eruptions = df_eruptions[(df_eruptions['VEI'] >=4) &(df_eruptions['Start Year'] >=1880)] # фильтруем данные по VEI  # это строки посвящены формированию столбцов с датами начала и конца изврежения df_eruptions[['Start Year', 'Start Month', 'Start Day',                 'End Year', 'End Month', 'End Day']] = df_eruptions[['Start Year', 'Start Month', 'Start Day', 'End Year', 'End Month', 'End Day']].astype(str) df_eruptions['start_date'] = pd.to_datetime(df_eruptions['Start Year'] + '/'                                              + df_eruptions['Start Month'] + '/' + df_eruptions['Start Day'], format='%Y/%m.0/%d.0' ) df_eruptions['end_date'] = pd.to_datetime(df_eruptions['End Year'] + '/'                                              + df_eruptions['End Month'] + '/' + df_eruptions['End Day'], format='%Y.0/%m.0/%d.0')

График

Данные готовы — пора создать визуализацию.
plt.subplots() — позволят создавать сет субграфиков и общий макет подзаголовков. Например, здесь можно задать размер итогового изображения
ax.plot — строим кривую целевых значений (вместо plot, может быть например scatter или другой). В функцию подаем x и y, для легенды можно обозначить label.
ax.axvspan — выделит цветом временной диапазон. На вход нужно подать даты начала и конца интервала. В дополнительные параметры можно передать цвет и лейбл.
В этом коде цвет присваивается извержению, т.е. извержения Шивелуча 1964 года и записанное в одно большое извержения с 1999 года имеют разные цвета. Соответственно и легенда раздувается. В функции ниже реализованы одинаковые цвета.
Поскольку временных интервалов много — реализован цикл пробегающий по спискам с данными. Преобразование в списки продиктовано удобством и понятностью кода.

fig, ax = plt.subplots(figsize=(20, 6)) # задаем сабплот и размеры графика ax.plot(df_temp['Year'], df_temp['Mean'], marker='x',label='Среднее отклонение от базовой температуры') eruption_started = df_eruptions['start_date'].to_list() eruption_ended = df_eruptions['end_date'].to_list() for i in range(len(eruption_started)):     ax.axvspan(eruption_started[i], eruption_ended[i], alpha=0.3, color=np.random.rand(3,), label=df_eruptions['Volcano Name'].to_list()[i] ) plt.legend() plt.show()
Получившийся график с изменением температуры и периодами извержений
Получившийся график с изменением температуры и периодами извержений

Функция

Реализуем в виде функции highlighted_date. На вход она принимает:

  • pandas.Series с координатами х и у для целевого графика;

  • str — название целевого графика для легенды;

  • pandas.Series с координатами х и у для временных интервалов;

  • pandas.Series с лейблами временных интервалов Функция выводит график с scatter целевой функции (для соединений можно заменить на plot), временные диапазоны окрашиваются по labelт.е. все извержения Шивелуча имеют одинаковый цвет и обозначен в легенде единожды. Отображается легенда.

def highlighted_date (x, y, label, x_2, y_2, label_2):     """     main_prepare_data(series,series, str, series,series,series)     подсвечивает временные отрезки и выводит целевую кривую          """     fig, ax = plt.subplots(figsize=(20, 6)) # задаём параметры графика     ax.scatter(x, y, marker='x', label=label) # строим график целевого значения     x_2 = x_2.to_list()     y_2 = y_2.to_list()     color_dict = {}     already_labeled = [] # для фильтрации уже вынесенных в легенду     for j in label_2.unique(): # создаём словарь цветов для уникальных названий         color_dict[j] = np.random.rand(3,) # цвета задаются рандомом     label_2 = label_2.to_list()     for i in range(len(x_2)):         if(label_2[i] in already_labeled):                 ax.axvspan(x_2[i], y_2[i], alpha=0.3, color=color_dict[label_2[i]])         else:             ax.axvspan(x_2[i], y_2[i], alpha=0.3, color=color_dict[label_2[i]], label=label_2[i])             already_labeled.append(label_2[i])     plt.legend()     plt.show()

Вызов выглядит так:

highlighted_date(df_temp['Year'], df_temp['Mean'],                   'Среднее отклонение от базовой температуры',                  df_eruptions['start_date'], df_eruptions['end_date'],                   df_eruptions['Volcano Name'])
Теперь каждый вулкан имеет свой цвет и в легенде встречается один раз
Теперь каждый вулкан имеет свой цвет и в легенде встречается один раз

Вывод

Интересно, насколько велико влияние извержения вулкана Безымянного 1955 года на резкий скачок температуры?

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


ссылка на оригинал статьи https://habr.com/ru/post/710530/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *