Вывести временной интервал на график временного ряда с помощь Python
Часто для анализа временных рядов нужно перебрать много факторов в поисках возможных связей. И обычно, факторы - это просто какие-то события происходившие в некоторый период времени и на прямую не влиявшие на целевой показатель. То есть хочется "подсветить" диапазон времени на графике временного ряда, а ещё так чтобы каждый тип события имел свой цвет.
Как оказалось, найти решение гораздо сложнее, чем его реализовать. Код базируется на статье с geeksforgeeks.org, однако моё решение представлено в виде функции, пусть и не очень изящной, позволяет автоматически генерировать разные цвета для разных диапазонов времени, а также собирает легенду.
Для иллюстрации посмотрим, есть ли какие-то взаимосвязи между извержениями вулканов России и средним отклонением мировой температуры от базовой. Кривую отклонения температур берём от сюда, а базу с извержением вулканов можно скачать здесь. Чтобы не загружать график выберем только извержения с VEI от 4 и больше (Volcanic Explosivity Index - метрика силы извержения).
Последовательность действий
В целом, всё что нужно сделать укладывается в 4 пункта:
объявляем
subplots
"строим"
scatter
илиplot
с целевым значениемс помощью
axvspan
добавляем нужные временные отрезки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 можно понятно и красиво отображать временные интервалы на графике временных данных. Разные события можно окрасить в разные цвета или сгруппировать в один. Иногда, такая визуализация может существенно повысить качество анализа и найти взаимосвязи.