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

# Введение
Если вы работаете с показаниями датчиков, метриками серверов или любыми данными, поступающими с течением времени, вы уже знаете, что стандартные конвейеры scikit-learn не совсем подходят. Данные временных рядов имеют структуру, которую табличные модели игнорируют: сезонность, тренд, временной порядок и тот факт, что будущие значения зависят от прошлых.
sktime — это библиотека Python, созданная специально для этой цели. Она предоставляет API в стиле scikit-learn — fit, predict, transform — но разработана с нуля для работы с временными рядами. Вы можете выполнять прогнозирование, классификацию, регрессию и кластеризацию временных рядов, используя при этом единый интерфейс.
В этой статье вы разберёте пример задачи: прогнозирование показаний температуры с промышленного датчика системы отопления, вентиляции и кондиционирования воздуха. Вы узнаете, как sktime обрабатывает данные временных рядов, как создавать конвейеры предварительной обработки, как обучать модели прогнозирования и как оценивать их результаты.
Код можно найти на GitHub .
# Предварительные требования
Вам потребуется Python 3.10 или выше и базовые знания pandas. Установите все необходимое с помощью команды:
pip install sktime pmdarima statsmodels
Если вы предпочитаете установить все необязательные зависимости одним разом, команда `pip install sktime[all_extras]` решит эту проблему.
# Что делает sktime полезным
Это помогает понять проблему, которую решает sktime. В scikit-learn ваши данные представляют собой двумерную таблицу — строки являются выборками, столбцы — признаками. Данные временных рядов нарушают это предположение, поскольку каждая «строка» на самом деле представляет собой последовательность значений во времени, и порядок этих значений имеет значение.
Основные контейнеры данных, которые вы будете использовать, следующие:
| Тип данных | Представительство | Описание |
|---|---|---|
| Ряд | pd.Series или pd.DataFrame | Один временной ряд, используемый в стандартном прогнозировании. |
| Панель | pd.DataFrame с двухуровневым мультииндексом | Набор нескольких независимых временных рядов. |
| Иерархический | pd.DataFrame с мультииндексом 3+ уровней | Структурированный набор временных рядов с уровнями агрегирования по нескольким измерениям. |
Что касается самого временного индекса, sktime поддерживает несколько временных индексов: DatetimeIndex, PeriodIndex, Int64Index и RangeIndex для ваших объектов pandas. Индекс должен быть монотонным. Если вы используете DatetimeIndex, необходимо установить атрибут freq.
# Настройка набора данных
Давайте создадим реалистичный набор данных. Представьте себе датчик системы отопления, вентиляции и кондиционирования воздуха на заводе, который регистрирует температуру каждый час. Показания имеют суточную сезонную закономерность (выше в рабочее время), небольшой восходящий тренд из-за лета и некоторый шум.
import numpy as np import pandas as pd np.random.seed(42) # 90 дней почасовых показаний, начиная с 1 января 2026 г. n_hours = 90 * 24 timestamps = pd.date_range(start=»2026-01-01″, periods=n_hours, freq=»h») # Тренд: постепенное повышение на 5 градусов за 90 дней trend = np.linspace(0, 5, n_hours) # Сезонность в течение дня: пик температуры в 14:00, спад в 4:00 hour_of_day = np.arange(n_hours) % 24 daily_cycle = 4 * np.sin(2 * np.pi * (hour_of_day — 4) / 24) # Шум noise = np.random.normal(0, 0.8, n_hours) # Базовая температура около 20°C temperature = 20 + trend + daily_cycle + noise # Вводим несколько пропущенных значений (выпадение сигнала датчика) dropout_indices = [300, 301, 302, 1440, 1441] temperature[dropout_indices] = np.nan y = pd.Series(temperature, index=timestamps, name=»temp_celsius») y.index.freq = pd.tseries.frequencies.to_offset(«h») print(y.head()) print(f»nShape: {y.shape}») print(f»Missing values: {y.isna().sum()}») print(f»Index type: {type(y.index)}»)
Выход:
2026-01-01 00:00:00 16.933270 2026-01-01 01:00:00 17.063277 2026-01-01 02:00:00 18.522783 2026-01-01 03:00:00 20.190095 2026-01-01 04:00:00 19.821941 Частота: ч, Имя: temp_celsius, тип данных: float64 Форма: (2160,) Отсутствующие значения: 5 Тип индекса:
# Разделение данных временных рядов для обучения и тестирования
Разделение данных временных рядов отличается от разделения табличных данных — нельзя перемешивать строки. Разделение всегда должно быть хронологическим: обучение на более ранних данных, тестирование на более поздних.
Для этой цели библиотека sktime предоставляет параметр temporal_train_test_split:
from sktime.split import temporal_train_test_split # Выбираем последние 7 дней (168 часов) в качестве тестового набора y_train, y_test = temporal_train_test_split(y, test_size=168) print(f»Train: {y_train.index[0]} → {y_train.index[-1]}») print(f»Test: {y_test.index[0]} → {y_test.index[-1]}») print(f»Train size: {len(y_train)}, Test size: {len(y_test)}»)
Выход:
Поезд: 2026-01-01 00:00:00 → 2026-03-24 23:00:00 Тест: 2026-03-25 00:00:00 → 2026-03-31 23:00:00 Размер поезда: 1992, Размер теста: 168
Эта функция гарантирует чистое и хронологическое разделение — исключается утечка данных из будущего в обучающий набор.
# Определение горизонта прогнозирования
Перед построением любой модели необходимо указать sktime, на каких временных шагах вы хотите производить прогнозирование. Это параметр ForecastingHorizon.
from sktime.forecasting.base import ForecastingHorizon # Прогнозирование на 168 шагов вперед (7 дней почасовых данных) # is_relative=False означает, что мы используем абсолютные временные метки fh = ForecastingHorizon(y_test.index, is_relative=False) print(f»Длина горизонта: {len(fh)}») print(f»Первая точка прогноза: {fh[0]}») print(f»Последняя точка прогноза: {fh[-1]}»)
В результате получаем:
Длина горизонта: 168 Первая точка прогноза: 25.03.2026 00:00:00 Последняя точка прогноза: 31.03.2026 23:00:00
Также можно использовать относительные горизонты, например, fh = [1, 2, 3, …, 168], что означает «на 1 шаг вперед, на 2 шага вперед, …». Абсолютные горизонты удобнее, когда у вас есть фактические временные метки, для которых вы хотите получить прогнозы.
# Создание конвейера предварительной обработки и прогнозирования
В реальных данных с датчиков присутствуют пропущенные значения, сезонные закономерности и тренды — все это необходимо учитывать до или во время прогнозирования. Функция TransformedTargetForecaster из пакета sktime позволяет объединять преобразования с прогнозированием в единый оценщик. Преобразования применяются к целевому ряду y перед подгонкой и автоматически отменяются при выходе из модели во время прогнозирования.
from sktime.forecasting.exp_smoothing import ExponentialSmoothing from sktime.forecasting.compose import TransformedTargetForecaster from sktime.transformations.series.impute import Imputer from sktime.transformations.series.detrend import Deseasonalizer, Detrender pipeline = TransformedTargetForecaster( steps=[ # Шаг 1: Заполнение пропущенных показаний датчиков с использованием линейной интерполяции («imputer», Imputer(method=»linear»)), # Шаг 2: Удаление линейного тренда, чтобы прогнозист видел стационарный ряд («detrender», Detrender()), # Шаг 3: Удаление суточной сезонности (sp=24 для почасовых данных с 24-часовыми циклами) («deseasonalizer», Deseasonalizer(model=»additive», sp=24)), # Шаг 4: Прогнозирование очищенных стационарных остатков («forecaster», ExponentialSmoothing(trend=None, seasonal=None)), ] ) pipeline.fit(y_train, fh=fh) y_pred = pipeline.predict() print(y_pred.head())
Выход:
2026-03-25 00:00:00 21.210066 2026-03-25 01:00:00 21.788986 2026-03-25 02:00:00 22.615184 2026-03-25 03:00:00 23.688449 2026-03-25 04:00:00 24.621127 Freq: h, Name: temp_celsius, dtype: float64
Вот что делает каждый шаг:
- Функция Imputer(method=»linear») заполняет пропущенные значения путем линейной интерполяции между окружающими показаниями, что хорошо подходит для данных с датчиков.
- Функция Detrender() строит линейный тренд на основе обучающего ряда и вычитает его; при прогнозировании она добавляет тренд обратно.
- Функция Deseasonalizer(sp=24) удаляет 24-часовой цикл из остатков; sp обозначает сезонный период.
- Наконец, функция ExponentialSmoothing прогнозирует остатки без тренда и сезонных колебаний.
- При вызове функции predict() все обратные преобразования применяются автоматически в обратном порядке, и вы получаете прогнозы в исходной температурной шкале.
# Оценка прогноза
sktime интегрируется со стандартными метриками оценки. Для прогнозирования обычно используются средняя абсолютная ошибка (MAE) и средняя абсолютная процентная ошибка (MAPE).
from sktime.performance_metrics.forecasting import ( mean_absolute_error, mean_absolute_percentage_error, ) mae = mean_absolute_error(y_test, y_pred) mape = mean_absolute_percentage_error(y_test, y_pred) print(f»MAE: {mae:.3f} °C») print(f»MAPE: {mape*100:.2f}%»)
Выход:
MAE: 0,584 °C MAPE: 2,40%
# Замена прогнозиста
Одно из главных преимуществ интерфейса sktime заключается в том, что замена базового алгоритма требует изменения всего одной строки кода. Давайте попробуем использовать модель ARIMA вместо экспоненциального сглаживания и сравним результаты.
from sktime.forecasting.arima import ARIMA pipeline_arima = TransformedTargetForecaster( steps=[ («imputer», Imputer(method=»linear»)), («detrender», Detrender()), («deseasonalizer», Deseasonalizer(model=»additive», sp=24)), # ARIMA(1,1,1) на очищенных остатках («forecaster», ARIMA(order=(1, 1, 1), suppress_warnings=True)), ] ) pipeline_arima.fit(y_train, fh=fh) y_pred_arima = pipeline_arima.predict() mae_arima = mean_absolute_error(y_test, y_pred_arima) mape_arima = mean_absolute_percentage_error(y_test, y_pred_arima) print(f»ARIMA MAE: {mae_arima:.3f} °C») print(f»ARIMA MAPE: {mape_arima*100:.2f}%»)
Выход:
ARIMA MAE: 0,586 °C ARIMA MAPE: 2,41 %
Ключевой момент заключается в том, что этапы предварительной обработки — заполнение пропущенных данных, удаление тренда, устранение сезонности — остались неизменными. Вы изменили только конечный прогнозист, а все остальное органично вписалось в общую картину.
# Перекрестная проверка во времени
Использование одного тестового окна может ввести в заблуждение. sktime обеспечивает перекрестную проверку временных рядов с помощью разделителей, учитывающих временной порядок.
SlidingWindowSplitter использует скользящее окно: окно обучения сдвигается вперед во времени, всегда оставаясь одинаковой длины. ExpandingWindowSplitter увеличивает обучающий набор данных кумулятивно по мере продвижения вперед, что более подходит, когда необходимо использовать всю доступную историю.
from sktime.split import ExpandingWindowSplitter from sktime.forecasting.model_evaluation import evaluate # Расширяющееся окно: начинаем с обучающего набора данных на 1800 часов, оцениваем на окнах по 168 часов cv = ExpandingWindowSplitter( initial_window=1800, fh=list(range(1, 169)), step_length=168, ) results = evaluate( forecaster=pipeline, y=y, cv=cv, scoring=mean_absolute_error, return_data=False, ) print(results[[«test__DynamicForecastingErrorMetric», «fit_time»]].round(3)) print(f»nMean CV MAE: {results['test__DynamicForecastingErrorMetric'].mean():.3f} °C»)
Выход:
test__DynamicForecastingErrorMetric fit_time 0 0.627 0.274 1 0.585 0.100 Среднее значение CV MAE: 0.606 °C
Функция evaluate возвращает DataFrame с метриками для каждой итерации и временными параметрами. Показатель MAE при перекрестной проверке подтверждает, что модель демонстрирует стабильную обобщающую способность в различных временных интервалах данных.
# Следующие шаги
В этой статье был рассмотрен основной рабочий процесс прогнозирования в sktime, но библиотека позволяет решать задачи, выходящие далеко за рамки базового прогнозирования.
Он также поддерживает классификацию временных рядов, вероятностное прогнозирование с оценками неопределенности, обучение общих моделей на нескольких связанных временных рядах, адаптацию традиционных алгоритмов машинного обучения для последовательного прогнозирования, а также автоматизацию рабочих процессов выбора и настройки моделей.
Одно из главных преимуществ sktime — это его последовательный API и интеграция с более широкой экосистемой машинного обучения Python, что упрощает экспериментирование как для начинающих, так и для опытных специалистов. Документация и примеры блокнотов sktime написаны особенно хорошо и заслуживают того, чтобы их добавить в закладки, если вы регулярно работаете с задачами прогнозирования или временными данными.
Бала Прия С. — разработчик и технический писатель из Индии. Ей нравится работать на стыке математики, программирования, анализа данных и создания контента. В сферу её интересов и компетенции входят DevOps, анализ данных и обработка естественного языка. Она любит читать, писать, программировать и пить кофе! В настоящее время она работает над изучением и распространением своих знаний среди сообщества разработчиков, создавая учебные пособия, руководства, аналитические статьи и многое другое. Бала также создает увлекательные обзоры ресурсов и обучающие материалы по программированию.
Источник: www.kdnuggets.com
Похожие записи
Оцените материал:
Присоединяйтесь и подпишитесь на рассылку самых свежих новостей по Email
Получайте свежие новости и идеи на почту. Без спама — только самое интересное.
Нажимая «Подписаться», вы соглашаетесь с политикой конфиденциальности.
