Анализ временных рядов — что нужно знать аналитику

Что такое временные ряды

Временной ряд — это последовательность значений, упорядоченных по времени. DAU по дням, выручка по неделям, конверсия по месяцам — всё это временные ряды. Аналитик работает с ними постоянно, даже если не называет это «анализом временных рядов».

Ключевое отличие от обычных данных: порядок имеет значение. Если перемешать строки в таблице пользователей — ничего не изменится. Если перемешать строки временного ряда — данные потеряют смысл.

Компоненты временного ряда

Любой временной ряд можно разложить на компоненты:

Тренд (trend) — долгосрочное направление: рост, падение или стагнация. DAU растёт на 5% в месяц — это тренд.

Сезонность (seasonality) — повторяющиеся паттерны с фиксированным периодом. Каждый понедельник DAU падает, а в пятницу — растёт. Каждый декабрь выручка выше из-за праздников. Период может быть дневным, недельным, месячным, годовым.

Остаток (residual / noise) — всё, что не объясняется трендом и сезонностью. Случайные колебания, разовые акции, баги.

Формально:

  • Аддитивная модель: Y = Тренд + Сезонность + Остаток
  • Мультипликативная модель: Y = Тренд x Сезонность x Остаток

Аддитивная подходит, когда амплитуда сезонности постоянна. Мультипликативная — когда амплитуда растёт вместе с уровнем ряда (чем выше выручка, тем больше сезонные колебания в абсолютных числах).

Декомпозиция в Python

Python с библиотекой statsmodels делает декомпозицию в три строки:

import numpy as np
import pandas as pd
from statsmodels.tsa.seasonal import seasonal_decompose
import matplotlib.pyplot as plt

# Генерируем пример: DAU с трендом и недельной сезонностью
dates = pd.date_range("2025-01-01", periods=180, freq="D")
trend = pd.Series(range(180)) * 10 + 5000
seasonality = pd.Series([300, -200, -100, 0, 100, 400, -500] * 26)[:180]
noise = pd.Series(np.random.normal(0, 100, 180))
dau = trend + seasonality + noise
df = pd.DataFrame({"date": dates, "dau": dau}).set_index("date")

# Декомпозиция
result = seasonal_decompose(df["dau"], model="additive", period=7)
result.plot()
plt.tight_layout()
plt.savefig("decomposition.png")

На графике увидите четыре компоненты: оригинальный ряд, тренд, сезонность и остаток. Это первый шаг любого анализа временного ряда — понять структуру.

Скользящее среднее

Скользящее среднее (moving average) — простейший способ сгладить ряд и увидеть тренд. Для каждой точки берёте среднее за окно (например, 7 дней) и заменяете ей исходное значение.

В SQL скользящее среднее считается через оконные функции:

SELECT
    DATE,
    dau,
    AVG(dau) OVER (
        ORDER BY DATE
        ROWS BETWEEN 6 PRECEDING AND CURRENT ROW
    ) AS dau_ma_7d
FROM daily_metrics
ORDER BY DATE;

ROWS BETWEEN 6 PRECEDING AND CURRENT ROW — это окно в 7 дней (текущий + 6 предыдущих).

Для недельной сезонности — берите окно 7. Для месячной — 30. Для годовой — 365. Главное, чтобы окно было кратно периоду сезонности — тогда сезонные колебания «усреднятся».

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

Аналитик часто отвечает на вопрос: «А это нормально или нет?». DAU упал на 15% — это сезонное падение или баг?

Простой подход — правило сигм: если значение отклоняется от скользящего среднего больше чем на 2-3 стандартных отклонения — это аномалия.

WITH stats AS (
    SELECT
        DATE,
        dau,
        AVG(dau) OVER (
            ORDER BY DATE
            ROWS BETWEEN 27 PRECEDING AND CURRENT ROW
        ) AS ma_28d,
        STDDEV(dau) OVER (
            ORDER BY DATE
            ROWS BETWEEN 27 PRECEDING AND CURRENT ROW
        ) AS std_28d
    FROM daily_metrics
)
SELECT
    DATE,
    dau,
    ma_28d,
    CASE
        WHEN ABS(dau - ma_28d) > 3 * std_28d THEN 'anomaly'
        ELSE 'normal'
    END AS status
FROM stats
ORDER BY DATE;

