← Назад к вопросам

Что такое стационарность временных рядов?

1.7 Middle🔥 141 комментариев
#Временные ряды#Статистика и A/B тестирование

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое стационарность временных рядов?

Стационарность (Stationarity) — это свойство временного ряда, при котором его статистические характеристики (среднее, дисперсия, автокорреляция) не зависят от времени. Стационарные ряды проще моделировать, так как их вероятностные свойства остаются постоянными.

Определение

Строгая стационарность: Для любых t₁, t₂, ..., tₙ и сдвига τ совместное распределение (Yₜ₁, Yₜ₂, ..., Yₜₙ) совпадает с (Yₜ₁₊τ, Yₜ₂₊τ, ..., Yₜₙ₊τ).

Слабая стационарность (используется чаще):

  1. E[Yₜ] = μ (среднее постоянно)
  2. Var(Yₜ) = σ² (дисперсия постоянна)
  3. Cov(Yₜ, Yₜ₊ₖ) зависит только от k, а не от t

Типы нестационарности

1. Тренд (Trend)

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Ряд с трендом (нестационарный)
t = np.arange(100)
y_trend = 10 + 0.5 * t + np.random.normal(0, 5, 100)

plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(y_trend)
plt.title('Нестационарный ряд (с трендом)')
plt.ylabel('Y')

# Первая разность (differencing) — делает ряд стационарным
y_diff = np.diff(y_trend)
plt.subplot(1, 2, 2)
plt.plot(y_diff)
plt.title('Первая разность (стационарный ряд)')
plt.ylabel('ΔY')
plt.tight_layout()
plt.show()

2. Сезонность (Seasonality)

# Ряд с сезонностью (нестационарный)
t = np.arange(120)
seasonal = 10 * np.sin(2 * np.pi * t / 12) + np.random.normal(0, 1, 120)

plt.figure(figsize=(10, 3))
plt.plot(seasonal)
plt.title('Ряд с сезонностью (период = 12)')
plt.ylabel('Y')
plt.show()

# Сезонная разность (лаг = 12)
y_seasonal_diff = np.diff(seasonal, n=1)  # Убираем тренд
plt.plot(y_seasonal_diff)
plt.title('После удаления сезонности')
plt.show()

Тестирование стационарности

1. ADF тест (Augmented Dickey-Fuller)

from statsmodels.tsa.stattools import adfuller, kpss
import pandas as pd

df = pd.read_csv('time_series.csv')
y = df['value'].values

# ADF тест (нулевая гипотеза: ряд нестационарный)
adf_result = adfuller(y)

print(f"ADF Статистика: {adf_result[0]:.4f}")
print(f"P-value: {adf_result[1]:.4f}")
print(f"Критические значения:")
for key, value in adf_result[4].items():
    print(f"  {key}: {value:.3f}")

# Интерпретация
if adf_result[1] < 0.05:
    print("\nРяд стационарный (отклоняем H0)")
else:
    print("\nРяд нестационарный (не отклоняем H0)")

2. KPSS тест (дополняет ADF)

# KPSS тест (нулевая гипотеза: ряд стационарный)
kpss_result = kpss(y, regression='c')

print(f"KPSS Статистика: {kpss_result[0]:.4f}")
print(f"P-value: {kpss_result[1]:.4f}")

# Если ADF говорит нестационарный, а KPSS — стационарный,
# то ряд содержит тренд (trend-stationary)

3. Визуальный анализ

from pandas.plotting import autocorrelation_plot
import matplotlib.pyplot as plt

fig, axes = plt.subplots(3, 1, figsize=(12, 8))

# Временной ряд
axes[0].plot(y)
axes[0].set_title('Временной ряд')
axes[0].set_ylabel('Y')

# Автокорреляционная функция (ACF)
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
plot_acf(y, lags=40, ax=axes[1])

# Частная автокорреляционная функция (PACF)
plot_pacf(y, lags=40, ax=axes[2])

plt.tight_layout()
plt.show()

# Интерпретация:
# Стационарный ряд: ACF быстро убывает к нулю
# Нестационарный ряд: ACF медленно убывает

Методы преобразования нестационарного ряда

1. Дифференцирование (Differencing)

# Первая разность
y_diff = np.diff(y, n=1)  # Yₜ - Yₜ₋₁

# Вторая разность
y_diff2 = np.diff(y, n=2)  # ΔYₜ - ΔYₜ₋₁

# Сезонная разность (для периода 12)
y_seasonal_diff = np.diff(y, n=12)  # Yₜ - Yₜ₋₁₂

2. Логарифмирование (для стабилизации дисперсии)

import numpy as np

# Если дисперсия растёт с уровнем ряда
y_log = np.log(y + 1)  # +1 для избежания log(0)

# После лага
y_log_diff = np.diff(np.log(y + 1))

3. Вычитание тренда

from scipy import signal

# Линейный тренд
t = np.arange(len(y))
trend = np.polyfit(t, y, 1)[0] * t + np.polyfit(t, y, 1)[1]
y_detrended = y - trend

# Или через регрессию
from sklearn.linear_model import LinearRegression
X = t.reshape(-1, 1)
model = LinearRegression().fit(X, y)
trend_pred = model.predict(X)
y_detrended = y - trend_pred

4. Удаление сезонности

# Сезонное разложение (STL decomposition)
from statsmodels.tsa.seasonal import seasonal_decompose

decomposition = seasonal_decompose(y, model='additive', period=12)

# Остаток (после удаления тренда и сезонности) обычно стационарен
residue = decomposition.resid

ARIMA и стационарность

from statsmodels.tsa.arima.model import ARIMA
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

# ARIMA(p, d, q)
# d — параметр дифференцирования
# Если ряд нестационарный → d=1 или 2

# Пример: ARIMA(1, 1, 1)
model = ARIMA(y, order=(1, 1, 1))  # Автоматическое дифференцирование
results = model.fit()
print(results.summary())

# d=1 означает, что модель будет дифференцировать ряд перед моделированием

Практические рекомендации

Проверка перед применением ARIMA/SARIMA:

def check_stationarity(y):
    adf_test = adfuller(y)
    kpss_test = kpss(y, regression='c')
    
    print(f"ADF p-value: {adf_test[1]:.4f}")
    print(f"KPSS p-value: {kpss_test[1]:.4f}")
    
    if adf_test[1] < 0.05 and kpss_test[1] > 0.05:
        print("Ряд стационарный ✓")
        return True
    else:
        print("Ряд нестационарный → нужно преобразование")
        return False

if not check_stationarity(y):
    y = np.diff(y, n=1)
    check_stationarity(y)

Когда стационарность важна

  • ARIMA/SARIMA — требуют стационарный ряд
  • Авторегрессионные модели — работают лучше со стационарными
  • Коинтеграция — анализ связей между нестационарными рядами
  • Регрессия временных рядов — без стационарности получаются spurious regression

Стационарность — ключевое свойство при работе с временными рядами в ML и статистике.

Что такое стационарность временных рядов? | PrepBro