Моделі прогнозування часових рядів у Python: що обрати для курсової чи диплому

Якщо ви читаєте цю статтю, то скоріш за все вам потрібно зробити проект з прогнозування. Може це курсова з аналізу продажів, може диплом з передбачення курсу криптовалюти, а може магістерська з прогнозування енергоспоживання. Неважливо що саме — підходи будуть схожі, і я хочу розповісти про них простою мовою, без зайвого академічного пафосу.
Я сам пройшов через це ще в університеті, потім допомагав друзям, а зараз це частина моєї роботи. Тому знаю типові помилки і знаю що реально працює.
Спочатку трохи теорії (коротко, обіцяю)
Часовий ряд — це просто послідовність значень у часі. Температура щогодини, ціна акцій щодня, кількість замовлень щомісяця. Здавалось би, що тут складного — візьми попередні значення і екстраполюй. Але ні, все цікавіше.
У будь-якому часовому ряді є кілька компонентів. Тренд — це загальний напрямок руху, вгору чи вниз. Сезонність — регулярні коливання, як продажі морозива влітку. І шум — випадкові коливання, які ніяк не передбачиш.
Задача моделі — розділити ці компоненти і зрозуміти, який тренд і яка сезонність будуть у майбутньому. Шум, зрозуміло, передбачити неможливо, тому ідеального прогнозу не буває.
ARIMA — стара школа, яка все ще тримається
ARIMA розшифровується як AutoRegressive Integrated Moving Average. Звучить страшно, але ідея проста. Модель дивиться на попередні значення ряду і на попередні помилки прогнозу, і на основі цього робить новий прогноз.
Головна перевага ARIMA — вона працює на невеликих даних. Якщо у вас
Ось як це виглядає в коді. Спочатку імпортуємо бібліотеки:
import pandas as pd from statsmodels.tsa.arima.model import ARIMA from sklearn.metrics import mean_absolute_error import numpy as np
Завантажуємо дані і ділимо на train/test:
df = pd.read_csv('sales_data.csv', parse_dates=['date'], index_col='date')
train = df[:-30]
test = df[-30:]
Створюємо модель. Параметри (5,1,2) означають: дивимось на 5 попередніх значень, робимо одне диференціювання для видалення тренду, враховуємо 2 попередні помилки:
model = ARIMA(train['sales'], order=(5, 1, 2)) fitted_model = model.fit() forecast = fitted_model.forecast(steps=30)
Як обрати ці параметри? Можна мучитись з ACF і PACF графіками, а можна просто використати auto_arima з бібліотеки pmdarima — вона сама підбере оптимальні значення:
from pmdarima import auto_arima auto_model = auto_arima(train['sales'], seasonal=True, m=7, suppress_warnings=True) print(auto_model.summary())
Чесно кажучи, для курсової цього достатньо. Показали що знаєте ARIMA, підібрали параметри, порахували похибку — викладач задоволений.
Prophet — коли потрібно швидко і красиво
Prophet зробили в Facebook (тепер Meta) для внутрішнього бізнес-прогнозування. Потім викотили у відкритий доступ, і студенти по всьому світу сказали «дякую».
Чому Prophet такий популярний? Бо він вимагає мінімум зусиль. Даєш йому дві колонки — дату і значення — і він сам розбирається з трендом, сезонністю, святами. І ще й графіки малює гарні, які можна прямо в презентацію вставляти.
Код максимально простий:
from prophet import Prophet
import pandas as pd
df = pd.read_csv('sales_data.csv')
df = df.rename(columns={'date': 'ds', 'sales': 'y'})
model = Prophet(yearly_seasonality=True, weekly_seasonality=True)
model.fit(df[:-30])
future = model.make_future_dataframe(periods=30)
forecast = model.predict(future)
fig = model.plot(forecast)
І все! Серйозно, це весь код для базового прогнозу.
Крута фіча Prophet — можна додати свята. Модель зрозуміє, що 8 березня чи Новий рік впливають на продажі:
ua_holidays = pd.DataFrame({
'holiday': 'ukrainian_holidays',
'ds': pd.to_datetime(['2025-01-01', '2025-01-07', '2025-03-08', '2025-08-24', '2025-12-25']),
'lower_window': -1,
'upper_window': 1
})
model = Prophet(holidays=ua_holidays)
Мінус Prophet — він не дуже любить високочастотні дані. Якщо у вас погодинні або похвилинні спостереження, краще дивитись в бік інших рішень.
XGBoost — коли є купа додаткових факторів
ARIMA і Prophet працюють тільки з самим часовим рядом. А що робити, якщо у вас є додаткова інформація? Наприклад, ви прогнозуєте продажі, і знаєте коли були рекламні кампанії, яка була погода, чи був це вихідний день.
Тут на сцену виходить XGBoost і подібні моделі градієнтного бустингу. Вони не розуміють концепцію «часового ряду» — для них це просто таблиця з колонками. Тому ваша задача — створити правильні ознаки.
Ось типовий набір ознак для часового ряду:
def create_features(df): df = df.copy() # Базові часові ознаки df['dayofweek'] = df.index.dayofweek df['month'] = df.index.month df['is_weekend'] = df['dayofweek'].isin([5, 6]).astype(int) # Лагові ознаки — попередні значення df['lag_1'] = df['sales'].shift(1) df['lag_7'] = df['sales'].shift(7) df['lag_30'] = df['sales'].shift(30) # Ковзні середні df['rolling_mean_7'] = df['sales'].shift(1).rolling(7).mean() df['rolling_mean_30'] = df['sales'].shift(1).rolling(30).mean() return df
Зверніть увагу на shift(1) перед rolling — це важливо! Без нього ви випадково «підглянете» в майбутнє і отримаєте нереалістично хороші результати на тесті, а потім модель буде працювати погано на реальних даних.
Далі стандартний XGBoost:
from xgboost import XGBRegressor df = create_features(df) df = df.dropna() feature_cols = ['dayofweek', 'month', 'is_weekend', 'lag_1', 'lag_7', 'lag_30', 'rolling_mean_7', 'rolling_mean_30'] X = df[feature_cols] y = df['sales'] X_train, X_test = X[:-30], X[-30:] y_train, y_test = y[:-30], y[-30:] model = XGBRegressor(n_estimators=500, max_depth=6, learning_rate=0.05) model.fit(X_train, y_train) predictions = model.predict(X_test)
Бонус XGBoost — можна подивитись важливість ознак. Це дуже корисно для розділу «аналіз результатів» у дипломі:
importance = pd.DataFrame({
'feature': feature_cols,
'importance': model.feature_importances_
}).sort_values('importance', ascending=False)
print(importance)
До речі, є ще LightGBM — це по суті швидша версія XGBoost. На великих датасетах працює в рази швидше, а точність часто навіть краща. Синтаксис майже ідентичний:
import lightgbm as lgb model = lgb.LGBMRegressor(n_estimators=500, max_depth=6, learning_rate=0.05) model.fit(X_train, y_train)
Якщо ваш датасет більше 50 тисяч рядків — однозначно беріть LightGBM.
Ensemble — комбінуємо моделі для кращого результату
Окремо хочу сказати про ensemble підхід. Ідея проста: кожна модель має свої сильні і слабкі сторони. ARIMA добре ловить тренд, Prophet — сезонність, XGBoost — вплив зовнішніх факторів. Якщо їх скомбінувати, результат часто кращий ніж у будь-якої окремої моделі.
Найпростіший варіант — просто усереднити прогнози:
ensemble_pred = (pred_arima + pred_prophet + pred_xgb) / 3
Трохи розумніший варіант — зважене усереднення. Даємо більшу вагу моделі, яка краще показала себе на валідації:
# Припустимо XGBoost мав найменшу помилку weights = [0.25, 0.25, 0.5] ensemble_pred = weights[0]*pred_arima + weights[1]*pred_prophet + weights[2]*pred_xgb
Ще крутіший варіант — stacking. Тренуємо окрему модель, яка вчиться комбінувати прогнози базових моделей:
from sklearn.linear_model import Ridge
stacking_features = pd.DataFrame({
'arima': pred_arima_validation,
'prophet': pred_prophet_validation,
'xgb': pred_xgb_validation
})
meta_model = Ridge()
meta_model.fit(stacking_features, y_validation)
Для магістерської stacking — це майже обов’язкова тема. Комісія любить коли показуєш щось складніше за просте усереднення.
LSTM — нейронки для тих, хто хоче вразити комісію
LSTM (Long Short-Term Memory) — це тип рекурентної нейронної мережі, який вміє запам’ятовувати довгострокові залежності. Звучить круто, і для магістерської це справді може бути плюсом.
Але є нюанси. LSTM потребує багато даних — мінімум кілька тисяч спостережень, краще десятки тисяч. На типових студентських датасетах з 500 рядків LSTM буде працювати гірше за Prophet.
Якщо все ж хочете спробувати, ось базовий приклад:
import numpy as np import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from sklearn.preprocessing import MinMaxScaler # Нормалізація даних — для нейронок це обов'язково scaler = MinMaxScaler() scaled_data = scaler.fit_transform(df[['sales']]) # Створюємо послідовності: кожен приклад — це 60 попередніх днів def create_sequences(data, seq_length): X, y = [], [] for i in range(len(data) - seq_length): X.append(data[i:i+seq_length]) y.append(data[i+seq_length]) return np.array(X), np.array(y) X, y = create_sequences(scaled_data, 60) # Архітектура мережі model = Sequential([ LSTM(64, return_sequences=True, input_shape=(60, 1)), Dropout(0.2), LSTM(32), Dropout(0.2), Dense(1) ]) model.compile(optimizer='adam', loss='mse') model.fit(X_train, y_train, epochs=100, batch_size=32, validation_split=0.1)
Головне з LSTM — не забудьте зробити inverse_transform для результатів, щоб повернути їх до оригінального масштабу.
Що обрати для вашого проекту?
Якщо коротко:
Курсова робота — Prophet. Швидко, просто, гарні графіки. Додайте порівняння з наївним прогнозом (просто взяти минуле значення) і ARIMA — цього достатньо.
Бакалаврський диплом — Prophet + ARIMA + XGBoost. Покажіть, що вмієте працювати з різними підходами, порівняйте їх на ваших даних.
Магістерська — все вищеперелічене плюс LSTM. Додайте ensemble (комбінацію моделей), аналіз важливості ознак, можливо ablation study.
Як оцінювати якість прогнозу
Це важливо! Багато студентів будують модель, але забувають нормально оцінити результат.
MAE (Mean Absolute Error) — середня абсолютна помилка. Якщо MAE = 100, це означає що в середньому модель помиляється на 100 одиниць. Інтуїтивно зрозуміла метрика.
RMSE (Root Mean Squared Error) — корінь середньоквадратичної помилки. Більше «штрафує» за великі помилки. Якщо є викиди в даних, RMSE буде сильно вищим за MAE.
MAPE (Mean Absolute Percentage Error) — помилка у відсотках. Корисно коли порівнюєте різні датасети або коли значення сильно відрізняються за масштабом.
from sklearn.metrics import mean_absolute_error, mean_squared_error
mae = mean_absolute_error(y_test, predictions)
rmse = np.sqrt(mean_squared_error(y_test, predictions))
mape = np.mean(np.abs((y_test - predictions) / y_test)) * 100
print(f'MAE: {mae:.2f}')
print(f'RMSE: {rmse:.2f}')
print(f'MAPE: {mape:.2f}%')
І обов’язково побудуйте графік «прогноз vs реальність» — це перше що дивиться викладач.
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.plot(y_test.index, y_test.values, label='Реальні значення', color='blue')
plt.plot(y_test.index, predictions, label='Прогноз', color='red', linestyle='--')
plt.xlabel('Дата')
plt.ylabel('Значення')
plt.title('Порівняння прогнозу з реальними даними')
plt.legend()
plt.savefig('forecast_comparison.png', dpi=150)
plt.show()
Такий графік одразу показує де модель справляється, а де провалюється. Плюс виглядає професійно в дипломі.
Крос-валідація для часових рядів
Окрема тема — як правильно валідувати модель. Звичайний train_test_split тут не підходить, бо він перемішує дані випадково. А в часових рядах порядок важливий!
Правильний підхід — TimeSeriesSplit. Він створює кілька «вікон» для валідації, кожне наступне більше за попереднє:
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)
scores = []
for train_idx, test_idx in tscv.split(X):
X_train_cv, X_test_cv = X.iloc[train_idx], X.iloc[test_idx]
y_train_cv, y_test_cv = y.iloc[train_idx], y.iloc[test_idx]
model = XGBRegressor(n_estimators=100, max_depth=4)
model.fit(X_train_cv, y_train_cv)
pred = model.predict(X_test_cv)
score = mean_absolute_error(y_test_cv, pred)
scores.append(score)
print(f'Середня MAE по фолдах: {np.mean(scores):.2f} +/- {np.std(scores):.2f}')
Якщо стандартне відхилення велике — модель нестабільна, потрібно спрощувати або збирати більше даних.
Робота з пропусками в даних
Реальні дані майже завжди мають пропуски. Сервер впав, датчик зламався, забули записати — причин багато. Як з цим боротися?
Найпростіше — інтерполяція:
df['sales'] = df['sales'].interpolate(method='linear')
Для даних з сезонністю краще працює сезонна інтерполяція:
df['sales'] = df['sales'].interpolate(method='time')
Prophet взагалі сам справляється з пропусками — просто не включайте ці рядки в датафрейм, і він розбереться.
А от для ARIMA пропуски — це проблема. Або заповнюйте їх заздалегідь, або використовуйте спеціальні версії типу ARIMAX.
Типові помилки, яких краще уникати
Data leakage — випадкове використання майбутньої інформації при навчанні. Типовий приклад: забули зробити shift() при створенні ковзного середнього.
Неправильний split — для часових рядів не можна робити випадковий train/test split! Тільки хронологічний: спочатку навчання на старих даних, потім тест на нових.
Занадто оптимістичні результати — якщо ваша модель показує MAPE 0.5%, це підозріло. Перевірте чи немає витоку даних.
Ігнорування baseline — завжди порівнюйте з наївним прогнозом. Якщо ваша складна модель ледве перемагає просте «завтра буде як сьогодні», то щось не так.
На закінчення
Прогнозування — це не магія. Це послідовний процес: зрозуміти дані, обрати підходящу модель, налаштувати її, оцінити результат, покращити. І так по колу.
Не бійтесь експериментувати. Спробуйте кілька моделей, порівняйте їх, зробіть висновки. Саме це хочуть бачити викладачі — не ідеальний результат, а розуміння процесу.
Успіхів з вашим проектом!
---
Якщо стаття була корисною і ви хочете заглибитись в тему — пишіть в коментарях, розкажу більше про конкретні аспекти.

9 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів