- BrainTools - https://www.braintools.ru -
В Python хватает инструментов для работы с временными рядами, но обычно приходится жонглировать тремя‑четырьмя пакетами с разными API. Darts — библиотека, которая собирает всё в одном месте: статистические модели, градиентный бустинг, нейросети — и работает по знакомой схеме fit() / predict(). Сегодня разберём её подробно: что умеет, где удобна, как использовать в задачах.
Установка
# Минимальная установка — статистические модели
pip install darts
# Полная установка — включая PyTorch-модели и Prophet
pip install "u8darts[all]"
# Если хотите точечно:
pip install "u8darts[torch]" # нейросети
pip install "u8darts[prophet]" # Facebook Prophet
Рекомендую ставить в чистый venv.
В Darts всё крутится вокруг объекта TimeSeries. Это иммутабельный контейнер, который хранит временной индекс, значения, и умеет представлять стохастические ряды.
import pandas as pd
import numpy as np
from darts import TimeSeries
# Из pandas DataFrame
df = pd.DataFrame({
"date": pd.date_range("2022-01-01", periods=365, freq="D"),
"sales": np.random.poisson(lam=50, size=365) +
np.sin(np.arange(365) * 2 * np.pi / 365) * 15 # сезонность
})
series = TimeSeries.from_dataframe(df, time_col="date", value_cols="sales")
# Из numpy-массива с указанием частоты
values = np.random.randn(100)
series_np = TimeSeries.from_values(values, fill_missing_dates=True)
# Из встроенных датасетов
from darts.datasets import AirPassengersDataset, MonthlyMilkDataset
air = AirPassengersDataset().load() # классика — авиапассажиры, 1949–1960
milk = MonthlyMilkDataset().load() # производство молока
TimeSeries поддерживает многомерные ряды, срезы по времени, арифметические операции. Можно складывать два ряда, умножать на скаляр — и временной индекс сохранится.
# Разделение на train/val
train, val = air[:-36], air[-36:] # последние 3 года — валидация
# Мультивариатный ряд
df_multi = pd.DataFrame({
"date": pd.date_range("2022-01-01", periods=365, freq="D"),
"sales": np.random.poisson(50, 365),
"returns": np.random.poisson(5, 365),
"page_views": np.random.poisson(500, 365)
})
multi_series = TimeSeries.from_dataframe(
df_multi, time_col="date",
value_cols=["sales", "returns", "page_views"]
)
Все модели в Darts — один интерфейс. fit(series) → predict(n). Вот так:
from darts.models import (
NaiveSeasonal, NaiveDrift,
ExponentialSmoothing, AutoARIMA, Theta
)
from darts.metrics import mape, rmse
train, val = air[:-36], air[-36:]
# Наивная сезонная модель: повторяем значения годичной давности
naive = NaiveSeasonal(K=12)
naive.fit(train)
pred_naive = naive.predict(36)
# Экспоненциальное сглаживание
es = ExponentialSmoothing(seasonal_periods=12)
es.fit(train)
pred_es = es.predict(36)
# AutoARIMA — автоматический подбор (p, d, q)
arima = AutoARIMA()
arima.fit(train)
pred_arima = arima.predict(36)
# Theta — метод, который выиграл M3-competition
theta = Theta(theta=2)
theta.fit(train)
pred_theta = theta.predict(36)
# Сравнение
for name, pred in [("Naive", pred_naive), ("ExpSmoothing", pred_es),
("AutoARIMA", pred_arima), ("Theta", pred_theta)]:
print(f"{name:15s} | MAPE: {mape(val, pred):.2f}% RMSE: {rmse(val, pred):.1f}")
Результат (на AirPassengers) будет таким:
Naive | MAPE: 8.13% RMSE: 45.2
ExpSmoothing | MAPE: 5.11% RMSE: 28.3
AutoARIMA | MAPE: 12.70% RMSE: 63.8
Theta | MAPE: 8.15% RMSE: 44.9
AutoARIMA на дефолтных параметрах не блещет, это нормально, у него свои сильные стороны на других данных. ExponentialSmoothing хорошо ловит и тренд, и сезонность.
Перед обучением [2] нейросетей данные обычно нужно нормализовать. В Darts для этого есть Scaler и другие трансформеры:
from darts.dataprocessing.transformers import Scaler, MissingValuesFiller
# Заполнение пропусков
filler = MissingValuesFiller()
series_filled = filler.transform(series)
# Масштабирование (MinMaxScaler по дефолту)
scaler = Scaler()
train_scaled = scaler.fit_transform(train)
val_scaled = scaler.transform(val)
# После прогноза — обратное преобразование
# pred_scaled = model.predict(36)
# pred = scaler.inverse_transform(pred_scaled)
Трансформеры в Darts с состоянием. fit_transform запоминает параметры (min, max), а inverse_transform возвращает прогноз в оригинальный масштаб. Работают точно как sklearn.preprocessing, только для временных рядов.
Большинство реальных задач предполагает, что помимо самого ряда у вас есть дополнительная информация: температура за окном влияет на продажи мороженого, день недели — на трафик сайта, праздники — на объём заказов.
В Darts ковариаты бывают двух видов:
past_covariates — данные, которые известны только до момента прогноза (включительно). Пример: фактическая погода за прошлые дни.
future_covariates — данные, известные заранее. Пример: день недели, месяц, запланированные акции.
from darts import TimeSeries, concatenate
from darts.models import LinearRegressionModel
from darts.utils.timeseries_generation import datetime_attribute_timeseries
# Создаём future covariates из календарных признаков
month_series = datetime_attribute_timeseries(
air, attribute="month", one_hot=False
)
year_series = datetime_attribute_timeseries(
air, attribute="year", one_hot=False
)
# Объединяем в мультивариатный covariate
covariates = month_series.stack(year_series)
# Масштабируем covariates тоже
scaler_cov = Scaler()
cov_scaled = scaler_cov.fit_transform(covariates)
# Разделяем
train_cov = cov_scaled[:len(train)]
val_cov = cov_scaled[len(train):] # нужно покрыть горизонт прогноза
# Модель с future covariates
model = LinearRegressionModel(
lags=12, # 12 прошлых значений target
lags_future_covariates=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
output_chunk_length=12
)
model.fit(train, future_covariates=train_cov)
pred = model.predict(
n=36,
future_covariates=cov_scaled # нужен полный ряд до конца прогноза
)
print(f"LinReg + covariates MAPE: {mape(val, pred):.2f}%")
Многие модели поддерживают ковариаты.
А ещё есть фича add_encoders — прямо при создании модели можно сказать «добавь мне месяц и год как ковариаты», и Darts сгенерирует их автоматически:
from darts.models import NBEATSModel
model = NBEATSModel(
input_chunk_length=24,
output_chunk_length=12,
n_epochs=50,
add_encoders={
"cyclic": {"future": ["month"]},
"datetime_attribute": {"future": ["year"]},
}
)
model.fit(train)
pred = model.predict(n=36)
Darts оборачивает несколько нейросетевых архитектур через PyTorch Lightning. Два параметра:
input_chunk_length — сколько прошлых точек модель видит за раз.
output_chunk_length — сколько точек модель прогнозирует за раз.
Если вы вызываете predict(n) с n > output_chunk_length, модель работает авторегрессионно: прогнозирует output_chunk_length точек, подставляет их на вход и прогнозирует следующую порцию. Ошибки [3] при авторегрессии накапливаются.
from darts.models import NBEATSModel, TFTModel, TCNModel
from darts.dataprocessing.transformers import Scaler
scaler = Scaler()
train_scaled = scaler.fit_transform(train)
val_scaled = scaler.transform(val)
# N-BEATS — чистая нейросеть для временных рядов,
# не требует ковариат (но может их использовать)
nbeats = NBEATSModel(
input_chunk_length=24,
output_chunk_length=12,
n_epochs=100,
batch_size=32,
random_state=42,
# force_reset=True, # раскомментируем при повторном обучении
# pl_trainer_kwargs={"accelerator": "gpu"} # для GPU
)
nbeats.fit(train_scaled)
pred_nbeats_scaled = nbeats.predict(n=36)
pred_nbeats = scaler.inverse_transform(pred_nbeats_scaled)
print(f"N-BEATS MAPE: {mape(val, pred_nbeats):.2f}%")
Для более сложных задач есть TFT (Temporal Fusion Transformer). Он поддерживает past_covariates, future_covariates и static_covariates одновременно. Мощная штука для мультисерийных задач (обучение на тысячах рядов сразу):
# TFT — нужны ковариаты для лучших результатов
tft = TFTModel(
input_chunk_length=24,
output_chunk_length=12,
hidden_size=64,
lstm_layers=1,
attention_head_size=4,
dropout=0.1,
n_epochs=50,
batch_size=32,
add_encoders={
"cyclic": {"future": ["month"]},
"datetime_attribute": {"future": ["year"]}
},
random_state=42,
)
tft.fit(train_scaled)
pred_tft_scaled = tft.predict(n=36)
pred_tft = scaler.inverse_transform(pred_tft_scaled)
print(f"TFT MAPE: {mape(val, pred_tft):.2f}%")
Darts позволяет использовать gradient boosting для прогнозирования. Под капотом — создание табличного датасета из лагов целевой переменной и ковариат:
from darts.models import XGBModel, LightGBMModel
# XGBoost с лагами
xgb = XGBModel(
lags=24, # 24 прошлых значения target
lags_future_covariates=[0, 1, 2, 3, 4, 5],
output_chunk_length=12,
add_encoders={
"cyclic": {"future": ["month"]},
"datetime_attribute": {"future": ["year"]}
},
)
xgb.fit(train)
pred_xgb = xgb.predict(n=36)
print(f"XGBoost MAPE: {mape(val, pred_xgb):.2f}%")
# LightGBM — аналогично, часто работает быстрее
lgbm = LightGBMModel(
lags=24,
output_chunk_length=12,
add_encoders={"cyclic": {"future": ["month"]}},
)
lgbm.fit(train)
pred_lgbm = lgbm.predict(n=36)
print(f"LightGBM MAPE: {mape(val, pred_lgbm):.2f}%")
Модели‑деревья в Darts особенно удобны, потому что они обучаются быстро, не требуют масштабирования данных, поддерживают ковариаты, и их результаты можно объяснить через SHAP (Darts это тоже поддерживает).
Разовый split на train/val — это ок для первого знакомства. Но для прода нам нужен бэктестинг (walk‑forward validation): модель обучается на данных до момента T, прогнозирует на горизонт H, потом T сдвигается вперёд на stride шагов — и снова.
from darts.models import ExponentialSmoothing
from darts.metrics import mape
model = ExponentialSmoothing(seasonal_periods=12)
# historical_forecasts — основной метод для бэктестинга
backtest = model.historical_forecasts(
series=air,
start=0.7, # начать с 70% ряда
forecast_horizon=12, # прогнозировать на 12 шагов
stride=1, # сдвигать на 1 шаг каждый раз
retrain=True, # переобучать модель на каждом шаге
verbose=True
)
# backtest — это TimeSeries прогнозов
print(f"Backtest MAPE: {mape(air, backtest):.2f}%")
# Визуализация
import matplotlib.pyplot as plt
air.plot(label="actual")
backtest.plot(label="backtest forecast")
plt.title("Walk-Forward Backtest")
plt.legend()
plt.tight_layout()
plt.savefig("backtest.png", dpi=150)
plt.close()
Для нейросетей retrain=True на каждом шаге слишком дорого. Используем retrain=False (модель обучается один раз) или retrain=10 (переобучение каждые 10 шагов):
# Бэктест нейросети без переобучения на каждом шаге
nbeats = NBEATSModel(
input_chunk_length=24, output_chunk_length=12,
n_epochs=100, random_state=42
)
nbeats.fit(train_scaled)
backtest_nn = nbeats.historical_forecasts(
series=scaler.transform(air),
start=0.7,
forecast_horizon=12,
stride=12, # сдвигаемся на output_chunk_length
retrain=False, # не переобучаем
verbose=True
)
backtest_nn = scaler.inverse_transform(backtest_nn)
print(f"N-BEATS Backtest MAPE: {mape(air, backtest_nn):.2f}%")
Darts предлагает два типа ансамблей. NaiveEnsembleModel — простое среднее прогнозов. RegressionEnsembleModel — обученная регрессия на выходах моделей (stacked generalization):
from darts.models import (
NaiveEnsembleModel,
RegressionEnsembleModel,
ExponentialSmoothing,
Theta,
LinearRegressionModel,
NaiveSeasonal
)
# Наивный ансамбль — среднее двух моделей
naive_ens = NaiveEnsembleModel(
forecasting_models=[
ExponentialSmoothing(seasonal_periods=12),
Theta(theta=2)
]
)
naive_ens.fit(train)
pred_ens = naive_ens.predict(36)
print(f"Naive Ensemble MAPE: {mape(val, pred_ens):.2f}%")
# Regression ensemble — обучает мета-модель поверх прогнозов
reg_ens = RegressionEnsembleModel(
forecasting_models=[
NaiveSeasonal(K=12),
ExponentialSmoothing(seasonal_periods=12),
Theta(theta=2)
],
regression_train_n_points=24 # сколько точек для обучения мета-модели
)
reg_ens.fit(train)
pred_reg_ens = reg_ens.predict(36)
print(f"Regression Ensemble MAPE: {mape(val, pred_reg_ens):.2f}%")
Regression ensemble часто даёт ощутимый прирост, потому что мета‑модель учится взвешивать прогнозы в зависимости от их сильных сторон.
В продакшене часто нужен не точечный прогноз, а доверительный интервал. Darts поддерживает это нативно:
from darts.models import ExponentialSmoothing
model = ExponentialSmoothing(seasonal_periods=12)
model.fit(train)
# num_samples > 1 — вероятностный прогноз
pred_prob = model.predict(36, num_samples=500)
# pred_prob содержит 500 сэмплов прогноза
# Можно извлечь квантили:
q05 = pred_prob.quantile_timeseries(0.05)
q95 = pred_prob.quantile_timeseries(0.95)
import matplotlib.pyplot as plt
air.plot(label="actual")
pred_prob.plot(label="forecast (median + 90% CI)")
plt.legend()
plt.tight_layout()
plt.savefig("probabilistic.png", dpi=150)
plt.close()
Для нейросетей вероятностное прогнозирование включается через likelihood:
from darts.models import NBEATSModel
from darts.utils.likelihood_models import GaussianLikelihood
nbeats_prob = NBEATSModel(
input_chunk_length=24,
output_chunk_length=12,
n_epochs=100,
likelihood=GaussianLikelihood(), # модель прогнозирует mu и sigma
random_state=42
)
nbeats_prob.fit(train_scaled)
pred_prob = nbeats_prob.predict(n=36, num_samples=500)
pred_prob = scaler.inverse_transform(pred_prob)
Darts умеет не только прогнозировать, но и детектить аномалии. Идея: обучаем модель, прогнозируем, сравниваем с фактом — большое расхождение = аномалия:
from darts.ad import QuantileDetector, ForecastingAnomalyModel
from darts.models import ExponentialSmoothing
# Аномалии через прогнозную модель
anomaly_model = ForecastingAnomalyModel(
model=ExponentialSmoothing(seasonal_periods=12),
scorer="difference" # разница между прогнозом и фактом
)
# Обучаем на «нормальных» данных
anomaly_model.fit(train)
# Детектим на новых данных
anomaly_scores = anomaly_model.score(air)
# Бинарный детектор по квантилю
detector = QuantileDetector(high_quantile=0.99)
detector.fit(anomaly_scores)
binary_anomalies = detector.detect(anomaly_scores)
Соберём всё вместе — пайплайн, который можно адаптировать под свою задачу:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from darts import TimeSeries
from darts.datasets import AirPassengersDataset
from darts.dataprocessing.transformers import Scaler
from darts.models import (
ExponentialSmoothing, AutoARIMA, Theta,
NBEATSModel, RegressionEnsembleModel, NaiveSeasonal
)
from darts.metrics import mape, rmse, mae
# 1. Загрузка данных
series = AirPassengersDataset().load()
# 2. Split
HORIZON = 36
train, val = series[:-HORIZON], series[-HORIZON:]
# 3. Масштабирование (для нейросетей)
scaler = Scaler()
train_scaled = scaler.fit_transform(train)
# 4. Обучение нескольких моделей
results = {}
# Классика
for name, model in [
("ExpSmoothing", ExponentialSmoothing(seasonal_periods=12)),
("AutoARIMA", AutoARIMA()),
("Theta", Theta(theta=2)),
]:
model.fit(train)
pred = model.predict(HORIZON)
results[name] = {"pred": pred, "mape": mape(val, pred), "rmse": rmse(val, pred)}
# Нейросеть
nbeats = NBEATSModel(
input_chunk_length=24, output_chunk_length=12,
n_epochs=100, batch_size=32, random_state=42
)
nbeats.fit(train_scaled)
pred_nbeats = scaler.inverse_transform(nbeats.predict(HORIZON))
results["N-BEATS"] = {"pred": pred_nbeats, "mape": mape(val, pred_nbeats),
"rmse": rmse(val, pred_nbeats)}
# Ансамбль
ensemble = RegressionEnsembleModel(
forecasting_models=[
NaiveSeasonal(K=12),
ExponentialSmoothing(seasonal_periods=12),
Theta(theta=2)
],
regression_train_n_points=24
)
ensemble.fit(train)
pred_ens = ensemble.predict(HORIZON)
results["Ensemble"] = {"pred": pred_ens, "mape": mape(val, pred_ens),
"rmse": rmse(val, pred_ens)}
# 5. Сравнение
print(f"{'Model':20s} | {'MAPE':>8s} | {'RMSE':>8s}")
print("-" * 42)
for name, r in sorted(results.items(), key=lambda x: x[1]["mape"]):
print(f"{name:20s} | {r['mape']:7.2f}% | {r['rmse']:8.1f}")
# 6. Визуализация лучшей модели
best_name = min(results, key=lambda x: results[x]["mape"])
best_pred = results[best_name]["pred"]
series.plot(label="Actual")
best_pred.plot(label=f"Best: {best_name}")
plt.title(f"Best model: {best_name} (MAPE: {results[best_name]['mape']:.2f}%)")
plt.legend()
plt.tight_layout()
plt.savefig("forecast_comparison.png", dpi=150)
plt.close()
Darts хорош, когда нужно быстро протестировать десяток моделей на одних и тех же данных с одним и тем же API. Darts не ок, если вам нужен полный контроль над архитектурой нейросети или у вас сильно кастомная задача, где стандартные обёртки только мешают.