На практике этого достаточно для 80% случаев. Более продвинутые методы — Isolation Forest, Prophet от Meta, алгоритмы на основе STL-декомпозиции.

SQL для временных рядов

Оконные функции — ваш главный инструмент. Помимо скользящего среднего:

LAG — сравнение с предыдущим периодом:

SELECT
    DATE,
    revenue,
    LAG(revenue, 7) OVER (ORDER BY DATE) AS revenue_7d_ago,
    revenue - LAG(revenue, 7) OVER (ORDER BY DATE) AS wow_change
FROM daily_metrics;

Кумулятивная сумма (running total):

SELECT
    DATE,
    revenue,
    SUM(revenue) OVER (ORDER BY DATE) AS cumulative_revenue
FROM daily_metrics;

Процент роста week-over-week:

SELECT
    DATE,
    revenue,
    ROUND(100.0 * (revenue - LAG(revenue, 7) OVER (ORDER BY DATE))
        / NULLIF(LAG(revenue, 7) OVER (ORDER BY DATE), 0), 1) AS wow_pct
FROM daily_metrics;

Если вы строите аналитику временных рядов — визуализация обязательна. Числа в таблице не покажут паттерн, а линейный график покажет.

Прогнозирование: базовые подходы

На собеседованиях вряд ли попросят строить ARIMA, но знать основы полезно:

  • Наивный прогноз: значение = прошлый период (или прошлый год для сезонных данных). Удивительно часто работает не хуже сложных моделей.
  • Экспоненциальное сглаживание: как скользящее среднее, но недавние наблюдения имеют больший вес.
  • Prophet: библиотека от Meta для прогнозирования с трендом, сезонностью и праздниками. Простой API, хорошо работает из коробки.
  • ARIMA / SARIMA: классические статистические модели. Мощные, но требуют подбора параметров и стационарности ряда.

Для аналитика важнее уметь анализировать и интерпретировать временные ряды, чем строить сложные модели прогнозирования.

Вопросы с собеседований

Из каких компонент состоит временной ряд? — Тренд (долгосрочное направление), сезонность (повторяющиеся паттерны с фиксированным периодом) и остаток (шум, случайные колебания). Модель может быть аддитивной (компоненты складываются) или мультипликативной (умножаются).

Как бы вы определили, что падение метрики — аномалия, а не сезонность? — Посчитал бы скользящее среднее и стандартное отклонение за предыдущий период. Если текущее значение отклоняется от MA больше чем на 2-3 сигмы — скорее аномалия. Также сравнил бы с аналогичным периодом прошлого года — если паттерн повторяется ежегодно, это сезонность.

Как в SQL посчитать week-over-week изменение? — Используя LAG с окном 7: LAG(metric, 7) OVER (ORDER BY date). Разность текущего и прошлого значения — абсолютное изменение, отношение разности к прошлому значению — процентное.

Когда использовать аддитивную, а когда мультипликативную модель? — Аддитивную — когда амплитуда сезонности постоянна (сезонные колебания +-1000 независимо от уровня). Мультипликативную — когда амплитуда растёт пропорционально уровню ряда (декабрь всегда +30% от текущего уровня, а не +1000 в абсолюте).

FAQ

Что такое декомпозиция временного ряда?

Декомпозиция — это разложение ряда на составляющие: тренд, сезонность и остаток. Позволяет отделить долгосрочные изменения от циклических паттернов и шума. В Python делается через seasonal_decompose из statsmodels — достаточно указать период сезонности.

Какие оконные функции SQL нужны для анализа временных рядов?

Основные: LAG/LEAD (сравнение с предыдущим/следующим периодом), AVG/SUM с ROWS BETWEEN (скользящее среднее, кумулятивная сумма), STDDEV (для обнаружения аномалий). Этих функций достаточно для 90% задач аналитика по временным рядам.

Как выбрать размер окна для скользящего среднего?

Окно должно быть кратно периоду сезонности. Для дневных данных с недельной сезонностью — 7 дней. Для месячных данных с годовой сезонностью — 12 месяцев. Чем больше окно, тем сильнее сглаживание, но тем больше запаздывание: тренд «реагирует» на изменения с задержкой.


Потренируйте вопросы по SQL и аналитике — откройте тренажёр. 1500+ вопросов для собеседования аналитика. Бесплатно.