Приведение к стационарности: разности и логарифм
Осваиваем два главных инструмента стабилизации ряда: разности убирают тренд, логарифм усмиряет растущую амплитуду.
Дифференцирование — замена ряда на ряд его приращений (x_t - x_{t-1}); оно убирает тренд и часто делает ряд стационарным.
Зачем преобразовывать ряд
Если ADF сказал «нестационарен», ряд нельзя подавать в ARIMA как есть. Его приводят к стационарному виду двумя приёмами: разности убирают тренд (и сезонные разности — сезонность), а логарифм стабилизирует дисперсию у мультипликативных рядов. Часто применяют оба: сначала лог, потом разность.
За этой технической формулировкой стоит очень понятная бизнес-логика. Когда продажи растут из месяца в месяц, аналитику почти никогда не интересен сам уровень — интересен прирост: на сколько мы выросли по сравнению с прошлым месяцем, ускоряемся мы или замедляемся. Дифференцирование как раз и переводит разговор с языка «сколько всего» на язык «насколько изменилось», и именно в этом языке ряд оказывается стационарным. А логарифм отвечает на другую типичную проблему: у растущего бизнеса колебания тоже растут — сезонный всплеск на ранний миллион выручки и на поздние десять миллионов выглядит как ±10%, но в абсолютных числах различается в десять раз. Логарифм уравнивает эти проценты, превращая «качели, что раскачиваются всё сильнее» в ровную полосу.
Обычное дифференцирование
Первая разность превращает растущий ряд в ряд приращений, у которого среднее уже не уходит.
x = [100, 110, 121, 133, 146, 161] # растёт примерно на 10%
d1 = [x[i]-x[i-1] for i in range(1, len(x))]
print("Исходный:", x)
print("1-я разность:", d1)
print("Среднее разностей:", round(sum(d1)/len(d1), 1))
Вывод:
Исходный: [100, 110, 121, 133, 146, 161] 1-я разность: [10, 11, 12, 13, 15] Среднее разностей: 12.2
Тренд из «уровня» ушёл: вместо растущих чисел мы получили ряд приращений вокруг постоянного значения. Число выполненных дифференцирований — это параметр d в ARIMA.
Обратите внимание на цену: разность всегда короче исходного ряда на один элемент — у самой первой точки нет предшественника, из которого можно вычесть. На длинной истории потеря одного-двух наблюдений несущественна, но на коротком ряде каждый отброшенный отсчёт ощутим, и это ещё одна причина не дифференцировать сверх необходимого. Заметьте также, что приращения здесь не идеально постоянны (10, 11, 12, 13, 15) — они слегка подрастают, потому что исходный рост был не линейным, а примерно процентным. Это намёк: чисто разностью такой ряд до конца не выпрямить, и тут на сцену выходит логарифм.
Логарифм против растущей амплитуды
Когда колебания растут вместе с уровнем, логарифм сжимает их к постоянному размаху.
import math
mult = [100, 120, 90, 200, 240, 180, 400, 480, 360] # амплитуда растёт
logged = [round(math.log(v), 3) for v in mult]
print("Лог-ряд:", logged)
# разброс соседних разностей в лог-шкале стабилен
d = [round(logged[i]-logged[i-1], 3) for i in range(1, len(logged))]
print("Разности лога:", d)
Вывод:
Лог-ряд: [4.605, 4.787, 4.5, 5.298, 5.481, 5.193, 5.991, 6.174, 5.886] Разности лога: [0.182, -0.287, 0.798, 0.183, -0.288, 0.798, 0.183, -0.288]
В лог-шкале сезонный паттерн повторяется почти точь-в-точь (примерно 0.18, -0.29, 0.80 снова и снова), хотя в исходных числах амплитуда утраивалась. Это и есть превращение мультипликативной структуры в аддитивную.
Стоит проговорить, почему так получается. Если значения умножаются на одни и те же коэффициенты (×1.2, ×0.75, ×2.2 в каждом «сезоне»), то после логарифма умножение становится сложением: log(a·b) = log(a) + log(b). Поэтому повторяющийся мультипликативный узор превращается в повторяющийся аддитивный сдвиг, не зависящий от того, на каком уровне мы находимся — отсюда и одинаковые числа в разностях лога. У этого приёма есть и приятный побочный эффект для интерпретации: разность логарифмов приближённо равна относительному приросту, так что значение 0.182 читается примерно как «+18% за шаг». Поэтому в финансах и аналитике лог-разности — это, по сути, лог-доходности, понятная всем мера темпа.
Сезонное дифференцирование
Если период сезонности равен m, берут разность x_t - x_{t-m}. Она убирает повторяющийся паттерн: «сколько прибавилось по сравнению с тем же днём прошлой недели».
Выбор m диктуется природой данных, а не вкусом: для дневных продаж с недельным циклом m = 7, для месячных данных с годовой сезонностью m = 12, для почасового трафика с суточным ритмом m = 24. Сравнивая точку с её «прошлогодним близнецом», мы вычитаем именно ту часть колебаний, что объясняется календарём, и оставляем чистую динамику. Сезонную и обычную разность нередко комбинируют: сначала снимают сезонность, затем оставшийся тренд — так получаются сезонные модели вида SARIMA с двумя параметрами дифференцирования, обычным d и сезонным D.
Как работает под капотом
Дифференцирование — дискретный аналог производной. Линейный тренд (степень 1) убирается одной разностью, квадратичный — двумя. Но переусердствовать опасно: лишнее дифференцирование вносит искусственную отрицательную автокорреляцию и раздувает дисперсию. Поэтому d редко бывает больше 2. Логарифм же действует на дисперсию: производная log(x) равна 1/x, поэтому большие значения «сжимаются» сильнее малых.
Связь с производной даёт удобную проверку «достаточности». Подобно тому как первая производная многочлена понижает его степень на единицу, первая разность гасит линейный тренд, вторая — параболический, и так далее. Если после одной разности ряд всё ещё явно ползёт, берут вторую; если же и одна сделала его «плоским», вторая уже не нужна и только навредит. Хорошее эмпирическое правило: дифференцируйте, пока ADF не начнёт уверенно отвергать нестационарность, и остановитесь на этом — минимальное d, которое сработало, почти всегда и есть правильное. Логарифм и разность при этом отвечают за разные болезни (дисперсия против тренда), поэтому их не выбирают «или-или», а назначают по симптомам, и нередко применяют последовательно.
Частые ошибки
- Дифференцировать «на всякий случай» лишний раз — добавляется ложная автокорреляция.
- Логарифмировать ряд с нулями или отрицательными значениями (log не определён) без сдвига.
- Забыть обратное преобразование: прогноз в разностях/логах нужно вернуть в исходную шкалу.
- Снимать сезонность обычной разностью вместо сезонной — недельный или годовой узор так не уберёшь.
- Применять логарифм там, где разброс на самом деле постоянен: преобразование без нужды только усложняет интерпретацию.
Итоги
- Разности убирают тренд (обычные) и сезонность (сезонные), задают параметр d.
- Логарифм стабилизирует растущую дисперсию, превращая мультипликативность в аддитивность.
- Не переусердствуйте с d и не забывайте обратное преобразование прогноза.
- Период сезонной разности m диктуется данными (7, 12, 24…), а не выбирается произвольно.
- Логарифм и разность лечат разные болезни — дисперсию и тренд — и часто применяются вместе.