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

Вторая часть

Источник: Dall‑E.
Источник: Dall‑E.

(Да, я пыталась сгенерировать графики временных рядов с помощью ИИ. И на самом деле я приятно удивлена результатом).

Почему так важно быть очень внимательным при анализе временных рядов

Анализ данных временных рядов часто оказывается непростой задачей.

Этот вид данных обладает уникальными свойствами и проблемами, которые обычно не встречаются в других наборах данных.

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

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

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

Это связано с тем, что данные временных рядов имеют свою уникальную специфику: они изменяются с течением времени, а это означает, что необходимо учитывать такие важные аспекты, как сезонность, тренды и стационарность!

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

Давайте же вместе разберемся с этими важными аспектами.

Всем привет!

Меня зовут Сара, и я data scientist, специализирующийся на проектировании систем искусственного интеллекта. Я получила степень магистра в области физики, а позже перешла в захватывающий мир Data Science.

Я делюсь своими знаниями о data science, искусственном интеллекте и советами о карьере в этой сфере. Обязательно подписывайтесь, чтобы получать уведомления о новых публикациях!

В этой статье мы рассмотрим:

  • Сезонность и тренды: выявление закономерностей в ваших данных

  • Конструирование признаков для временных рядов

  • Разделение данных временных рядов — предотвращение утечки данных

  • Как выполнить перекрестную проверку данных временных рядов

  • Стационарность и преобразования

  • Бонус: Обнаружение и обработка выбросов

Наши данные

В этом руководстве мы будем использовать набор данных временных рядов Electric Production от Kaggle, который вы можете найти и скачать здесь.

# Предварительная обработка данных: преобразуем 'DATE' в datetime и установим его в качестве индекса
file_path = 'Electric_Production.csv'
df = pd.read_csv(file_path)
df['DATE'] = pd.to_datetime(df['DATE'])
df.set_index('DATE', inplace=True)
df.rename(columns={'IPG2211A2N': 'value'}, inplace=True)

Давайте взглянем на данные:

# Добавление данных на график
plt.figure(figsize=(10, 6))
plt.plot(df['DATE'], df['value'], label='Electric Production')

# Добавление заголовков и меток
plt.title('Electric Production Over Time')
plt.xlabel('Date')
plt.ylabel('Electric Production Value')
plt.xticks(rotation=45)
plt.grid(True)
plt.legend()

# Отображение графика
plt.show()
Изображение 1: Линейный график, отражающий значения выработки электроэнергии с течением времени | Изображение автора
Изображение 1: Линейный график, отражающий значения выработки электроэнергии с течением времени | Изображение автора

Для более глубокого изучения этих данных вы можете воспользоваться различными методами EDA (исследовательского анализа данных), однако в данной статье мы не будем их рассматривать.

Сезонность и тренды: выявление закономерностей в ваших данных

Понимание сезонности и трендов очень важно при анализе временных рядов.

Что такое сезонность?

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

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

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

Выявление сезонности

Один из способов определить сезонность в ваших данных — это банальный визуальный осмотр. Построение графиков ваших временных данных может помочь обнаружить очевидные циклические закономерности.

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

from statsmodels.tsa.seasonal import STL
import matplotlib.pyplot as plt

# Предполагая, что 'df' — это ваш DataFrame со столбцом 'value'
stl = STL(df['value'], seasonal=12) # Adjust 'seasonal' parameter based on your data
result = stl.fit()
# Извлечение компонентов
trend = result.trend
seasonal = result.seasonal
residual = result.resid

# Построение графика декомпозиции
plt.figure(figsize=(12, 8))
plt.subplot(411)
plt.plot(df['value'], label='Original Time Series')
plt.legend(loc='upper left')
plt.subplot(412)
plt.plot(trend, label='Trend Component')
plt.legend(loc='upper left')
plt.subplot(413)
plt.plot(seasonal, label='Seasonal Component')
plt.legend(loc='upper right')
plt.subplot(414)
plt.plot(residual, label='Residual Component')
plt.legend(loc='upper right')
plt.tight_layout()
plt.show()
Изображение 2: Пример: сезонность, тренд и остаточные компоненты из STL‑декомпозиции данных временных рядов | Изображения автора
Изображение 2: Пример: сезонность, тренд и остаточные компоненты из STL‑декомпозиции данных временных рядов | Изображения автора

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

Параметр seasonal в STL управляет сглаживанием сезонного компонента. Вы можете настроить его в зависимости от частоты ваших данных (например, ежемесячно, ежеквартально).

Примечание: STL предполагает, что значения упорядочены по времени. В рамках STL‑декомпозиции предполагается, что значения следуют друг за другом в их естественном временном порядке.

Понимание трендов

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

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

Для этого вы можете использовать скользящее среднее (rolling average), которое помогает сгладить краткосрочные колебания. Вот быстрый способ реализовать это:

# Расчет скользящего среднего за 12 месяцев
df['rolling_avg'] = df['value'].rolling(window=12).mean()

plt.figure(figsize=(10, 5))
plt.plot(df['value'], label='Original Data')
plt.plot(df['rolling_avg'], label='12-Month Rolling Average', color='orange')
plt.title('Original Data vs. Rolling Average')
plt.legend()
plt.show()
Изображение 3: График сравнения исходных данных выработки электроэнергии с трендом скользящего среднего за 12 месяцев | Изображение автора.
Изображение 3: График сравнения исходных данных выработки электроэнергии с трендом скользящего среднего за 12 месяцев | Изображение автора.

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

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

Если вам нужна большая чувствительность к краткосрочным трендам без чрезмерного сглаживания, вы можете попробовать 6-месячное окно.

