Сезонные колебания

Содержание

Сезонные колебания

Если есть регулярный вброс энергии или вещества в систему, то из-за экспоненциального спада возникает чередование периодов подъема и спуска. Биологическое значение обычно имеет одна из этих фаз.

В северном полушарии, по сути, два сезона:

  • лето - активный плодотворный период в природе

  • зима - период покоя

Остальные - всего лишь переходы, однако именно в переходные периоды идет активная учеба - осенний и весенний семестры. Учеба в общественных и биологических системах - переход от незнания к знанию.

Название активного периода становится единицей счета циклов для измерения интервалов:

  • активность летом - 5 лет

  • активность днем - 5 дней

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

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

Поскольку информация о колонках указана в комментариях, то мы при импорте данных в таблицу указываем параметры

  • без заголовков ( header=None, )

  • свой перечень кратких имен (names=[...])

# u='http://berkeleyearth.lbl.gov/auto/Stations/TAVG/Text/169045-TAVG-Data.txt'
u='d/169045-TAVG-Data.txt'
D = pd.read_csv(u, delim_whitespace=' ',  comment='%', header=None,
                 names='year month v0 dv0 q br v dv reg dreg'.split())
D.tail()
year month v0 dv0 q br v dv reg dreg
1536 2013 7 24.486 1.279 0.0 0.0 24.417 1.210 23.569 0.536
1537 2013 8 24.419 1.928 0.0 0.0 24.408 1.916 24.037 1.720
1538 2013 9 14.553 -2.249 0.0 0.0 14.567 -2.235 14.302 -2.325
1539 2013 10 8.736 -0.628 0.0 0.0 8.686 -0.678 8.523 -0.667
1540 2013 11 9.600 6.366 1.0 0.0 NaN NaN NaN NaN

Как мы видим, данные обрываются на дате 2013-11. В 19-м веке сбор данных также был нерегулярным. Поэтому для целей визуализации отберем колонку «Выверенные температурные данные» за 100 лет.

X = D.loc[D.year>1912, ['year','month','v']]
X.head()
year month v
330 1913 1 -4.859
331 1913 2 -6.552
332 1913 3 3.278
333 1913 4 12.135
334 1913 5 13.987

Готовим названия месяцев (для красоты).

# так можно получить готовый список 3-буквенных названий месяцев на английском
# momo = pd.tseries.frequencies.MONTHS
# momo = list(map(str.capitalize, momo))

# а так мы получим локализованные названия
import locale
locale.setlocale(locale.LC_TIME, '')
momo=[datetime.datetime(2016,i,1).strftime('%b') for i in arange(1,13)]
print(' '.join(momo))
янв фев мар апр май июн июл авг сен окт ноя дек

Группируя по году и месяцу, мы создаем двойной индекс, который методом unstack() разделяем на индекс строк и индекс колонок, получая таким образом широкую таблицу.

W = X[["year", "month", "v"]].groupby(by=["year", "month"]).max().unstack()
W.columns=momo
W.head()
янв фев мар апр май июн июл авг сен окт ноя дек
year
1913 -4.859 -6.552 3.278 12.135 13.987 17.212 21.268 23.346 16.663 7.469 4.932 1.66
1914 -2.559 0.648 6.878 9.135 16.487 19.612 23.368 20.246 14.563 8.669 -2.168 -3.44
1915 3.141 -2.852 1.978 10.835 13.987 19.412 23.568 19.246 14.063 7.469 4.332 -0.34
1916 -3.059 -2.352 1.878 9.935 15.387 19.012 22.168 19.946 13.363 9.469 3.032 -2.54
1917 -4.459 -8.952 -0.522 11.935 13.087 20.412 21.468 21.546 15.963 10.369 7.332 -0.84
figure(figsize=(12, 8))
sns.heatmap(W, yticklabels=8, cmap="coolwarm")
xlabel('месяц'); ylabel('год'); title('Среднемесячная температура'); 
_images/i_season_13_0.png

Обратите внимание:

  • параметр yticklabels задает интервал между отметками по оси, чтобы метки не наезжали друг на друга;

  • дивергентная цветовая карта позволяет сосредоточиться на особо жарких и холодных месяцах;

  • зимой 1941-го и во время оккупации данные не собирались.

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

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

Оценка аномалий

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

W_anomal = D.loc[D.year>1912,["year", "month", "dv"]].groupby(by=["year", "month"]).max().unstack() #делаем широкую таблицу
W_anomal.columns=momo
sns.heatmap(W_anomal, yticklabels=8, cmap="coolwarm")
xlabel('месяц'); ylabel('год'); title('Аномалия в температуре'); 
_images/i_season_17_0.png

Сравним аномальность из таблицы с отклонением значений от среднего.

W_dev = W - W.mean()
sns.heatmap(W_dev, yticklabels=8, cmap="coolwarm")
xlabel('месяц'); ylabel('год'); title('Отклонение от среднего'); 
_images/i_season_19_0.png

По идентичности рисунков становится понятно, что «аномальность» именно так и рассчитали. Небольшие расхождения в нашем примере связаны с тем, что мы взяли среднее не за всю историю наблюдений, а за 100 лет.

Обратите внимание, что в зимние месяцы температура нестабильна из года в год.

W.std().plot(kind='bar', title='Вариативность среднемесячных температур в течение года');
_images/i_season_22_0.png