Darts: библиотека для временных рядов. darts.. darts. python.

В Python хватает инструментов для работы с временными рядами, но обычно приходится жонглировать тремя‑четырьмя пакетами с разными API. Darts — библиотека, которая собирает всё в одном месте: статистические модели, градиентный бустинг, нейросети — и работает по знакомой схеме fit() / predict(). Сегодня разберём её подробно: что умеет, где удобна, как использовать в задачах.

Установка

# Минимальная установка — статистические модели
pip install darts

# Полная установка — включая PyTorch-модели и Prophet
pip install "u8darts[all]"

# Если хотите точечно:
pip install "u8darts[torch]"    # нейросети
pip install "u8darts[prophet]"  # Facebook Prophet
Darts: библиотека для временных рядов - 1

Рекомендую ставить в чистый 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()    # производство молока
Darts: библиотека для временных рядов - 2

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: библиотека для временных рядов - 3

Первые модели: от наивных до классических

Все модели в 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}")
Darts: библиотека для временных рядов - 4

Результат (на 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
Darts: библиотека для временных рядов - 5

AutoARIMA на дефолтных параметрах не блещет, это нормально, у него свои сильные стороны на других данных. ExponentialSmoothing хорошо ловит и тренд, и сезонность.

Предобработка

Перед обучением нейросетей данные обычно нужно нормализовать. В 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: библиотека для временных рядов - 6

Трансформеры в 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}%")
Darts: библиотека для временных рядов - 7

Многие модели поддерживают ковариаты.

А ещё есть фича 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: библиотека для временных рядов - 8

Нейросети: N‑BEATS, TFT и компания

Darts оборачивает несколько нейросетевых архитектур через PyTorch Lightning. Два параметра:

  • input_chunk_length — сколько прошлых точек модель видит за раз.

  • output_chunk_length — сколько точек модель прогнозирует за раз.

Если вы вызываете predict(n) с n > output_chunk_length, модель работает авторегрессионно: прогнозирует output_chunk_length точек, подставляет их на вход и прогнозирует следующую порцию. Ошибки при авторегрессии накапливаются.

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}%")
Darts: библиотека для временных рядов - 9

Для более сложных задач есть 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: библиотека для временных рядов - 10

XGBoost и LightGBM: деревья для временных рядов

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: библиотека для временных рядов - 11

Модели‑деревья в 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()
Darts: библиотека для временных рядов - 12

Для нейросетей 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: библиотека для временных рядов - 13

Ансамбли: комбинируем модели

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}%")
Darts: библиотека для временных рядов - 14

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()
Darts: библиотека для временных рядов - 15

Для нейросетей вероятностное прогнозирование включается через 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: библиотека для временных рядов - 16

Обнаружение аномалий

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)
Darts: библиотека для временных рядов - 17

Полный пайплайн

Соберём всё вместе — пайплайн, который можно адаптировать под свою задачу:

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: библиотека для временных рядов - 18

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

Darts: библиотека для временных рядов - 19

Если хотите перейти от экспериментов к промышленным решениям, курс «Машинное обучение. Продвинутый уровень» (Machine Learning. Professional) даёт практику на реальных данных по нейросетям, обработке естественного языка, рекомендательным системам и работе с временными рядами, а также глубокую проработку подготовки данных и автоматизации производственных конвейеров. Это не обзор, а набор отточенных приёмов и упражнений, которые позволят воспроизводимо строить и оптимизировать модели в рабочем коде.

Чтобы узнать больше о формате обучения и задать вопросы преподавателям, приходите на бесплатные уроки:

  • 12 марта, 20:00. «Метод градиентного спуска: лучше гор могут быть только горы…». Записаться

  • 18 марта, 20:00. «Random Forest – мощный метод ансамблирования в ML». Записаться

Автор: badcasedaily1

Источник

  • Запись добавлена: 06.03.2026 в 14:33
  • Оставлено в
Rambler's Top100