Прогнозиране на времеви редове в Python: ARIMA, Prophet и Statsmodels (2026)
Как да прогнозирате времеви редове в Python с ARIMA, SARIMA и Prophet: от ADF тест и стационарност до walk-forward валидация и MAPE. С готов код за statsmodels 0.14+ и Prophet 1.1.5+, сравнителна таблица и практически съвети.
Прогнозиране на времеви редове в Python е процесът на предсказване на бъдещи стойности от подредена във времето последователност чрез статистически модели като ARIMA/SARIMA (statsmodels), адитивния модел Prophet на Meta и експоненциалното изглаждане Holt-Winters. Изборът зависи от три неща: наличие на тренд и сезонност, дължина на историята и толерантност към предположения за стационарност. В това ръководство ще минем от суров DataFrame до валидирана прогноза с walk-forward оценка, като покрия и метриките, които не лъжат: MAE, RMSE и MAPE.
ARIMA/SARIMA изисква стационарен ред. Проверявайте с ADF тест преди фитване, а разликата (diff) премахва тренда без загуба на информация за краткосрочна прогноза.
Prophet 1.1.5+ работи най-добре при силна годишна или седмична сезонност и празници; не изисква стационарност и толерира липсващи стойности.
Holt-Winters (ExponentialSmoothing) е подходящ за къси серии (под 200 наблюдения) с ясен адитивен или мултипликативен сезонен компонент.
Никога не използвайте train_test_split. Само walk-forward валидация (TimeSeriesSplit) запазва времевата подредба и дава реалистичен MAPE.
Лаговите променливи и подвижните средни често надминават чистите статистически модели, когато се комбинират с XGBoost или LightGBM.
Официалната препоръка на Hyndman (2021, FPP3) е да сравнявате поне два модела с наивна базова линия (naive или seasonal_naive). Иначе не знаете дали моделът ви върши работа.
Какво е времеви ред и защо изборът на модел има значение
Времеви ред (time series) е поредица от наблюдения, индексирани в равномерни времеви интервали: дневни продажби, часови сензорни отчети, месечен инфлационен индекс. За разлика от табличните данни, тук редът на редовете носи информация. Наблюдение t зависи от t-1, а често и от t-52 (годишна сезонност при седмични данни). Това нарушение на предположението за независимо и еднакво разпределени (i.i.d.) наблюдения прави повечето класически ML техники неприложими без адаптация.
Честно казано, в практиката си като data scientist разделям времевите редове на три категории, преди дори да отворя Jupyter. Първо: трендови редове с ясна дългосрочна посока (цена на актив). Второ: сезонни редове с повтарящ се вътрешногодишен модел (търговия на дребно). Трето: шумни редове без ясна структура, които често се обясняват от външни фактори (регресори). За първите две ARIMA/SARIMA или Prophet работят добре. За третия тип по-често се обръщам към градиентно бустиране с лагови признаци.
И една практическа граница, която научих по трудния начин: класическите статистически модели се борят с дълги хоризонти (над 10% от историята). За стратегическо планиране в години напред обикновено съчетавам експертни допускания с прогноза, вместо да разчитам сляпо на екстраполация.
Подготовка на данните и проверка на стационарност
Стационарност означава, че статистическите свойства на реда (средно, дисперсия, автокорелация) не се променят във времето. ARIMA изисква стационарност, иначе коефициентите са нестабилни. Стандартният тест е Augmented Dickey-Fuller (ADF). Нулева хипотеза е "редът има единичен корен" (не е стационарен). Ако p-стойността е под 0.05, отхвърляме нулевата хипотеза и приемаме стационарност.
import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import adfuller
# Зареждане на примерни дневни продажби
df = pd.read_csv("sales.csv", parse_dates=["date"], index_col="date")
df = df.asfreq("D").interpolate() # регулярен дневен индекс без пропуски
def check_stationarity(series: pd.Series) -> None:
result = adfuller(series.dropna(), autolag="AIC")
print(f"ADF статистика: {result[0]:.4f}")
print(f"p-стойност: {result[1]:.4f}")
print(f"Стационарен: {'да' if result[1] < 0.05 else 'не'}")
check_stationarity(df["sales"])
# Ако не е стационарен, първа разлика:
df["sales_diff"] = df["sales"].diff()
check_stationarity(df["sales_diff"])
След тестването визуализирайте автокорелационната функция (ACF) и частичната автокорелационна функция (PACF). Те подсказват реда p и q на ARIMA(p, d, q). Ако PACF рязко спада след лаг 2, започнете с AR(2). За по-задълбочен процес по подготовка на признаци вижте инженеринг на признаци с Pandas и Scikit-learn.
ARIMA и SARIMA със statsmodels 0.14+
ARIMA (AutoRegressive Integrated Moving Average) е тройка от компоненти. AR(p) моделира зависимост от предходни стойности, I(d) прилага d пъти разлика за постигане на стационарност, а MA(q) моделира зависимост от предходни шумове. SARIMA добавя сезонен блок (P, D, Q, s), където s е сезонният период (7 за седмична, 12 за месечна годишна сезонност).
from statsmodels.tsa.statespace.sarimax import SARIMAX
# SARIMAX е препоръчаният API в statsmodels 0.14+
# ARIMA(1, 1, 1) с сезонен компонент (1, 1, 1, 7) за седмична сезонност
model = SARIMAX(
df["sales"],
order=(1, 1, 1),
seasonal_order=(1, 1, 1, 7),
enforce_stationarity=False,
enforce_invertibility=False,
)
fitted = model.fit(disp=False)
print(fitted.summary())
# Прогноза за следващите 30 дни с доверителен интервал 95%
forecast = fitted.get_forecast(steps=30)
mean = forecast.predicted_mean
ci = forecast.conf_int(alpha=0.05)
За автоматичен избор на (p, d, q) използвайте pmdarima.auto_arima, който минимизира AIC/BIC чрез поетапно търсене. За големи каталози с хиляди редове обаче auto_arima е бавен. В такива случаи фиксирам разумни граници и паралелизирам с joblib. Официалната документация на statsmodels за time series analysis обяснява различните варианти на state-space моделите (VARMAX, dynamic factor models) за многомерни редове.
Прогнозиране с Prophet: бърз старт
Prophet, разработен от екипа на Meta (Taylor & Letham, 2018), е адитивен модел y(t) = g(t) + s(t) + h(t) + ε, където g е тренд, s е множествена сезонност (година, седмица, ден), а h е ефект от празници. Той е "opinionated": не изисква стационарност, толерира липсващи стойности и няколко изхвърляния, и е много бърз за фитване (Stan backend).
from prophet import Prophet
# Prophet изисква колоните да се казват точно "ds" и "y"
prophet_df = df.reset_index().rename(columns={"date": "ds", "sales": "y"})
model = Prophet(
yearly_seasonality=True,
weekly_seasonality=True,
daily_seasonality=False,
changepoint_prior_scale=0.05, # по-високо = по-гъвкав тренд
seasonality_mode="multiplicative",
)
# Добавяне на български официални празници (Prophet 1.1.5+ поддържа "BG")
model.add_country_holidays(country_name="BG")
model.fit(prophet_df)
future = model.make_future_dataframe(periods=30, freq="D")
forecast = model.predict(future)
print(forecast[["ds", "yhat", "yhat_lower", "yhat_upper"]].tail())
Експоненциално изглаждане Holt-Winters
Holt-Winters е тройно експоненциално изглаждане: интуитивен и бърз метод, който запазва компонентите ниво, тренд и сезонност чрез рекурсивни средни. Работи изключително добре при къси редове (месечни данни за 2 до 3 години), където ARIMA се затруднява с недостатъчно наблюдения за оценка на автокорелационната структура.
from statsmodels.tsa.holtwinters import ExponentialSmoothing
model = ExponentialSmoothing(
df["sales"],
trend="add", # адитивен тренд
seasonal="mul", # мултипликативна сезонност
seasonal_periods=7, # седмичен период
initialization_method="estimated",
)
fit = model.fit(optimized=True, use_brute=True)
forecast = fit.forecast(steps=30)
Правилото ми на практика е просто. Ако имате под 200 наблюдения и ясна сезонност, започнете с Holt-Winters като базова линия. Ако имате над 500 наблюдения, тествайте SARIMA. Ако имате множествена сезонност (например часова плюс седмична плюс годишна), вземете Prophet или TBATS.
Инженеринг на признаци за времеви редове
Когато преминете от чисто статистически модели към ML подход (XGBoost, LightGBM, CatBoost), нуждаете се от таблични признаци, извлечени от времевата структура. Най-полезните категории са: лагови променливи (стойност преди k периода), подвижни средни (rolling mean/std за 7, 14, 30 дни), календарни признаци (ден от седмицата, месец, тримесечие, флаг за празник), и Fourier компоненти за плавна сезонност.
def create_time_features(df: pd.DataFrame, target: str, lags: list[int]) -> pd.DataFrame:
"""Създава лагови и подвижни признаци за градиентно бустиране."""
out = df.copy()
# Лагове
for lag in lags:
out[f"lag_{lag}"] = out[target].shift(lag)
# Подвижни средни (използваме shift(1), за да избегнем изтичане)
for window in [7, 14, 30]:
out[f"rmean_{window}"] = out[target].shift(1).rolling(window).mean()
out[f"rstd_{window}"] = out[target].shift(1).rolling(window).std()
# Календарни признаци
out["dow"] = out.index.dayofweek
out["month"] = out.index.month
out["quarter"] = out.index.quarter
out["is_month_end"] = out.index.is_month_end.astype(int)
return out.dropna()
features = create_time_features(df, "sales", lags=[1, 7, 14, 28])
Walk-forward валидация и метрики
Класическото случайно разделяне train_test_split нарушава времевата подредба и води до изтичане. Правилният подход е walk-forward валидация. Започнете с ранен прозорец за обучение, прогнозирайте следващите h стъпки, изместете прозореца напред и повторете. scikit-learn предоставя TimeSeriesSplit, а Prophet има вграден cross_validation.
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import mean_absolute_error, mean_squared_error
def mape(y_true, y_pred):
return np.mean(np.abs((y_true - y_pred) / y_true)) * 100
tscv = TimeSeriesSplit(n_splits=5, test_size=30, gap=0)
scores = []
for fold, (train_idx, test_idx) in enumerate(tscv.split(df), start=1):
train, test = df.iloc[train_idx], df.iloc[test_idx]
model = SARIMAX(train["sales"], order=(1, 1, 1),
seasonal_order=(1, 1, 1, 7)).fit(disp=False)
pred = model.forecast(steps=len(test))
scores.append({
"fold": fold,
"MAE": mean_absolute_error(test["sales"], pred),
"RMSE": np.sqrt(mean_squared_error(test["sales"], pred)),
"MAPE": mape(test["sales"].values, pred.values),
})
print(pd.DataFrame(scores))
Изборът на метрика зависи от бизнес контекста. MAE (mean absolute error) е в оригиналните единици и е устойчив на изхвърляния. RMSE наказва големите грешки квадратично, така че го предпочитайте, когато голяма грешка е несъразмерно скъпа. MAPE е процентен и позволява сравнение между различни редове, но е нестабилен при стойности близо до нула. За допълнителен статистически анализ на грешките вижте тестване на хипотези със SciPy и Statsmodels.
ARIMA срещу Prophet: сравнителна таблица
Един от най-често задаваните въпроси в общността е кой от двата инструмента да се избере. Няма универсален отговор, но следната таблица обобщава компромисите въз основа на бенчмарковете от M4 състезанието (2018) и последващите отраслови изследвания.
Характеристика
SARIMA (statsmodels)
Prophet (Meta)
Предположение за стационарност
Изисква се (или диференциране)
Не се изисква
Толерантност към липсващи стойности
Ниска, попълвайте предварително
Висока, приема NaN директно
Множествена сезонност
Ограничена, един сезонен блок
Вградена (година + седмица + ден)
Празници и събития
Ръчно като екзогенни регресори
Вградено (add_country_holidays)
Интерпретируемост
Висока, статистически коефициенти
Средна, компонентен график
Скорост при 10 000 реда
~5 сек за (1,1,1)(1,1,1,7)
~2 сек с MCMC изключен
Дълги хоризонти (>90 дни)
Разширяване на доверителния интервал
По-стабилен благодарение на changepoint модел
Крива на обучение
Стръмна (Box-Jenkins методология)
Ниска, plug-and-play
Чести грешки и добри практики за 2026
След години на прегледи на модели в производство виждам едни и същи грешки да се повтарят. Първо: пренебрегване на базовата линия. Преди да похвалите SARIMA с MAPE 8%, сравнете с seasonal_naive (стойността от преди s периода). Често разликата е под 1 процентен пункт, което не оправдава сложността. Rob Hyndman обръща специално внимание на този принцип в Forecasting: Principles and Practice (3rd ed.), безплатния онлайн учебник, който смятам за задължителен.
Второ: рефитване веднъж, прогнозиране цяла година. Реалният свят се променя и концепт дрифтът е норма. Настройте pipeline, който рефитва модела веднъж дневно или седмично, в зависимост от волатилността. Трето: липса на probabilistic forecasts. Единна точкова оценка не помага за планиране на инвентар. Използвайте predicted_mean заедно с conf_int() и оптимизирайте бизнес решенията върху разпределението на прогнозата, не върху средното.
Четвърто: сляпо доверие в auto_arima. Автоматизираният избор минимизира AIC, но AIC не съответства на бизнес метриката. Винаги валидирайте с holdout, дори когато auto_arima ви даде "най-добрия" модел. Официалните насоки на Prophet подчертават същия принцип: моделът не замества експертно познание за домейна.
За 2026 г. препоръчвам да следите развитието на невронните архитектури за времеви редове, като Temporal Fusion Transformers (TFT), N-BEATS и TimesFM. Те надминават класическите модели върху дълги последователности и множествени времеви редове, но за повечето бизнес приложения с една серия и няколко хиляди наблюдения ARIMA/Prophet остават конкурентни и много по-евтини за поддръжка.
Често задавани въпроси
Коя е най-добрата библиотека за прогнозиране на времеви редове в Python през 2026 г.?
Няма единствен победител. За класически едномерни редове с ясна сезонност изберете statsmodels (SARIMAX) или Prophet. За многомерни редове с външни регресори погледнете sktime или darts. За дълги хоризонти и много серии едновременно вземете невронни модели като N-BEATS през neuralforecast.
Как проверявам дали времеви ред е стационарен?
Използвайте Augmented Dickey-Fuller тест (statsmodels.tsa.stattools.adfuller). Ако p-стойността е под 0.05, редът е стационарен. За надеждност допълвайте с KPSS тест. Двата теста заедно дават по-точна диагноза, особено при сезонни редове.
ARIMA срещу Prophet, кой да избера?
Изберете Prophet, когато имате множествена сезонност, празници или липсващи данни и искате бърз резултат. Изберете SARIMA, когато редът ви е стационарен (или лесно се прави такъв) и се нуждаете от статистически интерпретируеми коефициенти. Винаги сравнявайте и двата с seasonal_naive базова линия.
Как да управлявам сезонност в данните?
Има три подхода: (1) сезонни оператори в SARIMA чрез seasonal_order, (2) декомпозиция със STL (statsmodels.tsa.seasonal.STL) и моделиране на остатъка, (3) Fourier признаци (синус и косинус) като регресори в ML модел. Prophet прави декомпозиция автоматично.
Как да оценя качеството на прогнозата?
Използвайте walk-forward валидация с TimeSeriesSplit, изчислете MAE, RMSE и MAPE на holdout сетовете, и сравнете с наивен модел (стойността от предходния период или от предходния сезон). Ако не побеждавате наивния модел с поне 10%, вашият модел не носи стойност.
Практическо ръководство за Polars 1.x в Python: мързелив оптимизатор, streaming engine, миграция от Pandas и интеграция със scikit-learn с реални примери и бенчмаркове за 2026.
Практическо ръководство за Optuna 4.x в Python: TPE и GP samplers, pruning, multi-objective оптимизация и работещи примери със Scikit-learn и XGBoost за 2026 г.
Научете как да създавате ефективни признаци за ML модели с Pandas 3.0 и Scikit-learn. Практическо ръководство с код за кодиране, скалиране, TargetEncoder, ColumnTransformer и селекция на признаци.