Если хотите перейти от экспериментов к промышленным решениям, курс «Машинное обучение. Продвинутый уровень» [4] (Machine Learning. Professional) даёт практику на реальных данных по нейросетям, обработке естественного языка, рекомендательным системам и работе с временными рядами, а также глубокую проработку подготовки данных и автоматизации производственных конвейеров. Это не обзор, а набор отточенных приёмов и упражнений, которые позволят воспроизводимо строить и оптимизировать модели в рабочем коде.
Чтобы узнать больше о формате обучения и задать вопросы преподавателям, приходите на бесплатные уроки:
12 марта, 20:00. «Метод градиентного спуска: лучше гор могут быть только горы…». Записаться [5]
18 марта, 20:00. «Random Forest – мощный метод ансамблирования в ML». Записаться [6]
Автор: badcasedaily1
Источник [7]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/26713
URLs in this post:
[1] Image: https://sourcecraft.dev/
[2] обучением: http://www.braintools.ru/article/5125
[3] Ошибки: http://www.braintools.ru/article/4192
[4] «Машинное обучение. Продвинутый уровень»: https://otus.pw/gsd8/
[5] Записаться: https://otus.pw/ZCA3/
[6] Записаться: https://otus.pw/gFaF/
[7] Источник: https://habr.com/ru/companies/otus/articles/1003098/?utm_campaign=1003098&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.