Изучение преобразований для временных рядов и способов их отмены с помощью масштабирования в Python

При прогнозировании данных временных рядов важным фактором является стационарность. Некоторые модели — ARIMA, Holt-Winters, Exponential Smoothing и другие — специализированы для временных рядов и не обязательно требуют стационарных данных. Стационарность ряда относится к его тенденции возвращаться к своему среднему значению с течением времени. Нестационарный ряд вводит тренд в набор данных, который может привести к функциям, которые слишком сильно полагаются на этот тренд, что приводит к ложным результатам (результатам, которые кажутся обманчиво хорошими).

Модели экспоненциального сглаживания сглаживают недавние тенденции для учета нестационарности, и ARIMA будет различать данные, когда коэффициент интеграции (средний член в стандартном порядке ARIMA) установлен выше 0. Другие модели временных рядов по-разному обрабатывают стационарность. .

Однако популярная модель машинного обучения, такая как XGBoost, не учитывает стационарность ряда, поскольку она не была разработана для учета особенностей данных временного ряда. Это не обязательно означает, что вы не должны прогнозировать с его помощью, но прежде чем делать это, неплохо иметь дело с потенциальной нестационарностью. Обычно это означает применение преобразования — каким-то образом манипулирование необработанными значениями — чтобы исключить тенденции и иным образом сделать модель более идеальной для прогнозирования. Некоторые не решаются проводить преобразования, потому что это добавляет еще один шаг к подготовке данных и затрудняет возврат результатов, чтобы прогнозы давали полезную информацию. Но это не обязательно так.

Сегодня я расскажу, как преобразование, прогнозирование и восстановление временных рядов можно легко выполнить в Python. Необходима следующая установка:

pip install --upgrade scalecast

Базовый синтаксис

Синтаксис применения преобразований с помощью пакета scalecast прост. Сначала мы импортируем необходимые пакеты и извлекаем данные временных рядов. Я использую набор данных авиакомпаний, доступный на Kaggle с лицензией Open Database.

import pandas as pd
import matplotlib.pyplot as plt
from scalecast.Forecaster import Forecaster
from scalecast.SeriesTransformer import SeriesTransformer

data = pd.read_csv('AirPassengers.csv')

Затем мы создаем объект Forecaster:

f = Forecaster(
    current_dates = data['Month'],
    y = data['#Passengers'],
    future_dates = 24,
)

Используйте объект Forecaster для передачи объекта SeriesTransformer:

transformer = SeriesTransformer(f)

Вот и все. Теперь вам доступно множество различных трансформаций, включая пользовательские трансформации. К ним относятся первое дифференцирование, второе дифференцирование и другие, сезонное дифференцирование, линейное удаление тренда, полиномиальное удаление тренда, логарифмическое удаление тренда, масштабирование, преобразования Бокскокса и многое другое. Все преобразования являются наращиваемыми и полностью обратимыми. Например, если я хочу применить сезонную разницу, а затем убрать тренд из своего ряда, я могу сделать это:

f = transformer.DiffTransform(12) # 12 periods is one seasonal difference for monthly data
f = transformer.DetrendTransform()

# plot results
f.plot();

Затем я вызову модель прогноза с помощью XGBoost:

f.set_estimator('xgboost')
f.add_ar_terms(12)
f.manual_forecast(n_estimators=100,gamma=2)
f.plot();

Чтобы отменить примененные преобразования, я вызываю функции возврата в порядке, обратном порядку их преобразования:

f = transformer.DetrendRevert()
f = transformer.DiffRevert(12)
f.plot();

Автопреобразование

С таким количеством вариантов преобразования было бы неплохо получить индивидуальную рекомендацию, основанную на том, что может максимизировать точность прогноза для данной серии? Это также возможно с масштабированием (doc):

from scalecast.util import find_optimal_transformation
# default args below
transformer, reverter = find_optimal_transformation(
    f, # Forecaster object to try the transformations on
    estimator=None, # model used to evaluate each transformation, default mlr
    monitor='rmse', # out-of-sample metric to monitor
    test_length = None, # default is the fcst horizon in the Forecaster object
    train_length = None, # default is the max available
    num_test_sets = 1, # number of test sets to iterate through, final transformation based on best avg. metric
    space_between_sets = 1, # space between consectutive train sets
    lags='auto', # uses the length of the inferred seasonality
    try_order = ['detrend','seasonal_adj','boxcox','first_diff','first_seasonal_diff','scale'], # order of transformations to try
    boxcox_lambdas = [-0.5,0,0.5], # box-cox lambas
    detrend_kwargs = [{'loess': True},{'poly_order':1},{'poly_order':2}], # detrender transform kwargs (tries as many detrenders as the length of this list)
    scale_type = ['Scale','MinMax'], # scale transformers to try
    m = 'auto', # the seasonal length to try for the seasonal adjusters, accepts multiple
    model = 'add', # the model to use when seasonally adjusting
    # specific model kwargs also accepted
)
# see what it chose
reverter

Эта функция перебирает множество возможных преобразований в заданном порядке и возвращает набор преобразований, которые, по ее мнению, создают наилучшие шансы для точных прогнозов, отслеживая метрику вне выборки, чтобы сделать это определение. Из вывода видно, что было выбрано преобразование бокскокса, за которым следует первая разность и первая сезонная разность:

Reverter(
  reverters = [
    ('DiffRevert', 12),
    ('DiffRevert', 1),
    ('Revert', <function find_optimal_transformation.<locals>.boxcox_re at 0x0000029FA6900EE0>, {'lmbda': -0.5})
  ],
  base_transformer = Transformer(
  transformers = [
    ('Transform', <function find_optimal_transformation.<locals>.boxcox_tr at 0x0000029FA6E579D0>, {'lmbda': -0.5}),
    ('DiffTransform', 1),
    ('DiffTransform', 12)
  ]
)
)

Затем возвращенные объекты можно передать в конвейер, где применяются другие методы автоматического ML:

from scalecast.Pipeline import Pipeline
from scalecast import GridGenerator

GridGenerator.get_example_grids()

transformer, reverter = find_optimal_transformation(f)
def forecaster(f):
    f.auto_Xvar_select()
    f.tune_test_forecast(
        ['elasticnet','xgboost'],
        cross_validate=True,
        limit_grid_size = .2,
    )
    
pipeline = Pipeline(
    steps = [
        ('Transform',transformer),
        ('Forecast',forecaster),
        ('Revert',reverter),
    ],
)

f = pipeline.fit_predict(f)
f.plot()
plt.title('Automated Forecasting with Transformations');

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

Заключение

Сегодня я рассмотрел преобразования временных рядов в Python с использованием масштабирования. Подход был расширен за счет включения методов автоматического машинного обучения, а преобразования, а также соответствующие функции возврата были помещены в конвейер для оптимизированного приложения. Спасибо, что следите за нами! Не забудьте поставить scalecast звезду на GitHub. Вот полный код, используемый в этой публикации.