В долгосрочном планировании, когда требуется меньший уровень шума, лучше использовать 24-месячный период.

Важность учета сезонности и трендов

Интеграция сезонного анализа и анализа трендов в моделирование временных рядов может значительно повысить точность прогнозирования.

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

Это может значительно улучшить понимание данных вашей моделью и, в конечном итоге, повысить точность прогнозов!

Понимание стационарности данных временных рядов: ключевые преобразования

Давайте обсудим еще один важный аспект анализа временных рядов, который может вызывать затруднения даже у опытных дата‑аналитиков: стационарность.

Что такого особенного в стационарности?

Стационарность можно сравнить с состоянием покоя ваших данных временных рядов.

Это означает, что статистические свойства ваших данных, такие как среднее значение и дисперсия, не изменяются со временем.

Почему это важно?

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

Вот один из возможных способов проверки стационарности:

from statsmodels.tsa.stattools import adfuller

def check_stationarity(timeseries):
result = adfuller(timeseries, autolag='AIC')
return result[1] # Return the p‑value
# Предполагая, что 'df' — это ваш DataFrame с несколькими столбцами
for column in df.columns:
p_value = check_stationarity(df[column])
print(f”Column '{column}': p‑value = {p_value}”)
if p_value <= 0.05:
print(f” The series '{column}' is likely stationary”)
else:
print(f” The series '{column}' is likely non‑stationary”)
print()

В результате будет выведено:

Column 'value': p‑value = 0.18 621 469 116 586 592
The series 'value' is likely non‑stationary

Здесь мы используем ADF‑тест.

В очень широком смысле ADF‑тест определяет, является ли временной ряд стационарным, проверяя нулевую гипотезу о том, что в ряду присутствует единичный корень, что указывает на нестационарность.

В этом случае набор данных оказался нестационарным.

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

Если значение p меньше 0.05 — отлично! Скорее всего, ваши данные стационарны. Если нет, не паникуйте — решения есть.

Преобразование нестационарных данных

Если ваши данные не являются стационарными, вот несколько распространенных преобразований, которые вы можете попробовать:

1. Дифференцирование: Вычитаем каждое наблюдение из предыдущего.

df['diff'] = df['value'].diff()

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

2. Логарифмическое преобразование: Отлично подходит для данных с экспоненциальным трендом.

df['log'] = np.log(df['value'])

3. Скользящее среднее: Сглаживает краткосрочные колебания.

df['MA'] = df['value'].rolling(window=12).mean()

Перепроверка на стационарность после преобразований

После того как вы применили преобразования к своим данным временных рядов, крайне важно убедиться, что вы все‑таки достигли стационарности!

print(“Stationarity check for original data:”)
check_stationarity(df['value'])
print(“\nStationarity check after differencing:”)
# Отбрасываем значения NaN перед проверкой на стационарность
check_stationarity(df['diff'].dropna())

Обратные преобразования для предсказаний

Очень важно не забывать об обратных преобразованиях!

Когда вы делаете прогнозы, используя модель, обученную на преобразованных данных, вам нужно «обратить» эти преобразования, чтобы получить значимые результаты.

Вот как вы могли бы это сделать (в другом наборе данных, не том, который мы рассматриваем в этой статье):

import numpy as np

# Допустим, мы обучили модель на данных с логарифмическим и дифференциальным преобразованиями
# и сделал несколько прогнозов
log_diff_predictions = model.predict(X_test)

# Шаг 1: Обращение логарифмического преобразования
diff_predictions = np.exp(log_diff_predictions)
# Шаг 2: Обращение дифференциального преобразования
# Нам нужно последнее фактическое значение, чтобы запустить процесс
last_actual_value = df['value'].iloc[-1]
original_scale_predictions = []
for diff in diff_predictions:
prediction = last_actual_value + diff
original_scale_predictions.append(prediction)
last_actual_value = prediction
# Теперь 'original_scale_predictions' содержит наши прогнозы 

# в масштабе наших исходных данных
print(“Original scale predictions:”)
print(original_scale_predictions)

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

Почему это важно? Представьте, что вы представляете прогноз продаж своему генеральному директору:

«Наша модель прогнозирует, что продажи в следующем месяце составят 1.2»

Генеральный директор: «1.2 чего? Миллионов? Тысяч?»

Вы: «Ну, это натуральный логарифм разницы с продажами за прошлый месяц».

Генеральный директор: ?

Вместо этого, если вы выполните обратные преобразования, то сможете уверенно сказать:

«Наша модель прогнозирует, что продажи в следующем месяце составят 4,5 миллиона долларов, что на 500 000 долларов больше, чем в прошлом месяце».

Генеральный директор: ?

Помните, цель data science — предоставлять полезную информацию!

Во второй части этого руководства мы поговорим о более технических аспектах работы с данными временных рядов: конструировании признаков, разделении данных и обнаружении выбросов в ваших данных.

Вторая часть


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

Уже на этой неделе проходят два открытых урока:

  1. «Пользовательские сценарии (Use Cases): как превратить бизнес‑требования заказчика в задачи на разработку» — 18 июня

  2. «Инструменты ИИ для системного аналитика: автоматизируем рутину и улучшаем эффективность» — 19 июня

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

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


  1. n0isy
    17.06.2025 17:20

    Странно. Я конечно не дата саентист, но разве с помощью БПФ нельзя СРАЗУ выявить любую сезонность, вне зависимости от её периода?


    1. Asterris
      17.06.2025 17:20

      А как именно для этого использовать БПФ?


    1. Refridgerator
      17.06.2025 17:20

      Можно. Но для этого про существование БПФ надо хотя бы подозревать)