Временные ряды

  • Введение во временные ряды
  • Методы анализа и базовая модель
  • Процесс скользящего среднего
  • Авторегрессионный процесс
  • ARIMA и SARIMA
  • Анализ режимов временных рядов в Python
  • Руководство по анализу временных рядов с помощью Python
  • Эконометрические и статистические модели во временных рядах
  • SARIMA: сезонный компонент в авторегрессивных моделях
  • Применение трансформеров к моделям временных рядов
  • Transformers vs. LSTM для прогнозирования временных рядов цен на акции
  • Модели MA, ARMA и ARIMA в прогнозировании временных рядов
  • Прогнозирование нестационарных временных рядов
  • Оценка синтетических временных рядов
  • Обнаружение аномалий временных рядов
  • Модели пространства состояний и фильтрация Калмана
  • VAR: векторная авторегрессионная модель во временных рядах
  • PyTimeTK — пакет анализа временных рядов
  • Анализ временных рядов в машинном обучении
  • Срез временных рядов и извлечение ценовых шаблонов

Введение во временные ряды

Временные ряды используются для предсказаний, основанных на текущих рядах, а также используются при проверке гипотез.

Доступ к репозиторию GitHub можно получить здесь.

Основные понятия во временных рядах:

Стационарный: статистические свойства ряда не изменяются со временем. Если среднее значение, дисперсия и ковариация временного ряда остаются постоянными во времени, ряд называется стационарным.

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

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

Цикл: Периодические изменения в экономике, не связанные с сезонными изменениями. Например, краткосрочное расширение или сокращение экономики, независимо от общей тенденции, описывает циклический процесс.

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

Что нужно знать, чтобы понять природу моделей временных рядов:

  1. Скользящая средняя: Будущее значение временного ряда является средним значением его k-числа предыдущих значений.
  2. Средневзвешенное значение: Подобно скользящей средней, она несет в себе основную идею придания большего веса недавним наблюдениям.
  3. Методы сглаживания:

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

Двойное экспоненциальное сглаживание: Используется в сериях с уровнем и трендом, не должно быть сезонности. Производит экспоненциальную коррекцию с учетом трендового эффекта.DES = Уровень (SES) + ТрендБазовый подход такой же, как и у СЭС, но тенденция также учитывается дополнительноОн подходит для трендовых и сезонных одномерных временных рядов.

Тройное экспоненциальное сглаживание: также известное как метод Холта-Винтерса используется в сериях с уровнем, трендом и сезонностью.Уровень (SES) + Тренд + СезонностьТройное экспоненциальное сглаживание является наиболее продвинутым методом сглаживанияЭтот метод динамически оценивает влияние уровня, тренда и сезонности и может использоваться в одномерных рядах, включающих тренд и/или сезонность.

После того, как временные ряды разложены на все эти составляющие компоненты, временной ряд y может быть выражен как аддитивно компонентов за определенный промежуток времени методом мультипликативно.

0. Теперь, имея эту информацию, давайте рассмотрим простой набор данных:

В этом проекте я использовал набор данных Global Land and Ocean-and-Land Temperature (GlobalTemperatures.csv).

Наш основной набор данных дает среднее значение месячных температур воздуха по штатам.

Чтобы получить более подробную информацию о наборе данных, я посмотрел на общие средние значения по странам с помощью операции groupby.y.rename(columns={«dt»: «Date», «AverageTemperature»: «Avg_Temp», «AverageTemperatureUncertainty»: «confidence_interval_temp»}, inplace=True)y[[‘Country’, «Avg_Temp»]].groupby([«Country»]).mean().sort_values(«Avg_Temp»)

Позже я получил данные из Флориды в наборе данных. Поэтому я сосредоточусь на меньшем фокусе и сделаю операции по порядку. Я хотел бы начать с того, что сначала посмотрел на график для Флориды.florida.head()
florida.tail(5)
florida[«Date»] = pd.to_datetime(florida[«Date»])
florida.set_index(‘Date’, inplace = True)

plt.figure(figsize = (6,4))
sns.lineplot(x = ‘Year’, y = ‘Avg_Temp’, data = florida)
plt.show()

Позже я определил часть до конца 1994 года как образовательные данные. Я оставлю более поздние месяцы в качестве тестовых данных. В этом случае наши тестовые данные имеют 225 месяцев.train = florida[:»1994-12-01″]
len(train)
test = florida[«1995-01-01»:]
len(test)

1. Прогнозирование с одиночным экспоненциальным сглаживанием

Его можно использовать в фиксированной серии. Во-первых, я применил тест Дики-Фуллера, поскольку мы знаем, что он недоступен при наличии тренда и сезонности.def is_stationary(y):
print(‘H0: Series Is Not Stationary’)
print(«H1: Series Is Stationary»)
p_value = sm.tsa.stattools.adfuller(y)[1]
if p_value < 0.05:
print(«Result is stationary»,p_value)
else:
print(«Series is not stationary», p_value)
is_stationary(florida)

ses_model = SimpleExpSmoothing(train).fit(smoothing_level = 0.5)
y_pred = ses_model.forecast(225)
train.plot(title=»Single Exponential Smoothing»)
test.plot()
y_pred.plot()
plt.show()
mean_absolute_error(test, y_pred)

Зеленая линия, показанная на графике, является оценкой нашей модели, а оранжевая линия — фактическими значениями. На данном этапе мы не могли предсказать тренд или сезонность.

Эти функции находят параметры, используя метод максимального логарифмического правдоподобия.

1.1 Оптимизация нашей модели SES

Теперь создадим сетку и сделаем ее менее альфа-канальной, оптимизируем ее методом boothforce.def optimize_ses(train,alphas, step=225):
for alpha in alphas:
ses_model = SimpleExpSmoothing(train).fit(smoothing_level=alpha)
y_pred = ses_model.forecast(step)
mae = mean_absolute_error(test, y_pred)
print(«alpha», round(alpha,2), «mae:», round(mae,4))
alphas = np.arange(0.01, 1,0.01)
optimize_ses(train, alphas)

### Final Ses Model ###
ses_model = SimpleExpSmoothing(train).fit(smoothing_level=0.14)
y_pred = ses_model.forecast(225)
train[«1985»:].plot(title=’Single Exponential Smoothing’)
test.plot()
y_pred.plot()
plt.show()
mean_absolute_error(test, y_pred)

Мы не можем принять эту модель, потому что мы не можем уловить тренд и сезонность. Несмотря на то, что мы снизили значение MAE, наша модель оказалась не такой успешной, как нам хотелось. Итак, переходим к следующему этапу.

2. Прогнозирование с двойным экспоненциальным сглаживанием

# DES = Level + Trend
des_model = ExponentialSmoothing(train, trend=»add»).fit(smoothing_level=0.5,
smoothing_slope=0.5)
y_pred = des_model.forecast(225)
train[«1985″:].plot(title=»Double Exponential Smoothing»)
test.plot()
y_pred.plot()
plt.show()

Мы уловили тенденцию, но не могли предсказать, как долго она продлится, и тенденция всегда продолжалась. Поэтому наше значение MAE оказалось достаточно высоким. Как видно на графике, зеленая линия продолжилась, уменьшаясь на всех 225 шагах.

2.1 Оптимизация нашей модели DESalphas = np.arange(0.01, 1, 0.05)
betas = np.arange(0.01, 1, 0.05)

optimize_des(train, alphas, betas)

def optimize_des(train, alphas, betas, step=225):
print(«Optimizing parameters…»)
results = []
for alpha in alphas:
for beta in betas:
des_model = ExponentialSmoothing(train, trend=»add»).fit(smoothing_level=alpha,
smoothing_slope=beta)
y_pred = des_model.forecast(step)
mae = mean_absolute_error(test, y_pred)
results.append([round(alpha, 2), round(beta, 2), round(mae, 6)])
results = pd.DataFrame(results, columns=[«alpha», «beta», «mae»]).sort_values(«mae»)
print(results)

optimize_des(train, alphas, betas)

В качестве параметров мы добавляем в нашу модель значения альфа (уровень сглаживания) и бета (наклон сглаживания), которые дают наименьшее значение MAE в оптимизации.final_des_model = ExponentialSmoothing(train, trend=»add»).fit(smoothing_level=0.06,
smoothing_slope=0.01)
y_pred = final_des_model.forecast(225)
train[«1985″:].plot(title=»Double Exponential Smoothing»)
test.plot()
y_pred.plot()
plt.show()

Несмотря на то, что мы значительно снизили значение MAE, наша модель все еще была не такой успешной, как мы хотели. Итак, мы переходим к следующему шагу, методу Холта-Уинтерса.

3. Прогнозирование с тройным экспоненциальным сглаживанием (HOLT WINTERS)

Прежде всего, мы настроили нашу модель, присвоив всем значениям 0,5 в качестве базовой модели.tes_model = ExponentialSmoothing(train,
trend=’add’,
seasonal=»add»,
seasonal_periods=12).fit(smoothing_level=0.5,
smoothing_slope=0.5,
smoothing_seasonal=0.5)
y_pred = tes_model.forecast(225)
train[«1985»:].plot(title = «Triple Exponential Smoothing»)
test.plot()
y_pred.plot()
plt.show()

Как видно, наша модель оказалась настолько неудачной, что наше значение MAE было выше, чем когда-либо. Теперь мы оптимизируем эту модель для достижения лучшего результата.

3.1 Оптимизация модели Холта-Уинтерсаimport itertools
alphas = betas = gammas = np.arange(0.01, 1, 0.05)
abg = list(itertools.product(alphas, betas, gammas))
abg[0][2]

def optimize_tes(train, abg, step=225):
print(«Optimizing parameters…»)
results = []
for comb in abg:
tes_model = ExponentialSmoothing(train, trend=»add»,
seasonal=»add»,
seasonal_periods=12).\
fit(smoothing_level=comb[0],
smoothing_slope=comb[1],
smoothing_seasonal=comb[2])

y_pred = tes_model.forecast(step)
mae = mean_absolute_error(test, y_pred)

print([round(comb[0], 2), round(comb[1], 2), round(comb[2], 2), round(mae, 2)])

results.append([round(comb[0], 2), round(comb[1], 2), round(comb[2], 2), round(mae, 2)])
results = pd.DataFrame(results, columns=[«alpha», «beta», «gamma», «mae»]).sort_values(«mae»)
print(results)

alphas = betas = gammas = np.arange(0.01, 1, 0.05)
abg = list(itertools.product(alphas, betas, gammas))

optimize_tes(train, abg)

В результате оптимизации мы вводим наши лучшие альфа-, бета- и гамма-значения в качестве параметров в нашу модель.# Final TES Model
#################################
final_tes_model = ExponentialSmoothing(train, trend=»add», seasonal=»add», seasonal_periods=12).\
fit(smoothing_level=0.06, smoothing_slope=0.06, smoothing_seasonal=0.06)
y_pred = final_tes_model.forecast(225)
train[«1985″:].plot(title=»Triple Exponential Smoothing»)
test.plot()
y_pred.plot()
plt.show()

Как видно, наше значение MAE достигло самого низкого значения.

Заключение

Если мы посмотрим на нашу окончательную модель, очень важно создать модель TES (HOLT Winters) в переменных, но сезонных данных, таких как погода. Сезонность имеет отношение к прошлому, тенденция имеет отношение к прошлому, а уровень в равной степени важен к прошлому, указывая на то, что времена года и температуры протекают по аналогичной схеме для нас.

Методы анализа и базовая модель

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

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

В этой статье мы рассмотрим характеристики временных рядов на основе данных о закрытии акций Google за последний 1 год. Вы можете найти коды в репозитории github.

На графике ниже представлены изменения цен на акции с течением времени. Изучив график, мы можем заметить, что в последнее время наблюдается тенденция к росту.

Цены закрытия Google меняются со временем

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

Все временные ряды можно разделить на три составляющие:

  1. Тенденция: Медленные изменения, происходящие во временных рядах.
  2. Сезонность: Паттерны вариаций, которые повторяются через определенные промежутки времени. Они могут быть еженедельными, ежемесячными, годовыми и т.д. Сезонные изменения указывают на отклонения от тренда в конкретных направлениях.
  3. Остатки: Необычные события, которые происходят в данных, такие как внезапное увеличение частоты сердечных сокращений у человека во время тренировки. Они вызывают случайные ошибки и также называются «белым шумом».

Визуализация вышеперечисленных компонентов называется «декомпозицией».

Декомпозиция данных

При изучении графиков в последнее время можно наблюдать тенденцию к росту. Кроме того, в данных присутствует сезонность. Если бы в данных не было сезонности, график «Сезонный» продолжался бы в виде прямой линии, начиная с 0.

Наивное предсказание

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

  • Среднее арифметическое: Например, если у нас есть ежедневные данные об акциях и мы хотим спрогнозировать следующие 7 дней, мы можем использовать среднее арифметическое, взяв среднее значение за предыдущие 7 дней в качестве прогноза на следующие 7 дней.
  • Последнее наблюдаемое значение: Опять же, рассматривая данные по акциям, если дата, которую мы хотим предсказать, 20.01.2023, мы будем использовать значение, наблюдаемое 19.01.2023, в качестве прогноза на 20.01.2023.

При создании этих моделей (прогнозов) мы также можем отметить их оценки, такие как MSE (среднеквадратичная ошибка), MAPE (средняя абсолютная процентная ошибка) и т. д., чтобы измерить, насколько лучше работают наши будущие сложные модели по сравнению с этими базовыми моделями.

Случайное блуждание

Random Walk представляет собой путешествие, которое происходит случайным образом, как следует из названия. В этой модели значение следующего шага определяется случайным образом на основе значения текущего шага. Значения, получаемые на каждом шаге, обычно определяются случайными величинами, которые являются независимыми и имеют одинаковое распределение.

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

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

Рисунок из статьи Прогнозирование временных рядов в python — Марко Пейшерио

Стационарные данные

Модели, используемые для прогнозирования временных рядов, такие как MA, AR, ARMA, предназначены для работы со стационарными данными.

Стационарный процесс – это процесс, статистические свойства которого не изменяются с течением времени. Если среднее значение, дисперсия и автокорреляция временного ряда не изменяются с течением времени, то данные считаются стационарными.

При работе с реальными данными данные, которые мы часто получаем, не соответствуют определению стационарности. Тем не менее, существуют методы, позволяющие сделать данные стационарными. К ним относятся:

  • Разностные: Это преобразование стабилизирует среднее значение, устраняет тренды и сезонные эффекты. Этот процесс достигается путем вычитания значения предыдущего временного шага y(t-1) из текущего значения y(t). Этот процесс можно выполнять несколько раз.
  • Берем логарифм ряда: Взятие логарифма помогает стабилизировать дисперсию ряда.

ЗАМЕТКА: Если мы применяем какое-либо преобразование к временным рядам, нам нужно обратить этот процесс на обратный при получении результатов прогнозирования модели.

Понимание того, являются ли данные стационарными

Чтобы определить, являются ли данные временного ряда стационарными, используется критерий Дики-Фуллера (ADF). Аналогично t-критерию, устанавливается порог значимости, и p-значение интерпретируется на основе этого порога.

  • Нулевая гипотеза: Данные нестационарны
  • Альтернативная гипотеза: Данные стационарны

Если:

  • p-значение < пороговое значение (например, 0.01, 0.05 и т.д.), мы можем отвергнуть нулевую гипотезу и сделать вывод, что данные стационарны.

Когда мы применяем тест Дики-Фуллера к данным о закрытии акций Google, мы получаем следующие результаты:

  • Статистика ADF: -1.4904778097402294
  • p-значение: 0,538216461195455

Если рассматривать порог 0,05 для p-значения, то можно сказать, что эти данные нестационарны.

Функция автокорреляции

После того, как у нас есть стационарный ряд, следующим шагом будет определение наличия автокорреляции. Автокорреляционная функция (ACF) измеряет линейную зависимость между запаздывающими значениями временного ряда, в частности, рассматривая связь между yt и yt-1.

При наличии тренда график АКФ показывает, что коэффициенты высоки для начальных лагов и линейно уменьшаются по мере увеличения лага. Кроме того, если в данных есть сезонность, график ACF также укажет на нее.

График ACF для цен закрытия Google

Если мы посмотрим на приведенный выше график АКФ, то можно сказать, что коэффициенты значимы, потому что они выходят за пределы заштрихованной области, которая представляет собой доверительный интервал.

Когда применяется преобразование данных: результаты теста Дики-Фуллера и график ACF

Когда применяется преобразование данных, оно может повлиять на результаты теста Дики-Фуллера и графика ACF. Преобразуя данные, мы стремимся сделать их более стационарными и устранить любые тенденции или сезонность.

Когда мы получаем приведенные выше результаты теста Дики-Фуллера и график ACF (который часто встречается в реальных данных), нам нужно применить преобразования, чтобы сделать данные стационарными. Когда мы применяем дифференциацию к данным, результаты изменяются следующим образом:

  • Статистика ADF: -15.35612522283515
  • p-значение: 3,664746148276414e-28
График АКФ для цен закрытия Google после трансформации

Так как p-значение меньше 0,05, то теперь можно сделать вывод, что мы получили стационарные данные. При рассмотрении графика АКФ мы видим, что коэффициенты находятся в пределах доверительного интервала, что указывает на незначительность.

После применения дифференциации к данным важно, чтобы данные, которые мы вводим в модель, были данными, полученными после дифференциации.

Базовая модель

Перед созданием базовой модели мы преобразовали выходные данные дифференциации в кадр данных, а затем разделили данные на обучающий и тестовый наборы.

Базовая модель будет создана на основе среднего арифметического и последнего наблюдаемого значения. Если мы посмотрим на приведенный ниже график прогнозирования, то увидим, что обе модели работают плохо. Однако это не существенно. Важно то, что модели, которые мы построим в будущем, превзойдут эти две модели. Для этого сравнения можно использовать метрику MSE. MSE для модели среднего арифметического составляет 2,86, а для модели последней наблюдаемой стоимости — 2,56.

Прогнозы базовой модели

Процесс скользящего среднего

В своей предыдущей статье я рассказывал о том, как нужно делать анализ временных рядов, методах анализа и создании базовой модели. Если вы не знакомы с этими темами, вы можете сначала просмотреть эту статью.

  1. Руководство по анализу временных рядов с помощью Python — 1: Методы анализа и базовая модель

В этой статье мы рассмотрим процесс скользящего среднего, модель, используемую во временных рядах. Во время этих проверок мы будем использовать данные о продажах виджетов. Все коды анализа можно найти в репозитории github.

Процесс скользящей средней (MA)

Процесс скользящей средней указывает на то, что текущее значение зависит от текущей и прошлой частоты ошибок. Если рассматривать формулу:

• μ: среднее значение ряда
• εt: значение текущей погрешности
• εt-1: историческое значение погрешности
• Theta-q: влияние прошлых ошибок на настоящее

Важным параметром, который нам нужно определить для MA, является q. Этот параметр определяет количество исторических ошибок, влияющих на текущее значение.

MA используется для моделирования одномерных временных рядов. Таким образом, если мы собираемся использовать MA, у нас есть только дата и данные, которые мы хотим спрогнозировать, мы не можем использовать какие-либо дополнительные аргументы.

Указание параметра q

Для определения этого параметра следует использовать график АКФ, о котором мы упоминали в первой статье. На графике ACF мы обычно ожидаем, что коэффициенты будут незначимыми, но часто это не так. Так что же делать, если сложилась несущественная ситуация? У нас есть дорожная карта, чтобы определить это.

Рисунок из статьи Прогнозирование временных рядов в python — Марко Пейшерио

На карте выше показан сценарий, который может развиться после построения графика АКФ. Соответственно, если есть автокорреляция, то есть если все коэффициенты не являются незначимыми, нам нужно выяснить, после какого запаздывания коэффициенты незначимы. Поясним на примере.# We made stationary series
widget_sales_diff = np.diff(df[‘widget_sales’], n=1)
plot_acf(widget_sales_diff, lags=20);

plt.tight_layout()

Приведенный выше график ACF был построен после того, как данные были неподвижны. По этому графику мы можем определить значение MA(q). В нашем предыдущем обзоре все коэффициенты были незначимыми (т.е. все они находились в синем доверительном интервале). Но сейчас, после второго лага, коэффициенты вошли в доверительный интервал. Таким образом, мы можем принять параметр q равным 2. В Lag 20 наблюдается только один значимый коэффициент. Мы можем игнорировать это, так как это не имеет непрерывности и никогда не наблюдалось до 20 лет.

Моделирование

Есть важный момент, о котором не стоит забывать, когда мы собираемся моделировать для MA(q). Модель МА не позволяет прогнозировать на 50 дней вперед. Модель MA(q) может предсказывать только будущие q шагов. Поэтому, если мы хотим предсказать далекое будущее, нам нужно добавить прогнозируемое значение q к данным, а затем переоценить его.

Важным моментом здесь является то, что мы должны делать моделирование на данных, которые мы сделали стационарными. Другими словами, при моделировании мы будем использовать widget_sales_diff данные, приведенные выше.df_diff = pd.DataFrame({‘widget_sales_diff’: widget_sales_diff})

train = df_diff[:449]
test = df_diff[449:]

Мы создаем датафрейм с данными, которые мы сделали стационарными, и делим его на обучение и тестирование.

С помощью следующей функции мы создадим как модель MA, так и наши базовые модели для сравнения. Эта функция будет использоваться как для процессов скользящего среднего, так и для процессов авторегрессии.def rolling_forecast(df: pd.DataFrame, train_len: int, horizon: int, window: int, method: str) -> list:

total_len = train_len + horizon

if method == ‘mean’:
pred_mean = []

for i in range(train_len, total_len, window):
mean = np.mean(df[:i].values)
pred_mean.extend(mean for _ in range(window))

return pred_mean

elif method == ‘last’:
pred_last_value = []

for i in range(train_len, total_len, window):
last_value = df[:i].iloc[-1].values[0]
pred_last_value.extend(last_value for _ in range(window))

return pred_last_value

elif method == ‘MA’:
pred_MA = []

for i in range(train_len, total_len, window):
model = SARIMAX(df[:i], order=(0,0,2))
res = model.fit(disp=False)
predictions = res.get_prediction(0, i + window — 1)
oos_pred = predictions.predicted_mean.iloc[-window:]
pred_MA.extend(oos_pred)

return pred_MA

«model = SARIMAX(df[:i], order=(0,0,2))» — это часть, в которой мы создаем модель и указываем найденный нами параметр q. После всего этого остается только тренировочный процесс.

Выше мы создали как наши базовые модели, так и нашу модель MA(2). Теперь мы можем сравнить эти три модели.

Когда мы изучаем сравнения моделей, мы видим, что лучшей моделью является модель MA(2), как мы и ожидали. Но чтобы получить реальные метрики, мы должны обратить разницу, которую мы применили.df[‘pred_widget_sales’] = pd.Series()
df[‘pred_widget_sales’][450:] = df[‘widget_sales’].iloc[450] + pred_df[‘pred_MA’].cumsum()

Когда мы обратим этот процесс в обратном направлении, мы сможем получить фактические значения продаж, предсказанные моделью. Фактические значения и оценку модели вы можете увидеть на графике ниже.

Авторегрессионный процесс

В предыдущей статье мы рассмотрели процесс скользящей средней. На этот раз мы рассмотрим еще одну статистическую модель – авторегрессионный процесс.

Что такое авторегрессионный процесс (AR)?

Авторегрессионный процесс — это статистическая модель, используемая для временных рядов для прогнозирования будущего на основе исторических данных. Подобно тому, как процесс скользящего среднего использует эффект прошлых ошибок, процесс авторегрессии говорит, что прошлые значения во временном ряду влияют на настоящее. Мы можем увидеть, как это используется в уравнении ниже. Здесь параметром, который мы хотим определить, является параметр «p». Это p-значение определяет, как далеко мы зайдем назад. Важно найти оптимальное количество.

Указание параметра p

Для определения этого параметра мы используем метод, аналогичный методу в процессе скользящей средней. Но на этот раз мы будем использовать график PACF (частичная автокорреляционная функция) вместо графика ACF. PACF дает частичную корреляцию временного ряда с его запаздывающими значениями.
Если мы посмотрим на дорожную карту ниже, то увидим, как мы можем шаг за шагом прийти к результату. Здесь, прежде всего, нам нужно посмотреть, соответствуют ли данные процессу скользящей средней. Если он не подходит, то нужно проверить, подходит ли он для авторегрессионного процесса с графом PACF.

Рисунок из статьи Прогнозирование временных рядов в python — Марко Пейшерио

Допустим, у нас есть стационарные данные. Затем нарисуем график АКФ с этими данными, и в результате получим следующий график.plot_acf(ar2_process)

plt.tight_layout()

Сюжет АКФ

При рассмотрении приведенного выше графика АКФ мы видим, что есть коэффициенты, которые значимы после лага 0. При рассмотрении этого графика в процессе скользящей средней мы ожидали, что только 1 значимый коэффициент, а остальные коэффициенты останутся в пределах доверительного интервала (синяя область). Так как здесь такой ситуации нет, то можно сказать, что эти данные не подходят для процесса скользящей средней и нарисовать график PACF.plot_pacf(ar2_process, lags=20)

plt.tight_layout()

Участок PACF

Если мы посмотрим на график PACF выше, то увидим, что коэффициенты после лага 2 незначимы. При этом с помощью этого графика мы определяем, что параметр «p» также равен 2, и у нас есть процесс AR(2).

Моделирование AR(2)

Следующие процессы очень похожи на процесс скользящей средней. Мы можем реализовать процесс AR(2) с незначительными изменениями. После разделения данных на обучение и тестирование мы можем использовать следующую функцию для моделирования.def rolling_forecast(df: pd.DataFrame, train_len: int, horizon: int, window: int, method: str) -> list:

total_len = train_len + horizon
end_idx = train_len

if method == ‘mean’:
pred_mean = []

for i in range(train_len, total_len, window):
mean = np.mean(df[:i].values)
pred_mean.extend(mean for _ in range(window))

return pred_mean

elif method == ‘last’:
pred_last_value = []

for i in range(train_len, total_len, window):
last_value = df[:i].iloc[-1].values[0]
pred_last_value.extend(last_value for _ in range(window))

return pred_last_value

elif method == ‘AR’:
pred_AR = []

for i in range(train_len, total_len, window):
model = SARIMAX(df[:i], order=(2,0,0))
res = model.fit(disp=False)
predictions = res.get_prediction(0, i + window — 1)
oos_pred = predictions.predicted_mean.iloc[-window:]
pred_AR.extend(oos_pred)

return pred_AR

Важной частью здесь является часть SARIMAX(df[:i], order=(2,0,0)) в условии «AR». В процессе MA(2) ордер записывался в виде (0,0,2). В AR(2) это записывается как (2,0,0).

На самом деле, процесс очень прост. И MA, и AR содержат очень похожие реализации. Вы можете найти коды для обоих в репозитории githup. Если мы посмотрим на AR-модель и результаты базовой модели, то увидим график ниже. Здесь мы видим, что процесс авторегрессии фиксирует максимумы и минимумы лучше, чем базовые модели, и работает лучше, чем базовая модель, как мы и ожидали.

Спасибо, что прочитали. До встречи в следующей статье.

ARIMA и SARIMA

В предыдущих статьях мы рассмотрели методы анализа, процесс скользящего среднего и процесс авторегрессии. В этой статье мы рассмотрим ARIMA (Auto Regressive Integrated Moving Average) и SARIMA (Seasonal ARIMA).

Вы можете найти полный код этой статьи на GitHub. Если вы готовы, давайте начнем.

Что такое ARIMA(p,d,q)?

Как следует из названия, ARIMA представляет собой комбинацию моделей AR и MA и порядка интеграции.

  • AR: авторегрессионный процесс говорит о том, что прошлые значения во временном ряду влияют на настоящее.
  • MA: Процесс скользящей средней указывает на то, что текущее значение зависит от текущей и прошлой частоты ошибок
  • I: Дифференцирование применяется для получения стационарного временного ряда, который не показывает тренда или сезонности.

Все эти компоненты создают параметры, которые будут использоваться ARIMA(p,d,q).

  • p: Это p-значение определяет, как далеко мы зайдем назад. Это порядок запаздывания.
  • d: Она равна количеству различий между рядами до тех пор, пока они не станут стационарными.
  • q: Этот параметр определяет количество исторических ошибок, влияющих на текущее значение

На самом деле, помимо моделей AR и MA, единственным новым параметром здесь является параметр d. На диаграмме ниже вы можете увидеть, как данные должны быть изучены после их получения.

Прежде всего, нужно обратить внимание на то, являются ли данные стационарными или нет. После применения преобразований, чтобы сделать данные стационарными (вы можете найти их в первой статье), нам нужно определить параметр d ARIMA. Этот параметр также можно определить по тому, сколько разностей мы получим при оформлении данных стационарными. Позже, чтобы найти оптимальные значения параметров p и q, мы можем создать отдельные списки для обоих и перебрать каждую комбинацию 3-х параметров с итерацией. Модель с наименьшим значением AIC может быть выбрана в качестве наилучшей модели. Здесь есть важный момент: если мы хотим сравнивать модели по метрике AIC, параметр d должен быть постоянным. Одна и та же d должна использоваться в каждой модели. После выбора модели не стоит говорить, что она самая лучшая, и на этом останавливаться. Здесь нам нужно сделать остаточный анализ для выбранной нами модели. Анализ невязок помогает оценить, адекватно ли модель отражает основные закономерности в данных временных рядов.

Что такое SARIMA(p,d,q)(P,D,Q)m?

Модель SARIMA включает в себя параметры P, D, Q, m в дополнение к ARIMA. Эти параметры помогают нам фиксировать сезонность.

  • P: порядок сезонных AR(P)
  • D: сезонный порядок интеграции
  • Q: порядок сезонных скользящих средних(Q)
  • m: Количество наблюдений за цикл (частота)

Обратите внимание, что модель SARIMA(p,d,q)(0,0,0)m эквивалентна модели ARIMA(p,d,q).

ПРАКТИКА КОДА

Теперь давайте сделаем все это на практике. Для определения оптимальной модели мы будем использовать auto_arima из библиотеки pmdarima.

В качестве примера мы будем использовать набор данных об авиапассажирах, доступный на Kaggle.

Этот набор данных содержит количество пассажиров на ежемесячной основе. Данные состоят из 144 строк и 2 столбцов. В период с 1949 по 1960 год существуют месячные периоды времени.

Если мы посмотрим на график количества пассажиров во времени, то отчетливо увидим, что и здесь есть сезонность.df = pd.read_csv(«AirPassengers.csv»)
df.rename(columns={«Month»:»month»,»#Passengers»:»passengers»}, inplace=True)
df.head()

plt.figure(figsize=(15,4))
plt.plot(df[«month»],df[«passengers»]);
plt.xlabel(‘Timesteps’);
plt.ylabel(‘Value’);
plt.xticks(df[‘month’][::10]);

Чтобы увидеть это более близко, нам нужно разложить данные по полочкам.
При изучении графиков можно заметить тенденцию к росту. Кроме того, в данных присутствует сезонность. Если бы в данных не было сезонности, график «Сезонный» продолжался бы в виде прямой линии, начиная с 0.# Decomposition

advanced_decomposition = STL(df.passengers, period=4).fit()

fig, (ax1, ax2, ax3, ax4) = plt.subplots(nrows=4, ncols=1, sharex=True)

ax1.plot(advanced_decomposition.observed)
ax1.set_ylabel(‘Observed’)

ax2.plot(advanced_decomposition.trend)
ax2.set_ylabel(‘Trend’)

ax3.plot(advanced_decomposition.seasonal)
ax3.set_ylabel(‘Seasonal’)

ax4.plot(advanced_decomposition.resid)
ax4.set_ylabel(‘Residuals’)

fig.autofmt_xdate()
plt.tight_layout()

Мы должны применить критерий Дики-Фуллера, чтобы определить, являются ли данные стационарными или нет.

  • Нулевая гипотеза: данные нестационарны
  • Альтернативная гипотеза: данные стационарны

def adfuller_test(y):
adf_result = adfuller(y)

print(«ADF Statistic:», adf_result[0])
print(«P-Value:», adf_result[1])

adfuller_test(df.passengers)

Когда мы применяем критерий Дики-Фуллера к переменной «Пассажиры», мы видим, что p-значение больше 0,05, что означает, что данные не являются стационарными. Мы можем выполнить дифференциацию, чтобы сделать данные стационарными.df_diff = np.diff(df[‘passengers’], n=1)
adfuller_test(df_diff)

print(«*»*50)

df_diff2 = np.diff(df_diff, n=1)
adfuller_test(df_diff2)

# d = 2

Дифференциация первого порядка не сделала данные стационарными. P-значение по-прежнему больше 0,05, поэтому мы снова выполняем дифференциацию. Во второй раз данные стали стационарными. Таким образом, мы определили, что наш параметр d должен быть равен 2.train = df[:-12]
test = df[-12:]

Мы делим данные на два: обучение и тестирование. Мы протестировали данные за 12 месяцев.

Теперь мы создадим модель ARIMA с помощью auto_arima. auto_arima позволяет нам легко находить оптимальные параметры. Он снижает рабочую нагрузку, так как выполняет итерации самостоятельно. При желании вы можете создать свою собственную функцию с помощью for lobs, не используя эту библиотеку.ARIMA_model = auto_arima(train[‘passengers’],
start_p=1,
start_q=1,
test=’adf’, # use adftest to find optimal ‘d’
tr=13, max_q=13, # maximum p and q
m=1, # frequency of series (if m==1, seasonal is set to FALSE automatically)
d=2,
seasonal=False, # No Seasonality for standard ARIMA
trace=True, #logs
error_action=’warn’, #shows errors (‘ignore’ silences these)
suppress_warnings=True,
stepwise=True)
ARIMA_model.summary()

Параметры модели:

  • Обучающие данные (train[«passengers»]: Во-первых, мы даем данные, которые хотим обучить. Так как данные, которые мы будем прогнозировать, — это пассажиры, мы передаем модели переменную passenger в данных о поезде.
  • start_p: С какого числа начинать поиск значения p для части AR(p)?
  • start_q: С какого числа начинать поиск значения q для части MA(q)?
  • test=’adf’: используйте adftest для нахождения оптимального ‘d’
  • max_p: Максимальное значение, которое может принять параметр p.
  • max_q: Максимальное значение, которое может принять параметр q.
  • m:частота. Если он равен 1, мы говорим, что сезонности нет.
  • d: порядок интеграции
  • Сезонность: есть сезонность или нет?

Когда мы запускаем модель, мы видим, что лучшими параметрами, которые она находит, являются SARIMAX(4, 2, 0). Причина, по которой он отображается как SARIMAX, заключается в том, что auto_arima также можете использовать модель SARIMAX. Так как эти модели уже разделены параметрами, то представление SARIMAX(4, 2, 0) равно представлению ARIMA(4, 2, 0).

Визуализировать остаточный анализ полученной модели можно следующим образом.

  • Нормируемый остаток: В невязках нет очевидной закономерности, значения имеют нулевое среднее значение и равномерную дисперсию.
  • Гистограмма плюс: Кривая KDE должна быть очень похожа на нормальное распределение (обозначено на графике как N(0,1))
  • График Q-Q: Большинство точек данных должны лежать на прямой линии
  • Коррелограмма (график АКФ): После лага 0 не должно быть значимых коэффициентов автокорреляции, но они есть.

ARIMA_model.plot_diagnostics(figsize=(10,7))
plt.show()

Следующим шагом является выполнение теста Ljung-Box на остатках, чтобы убедиться, что они независимы и не коррелируют.ARIMA_model = SARIMAX(train[«passengers»], order=(4,2,0), simple_differencing=False)
ARIMA_model_fit = ARIMA_model.fit(disp=False)

residuals = ARIMA_model_fit.resid
acorr_ljungbox(residuals, np.arange(1, 11, 1))

Здесь мы ожидаем, что все значения p-значения будут больше 0,05. Но значения, которые мы получили, не такие.

Для сравнения моделей ARIMA и SARIMA мы добавим прогнозы обеих моделей к тестовым данным.test[‘naive_seasonal’] = df[‘passengers’].iloc[120:132].values
ARIMA_pred = ARIMA_model_fit.get_prediction(132, 143).predicted_mean
test[‘ARIMA_pred’] = ARIMA_pred

«naive_seasonal» обрабатывает значения за последние 12 месяцев так же, как и 12 месяцев в тестовых данных, которые мы хотим спрогнозировать. Таким образом, мы предполагаем, что последние 2 года такие же. ARIMA_pred содержит значения за последние 12 месяцев, предсказанные моделью.

В модели SARIMA нам сначала нужно обратить внимание на дифференциацию. Как мы знаем из ARIMA, дифференциации первого порядка было недостаточно, чтобы сделать данные стационарными.df_diff = np.diff(df[‘passengers’], n=1)
adfuller_test(df_diff)

print(«*»*50)

df_diff2 = np.diff(df_diff, n=12)
adfuller_test(df_diff2)

Здесь мы сначала возьмем дифференциацию первого порядка. Так как данные не стационарны, проделываем этот процесс заново, но в параметре n записываем 12 (частота данных — параметр m) вместо 1. В результате мы видим, что данные стационарны и параметры таковы:

  • d = 1
  • D = 1

# Seasonal — fit stepwise auto-ARIMA
SARIMA_model = auto_arima(train[«passengers»], start_p=1, start_q=1,
test=’adf’,
max_p=5, max_q=5,
m=12, #12 is the frequncy of the cycle
start_P=1, start_Q=1,
max_P = 5, max_Q=5,
seasonal=True, #set to seasonal
d=1,
D=1,
trace=False,
error_action=’ignore’,
suppress_warnings=True,
stepwise=True)
SARIMA_model.summary()

Мы используем некоторые дополнительные параметры при использовании SARIMA.

  • start_P, start_Q: Начальное значение параметров P и Q, которые будут использоваться в SARIMA
  • max_P, max_Q: Максимальное значение параметров P и Q, используемое в SARIMA
  • D: дифференциальный порядок для SARIMA
  • м: Так как у нас есть сезонность, мы должны заменить этот параметр на частоту данных.
  • Сезонный: должен быть True, так как есть сезонность

В результате лучшей моделью оказалась SARIMAX(1, 1, 0)x(0, 1, 0, 12).

Когда мы исследуем остаточный анализ этого;SARIMA_model = SARIMAX(train[«passengers»], order=(1,1,0), seasonal_order=(0,1,0,12), simple_differencing=False)
SARIMA_model_fit = SARIMA_model.fit(disp=False)
SARIMA_model_fit.plot_diagnostics(figsize=(10,8));

  • Стандартизированная невязка: показывает, что невязки не демонстрируют тенденции или изменения дисперсии.
  • Гистограмма плюс: распределение невязок очень близко к нормальному распределению.
  • График Q-Q: Большинство точек данных должны располагаться на прямой линии, которая отображает довольно прямую линию
  • Коррелограмма (график АКФ): как нет значащих коэффициентов после лага 0. Поэтому все приводит к выводу, что остатки напоминают белый шум.

Чтобы убедиться в этом, мы также выполним тест Ljung-Box.residuals = SARIMA_model_fit.resid
acorr_ljungbox(residuals, np.arange(1, 11, 1))

Все возвращаемые p-значения больше 0,05. Поэтому мы не отвергаем нулевую гипотезу, а приходим к выводу, что остатки независимы и некоррелированы, как и белый шум.
Наша модель прошла все тесты по анализу остатков, и мы готовы использовать ее для прогнозирования.SARIMA_pred = SARIMA_model_fit.get_prediction(132, 143).predicted_mean
test[‘SARIMA_pred’] = SARIMA_pred

Мы можем визуализировать результаты обеих моделей и наблюдать, какая из них дает результаты, более близкие к реальности.fig, ax = plt.subplots()

ax.plot(df[‘month’], df[‘passengers’])
ax.plot(test[‘passengers’], ‘b-‘, label=’actual’)
ax.plot(test[‘naive_seasonal’], ‘r:’, label=’naive seasonal’)
ax.plot(test[‘ARIMA_pred’], ‘k—‘, label=’ARIMA’)
ax.plot(test[‘SARIMA_pred’], ‘g-.’, label=’SARIMA’)

ax.set_xlabel(‘Date’)
ax.set_ylabel(‘Number of air passengers’)
ax.axvspan(132, 143, color=’#808080′, alpha=0.2)
ax.legend(loc=2)
plt.xticks(np.arange(0, 145, 12), np.arange(1949, 1962, 1))
ax.set_xlim(110, 143)
fig.autofmt_xdate()
plt.tight_layout()

Серая область на графике выше — это наши тестовые данные. Синяя линия показывает фактические значения. Изучив результаты, можно сказать, что успех ARIMA очень низкий, но SARIMA успешна в своих прогнозах. Добавление сезонности оказалось успешным для модели.

Мы также можем оценить успешность модели с помощью MAPE (Mean Absolute Percentage Error). Здесь мы ожидаем, что MAPE будет небольшим.def mape(y_true, y_pred):
return np.mean(np.abs((y_true — y_pred) / y_true)) * 100

mape_naive_seasonal = mape(test[‘passengers’], test[‘naive_seasonal’])
mape_ARIMA = mape(test[‘passengers’], test[‘ARIMA_pred’])
mape_SARIMA = mape(test[‘passengers’], test[‘SARIMA_pred’])
fig, ax = plt.subplots()

x = [‘naive seasonal’, ‘ARIMA’, ‘SARIMA’]
y = [mape_naive_seasonal, mape_ARIMA, mape_SARIMA]
ax.bar(x, y, width=0.4)
ax.set_xlabel(‘Models’)
ax.set_ylabel(‘MAPE (%)’)

for index, value in enumerate(y):
plt.text(x=index, y=value + 1, s=str(round(value,2)), ha=’center’)
plt.tight_layout()

Когда мы изучаем метрику, мы видим, что лучшей моделью является модель SARIMA.

Анализ режимов временных рядов в Python

Это вводная статья к анализу режима временных рядов на python. Моя цель состоит в том, чтобы продемонстрировать, как обнаруживать и прогнозировать режимы во временных рядах, с применением, адаптированным к финансовым временным рядам. Полный текст записной книжки и кода для этой статьи можно найти здесь.

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

Чтобы подчеркнуть важность анализа режимов временных рядов, не нужно смотреть дальше, чем классические методы прогнозирования временных рядов. Например, модель линейной авторегрессии (AR) предполагает, что значение во времени имеет линейную зависимость от своих предыдущих значений и от термина ошибки. Ниже представлена авторегрессионная модель порядка p.

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

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

Авторегрессия переключения Маркова

Модель переключения Маркова является популярной моделью переключения режимов, которая основывается на предположении, что ненаблюдаемые состояния определяются лежащим в основе стохастическим процессом, известным как цепь Маркова. Марковская цепь — это система, описывающая возможные события, в которой вероятность каждого события зависит только от состояния, достигнутого в предыдущем событии. На рисунке ниже приведен простой пример марковской цепи с двумя состояниями (режимами) Е и А. Вероятность остаться в состоянии E равна 0,3. Вероятность перехода в состояние А при условии, что мы находимся в состоянии Е, равна 0,7. Обратите внимание, что сумма этих двух вероятностей перехода, связанных с E, равна 1.

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

Марковские коммутационные авторегрессионные модели (MS-AR) по-прежнему определяют цену актива как авторегрессионный процесс, однако каждый процесс также специфичен для режима. Представляя это математически, ниже показан простой для понимания процесс MS-AR с 3 режимами (S).

Теперь, переходя к реализации, это будет разделено на два подраздела. Первый раздел посвящен реализации MS-AR для обнаружения и классификации режимов. Второй раздел посвящен машинному обучению, Random Forest Classifier, реализации для прогнозирования следующего режима.

МС-АР

Прежде всего, нам понадобятся данные. Используется индекс ежедневной частоты Standard & Poor’s 500 (S&P500). S&P500 — это индекс фондового рынка, отслеживающий производительность 500 крупных компаний, котирующихся на фондовых биржах в Соединенных Штатах. Мы используем тикер S&P 500 ^GSPC, указывающий даты начала и окончания, а также суточный интервал. Данные получены от Yahoo finance.

df = yf.download('^GSPC', start="2000-01-01", end="2022-09-01", interval="1d")
df.head()

Построение скорректированных цен закрытия дает нам хорошее представление о том, с чем мы имеем дело. Очевидно, что это растущая серия с сокращениями вокруг крупных кризисов, таких как глобальный финансовый кризис 2008-2009 годов и пандемия COVID-19 2020 года.

Прежде чем соответствовать нашей модели, важно понять, чего мы пытаемся достичь с помощью анализа режима. Если бы мы инвестировали в фонд, отслеживающий индекс S&P 500, что бы мы сделали? Мы можем покупать и держать, получая солидную отдачу за последние 22 года. Однако было бы еще лучше избежать значительных просадок. Если бы у нас был способ охарактеризовать временные ряды в эти периоды потерь, надеясь выйти из рынка и вернуться только после периода просадки.

Выше я упомянул, что если бы мы могли охарактеризовать временные ряды, мы могли бы определить и избежать больших просадок. Слово «характеризовать» ведет в одну из самых важных частей статьи. Простой характеристикой, описывающей, как меняется цена, является процентное изменение цены от одного периода к другому. Мы назовем это возвратом цены. Возвращаясь к нашей дискуссии о том, что такое рынок, цена меняется, потому что участники покупают и продают актив. Таким образом, процентную доходность актива можно условно рассматривать как характеризующую поведение участников рынка при покупке и продаже. На самом деле есть гораздо больше, что может характеризовать такое поведение, например, объемы торгов или высокие-низкие спреды. Но для простоты давайте возьмем за один период возврат цены в качестве нашей единственной характеристики. Когда рынки спокойны, участники не лихорадочно покупают и продают активы, поэтому ежедневная ценовая доходность варьируется меньше. Цены также имеют тенденцию к росту. Мы можем описать ценовую доходность как имеющую низкую дисперсию, предполагая, что существует относительно низкая степень спреда в доходности. Когда рынки находятся в состоянии паники, участники лихорадочно покупают и продают активы, чтобы либо получить краткосрочную прибыль, либо избежать потерь. Мы можем описать ценовую доходность как имеющую высокую дисперсию, поскольку существует высокий спред в ежедневной доходности. Эта паника, вероятно, начнет возникать, когда активы становятся завышенными или в ответ на марко-событие.

Учитывая обсуждение выше, график ценовой доходности (избыточной доходности) начинает рисовать картину возможных базовых режимов и того, как доходность может характеризовать поведение участников.

Сосредоточив внимание на наших ключевых датах, 2008/2009 и 2020 годах, мы можем увидеть резкий скачок ценовой доходности. Это подтверждает гипотетическую динамику, на которую мы ссылались ранее. Кроме того, поддерживая использование ценовой доходности для характеристики поведения рынков и участников, предполагая, что на самом деле существуют базовые режимы, описываемые дисперсией доходности цен.

mod_kns = sm.tsa.MarkovRegression(ex_ret.dropna(), k_regimes=2, trend='n', switching_variance=True)
res_kns = mod_kns.fit()
res_kns.summary()

Подгонка марковской регрессии является самой простой частью анализа. Мы создаем экземпляр модели и помещаем ее только в две строки кода. Последняя строка кода даст нам краткое изложение результатов установки. При создании модели мы указываем количество режимов, которые мы хотим обнаружить. По моему опыту, попытка обнаружить и охарактеризовать более 3 или 4 режимов становится утомительной, а полезность результатов быстро разлагается. В этом случае мы указываем 2 режима, которые можно охарактеризовать как режим возврата с низкой дисперсией и режим возврата с высокой дисперсией. Тренд определяется как отсутствие тренда, так как мы изучаем стационарные (или близкие к стационарным) данные о доходности цен. Переключение дисперсий, установленное на true, означает, что мы ожидаем, что будет специфическая для режима гетероскедастичность. Другими словами, дисперсия наших остатков не постоянна, и мы ожидаем, что это будет зависеть от режима.

Next we want to view our actual regime classifications in time. It is important to note that the raw output of a 2 regime Markov switching regression is two values bounded between 0 and 1. That suggests the likelihood that the current time period is a member of either regime. The output can be interpreted probabilistically. The first value is the model’s probability expectation of low variance regime membership and the second value is the expectation of high variance regime membership. The sum across these values is always 1.

0         1
Date
2000-01-03 0.029665 0.970335
2000-01-04 0.000002 0.999998
2000-01-05 0.003385 0.996615
2000-01-06 0.003566 0.996434
2000-01-07 0.000665 0.999335

Для дальнейшей визуализации выходных данных мы можем построить график вероятностей сглаженного режима. Теперь мы можем видеть, как формируются наши классификации режимов, а наши кризисные даты, представляющие интерес, имеют доминирующие вероятности возвращения режима высокой дисперсии.

Хотя наличие вероятностных выходов полезно, это не помогает нам, когда мы хотим построить двоичные классификации нашей модели. Мы создаем двоичный вывод, кодируя каждый режим как 0 для низкой дисперсии и 1 для высокой дисперсии.

low_var = list(res_kns.smoothed_marginal_probabilities[0])
high_var = list(res_kns.smoothed_marginal_probabilities[1])

regime_list = []
for i in range(0, len(low_var)):
    if low_var[i] > high_var[i]:
        regime_list.append(0)
    else:
        regime_list.append(1)

Теперь все, что нам нужно сделать, это визуализировать наши режимы на графике скорректированной цены закрытия S& P 500.

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

Классификатор случайных лесов

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

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

Сначала нам нужно подготовить наш набор данных для классификатора случайных лесов. В дополнение к ценовым данным S&P 500 мы создадим функции возврата цены и объема. Это будут процентные изменения скорректированной цены закрытия и объема за несколько различных периодов оглядывания назад. Самое главное, что нам также необходимо добавить наши вероятности классификации режима MS-AR. Они будут описывать классификацию текущего режима к модели машинного обучения. Мы также добавим статистику волатильности доходности, чтобы описать волатильность прошлых цен. Наконец, нам нужны наши бинарные классификации состояний или режимов, это столбец, который мы преобразуем в наши метки для классификатора.

ml_df = yf.download('^GSPC', start="2000-01-01", end="2022-09-01", interval="1d")

# price and volume returns
for i in [1, 2, 3, 5, 7, 14, 21]:
    ml_df[f'Close_{i}_Value'] = ml_df['Adj Close'].pct_change(i)
    ml_df[f'Volume_{i}_Value'] = ml_df['Volume'].pct_change(i)
ml_df.dropna(inplace=True)

# probabilities
low_var_prob = list(res_kns.smoothed_marginal_probabilities[0])
high_var_prob = list(res_kns.smoothed_marginal_probabilities[1])
ml_df['Low_Var_Prob'] = low_var_prob[len(low_var_prob)-len(ml_df):] # adjust length
ml_df['High_Var_Prob'] = high_var_prob[len(high_var_prob)-len(ml_df):]

# volatility     
for i in [3, 7, 14, 21]:
    ml_df[f'Volt_{i}_Value'] = np.log(1 + ml_df['Close_1_Value']).rolling(i).std()
    
ml_df.dropna(inplace=True)

# states
ml_df['regimes'] = regime_list[len(regimes)-len(ml_df):] # adjust length

ml_df.head()

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

ml_df['regimes'] = ml_df['regimes'].shift(-1) 
ml_df.dropna(inplace=True)
ml_df

Теперь, когда наша колонка режимов является нашей целью, мы можем начать предварительную обработку данных. Я буду использовать функцию разделения train-test для создания списков X и y как для обучения, так и для тестирования. Я выбрал разделение 80-20 тестов с 80% данных, используемых для обучения, и 20%, зарезервированных для оценки модели.

X_train, X_test, y_train, y_test = train_test_split(rf_df, labels, test_size=0.2)
Counter(y_train)

При осмотре мы обнаруживаем, что у нас значительно меньше обучающих образцов с высоким дисперсионным режимом по сравнению с учебными образцами с низким дисперсионным режимом.

{0: 3113, 1: 1416}

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

under_sampler = RandomUnderSampler(random_state=40)
X_rs, y_rs = under_sampler.fit_resample(X_train, y_train)

Теперь, когда наши данные готовы, мы можем соответствовать нашей модели. Это легкая часть, так как все сложные вычисления и итерации выполняются за нас.

rf = RandomForestClassifier(n_estimators=20)
rf.fit(X_rs, y_rs)

В соответствии с нашей моделью нам нужно указать количество оценок, которое является количеством деревьев в нашем случайном лесу. Я произвольно решил инициализировать модель с 20 деревьями. Мы рассмотрим, как лучше выбрать этот параметр позже в статье.

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

y_pred = rf.predict(X_test)
y_prob_pred = rf.predict_proba(X_test)

Чтобы оценить производительность, мы будем использовать функцию кривой рабочих характеристик приемника (ROC) для получения ложноположительной и истинной положительной частоты. Кривая ROC дает нам диагностическую возможность двоичного классификатора. Он представляет собой компромисс между чувствительностью (истинный положительный показатель) и специфичностью (1 — коэффициент ложноположительных результатов).

acc_score = accuracy_score(y_test, y_pred)
false_positive_rate, true_positive_rate, thresholds = roc_curve(y_test, y_pred)
roc_auc = auc(false_positive_rate, true_positive_rate)
accuracy_score= 0.9947043248014121 roc_auc= 0.9945982714468629 FPR= 0.005121638924455826 TPR= 0.9943181818181818

На тестовом наборе (невидимые данные) наша модель достигла истинного положительного показателя 99,4%, что означает, что более 99% фактических режимов высокой дисперсии в следующем периоде были правильно предсказаны как режимы высокой дисперсии. Модель представлена с частотой ложных срабатываний 0,5%, что означает, что 0,5% фактических режимов с низкой дисперсией в следующем периоде были неправильно предсказаны как режимы высокой дисперсии. Чтобы получить представление об общей точности, мы можем вычислить площадь под кривой (AUC). Наша модель имеет AUC 99,5%, что означает, что модель способна различать режимы высокой и низкой дисперсии в 99,5% случаев. В качестве альтернативы мы также можем использовать вычисление оценки точности. Все метрики предполагают модель с высокой степенью точности и низкой погрешностью прогнозирования, обеспечивая удовлетворительный результат для этого случая использования.

Ранее я упоминал, что произвольно выбрал инициализацию случайного леса с 20 деревьями. Теперь это, кажется, работает относительно хорошо, но как мы будем настраивать этот гиперпараметр? Хотя настройка, вероятно, даст небольшое увеличение результатов, важно помнить о случайной природе машинного обучения и о том, что каждая подгонка модели будет содержать некоторые вариации. Чтобы настроить гиперпараметр, такой как количество деревьев, мы можем легко написать цикл, чтобы соответствовать моделям различных спецификаций и строить результаты. Визуально мы видим, что в этом случае не слишком много дополнительной производительности, чтобы получить от увеличения количества деревьев за пределы примерно 20 деревьев.

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

Как и следовало ожидать, нашими наиболее важными особенностями являются столбцы 20 и 21, которые являются нашими текущими столбцами вероятности дисперсионного режима. Казалось бы, наша модель выучила правила, связывающие нынешний режим, сопоставленный с будущим режимом.

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

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

Я надеюсь, что вы нашли это простое введение в анализ и прогнозирование режимов полезным. Мой вывод из этого процесса заключается в различном подходе к проблемам временных рядов. Хотя многие могут попытаться лучше предсказать серию, такой подход предполагает, что может быть лучше знать, когда предсказывать, а когда нет. С точки зрения финансов, это приводит к анализу того, когда торговать, а когда нет, торговать определенной стратегией. Полный текст записной книжки и кода для этой статьи можно найти здесь.

[1] https://en.wikipedia.org/wiki/Autoregressive_model

[2] https://en.wikipedia.org/wiki/Markov_chain

[3] Mendy, D. & Widodo, T. (2018). «Двухэтапная модель переключения Маркова: определение переломных моментов индонезийской рупии на доллар США после финансового кризиса 1997 года», MPRA Paper 86728.

Руководство по анализу временных рядов с помощью Python

Методы анализа и базовая модель

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

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

В этой статье мы рассмотрим характеристики временных рядов на основе данных о закрытии акций Google за последний 1 год. Вы можете найти коды в репозитории github.

На графике ниже представлены изменения цен на акции с течением времени. Изучив график, мы можем заметить, что в последнее время наблюдается тенденция к росту.

Цены закрытия Google меняются со временем

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

Все временные ряды можно разделить на три составляющие:

  1. Тенденция: Медленные изменения, происходящие во временных рядах.
  2. Сезонность: Паттерны вариаций, которые повторяются через определенные промежутки времени. Они могут быть еженедельными, ежемесячными, годовыми и т.д. Сезонные изменения указывают на отклонения от тренда в конкретных направлениях.
  3. Остатки: Необычные события, которые происходят в данных, такие как внезапное увеличение частоты сердечных сокращений у человека во время тренировки. Они вызывают случайные ошибки и также называются «белым шумом».

Визуализация вышеперечисленных компонентов называется «декомпозицией».

Декомпозиция данных

При изучении графиков в последнее время можно наблюдать тенденцию к росту. Кроме того, в данных присутствует сезонность. Если бы в данных не было сезонности, график «Сезонный» продолжался бы в виде прямой линии, начиная с 0.

Наивное предсказание

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

  • Среднее арифметическое: Например, если у нас есть ежедневные данные об акциях и мы хотим спрогнозировать следующие 7 дней, мы можем использовать среднее арифметическое, взяв среднее значение за предыдущие 7 дней в качестве прогноза на следующие 7 дней.
  • Последнее наблюдаемое значение: Опять же, рассматривая данные по акциям, если дата, которую мы хотим предсказать, 20.01.2023, мы будем использовать значение, наблюдаемое 19.01.2023, в качестве прогноза на 20.01.2023.

При создании этих моделей (прогнозов) мы также можем отметить их оценки, такие как MSE (среднеквадратичная ошибка), MAPE (средняя абсолютная процентная ошибка) и т. д., чтобы измерить, насколько лучше работают наши будущие сложные модели по сравнению с этими базовыми моделями.

Случайное блуждание

Random Walk представляет собой путешествие, которое происходит случайным образом, как следует из названия. В этой модели значение следующего шага определяется случайным образом на основе значения текущего шага. Значения, получаемые на каждом шаге, обычно определяются случайными величинами, которые являются независимыми и имеют одинаковое распределение.

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

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

Рисунок из статьи Прогнозирование временных рядов в python — Марко Пейшерио

Стационарные данные

Модели, используемые для прогнозирования временных рядов, такие как MA, AR, ARMA, предназначены для работы со стационарными данными.

Стационарный процесс – это процесс, статистические свойства которого не изменяются с течением времени. Если среднее значение, дисперсия и автокорреляция временного ряда не изменяются с течением времени, то данные считаются стационарными.

При работе с реальными данными данные, которые мы часто получаем, не соответствуют определению стационарности. Тем не менее, существуют методы, позволяющие сделать данные стационарными. К ним относятся:

  • Разностные: Это преобразование стабилизирует среднее значение, устраняет тренды и сезонные эффекты. Этот процесс достигается путем вычитания значения предыдущего временного шага y(t-1) из текущего значения y(t). Этот процесс можно выполнять несколько раз.
  • Берем логарифм ряда: Взятие логарифма помогает стабилизировать дисперсию ряда.

ЗАМЕТКА: Если мы применяем какое-либо преобразование к временным рядам, нам нужно обратить этот процесс на обратный при получении результатов прогнозирования модели.

Понимание того, являются ли данные стационарными

Чтобы определить, являются ли данные временного ряда стационарными, используется критерий Дики-Фуллера (ADF). Аналогично t-критерию, устанавливается порог значимости, и p-значение интерпретируется на основе этого порога.

  • Нулевая гипотеза: Данные нестационарны
  • Альтернативная гипотеза: Данные стационарны

Если:

  • p-значение < пороговое значение (например, 0.01, 0.05 и т.д.), мы можем отвергнуть нулевую гипотезу и сделать вывод, что данные стационарны.

Когда мы применяем тест Дики-Фуллера к данным о закрытии акций Google, мы получаем следующие результаты:

  • Статистика ADF: -1.4904778097402294
  • p-значение: 0,538216461195455

Если рассматривать порог 0,05 для p-значения, то можно сказать, что эти данные нестационарны.

Функция автокорреляции

После того, как у нас есть стационарный ряд, следующим шагом будет определение наличия автокорреляции. Автокорреляционная функция (ACF) измеряет линейную зависимость между запаздывающими значениями временного ряда, в частности, рассматривая связь между yt и yt-1.

При наличии тренда график АКФ показывает, что коэффициенты высоки для начальных лагов и линейно уменьшаются по мере увеличения лага. Кроме того, если в данных есть сезонность, график ACF также укажет на нее.

График ACF для цен закрытия Google

Если мы посмотрим на приведенный выше график АКФ, то можно сказать, что коэффициенты значимы, потому что они выходят за пределы заштрихованной области, которая представляет собой доверительный интервал.

Когда применяется преобразование данных: результаты теста Дики-Фуллера и график ACF

Когда применяется преобразование данных, оно может повлиять на результаты теста Дики-Фуллера и графика ACF. Преобразуя данные, мы стремимся сделать их более стационарными и устранить любые тенденции или сезонность.

Когда мы получаем приведенные выше результаты теста Дики-Фуллера и график ACF (который часто встречается в реальных данных), нам нужно применить преобразования, чтобы сделать данные стационарными. Когда мы применяем дифференциацию к данным, результаты изменяются следующим образом:

  • Статистика ADF: -15.35612522283515
  • p-значение: 3,664746148276414e-28
График АКФ для цен закрытия Google после трансформации

Так как p-значение меньше 0,05, то теперь можно сделать вывод, что мы получили стационарные данные. При рассмотрении графика АКФ мы видим, что коэффициенты находятся в пределах доверительного интервала, что указывает на незначительность.

После применения дифференциации к данным важно, чтобы данные, которые мы вводим в модель, были данными, полученными после дифференциации.

Базовая модель

Перед созданием базовой модели мы преобразовали выходные данные дифференциации в кадр данных, а затем разделили данные на обучающий и тестовый наборы.

Базовая модель будет создана на основе среднего арифметического и последнего наблюдаемого значения. Если мы посмотрим на приведенный ниже график прогнозирования, то увидим, что обе модели работают плохо. Однако это не существенно. Важно то, что модели, которые мы построим в будущем, превзойдут эти две модели. Для этого сравнения можно использовать метрику MSE. MSE для модели среднего арифметического составляет 2,86, а для модели последней наблюдаемой стоимости — 2,56.

Прогнозы базовой модели

Процесс скользящего среднего

В предыдущей части я рассказывал о том, как нужно делать анализ временных рядов, методах анализа и создании базовой модели. Если вы не знакомы с этими темами, вы можете сначала просмотреть эту статью.

  1. Руководство по анализу временных рядов с помощью Python — 1: Методы анализа и базовая модель

В этой статье мы рассмотрим процесс скользящего среднего, модель, используемую во временных рядах. Во время этих проверок мы будем использовать данные о продажах виджетов. Все коды анализа можно найти в репозитории github.

Процесс скользящей средней (MA)

Процесс скользящей средней указывает на то, что текущее значение зависит от текущей и прошлой частоты ошибок. Если рассматривать формулу:

• μ: среднее значение ряда
• εt: значение текущей погрешности
• εt-1: историческое значение погрешности
• Theta-q: влияние прошлых ошибок на настоящее

Важным параметром, который нам нужно определить для MA, является q. Этот параметр определяет количество исторических ошибок, влияющих на текущее значение.

MA используется для моделирования одномерных временных рядов. Таким образом, если мы собираемся использовать MA, у нас есть только дата и данные, которые мы хотим спрогнозировать, мы не можем использовать какие-либо дополнительные аргументы.

Указание параметра q

Для определения этого параметра следует использовать график АКФ, о котором мы упоминали в первой статье. На графике ACF мы обычно ожидаем, что коэффициенты будут незначимыми, но часто это не так. Так что же делать, если сложилась несущественная ситуация? У нас есть дорожная карта, чтобы определить это.

Рисунок из статьи Прогнозирование временных рядов в python — Марко Пейшерио

На карте выше показан сценарий, который может развиться после построения графика АКФ. Соответственно, если есть автокорреляция, то есть если все коэффициенты не являются незначимыми, нам нужно выяснить, после какого запаздывания коэффициенты незначимы. Поясним на примере.# We made stationary series
widget_sales_diff = np.diff(df[‘widget_sales’], n=1)
plot_acf(widget_sales_diff, lags=20);

plt.tight_layout()

Приведенный выше график ACF был построен после того, как данные были неподвижны. По этому графику мы можем определить значение MA(q). В нашем предыдущем обзоре все коэффициенты были незначимыми (т.е. все они находились в синем доверительном интервале). Но сейчас, после второго лага, коэффициенты вошли в доверительный интервал. Таким образом, мы можем принять параметр q равным 2. В Lag 20 наблюдается только один значимый коэффициент. Мы можем игнорировать это, так как это не имеет непрерывности и никогда не наблюдалось до 20 лет.

Моделирование

Есть важный момент, о котором не стоит забывать, когда мы собираемся моделировать для MA(q). Модель МА не позволяет прогнозировать на 50 дней вперед. Модель MA(q) может предсказывать только будущие q шагов. Поэтому, если мы хотим предсказать далекое будущее, нам нужно добавить прогнозируемое значение q к данным, а затем переоценить его.

Важным моментом здесь является то, что мы должны делать моделирование на данных, которые мы сделали стационарными. Другими словами, при моделировании мы будем использовать widget_sales_diff данные, приведенные выше.df_diff = pd.DataFrame({‘widget_sales_diff’: widget_sales_diff})

train = df_diff[:449]
test = df_diff[449:]

Мы создаем датафрейм с данными, которые мы сделали стационарными, и делим его на обучение и тестирование.

С помощью следующей функции мы создадим как модель MA, так и наши базовые модели для сравнения. Эта функция будет использоваться как для процессов скользящего среднего, так и для процессов авторегрессии.def rolling_forecast(df: pd.DataFrame, train_len: int, horizon: int, window: int, method: str) -> list:

total_len = train_len + horizon

if method == ‘mean’:
pred_mean = []

for i in range(train_len, total_len, window):
mean = np.mean(df[:i].values)
pred_mean.extend(mean for _ in range(window))

return pred_mean

elif method == ‘last’:
pred_last_value = []

for i in range(train_len, total_len, window):
last_value = df[:i].iloc[-1].values[0]
pred_last_value.extend(last_value for _ in range(window))

return pred_last_value

elif method == ‘MA’:
pred_MA = []

for i in range(train_len, total_len, window):
model = SARIMAX(df[:i], order=(0,0,2))
res = model.fit(disp=False)
predictions = res.get_prediction(0, i + window — 1)
oos_pred = predictions.predicted_mean.iloc[-window:]
pred_MA.extend(oos_pred)

return pred_MA

«model = SARIMAX(df[:i], order=(0,0,2))» — это часть, в которой мы создаем модель и указываем найденный нами параметр q. После всего этого остается только тренировочный процесс.

Выше мы создали как наши базовые модели, так и нашу модель MA(2). Теперь мы можем сравнить эти три модели.

Когда мы изучаем сравнения моделей, мы видим, что лучшей моделью является модель MA(2), как мы и ожидали. Но чтобы получить реальные метрики, мы должны обратить разницу, которую мы применили.df[‘pred_widget_sales’] = pd.Series()
df[‘pred_widget_sales’][450:] = df[‘widget_sales’].iloc[450] + pred_df[‘pred_MA’].cumsum()

Когда мы обратим этот процесс в обратном направлении, мы сможем получить фактические значения продаж, предсказанные моделью. Фактические значения и оценку модели вы можете увидеть на графике ниже.

Авторегрессионный процесс

В предыдущей статье мы рассмотрели процесс скользящей средней. На этот раз мы рассмотрим еще одну статистическую модель – авторегрессионный процесс.

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

2.Руководство по анализу временных рядов с помощью Python — 2: Процесс скользящих средних

Если вы готовы, давайте начнем.

Что такое авторегрессионный процесс (AR)?

Авторегрессионный процесс — это статистическая модель, используемая для временных рядов для прогнозирования будущего на основе исторических данных. Подобно тому, как процесс скользящего среднего использует эффект прошлых ошибок, процесс авторегрессии говорит, что прошлые значения во временном ряду влияют на настоящее. Мы можем увидеть, как это используется в уравнении ниже. Здесь параметром, который мы хотим определить, является параметр «p». Это p-значение определяет, как далеко мы зайдем назад. Важно найти оптимальное количество.

Указание параметра p

Для определения этого параметра мы используем метод, аналогичный методу в процессе скользящей средней. Но на этот раз мы будем использовать график PACF (частичная автокорреляционная функция) вместо графика ACF. PACF дает частичную корреляцию временного ряда с его запаздывающими значениями.
Если мы посмотрим на дорожную карту ниже, то увидим, как мы можем шаг за шагом прийти к результату. Здесь, прежде всего, нам нужно посмотреть, соответствуют ли данные процессу скользящей средней. Если он не подходит, то нужно проверить, подходит ли он для авторегрессионного процесса с графом PACF.

Рисунок из статьи Прогнозирование временных рядов в python — Марко Пейшерио

Допустим, у нас есть стационарные данные. Затем нарисуем график АКФ с этими данными, и в результате получим следующий график.plot_acf(ar2_process)

plt.tight_layout()

Сюжет АКФ

При рассмотрении приведенного выше графика АКФ мы видим, что есть коэффициенты, которые значимы после лага 0. При рассмотрении этого графика в процессе скользящей средней мы ожидали, что только 1 значимый коэффициент, а остальные коэффициенты останутся в пределах доверительного интервала (синяя область). Так как здесь такой ситуации нет, то можно сказать, что эти данные не подходят для процесса скользящей средней и нарисовать график PACF.plot_pacf(ar2_process, lags=20)

plt.tight_layout()

Участок PACF

Если мы посмотрим на график PACF выше, то увидим, что коэффициенты после лага 2 незначимы. При этом с помощью этого графика мы определяем, что параметр «p» также равен 2, и у нас есть процесс AR(2).

Моделирование AR(2)

Следующие процессы очень похожи на процесс скользящей средней. Мы можем реализовать процесс AR(2) с незначительными изменениями. После разделения данных на обучение и тестирование мы можем использовать следующую функцию для моделирования.def rolling_forecast(df: pd.DataFrame, train_len: int, horizon: int, window: int, method: str) -> list:

total_len = train_len + horizon
end_idx = train_len

if method == ‘mean’:
pred_mean = []

for i in range(train_len, total_len, window):
mean = np.mean(df[:i].values)
pred_mean.extend(mean for _ in range(window))

return pred_mean

elif method == ‘last’:
pred_last_value = []

for i in range(train_len, total_len, window):
last_value = df[:i].iloc[-1].values[0]
pred_last_value.extend(last_value for _ in range(window))

return pred_last_value

elif method == ‘AR’:
pred_AR = []

for i in range(train_len, total_len, window):
model = SARIMAX(df[:i], order=(2,0,0))
res = model.fit(disp=False)
predictions = res.get_prediction(0, i + window — 1)
oos_pred = predictions.predicted_mean.iloc[-window:]
pred_AR.extend(oos_pred)

return pred_AR

Важной частью здесь является часть SARIMAX(df[:i], order=(2,0,0)) в условии «AR». В процессе MA(2) ордер записывался в виде (0,0,2). В AR(2) это записывается как (2,0,0).

На самом деле, процесс очень прост. И MA, и AR содержат очень похожие реализации. Вы можете найти коды для обоих в репозитории githup. Если мы посмотрим на AR-модель и результаты базовой модели, то увидим график ниже. Здесь мы видим, что процесс авторегрессии фиксирует максимумы и минимумы лучше, чем базовые модели, и работает лучше, чем базовая модель, как мы и ожидали.

Ссылки

Эконометрические и статистические модели во временных рядах

Знакомство

У меня есть среднемесячные температуры из Флориды с 1743 года, о которых я говорил в предыдущей статье. В этой статье, во-первых, я объясню в основном некоторые модели и их определение, а также покажу вам основные операции во временных рядах с использованием таких моделей, как ARIMA, SARIMA с этим набором данных.

Не объясняйте мне, покажите код

Доступ к репозиторию GitHub можно получить здесь.

AR(p) : Авторегрессия

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

MA(q): Скользящая средняя

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

ARMA(p,q) = AR(p) +MA(q) Авторегрессионная скользящая средняя

Сочетает в себе методы AR и MA.
Оценка производится с помощью линейной комбинации исторических значений и прошлых ошибок.
Он подходит для одномерных временных рядов без тренда или сезонности.
p и q — числа временной задержки. p для модели AR, q для модели MA.

ARIMA(p, d, q) : (Авторегрессионная интегрированная скользящая средняя)

Модели ARIMA используются для анализа данных временных рядов и составления прогнозов на будущее. Обычно используется Арима (p, d, q). p используется для авторегрессионной (AR (P)) части, а d указывает, во сколько раз ряды данных отличаются. (новый ряд, полученный путем вычитания каждого из предыдущих данных) Наконец, q используется для части скользящей средней (ma (q)). В этом случае Arima (1,0,0) и ar (1) одинаковы.
ARIMA (0,0,1) и MA (1) — одно и то же.

Если мы хотим подвести итоги шаг за шагом:

  • Оценка производится путем линейной комбинации наблюдений и ошибок, отличающихся от предыдущих временных шагов.
  • Он подходит для одномерных, трендовых, но не сезонных данных.
  • p: число задержек фактического значения (степень авторегрессии)
    — Если p = 2, то это в моделях yt-1 и yt-2.
  • d: количество различных операций (степень разницы, I)
  • Q: Число задержки ошибки (степень скользящей средней)
  • Что значит быть замеченным?
    — Например, это вычесть значения сегодняшнего и предыдущего дня друг из друга.
  • Что делает процесс кровянистых выделений, почему нас замечают?
    — Для стабилизации серии.
  • Что значит стабилизировать серию?
    — Исключить ситуацию, когда статистические свойства ряда изменяются со временем.
  • Как можно понять, процесс взятия разницы здесь создал возможность делать прогнозы в рядах, которые сейчас в тренде.

SARIMA (p, d, q) (p, d, q)m: (сезонная авторегрессионная интегрированная скользящая средняя)

Это метод, используемый, в частности, при анализе временных рядов и используемый для объяснения и прогнозирования данных. В дополнение к модели Arima может быть на 3 параметра больше, чем модель очистки для использования в сезонных данных. Этими параметрами являются автокорреляция, разница и т.д. сезонных данных. являются ценностями.

Если мы хотим подвести итоги шаг за шагом:

  • ARIMA+ – сезонность
  • Его можно использовать в одномерных рядах, которые включают тренд и сезонность.
  • p, d, q — параметры из ARIMA. Элементы тренда*. ARIMA смогла смоделировать тенденцию.
  • p: число задержек фактического значения (степень авторегрессии)
    — Если P = 2, то это в моделях yt-1 и yt-2.
  • d: количество различных транзакций (степень разницы)
  • Q: Число задержки ошибки (степень скользящей средней)
  • Если q = 2, то это в моделях et-1 и et-2.
  • P, D, Q сезонные запаздывающие числа. Элементы сезона.
  • m — количество временных шагов за один сезонный период. Он выражает структуру сезонности.

0. Подготовка данных

Прежде всего, мы разделяем наши данные в качестве обучения/теста для нашей модели Arima. Как и в предыдущей статье, при определении данных найдены данные до 1995 года как состав.train = florida[:»1994-12-01″]
len(train)
test = florida[«1995-01-01»:]
len(test)

1. Модель Арима

В качестве первого шага мы настраиваем базовую модель Arima и смотрим на нашу тестовую ошибку, не применяя никаких операций настройки.arima_model = ARIMA(train, order = (1,0,1)).fit(disp=0)
arima_model.summary()

y_pred= arima_model.forecast(225)[0]
mean_absolute_error(test, y_pred)
# 4.39981

В визуализации я визуализирую после 1985 года для удобного и четкого визуала.train[«1985»:].plot(legend=True, label = ‘TRAIN’)
test.plot(legend=True, label = ‘TEST’, figsize = (6,4))
pd.Series(y_pred, index=test.index).plot(legend=True, label = ‘Prediction’)
plt.title(«Train, Test and Predicted Test»)
plt.show()

Как видите, наша догадка кажется не очень удачной. Итак, давайте проведем процесс настройки.

Существует два широко используемых метода оптимизации этих статистических моделей. Во-первых, определение класса модели на основе диаграмм ACF и PACF. Во-вторых, определение оценки модели на основе статистики AIC.

1.1 Определение марки модели в соответствии с графиками ACF и PACF

def acf_pacf(y, lags=30):plt.figure(figsize=(12, 7))layout = (2, 2)ts_ax = plt.subplot2grid(layout, (0, 0), colspan=2)acf_ax = plt.subplot2grid(layout, (1, 0))pacf_ax = plt.subplot2grid(layout, (1, 1))y.plot(ax=ts_ax)# Durağanlık testi (HO: Series is not Stationary. H1: Series is Stationary.)p_value = sm.tsa.stattools.adfuller(y)[1]ts_ax.set_title(‘Time Series Analysis Plots\n Dickey-Fuller: p={0:.5f}’.format(p_value))smt.graphics.plot_acf(y, lags=lags, ax=acf_ax)smt.graphics.plot_pacf(y, lags=lags, ax=pacf_ax)plt.tight_layout()plt.show()acf_pacf(florida)

Здесь мы пытаемся дать лучшее значение значениям p и q на графике.

Здесь речь идет о корреляциях между графиком ACF и значениями предыдущих периодов во временном ряду. Здесь мы рассматриваем транзакции с запаздыванием 30.

Итак, мы смотрим на yt-1, yt-2, …. Расширение до YT-30.

Автокорреляция не будет значимой в точках, оставшихся в светло-голубой области на графике, у нас есть много значимых, но мало бессмысленных автокорреляций.

Если мы хотим кратко резюмировать это:

— Это означает, что если ширина ACF «УМЕНЬШАЕТСЯ» в соответствии с задержками, а PACF p «CUT» после паттерна задержки AR (p).

— Если ширина ACF q равна «CUT OFF» после задержки, а ширина PACF «УМЕНЬШАЕТСЯ» в соответствии с задержкой, это означает, что это паттерн MA (q).

— Если ширина ACF и PACF уменьшается в соответствии с лагами, это означает, что это модель ARMA.df_diff = florida.diff()df_diff.dropna(inplace=True)acf_pacf(df_diff)

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

2 Определение ранга модели в соответствии со статистикой AIC и BIC

Теперь мы пытаемся найти наилучшие параметры, перепробовав все комбинации в диапазоне, который мы определили, чтобы найти значения, которые могут дать нам наилучший результат.# Generation of combinations of p and q

p = d = q = range(0, 4)
pdq = list(itertools.product(p, d, q))

def arima_optimizer_aic(train, orders):
best_aic, best_params = float(«inf»), None

for order in orders:
try:
arma_model_result = ARIMA(train, order).fit(disp=0)
aic = arma_model_result.aic
if aic < best_aic:
best_aic, best_params = aic, order
print(‘ARIMA%s AIC=%.2f’ % (order, aic))
except:
continue
print(‘Best ARIMA%s AIC=%.2f’ % (best_params, best_aic))
return best_params

best_params_aic = arima_optimizer_aic(train, pdq)

Добавляя лучшие значения, которые мы нашли, в нашу модель ARIMA, мы создаем нашу настроенную модель.arima_model = ARIMA(train, best_params_aic).fit(disp=0)
y_pred = arima_model.forecast(225)[0]
mean_absolute_error(test, y_pred)
# 1.4470

Когда мы визуализируем нашу настроенную модель, мы можем сказать, что у нас очень удачный результат модели по сравнению с предыдущей.

2. Модель SARIMA

Прежде всего, мы выполнили этот шаг, так как нам нужно будет создать проверку в нашем наборе данных.from statsmodels.tsa.statespace.sarimax import SARIMAX

train = florida[:»1994-12-01″]
len(train)
test = florida[«1995-01-01»:]
len(test)
val = train[«1991-01-01»:]
len(val)

2.1 Базовая модель

Мы сделаем это с помощью функции под названием SARIMAX.model = SARIMAX(train, order=(1,0,1), seasonal_order=(0,0,0,12))
sarima_model = model.fit(disp=0)

2.2 Ошибка проверкиpred = sarima_model.get_prediction(start = pd.to_datetime(‘1991-01-01’),dynamic=False)
pred_ci = pred.conf_int()

y_pred = pred.predicted_mean
mean_absolute_error(val, y_pred)
# 1.9192

2.3 Визуализация прогноза валидации

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

2.4 Ошибка тестаy_pred_test = sarima_model.get_forecast(steps=225)
pred_ci = y_pred_test.conf_int()
y_pred = y_pred_test.predicted_mean
mean_absolute_error(test, y_pred)
# 17.0051

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

2.5 Визуализация прогноза

Мы видим, насколько плох график.

2.6 Тюнинг моделиp = d = q = range(0, 2)
pdq = list(itertools.product(p, d, q))
seasonal_pdq = [(x[0], x[1], x[2], 12) for x in list(itertools.product(p, d, q))]

def sarima_optimizer_aic(train, pdq, seasonal_pdq):
best_aic, best_order, best_seasonal_order = float(«inf»), float(«inf»), None
for param in pdq:
for param_seasonal in seasonal_pdq:
try:
sarimax_model = SARIMAX(train, order=param, seasonal_order=param_seasonal)
results = sarimax_model.fit(disp=0)
aic = results.aic
if aic < best_aic:
best_aic, best_order, best_seasonal_order = aic, param, param_seasonal
print(‘SARIMA{}x{}12 — AIC:{}’.format(param, param_seasonal, aic))
except:
continue
print(‘SARIMA{}x{}12 — AIC:{}’.format(best_order, best_seasonal_order, best_aic))
return best_order, best_seasonal_order

best_order, best_seasonal_order = sarima_optimizer_aic(train, pdq, seasonal_pdq)

Здесь мы постарались сохранить диапазон небольшим (2) из-за избытка переменных. Можно увеличить дальность. Позже мы написали 12, потому что знаем сезонный период.

2.7 Окончательная модель и ее тестовая ошибкаmodel = SARIMAX(train, order=best_order, seasonal_order=best_seasonal_order)
sarima_final_model = model.fit(disp=0)
###############################
### Final Model Test Error ###
###############################

y_pred_test = sarima_final_model.get_forecast(steps=225)
pred_ci = y_pred_test.conf_int()
y_pred = y_pred_test.predicted_mean
mean_absolute_error(test, y_pred)
# 1.013578872841977

2.8 Визуализация окончательной модели

2.9 Оптимизатор SARIMA на основе MAE

Мы оптимизировали модель в соответствии с AIC, но мы также могли бы сделать это в соответствии с MAE. Для этого я хотел показать это, открыв здесь дополнительный раздел.def fit_model_sarima(train, val, pdq, seasonal_pdq):
sarima_model = SARIMAX(train, order=pdq, seasonal_order=seasonal_pdq).fit(disp=0)
y_pred_val = sarima_model.get_forecast(steps=48)
y_pred = y_pred_val.predicted_mean
return mean_absolute_error(val, y_pred)

fit_model_sarima(train, val, (0, 1, 0), (0, 0, 0, 12))

p = d = q = range(0, 2)
pdq = list(itertools.product(p, d, q))
seasonal_pdq = [(x[0], x[1], x[2], 12) for x in list(itertools.product(p, d, q))]

def sarima_optimizer_mae(train, val, pdq, seasonal_pdq):
best_mae, best_order, best_seasonal_order = float(«inf»), float(«inf»), None
for param in pdq:
for param_seasonal in seasonal_pdq:
try:
mae = fit_model_sarima(train, val, param, param_seasonal)
if mae < best_mae:
best_mae, best_order, best_seasonal_order = mae, param, param_seasonal
print(‘SARIMA{}x{}12 — MAE:{}’.format(param, param_seasonal, mae))
except:
continue
print(‘SARIMA{}x{}12 — MAE:{}’.format(best_order, best_seasonal_order, best_mae))
return best_order, best_seasonal_order

best_order, best_seasonal_order = sarima_optimizer_mae(train, val, pdq, seasonal_pdq)

model = SARIMAX(train, order=best_order, seasonal_order=best_seasonal_order)
sarima_final_model = model.fit(disp=0)

y_pred_test = sarima_final_model.get_forecast(steps=225)
pred_ci = y_pred_test.conf_int()
y_pred = y_pred_test.predicted_mean
mean_absolute_error(test, y_pred)
# 0.92

Ссылки

[1] https://www.veribilimiokulu.com

[2] https://www.analyticsvidhya.com/blog/2020/10/how-to-create-an-arima-model-for-time-series-forecasting-in-python/

[3] https://www.statisticssolutions.com/time-series-analysis/

SARIMA: сезонный компонент в авторегрессивных моделях

SARIMA, что расшифровывается как Seasonal AutoRegressive Integrated Moving Average, представляет собой продвинутую статистическую модель, используемую в прогнозировании временных рядов. Это расширение модели ARIMA (AutoRegressive Integrated Moving Average), включающее в свою структуру сезонность.

Если вы не уверены в компоненте ARIMA в SARIMA, обратитесь к следующему сообщению.

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

Помимо этих аспектов, мы включаем сезонные компоненты…

  • Сезонная AR: Аналогично части AR, она фиксирует авторегрессионный характер данных с сезонным запаздыванием. Например, наблюдение за тем же сезоном в предыдущем цикле.
  • Сезонность I: Дифференциация на сезонных интервалах, а не только на первом лаге.
  • Seasonal MA: компоненты скользящей средней для сезонной части временного ряда.
Уравнение (4 – здесь сезонный период). Источник

Итак, обозначим модели SARIMA как SARIMA(p, d, q)(P, D, Q)s, где:

  • p — количество членов авторегрессии,
  • d – число несезонных различий, необходимых для стационарности,
  • q — количество запаздывающих ошибок прогноза в уравнении предсказания,
  • PD и Q являются сезонными эквивалентами pd и q,
  • s – продолжительность сезонного цикла.

Код на Python

Мы можем использоватьstatsmodels.tsa.statespace.sarimax.SARIMAX из библиотеки statsmodels в Python.

X в SARIMAX означает eXogenous регрессор. Это переменные, отличные от самого временного ряда, которые могут влиять на целевую переменную. Например, в случае прогнозирования продаж экзогенными переменными могут быть расходы на рекламу, погодные условия и т. д. Эта особенность позволяет учесть в модели влияние внешних факторов на временные ряды.class statsmodels.tsa.statespace.sarimax.SARIMAX(
endog,
exog=None,
order=(1, 0, 0),
seasonal_order=(0, 0, 0, 0),
trend=None,
measurement_error=False,
time_varying_regression=False,
mle_regression=True,
simple_differencing=False,
enforce_stationarity=True,
enforce_invertibility=True,
hamilton_representation=False,
concentrate_scale=False,
trend_offset=1,
use_exact_diffuse=False,
dates=None,
freq=None,
missing=’none’,
validate_specification=True,
**kwargs
)

  • endog: ваш основной набор данных.
  • exog: Необязательные внешние переменные, которые могут повлиять на вашу переменную (endog), но не зависят от нее. Это могут быть данные о погоде, экономические показатели и т.д.
  • order: Кортеж ((p, d, q) представляющий порядок частей модели AR (авторегрессия), I (интегрированная) и MA (скользящая средняя) соответственно.
  • seasonal_order: кортеж (P, D, Q, s), Q, s) для сезонной составляющей модели, где PD и Q — сезонные порядки AR, дифференциации и MA, s — количество временных шагов в сезоне.
  • trend: необязательный параметр для указания тренда в данных. Это может быть полином, например ‘n’ (отсутствие тренда), ‘'c'‘ (константа), ‘'t'‘ (линейный тренд), ‘'ct' (постоянный и линейный тренд) или более сложные полиномы.'n'
  • measurement_error: логическое значение, указывающее, следует ли включать ошибку измерения в уравнение наблюдения. Если true, то предполагается, что эндогенные переменные (эндогенные переменные) измеряются с ошибкой.
  • time_varying_regression: Логическое значение, указывающее, могут ли коэффициенты регрессии для экзогенных переменных (exog) изменяться с течением времени.
  • mle_regression: Логическое значение. Если true, то коэффициенты для экзогенных переменных оцениваются с использованием метода максимального правдоподобия (MLE). Если false, то они оцениваются в модели пространства состояний с помощью фильтра Калмана.
  • simple_differencing: Если true, то дифференциация выполняется перед анализом, что может быть полезно в определенных контекстах, например, с нестабильными временными рядами.
  • enforce_stationarity: логическое значение для обеспечения стационарности в авторегрессионном компоненте модели. Если false, стационарность не применяется.
  • enforce_invertibility: логическое значение для принудительного применения обратимости в компоненте скользящего среднего модели. Если false, обратимость не применяется.
  • hamilton_representation: Если true, использует представление Гамильтона для описания процесса ARMA в модели пространства состояний. В некоторых случаях это может иметь вычислительные преимущества.
  • concentrate_scale: Логическое значение для использования концентрированной шкалы для оценки максимального правдоподобия, что может снизить вычислительную нагрузку в некоторых случаях.
  • trend_offset: Целочисленное смещение, с которого начинается тренд. Значение по умолчанию — 1.
  • use_exact_diffuse: Логическое значение, которое, если оно истинно, использует точный начальный фильтр Калмана для нестационарных состояний, как описано Дурбином и Купманом.
  • dates: необязательные даты, связанные с наблюдениями. Может быть полезен для построения временных рядов.
  • freq: Частота временного ряда. Это необязательная строка, обозначающая частоту временных рядов (например, ‘D’ для ежедневных, ‘M’ для ежемесячных и т. д.).
  • missing: Как обрабатывать отсутствующие точки данных. Возможные варианты: ‘none’, ‘drop’ и ‘raise’. «Перебросить» удаляет недостающие данные.
  • validate_specification: Логическое значение для проверки того, является ли переданная спецификация (параметры) допустимой для модели.

Давайте создадим фиктивный набор данных для применения SARIMAX :import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.statespace.sarimax import SARIMAX
from sklearn.metrics import mean_squared_error, mean_absolute_error

# Generate synthetic monthly average temperature data
np.random.seed(42)
data_length = 120 # 10 years of monthly data
monthly_avg_temp = 20 + np.random.normal(0, 1, data_length) + np.sin(np.linspace(0, 3.14 * 10, data_length))
dates = pd.date_range(start=’2010-01-01′, periods=data_length, freq=’M’)
temperature_df = pd.DataFrame({‘Temperature’: monthly_avg_temp}, index=dates)

temperature_df.plot(title=’Monthly Average Temperature’)
plt.show()

Среднемесячная температура. Изображение автора.

Далее мы подгоняем модель и прогнозируем следующее значение:model = SARIMAX(temperature_df, order=(1, 1, 1), seasonal_order=(1, 1, 1, 12))
model_fit = model.fit(disp=False)

# Forecast the next value
next_value = model_fit.forecast()
print(«Next Value Forecast:», next_value[0])

# Next Value Forecast: 20.047071105779583

Мы также можем спрогнозировать следующие n значений:# Forecast the next ‘n’ values
n_forecast = 12
forecast = model_fit.forecast(steps=n_forecast)
print(forecast)

«»»
2020-01-31 20.047071
2020-02-29 19.619796
2020-03-31 19.788017
2020-04-30 20.398506
2020-05-31 19.970861
2020-06-30 20.163995
2020-07-31 20.454585
2020-08-31 20.400319
2020-09-30 20.042237
2020-10-31 20.170135
2020-11-30 20.470334
2020-12-31 19.797711
Freq: M, Name: predicted_mean, dtype: float64
«»»

Мы можем выполнить скользящий прогноз, чтобы прогнозировать несколько значений шаг за шагом, каждый раз обновляя модель фактическим значением.# Rolling forecast
n_rolling_forecasts = 12
rolling_forecasts = []

for end in range(len(temperature_df) — n_rolling_forecasts, len(temperature_df)):
train = temperature_df[:end]
model = SARIMAX(train, order=(1, 1, 1), seasonal_order=(1, 1, 1, 12))
model_fit = model.fit(disp=False)
yhat = model_fit.forecast()
rolling_forecasts.append(yhat[0])

print(rolling_forecasts)

«»»
[20.391928622498774, 19.625651592488346, 19.356709655621298,
19.757482775258183, 19.372911878674003, 19.641333779806185,
20.034747061331508, 19.897685787339714, 19.14932598912717,
19.01320914483015, 19.25214631634275, 19.569773676989744]
«»»

Мы рассчитываем некоторые общие метрики, такие как среднеквадратичная ошибка (MSE), для оценки производительности модели.# Metrics computation
test_values = temperature_df[-n_rolling_forecasts:]
mse = mean_squared_error(test_values, rolling_forecasts)
mae = mean_absolute_error(test_values, rolling_forecasts)

print(«MSE:», mse)
print(«MAE:», mae)

«»»
MSE: 1.159197220001894
MAE: 0.871188739576516
«»»

Наконец, давайте создадим визуализации для невязок, прогнозов и фактических значений для анализа производительности модели.# Plotting the forecasts and the residuals
fig, axes = plt.subplots(2, 1, figsize=(10, 8))

# Forecast vs Actuals
axes[0].plot(temperature_df, label=’Actual’)
axes[0].plot(pd.date_range(start=temperature_df.index[-1], periods=n_rolling_forecasts+1, freq=’M’)[1:], rolling_forecasts, color=’red’, label=’Forecast’)
axes[0].set_title(‘Temperature Forecast vs Actuals’)
axes[0].legend()

# Residuals
residuals = test_values[‘Temperature’] — rolling_forecasts
axes[1].plot(residuals, label=’Residuals’)
axes[1].set_title(‘Residuals of Forecasts’)
axes[1].legend()

plt.tight_layout()
plt.show()

Диаграммы. Изображение автора.

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

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

Модель может выявлять и учитывать как восходящие, так и нисходящие тенденции в сезонных периодах, которые являются общими для деловых и экономических данных.

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

Применение трансформеров к моделям временных рядов

Используйте искусственный интеллект для улучшения результатов прогнозирования данных.

Автор: Эсекьель Ланца (евангелист ИИ с открытым исходным кодом)

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

С момента создания первого трансформатора в 2017 году произошел взрывной рост типов трансформаторов, включая мощные генеративные модели искусственного интеллекта, такие как ChatGPT* и DALL-E*. Несмотря на то, что преобразователи эффективны в моделях преобразования текста в текст или текста в изображение, при применении преобразователей к временным рядам возникает несколько проблем. На Open Source Summit North America* 2023 Эсекьель Ланца рассказал о проблемах с текущими моделями трансформаторов и представил новые трансформаторы, которые начинают показывать многообещающие результаты для временных рядов.

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

Много новых трансформеров было создано с момента создания ванильного трансформатора, первого трансформатора.

Обзор функциональных возможностей трансформера

Давайте посмотрим на роль трансформатора в Stable Diffusion*, модели глубокого обучения, которая может превратить фразу, например «Собака в очках», в изображение. Преобразователь получает текст, введенный пользователем, и генерирует встраивание текста. Встраивание текста — это представление текста, которое может быть прочитано сверточной нейронной сетью (CNN) — в данном случае U-NET. В то время как модели стабильной диффузии используют вложения для создания изображений, вложения можно использовать для создания дополнительных выходных данных, которые полезны для моделей временных рядов.

Как работают трансформеры

Чтобы понять, как применить преобразователь к модели временных рядов, нам нужно сосредоточиться на трех ключевых частях архитектуры преобразователя:

  • Встраивание и позиционное кодирование
  • Энкодер: Расчет самовнимания при работе с несколькими головками
  • Декодер: Вычисление самовнимания с несколькими головками

В качестве примера мы объясним, как работают ванильные трансформеры, трансформеры, которые переводят простые фразы с одного языка на другой.

Встраивание и позиционное кодирование: как вы представляете свои входные данные

Когда вы вводите фразу «Я люблю собак» в обычный преобразователь, алгоритм под названием Word2Vec преобразует каждое слово в список чисел, называемый вектором. Каждый вектор содержит информацию о том, что означает слово и как оно связано с другими словами, такими как синонимы и антонимы.

Модель также должна понимать положение каждого слова во фразе. Например, выражение «собаки, которых я люблю» не имеет того же значения, что и выражение «я люблю собак». Второй алгоритм, называемый позиционным вектором, использует сложное математическое уравнение, чтобы помочь модели понять порядок предложений. Собранная вместе, информация, предоставляемая Word2Vec и позиционными векторными алгоритмами, известна как встраивание текста, или ваша исходная фраза, представленная таким образом, чтобы машина могла ее прочитать.

Многоголовое самососредоточение на уровне энкодера

Затем кодировщики получают вложения текста и преобразуют их в новые векторы, добавляя информацию, помогающую модели различать связь между словами во фразе. Например, во фразе «Дети играют в парке» кодировщик присвоит наибольший вес словам «дети», «играющие» и «парк». Мы называем этот процесс вниманием к себе, потому что он диктует, на какие слова модель должна обращать наибольшее внимание.

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

Чтобы развить еще лучшее понимание взаимосвязи между словами, слой самовнимания может работать с несколькими головами одновременно. Этот процесс называется многоголовым вниманием, и он позволяет модели фокусироваться на разных частях фразы одновременно, например, при наличии краткосрочных и долгосрочных зависимостей. Например, во фразе «Животное не перешло дорогу, потому что оно слишком устало» многоголовое внимание говорит модели, что «животное» и «оно» относятся к одной и той же идее.

Время, необходимое для вычисления внимания квадратично, увеличивается с каждой новой точкой данных, добавляемой в ряд.

Самостоятельная работа с несколькими головками на уровне декодера

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

Использование трансформеров для временных рядов

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

Давайте рассмотрим, как мы сейчас решаем эту проблему и почему эти модели не работают.

Современные подходы

Модель авторегрессионного интегрированного скользящего среднего (ARIMA) работает для некоторых временных рядов, но требует глубокого понимания связанных с ними трендов, сезонных изменений и остаточных значений, и даже в этом случае она работает только для линейных зависимостей. Во многих временных рядах с многомерными задачами связь между зависимостями не является линейной, и ARIMA не будет работать.

Также существует несколько подходов, использующих нейронные сети.

  • Модели нейронных сетей прямого распространения (FNN) используют любые предыдущие шесть точек данных в ряду для прогнозирования следующих шести. Несмотря на то, что FNN допускают нелинейные зависимости, они требуют ручного создания модели, которая фокусируется на очень специфических проблемах или подмножествах данных, что делает эту модель слишком трудоемкой для создания больших наборов данных.
  • В модели рекуррентной нейронной сети (RNN) можно передать модели небольшое подмножество точек данных, относящихся к вашему временному ряду, и ячейки в RNN запомнят, какие точки данных важны и каков их вес. Однако, когда вы имеете дело с наборами данных с длинными зависимостями, вес становится менее важным, и точность модели со временем уменьшается.
  • Модели с длинной кратковременной памятью (LSTMпохожи на RNN, за исключением того, что каждая ячейка имеет память, которая позволяет чаще обновлять вес во время длинных последовательностей. Это делает LSTM хорошим решением для некоторых сценариев использования.
  • Seq2seq — это способ повысить производительность LSTM. Вместо того, чтобы подавать данные в сеть напрямую, вы можете передавать данные в кодировщик, который генерирует функции входных данных, которые передаются в декодер.

Как трансформеры могут улучшить временные ряды?

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

Проблема квадратичной сложности

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

Время, необходимое для вычисления внимания квадратично, увеличивается с каждой новой точкой данных, добавляемой в ряд.

Совершенствование модели трансформера для временных рядов

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

  • Позиционная кодировка: как мы представляем входные данные
  • Модуль «Внимание»: Способы снижения сложности по времени

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

Сетевая модификация No1: Позиционное кодирование

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

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

Сетевая модификация No2: Модуль «Внимание»

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

Внимание ProbSparse, используемое в новых моделях, таких как Informer*, сокращает время, вычисляя вероятность только на основе наиболее важных точек данных в ряду.

Испытание новых трансформеров

В то время как многие новые трансформаторы, такие как LogTrans*, Pyraformer* и FEDformer*, включают в себя эти сетевые модификации, здесь мы сосредоточимся на Informer и Spacetimeformer*, поскольку они имеют открытый исходный код. Репозитории GitHub* содержат справочную документацию и примеры, которые упрощают тонкую настройку моделей в соответствии с данными без необходимости разбираться во всех деталях уровня внимания.
Давайте рассмотрим, как Informer и Spacetimeformer используют эти сетевые модификации, и посмотрим, какие результаты они генерируют.

Архитектура информатора

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

Сочетая модель внимания ProbSparse и позиционное кодирование, Informer предлагает преимущества в производительности по сравнению с традиционными трансформаторами, такими как LSTM. При прогнозировании 24 точек данных в будущем Informer выдает немного лучшую среднеквадратичную ошибку (MSE) 0,577, чем MSE LSTM 0,650. При прогнозировании 720 точек данных разница в производительности увеличивается: Informer зарабатывает MSE 1,215 по сравнению с LSTM 1,960. Из этого мы можем сделать вывод, что Informer дает немного лучшие результаты в длинных сериях, но LSTM все еще может давать хорошие результаты для определенных краткосрочных сценариев использования.

Informer дает немного лучшие результаты, чем LSTM-модели, особенно для длинных рядов данных.

Архитектура пространственно-временного формирования

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

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

Как и Informer, Spacetimeformer предлагает немного лучшие результаты, чем LSTM. При прогнозировании на 40 часов вперед MSE Spacetimeformer 12,49 немного лучше, чем MSE LSTM 14,29. Несмотря на то, что этот предел увеличивается для более длинных последовательностей, Spacetimeformer пока не дает значительно лучших результатов, чем LSTM, для каждого случая использования.

Как и Informer, Spacetimeformer дает немного лучшие результаты, чем LSTM, особенно для длинных временных рядов.

Пример использования: задержка в микросервисной архитектуре

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

Применение прогнозирования временных рядов Informer к интернет-бутику с 11 микросервисами.

Чтобы продемонстрировать влияние на конечных пользователей, мы спрогнозируем, как долго пользователи должны ждать, пока каждая микрослужба обработает запрос. Основываясь на предыдущих 360 точках данных каждого сервиса, мы выполнили короткий прогноз на 36 точек данных в будущем и длинный прогноз на 120 точек данных в будущем.

При прогнозировании следующих 36 точек данных Informer выдал MSE 0,6, что немного лучше, чем LSTM. Однако Informer потребовалось больше времени на обработку. То же самое верно и для результатов длинной модели: прогнозы Informer более точны, но требуют больше времени для обработки.

Информер выдавал лучшие результаты как в коротких, так и в длинных рядах данных, но требовал немного больше времени на обработку.

Принимайте участие и приступайте к тестированию

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

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

Дополнительные материалы с открытым исходным кодом от Intel можно найти на сайте open.intel

Transformers vs. LSTM для прогнозирования временных рядов цен на акции

Диаграмма трансформатора

Поэтому я подумал, что должен продолжить обсуждение проблемы биржевых временных рядов, которую я начал в своем первом посте в блоге. В этом первом посте мы использовали архитектуры моделей LSTM и CNN-LSTM для прогнозирования будущих цен на акции, используя только предыдущие цены в качестве входных данных, и у нас это получилось довольно хорошо. Наша средняя абсолютная процентная ошибка была на уровне или ниже трех процентов, и все выглядело хорошо. В этом посте я упоминал, что буду обсуждать способы улучшения этой цифры, и первоначально я намеревался сделать это, создав ансамблевую LSTM-модель с использованием LSTM-сетей с различными гиперпараметрами, а затем усреднив их предсказание, чтобы получить (надеюсь) что-то лучшее. Но должен признаться, что я немного отвлекся, занявшись другим, на мой взгляд, более интересным направлением исследования. Одним словом, этот новый подход – «Трансформеры».

Что такое архитектура модели трансформатора?

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

На очень высоком уровне трансформатор представляет собой архитектуру сети с прямой связью, которая использует «механизм самовнимания» для сокращения времени обучения и количества параметров, оставаясь при этом высокопрогностическим. Я сформулировал это следующим образом: трансформаторы могут выполнять работу РНС без всех этих повторений, замедляющих работу и добавляющих параметры. Это работает следующим образом: механизм многоголового внимания преобразователя (реализованный tf.keras.layers.multiheadattentionв tensorflow) позволяет модели отслеживать каждую точку данных, относящуюся к конкретной другой точке данных. Если мы пытаемся предсказать завтрашнюю цену акций, то сегодняшняя цена явно имеет значение, но и другие цены тоже. Например, ценовое действие на этой неделе может быть очень похоже на то, что произошло три месяца назад, и поэтому для нас будет полезно, если наша модель сможет «запомнить» это ценовое действие. Эта потребность в «памяти» была одной из причин, по которой мы решили использовать рекуррентные нейронные сети и модели LSTM (длительная краткосрочная память). Позволяя предыдущим точкам данных передаваться в нашу модель в обратном направлении, RNN могут взвешивать предыдущие значения, создавая эффект памяти. Что отличает трансформеров, так это их уникальный механизм самовнимания, означающий, что они могут иметь тот же самый эффект «памяти» без всех повторений: данные подаются один раз, а самовнимание отслеживает соответствующие точки данных на каждом шаге (оно взвешивает то ценовое действие, которое было похоже на то, что наблюдалось в настоящее время, скажем, три месяца назад). Это означает, что трансформаторы обучаются намного быстрее, чем RNN, поскольку мы вводим данные только один раз, и им требуется гораздо меньше параметров, что является беспроигрышным вариантом. Для более убедительного и экспертного объяснения трансформаторов я дам ссылку на оригинальную статью, описывающую их, в конце этого поста. А пока перейдем к нашей проблеме.

Задача биржевых временных рядов

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

  1. Создайте надежную базовую модель LSTM для оценки производительности наших трансформаторов.
  2. Построение модели трансформатора
  3. Обучайте и оценивайте наши модели

Часть первая: LSTM

Давайте построим нашу базовую модель. Из предыдущих экспериментов я обнаружил, что наличие трех слоев, слоя LSTM с 200 нуэронами и двух плотных узлов с 50 и 1 нуэроном (нуэронами) соответственно, очень хорошо работает для решения этой задачи. Вот код для реализации этого:from keras.models import Sequential
from keras.layers import Dense
from keras.layers import LSTM

def build_lstm(etl: ETL, epochs=25, batch_size=32) -> tuple[tf.keras.Model, tf.keras.History]]:
«»»
Builds, compiles, and fits our LSTM baseline model.
«»»
n_timesteps, n_features, n_outputs = 5, 1, 5
callbacks = [tf.keras.callbacks.EarlyStopping(patience=10,
restore_best_weights=True)]
model = Sequential()
model.add(LSTM(200, activation=’relu’,
input_shape=(n_timesteps, n_features)))
model.add(Dense(50, activation=’relu’))
model.add(Dense(n_outputs))
print(‘compiling baseline model…’)
model.compile(optimizer=’adam’, loss=’mse’, metrics=[‘mae’, ‘mape’])
print(‘fitting model…’)
history = model.fit(etl.X_train, etl.y_train,
batch_size=batch_size,
epochs=epochs,
validation_data=(etl.X_test, etl.y_test),
verbose=1,
callbacks=callbacks)
return model, history

А вот краткое описание модели, связанное с этой моделью после ее компиляции:

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

Часть вторая: Трансформер

Теперь для нашей архитектуры трансформатора мы будем использовать конструкцию, рекомендованную в документации keras (я дам ссылку в конце поста), но эта конфигурация создана для классификации, поэтому мы внесем небольшое изменение; Мы изменим функцию активации конечных выходных слоев с Softmax на Relu, а нашу функцию потерь на Mean squared Error. Кроме того, я установил гиперпараметры на то, что, по моему мнению, лучше всего подходит для этой задачи с помощью экспериментов. Вот что мы получаем:def transformer_encoder(inputs, head_size, num_heads, ff_dim,
dropout=0, attention_axes=None):
«»»
Creates a single transformer block.
«»»
x = layers.LayerNormalization(epsilon=1e-6)(inputs)
x = layers.MultiHeadAttention(
key_dim=head_size, num_heads=num_heads, dropout=dropout,
attention_axes=attention_axes
)(x, x)
x = layers.Dropout(dropout)(x)
res = x + inputs

# Feed Forward Part
x = layers.LayerNormalization(epsilon=1e-6)(res)
x = layers.Conv1D(filters=ff_dim, kernel_size=1, activation=»relu»)(x)
x = layers.Dropout(dropout)(x)
x = layers.Conv1D(filters=inputs.shape[-1], kernel_size=1)(x)
return x + res

def build_transfromer(head_size,
num_heads,
ff_dim,
num_trans_blocks,
mlp_units, dropout=0, mlp_dropout=0) -> tf.keras.Model:
«»»
Creates final model by building many transformer blocks.
«»»
n_timesteps, n_features, n_outputs = 5, 1, 5
inputs = tf.keras.Input(shape=(n_timesteps, n_features))
x = inputs
for _ in range(num_trans_blocks):
x = transformer_encoder(x, head_size, num_heads, ff_dim, dropout)

x = layers.GlobalAveragePooling1D(data_format=»channels_first»)(x)
for dim in mlp_units:
x = layers.Dense(dim, activation=»relu»)(x)
x = layers.Dropout(mlp_dropout)(x)

outputs = layers.Dense(n_outputs, activation=’relu’)(x)
return tf.keras.Model(inputs, outputs)

transformer = build_transfromer(head_size=128, num_heads=4, ff_dim=2,
num_trans_blocks=4, mlp_units=[256],
mlp_dropout=0.10, dropout=0.10,
attention_axes=1)

transformer.compile(
loss=»mse»,
optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),
metrics=[«mae», ‘mape’],
)

callbacks = [tf.keras.callbacks.EarlyStopping(patience=10,
restore_best_weights=True)]

t_hist = transformer.fit(data.X_train, data.y_train, batch_size=32,
epochs=25, validation_data=(data.X_test, data.y_test),
verbose=1, callbacks=callbacks)

И этот код построит модель, которая будет выглядеть примерно так (повторяется num_trans_blocks раза).

Это один трансформаторный блок, в нашей модели будет 4 таких блока, сложенных друг на друга.

Наконец, в общей сложности наш трансформаторный блок имеет 17 205 общих параметров, что составляет чуть более одной десятой от нашего LSTM. Теперь приступим к тренировкам.

Часть третья: Обучение и оценка

Обучим обе сети за 25 эпох с помощью оптимизатора Адама. Во-первых, давайте обсудим производительность наших базовых моделей на тестовых данных. Прежде всего, я хотел бы отметить, что LSTM удивительно стабилен, то есть, если вы обучите его 10 раз подряд, он даст вам прогнозы, которые будут работать почти одинаково на тестовом наборе во всех десяти случаях. Кроме того, он тренируется быстрее, чем я ожидал, в общей сложности 143 секунды. Он также хорошо справляется со временем вывода, занимая всего двадцать две секунды. Это тем более впечатляюще, что, по крайней мере, в этом сравнении, LSTM имеет вес в 171 000+ параметров, как упоминалось ранее.

Визуализация наших прогнозов LSTM.

С точки зрения его предсказательной способности, мы считаем, что LSTM является искусным (не удивительно, это наша базовая линия не просто так). Он набирает MAPE 2,44% в нашем тестовом наборе. В целом, LSTM является последовательной, легко обучаемой моделью, которая умело предсказывает данные временных рядов акций. Единственное его ограничение заключается в том, что он большой, и поэтому его нельзя легко масштабировать. Однако, когда дело доходит до цен на акции, на самом деле недостаточно данных для построения очень глубоких моделей, если вы это сделаете, вы фактически начнете терять производительность. Таким образом, я не думаю, что большое количество параметров действительно сильно вредит LSTM.

Вот график Трансформера. Он взят из модели с MAPE 2,8%. Мне не удалось получить визуализацию золотых моделей.

Далее поговорим о трансформаторе. Первое, что хотелось бы отметить, это то, что трансформатор более нестабилен, чем LSTM. Что я имею в виду? Я обучал одни и те же модели (читай: одни и те же гиперпараметры) много-много раз. Каков результат? Я обучил лучшую модель, которую я построил для прогнозирования цен на акции, и пришел с MAPE 2,37%. Я построил еще один с MAPE 2,41%. Оба показателя, как вы заметите, лучше, чем наш базовый уровень, который стабильно весил около 2,45%-2,5%. Но, к сожалению, трансформатор не смог последовательно воспроизвести эту производительность, даже с теми же гиперпараметрами, что и эти два золотых тренировочных прогона. Более того, нельзя сказать, что трансформатор работал немного хуже. Были времена, когда его MAPE превышал 3%. Это проблема, если мы пытаемся строить, а затем переобучать модели каждый месяц или, скажем, квартал. У вас может быть мощная модель трансформатора, вы можете переобучить ее и остаться с кучей хлама. Так что в этом отношении трансформатор не был идеальным.

Так где же на самом деле засиял трансформатор? Подсчет параметров. Он весит чуть более одной десятой по количеству параметров, чем LSTM. Это означало, что трансформатор обучался быстрее, чем LSTM, ему требовалось всего 138 секунд против 143 у LSTM. Но, как ни странно, логический вывод на LSTM был быстрее: трансформатор занял в общей сложности 25 секунд.

Наконец, с точки зрения относительной дисперсии прогнозов по сравнению с фактическими значениями, другими словами, насколько много неопределенности заложено в наши прогнозы, LSTM подстраховался: она составила от 2,4% до 2,6%.

Заключение

Прежде всего, следует отметить, что производительность трансформера была действительно впечатляющей. Имея всего ~ 17 000, он смог не отставать от LSTM с более чем 170 000. Это не подлые ноги. Однако она была непредсказуемой и нестабильной в условиях переподготовки. Я не знаю, является ли это общей особенностью трансформаторов или проблемой с моей реализацией и использованием их, но это определенно было очень заметно. Это может не быть проблемой, если вы обучаете большую языковую модель; Английский не меняется так быстро, чтобы каждые пару недель приходилось выпускать новую модель. Но для прогнозирования цен на акции, возможность переобучаться по мере поступления новых данных, не беспокоясь о том, что ваша ранее очень умелая модель теперь просто посредственна, что ж, это становится очень важным. Я думаю, что по этой причине, а также из-за того, что наша экономия параметров не ускорила время обучения, но настолько, я думаю, что за свои деньги я бы все равно выбрал LSTM. Кроме того, нам не нужно масштабировать эти модели до миллиардов параметров, в том режиме, в котором трансформаторы действительно блистают. Параметры 1B по сравнению с 10B — это совсем другой выбор, чем 17 000 против 170 000.

Модели MA, ARMA и ARIMA в прогнозировании временных рядов

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

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

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

Простая скользящая средняя (SMA)

Все просто. Он вычисляет среднее значение последних n наблюдений.

В качестве примера возьмем цены BTC.import requests
import pandas as pd
import matplotlib.pyplot as plt

url = «https://api.coingecko.com/api/v3/coins/bitcoin/market_chart?vs_currency=usd&days=30»
response = requests.get(url)
data = response.json()
data
«»»
{‘prices’: [[1689541233624, 30281.386722199204],
[1689544814377, 30304.575216650694],
[1689548480051, 30340.36733508724],
[1689552032724, 30237.334785146097],

«»»

Мы получаем данные о цене биткоина за последние 30 дней из API CoinGecko.prices = [x[1] for x in data[‘prices’]]
timestamps = [x[0] for x in data[‘prices’]]
df = pd.DataFrame({‘Price’: prices}, index=pd.to_datetime(timestamps, unit=’ms’))
df
«»»
Price
2023-07-16 21:00:33.624 30281.386722
2023-07-16 22:00:14.377 30304.575217
2023-07-16 23:01:20.051 30340.367335
2023-07-17 00:00:32.724 30237.334785
2023-07-17 01:01:15.180 30225.182677
… …
2023-08-15 17:00:31.224 29248.678234
2023-08-15 18:01:06.858 29288.518660
2023-08-15 19:00:08.043 29308.965002
2023-08-15 20:00:06.070 29144.072559
2023-08-15 20:30:28.000 29195.129324
«»»

Мы можем использовать rolling метод фреймов данных Pandas для вычисления скользящей средней цен.df[‘SMA’] = df[‘Price’].rolling(window=5).mean()plt.figure(figsize=(10,6))
plt.plot(df[‘Price’], label=’BTC Price’)
plt.plot(df[‘SMA’], label=’Simple Moving Average’, linestyle=’—‘)
plt.legend()
plt.xlabel(‘Date’)
plt.ylabel(‘Price in USD’)
plt.title(‘Bitcoin Price and Simple Moving Average’)
plt.show()

Цены BTC и SMA (5 периодов). Изображение автора.

Оранжевая пунктирная линия — это скользящая средняя цены BTC (синяя) за 5-дневное окно. Мы можем попробовать 25 дней:

Цены BTC и SMA (25 периодов). Изображение автора.

Экспоненциальная скользящая средняя (EMA)

Это придает больший вес недавним наблюдениям. Он вычисляется с использованием взвешенной скользящей средней с экспоненциально уменьшающимся весом.

Мы можем использовать метод ewm для фреймов данных Pandas. Он используется для предоставления экспоненциально взвешенных функций в Pandas и принимает несколько параметров для управления расчетом экспоненциальной скользящей средней.

  • alpha — коэффициент сглаживания.
  • span задает диапазон экспоненциальной скользящей средней, которая используется для вычисления затухания в терминах span. Он определяется как

span = (2/α) — 1

  • com определяет центр масс, который имеет более физическую интерпретацию и может быть рассмотрен в терминах диапазона как:

com = (диапазон-1)/2

  • halflife определяет период полураспада в единицах времени, когда известна частота данных.

Период полураспада = log(2) / log(1-α)

  • adjust вычисляет взвешенные суммы с использованием весов (1−α)^i —>, если установлено значение True.

df[‘EMA’] = df[‘Price’].ewm(alpha=0.001, adjust=False).mean()plt.figure(figsize=(10,6))
plt.plot(df[‘Price’], label=’BTC Price’)
plt.plot(df[‘SMA’], label=’Simple Moving Average’, linestyle=’—‘)
plt.plot(df[‘EMA’], label=’Exponential Moving Average’, linestyle=’-.’)
plt.legend()
plt.xlabel(‘Date’)
plt.ylabel(‘Price in USD’)
plt.title(‘Bitcoin Price, Simple Moving Average, and Exponential Moving Average’)
plt.show()

SMA (25 окон), EMA (альфа=0,001). Изображение автора.
SMA (25 окон), EMA (альфа=0.2). Изображение автора.

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

Модель скользящей средней порядка q, обозначаемая как MA(q), моделирует временной ряд, выражая значение ряда в виде линейной комбинации текущего и предыдущего членов ошибки белого шума.

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

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

Модель MA(q) выражает текущее значение ряда как функцию текущей ошибки и предыдущих ошибок q. За пределами этих q-лагов прошлые ошибки не влияют на текущее значение. Следовательно, «память» модели коротка и распространяется только на самые последние члены q ошибок.

Во многих реальных сценариях влияние прошлых ошибок со временем уменьшается. Свойство короткой памяти отражает эту реальность, гарантируя, что модель фокусируется на наиболее актуальной и актуальной информации.

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

Так как модель МА стационарна, то в долгосрочной перспективе ее прогнозы будут сходиться к среднему значению ряда. Это отличается от нестационарных моделей, в которых прогнозы могут демонстрировать тенденции или дрейфовать с течением времени.

Несмотря на то, что модель MA сама по себе является стационарной, она может быть чувствительна к нестационарности в нижележащем ряду. Если ряд демонстрирует нестационарное поведение (например, тренд или сезонность), возможно, потребуется преобразовать или изменить его для достижения стационарности перед моделированием с помощью модели MA.

Авторегрессионная скользящая средняя (ARMA)

Он сочетает в себе два компонента: часть авторегрессии (AR) и часть скользящей средней (MA).

Часть AR выражает значение ряда в любой момент времени в виде линейной комбинации его предыдущих значений. А часть MA моделирует значение ряда как линейную комбинацию прошлых членов ошибок белого шума.

Модель ARMA объединяет части AR и MA в единую модель, что позволяет ей фиксировать в данных как импульсные, так и ударные эффекты. Модель порядка ARMA (p,q) выражается в виде:

  • Процесс дополненной реальности p-порядка
  • Процесс количественной оценки q-порядка
  • c представляет собой константу (или дрейф)
  • φ — коэффициент i запаздывания ряда
  • θ – соответствующий коэффициент i прошедшей ошибки.
  • ε — ошибка белого шума.

Авторегрессионная интегрированная скользящая средняя (ARIMA)

Он расширяет модель ARMA (Autoregressive Moving Average), включая «интегрированный» (I) компонент, позволяющий модели обрабатывать нестационарные данные путем дифференциации рядов один или несколько раз. Модель ARIMA часто обозначается как ARIMA (p,d,q), где pd и q — порядки компонент авторегрессии, интегрированного и скользящего среднего соответственно.

Модель предполагает, что временной ряд либо стационарен, либо может быть сделан стационарным с помощью дифференциации. Если эти предположения не выполняются, модель может работать плохо.

from statsmodels.tsa.arima.model import ARIMA

Класс statsmodels.tsa.arima.model.ARIMA(endog, exog=None, order=(0, 0, 0(0, 0, 0), seasonal_order=(0, 0, 0, (0, 0, 0, 0)trend=NoneNone, enforce_stationarity=True, enforce_invertibility=TrueTrue, concentrate_scale=False, trend_offset=1dates=Нет, freq=NoneNonemissing='none', validate_specification=True)

  • endog— это данные временных рядов. Он должен быть предоставлен в виде одномерного массивоподобного объекта (например, массив NumPy или pandas Series).
  • exogотносится к экзогенным переменным, которые являются внешними или внешними переменными, которые могут оказывать влияние на временные ряды. Это переменные, которые не моделируются как часть самого временного ряда, но могут объяснить некоторые вариации в ряду.

Например, если вы моделируете ежемесячные данные о продажах и считаете, что расходы на рекламу влияют на продажи, вы можете включить переменную, представляющую ежемесячные расходы на рекламу в качестве экзогенной переменной.exog_data = df[‘advertising_spend’]
model = ARIMA(endog=sales_data, exog=exog_data, order=(1, 0, 1))
fit_model = model.fit()

Ее можно рассматривать как регрессионную модель с ошибками ARIMA. В процессе аппроксимации модель оценивает коэффициенты для экзогенных переменных вместе с параметрами ARIMA. Эти коэффициенты отражают влияние экзогенных переменных на временной ряд.

  • order: кортеж, задающий порядок модели ARIMA. Задается как (p,d,q)

p: Порядок компонента авторегрессии (AR).

d: Степень дифференциации (I компонент).

q: Порядок составляющей скользящей средней (MA).

  • seasonal_order определяет порядок сезонной модели ARIMA. Дано как (PDQ,s), где:

P: Порядок сезонной составляющей авторегрессии.

D: Степень сезонных различий.

В: Порядок компонента сезонной скользящей средней.

s: Количество периодов в сезоне.

  • trend задает трендовый компонент модели. Это может быть ‘n’, ‘c’, ‘t’ или ‘ct’ для отсутствия тренда, постоянная, линейная или постоянная с линейным трендом соответственно.
  • enforce_stationarity: Если True, параметры дополненной реальности создают стационарный процесс. По умолчанию True.
  • enforce_invertibility: Если True, параметры MA создают обратимый процесс. По умолчанию True.

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

  • concentrate_scale: Если значение True, шкала сконцентрирована вне правдоподобия. Это уменьшает количество оцениваемых параметров на единицу, но требует нелинейной оптимизации.
  • trend_offset: целое число, указывающее смещение, с которого начинается тренд. По умолчанию 1.
  • dates: Необязательные даты, связанные с наблюдениями в endog. Может быть полезен для построения графиков и прогнозирования.
  • freq: Необязательная частота данных временных рядов (например, ‘D’ для дня).
  • missing: Указывает, как обрабатывать отсутствующие значения. По умолчанию установлено значение ‘none’, что означает, что пропущенные значения не допускаются.
  • validate_specification: Если True, проверяет правильность спецификации модели (например, обеспечивает стационарность и обратимость). По умолчанию True.

Давайте создадим пример набора данных для применения ARIMA. Мы можем использовать ArmaProcess для создания образцов.import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.arima_process import ArmaProcess
np.random.seed(42)

# AR part: 1 — 0.5 * z — 0.25 * z^2
# MA part: 1 + 0.5 * z + 0.25 * z^2
ar = np.array([1, -0.5, -0.25])
ma = np.array([1, 0.5, 0.25])

arma_process = ArmaProcess(ar, ma)
simulated_data = arma_process.generate_sample(nsample=1000)
simulated_series = pd.Series(simulated_data)plt.figure(figsize=(12, 6))
plt.plot(simulated_series.index, simulated_series.values, label=’Historical’)
plt.title(«Sample»)
plt.xlabel(«Time»)
plt.ylabel(«Value»)
plt.legend()
plt.show()

Теперь давайте подгоним модель и получим несколько прогнозов.arima_model = ARIMA(simulated_series, order=(2, 0, 2))
fit_arima_model = arima_model.fit()

# Forecast the next 10 observations
arima_forecast = fit_arima_model.get_forecast(steps=10)
arima_forecast_index = pd.RangeIndex(start=len(simulated_series), stop=len(simulated_series) + 10)
arima_forecast_series = pd.Series(arima_forecast.predicted_mean.values, index=arima_forecast_index)
print(arima_forecast_series)
«»»
1000 0.381838
1001 0.447337
1002 0.372533
1003 0.324770
1004 0.285872
1005 0.255143
1006 0.230782
1007 0.211478
1008 0.196181
1009 0.184058
dtype: float64
«»»

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

Прогнозирование нестационарных временных рядов

Знакомство

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

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

Общие сведения о стационарности

Прежде чем углубляться в нестационарные временные ряды, важно усвоить концепцию стационарности. Стационарный временной ряд — это ряд, статистические свойства которого не изменяются с течением времени. К этим свойствам относятся среднее значение, дисперсия и автокорреляция. По сути, стационарный временной ряд имеет постоянное среднее значение и дисперсию и демонстрирует последовательные закономерности и тенденции на протяжении всех своих наблюдений. Стационарные данные легче моделировать и прогнозировать, так как они следуют стабильным статистическим свойствам.

Проблемы, связанные с нестационарными временными рядами

Нестационарные временные ряды, с другой стороны, демонстрируют изменяющиеся статистические свойства с течением времени. Это усложняет работу с ними и прогнозирование. Основными проблемами, связанными с нестационарными временными рядами, являются:

  1. Тренд: Нестационарные данные часто отображают тренд, который представляет собой долгосрочное систематическое движение в одном направлении, либо вверх (восходящий тренд), либо вниз (нисходящий тренд). Идентификация и устранение тренда имеет решающее значение для прогнозирования.
  2. Сезонность: Сезонность является еще одной распространенной характеристикой нестационарных данных. Он представляет собой периодические колебания, происходящие через равные промежутки времени, такие как ежедневные, ежемесячные или годовые закономерности. Учет сезонности жизненно важен для составления точных прогнозов.
  3. Гетероскедастичность: Нестационарные временные ряды часто демонстрируют различные уровни волатильности или дисперсии с течением времени. Такая гетероскедастичность может затруднить разработку моделей прогнозирования, предполагающих постоянную дисперсию.

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

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

  1. Дифференциация: Одним из основных методов работы с нестационарностью является дифференциация. Это включает в себя вычисление различий между последовательными наблюдениями, что может помочь стабилизировать среднее значение и устранить тенденции и сезонность.
  2. Сезонная декомпозиция: Сезонная декомпозиция временных рядов (STL) — это метод, который разделяет временной ряд на трендовые, сезонные и остаточные компоненты. Такая декомпозиция позволяет моделировать и прогнозировать каждый компонент отдельно, что упрощает работу с нестационарностью.
  3. Экспоненциальное сглаживание: Методы экспоненциального сглаживания, такие как метод Холта-Винтерса, эффективны для прогнозирования временных рядов с трендами и сезонностью. Эти методы присваивают различные веса прошлым наблюдениям, придавая большее значение недавним точкам данных.
  4. Модели ARIMA: Модели AutoRegressive Integrated Moving Average (ARIMA) широко используются для нестационарных временных рядов. Модели ARIMA включают в себя дифференциацию данных, чтобы сделать их стационарными, а затем моделирование с использованием компонентов авторегрессии и скользящего среднего.
  5. Сезонная ARIMA: Для временных рядов как с трендовыми, так и с сезонными моделями ARIMA (SARIMA) расширяют структуру ARIMA, включая компоненты сезонной дифференциации, сезонной авторегрессии и скользящего среднего.
  6. Модели пространства состояний: Модели пространства состояний, такие как фильтр Калмана, обеспечивают гибкую основу для моделирования и прогнозирования нестационарных временных рядов. Эти модели могут фиксировать сложную динамику и структурные изменения в данных.

Код

Прогнозирование нестационарных временных рядов обычно включает в себя ряд шагов, включая предварительную обработку данных, выбор модели, обучение и построение графика результатов. Вот пример кода на Python для прогнозирования нестационарного временного ряда с использованием модели ARIMA с набором данных и графиками:# Import necessary libraries
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from statsmodels.tsa.stattools import adfuller

# Generate or load a non-stationary time series dataset
# For demonstration, we’ll generate a simple non-stationary dataset
np.random.seed(42)

# Generate a non-stationary time series with a trend and seasonality
t = np.arange(1, 101)
seasonal_component = 10 * np.sin(0.2 * t)
trend_component = 0.5 * t
noise = np.random.normal(0, 2, 100)
data = seasonal_component + trend_component + noise

# Create a pandas DataFrame from the dataset
df = pd.DataFrame({‘Data’: data})

# Plot the original time series data
plt.figure(figsize=(12, 6))
plt.plot(df.index, df[‘Data’], label=’Original Data’)
plt.xlabel(‘Time’)
plt.ylabel(‘Value’)
plt.title(‘Non-Stationary Time Series Data’)
plt.legend()
plt.show()

# Check for stationarity using Augmented Dickey-Fuller test
def adf_test(series):
result = adfuller(series)
print(‘ADF Statistic:’, result[0])
print(‘p-value:’, result[1])
print(‘Critical Values:’, result[4])

adf_test(df[‘Data’])

# Differencing to make the time series stationary
df[‘Differenced_Data’] = df[‘Data’] — df[‘Data’].shift(1)
df = df.dropna()

# Plot the differenced time series data
plt.figure(figsize=(12, 6))
plt.plot(df.index, df[‘Differenced_Data’], label=’Differenced Data’)
plt.xlabel(‘Time’)
plt.ylabel(‘Value’)
plt.title(‘Stationary Time Series Data’)
plt.legend()
plt.show()

# ACF and PACF plots for determining ARIMA orders
plt.figure(figsize=(12, 6))
plot_acf(df[‘Differenced_Data’], lags=20, ax=plt.gca())
plt.title(‘ACF Plot’)
plt.show()

plt.figure(figsize=(12, 6))
plot_pacf(df[‘Differenced_Data’], lags=20, ax=plt.gca())
plt.title(‘PACF Plot’)
plt.show()

# Split the data into training and testing sets
train_size = int(0.8 * len(df))
train, test = df[‘Differenced_Data’][:train_size], df[‘Differenced_Data’][train_size:]

# Fit an ARIMA model to the training data
model = ARIMA(train, order=(1, 1, 1))
model_fit = model.fit()

# Forecast the test data
forecast = model_fit.forecast(steps=len(test))

# Calculate prediction intervals
residuals = test — forecast
prediction_interval = 1.96 * np.std(residuals) # 1.96 for a 95% prediction interval

# Plot the forecasts and the actual values with prediction intervals
plt.figure(figsize=(12, 6))
plt.plot(df.index[train_size:], test, label=’Actual’)
plt.plot(df.index[train_size:], forecast, label=’Forecast’, color=’red’)
plt.fill_between(df.index[train_size:], forecast — prediction_interval, forecast + prediction_interval, color=’pink’, alpha=0.3)
plt.xlabel(‘Time’)
plt.ylabel(‘Value’)
plt.title(‘ARIMA Forecasting with Prediction Intervals’)
plt.legend()
plt.show()

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

Обратите внимание, что в реальном сценарии данные временных рядов обычно загружаются из файла или базы данных. Кроме того, может потребоваться тонкая настройка параметров модели ARIMA в зависимости от характеристик конкретного набора данных.

Заключение

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

Оценка синтетических временных рядов

Изучение различных метрик для оценки синтетических временных рядов на практических примерах кода

Эта запись блога доступна в виде записной книжки Jupyter на GitHub и является частью TSGM, библиотеки для генеративного моделирования временных рядов.

Сегодня мы поговорим об оценке синтетических наборов данных временных рядов — наборов данных, которые искусственно создаются для представления реальных данных. Допустим, имеется синтетический набор данных D*, предназначенный для представления реального набора данных D. Важно количественно оценить, насколько хороши эти синтетические данные: хорошо ли D* представляет D? Защищены ли эти данные? Являются ли эти данные ценными для решения последующих задач? В этом руководстве мы рассмотрим методы, используемые для количественной и качественной оценки качества синтетических данных временных рядов.

Пример исходных и синтетических синусоидальных данных.

Для начала рассмотрим два случая из [1], описывающие возможные варианты использования синтетических данных:

Сценарий 1. Организация хочет нанять внешнего агента для анализа конфиденциальных данных или изучения статистических методов для решения данной проблемы. Обмен реальными данными может быть затруднен из-за конфиденциальности или коммерческих соображений. Синтетические аналоги могут обеспечить удобное решение этой проблемы.

Сценарий 2. Организация хочет обучить модель на относительно небольшом наборе данных. Однако набор данных недостаточен для желаемого качества моделирования. Такие ограниченные наборы данных могут быть дополнены синтетическими данными. Эти синтетические данные, которые должны быть похожи на реальные данные, направлены на повышение производительности модели или, в других случаях, на помощь в тестировании надежности модели.

В целом, в этом руководстве мы указываем и описываем следующие метрики:

  1. сходство реальных данных (рис. 1 и 2),
  • метрика расстояния,
  • дискриминационная метрика,
  • Максимальный средний балл расхождения

2. предиктивная согласованность (Sc. 1),

3. эффективность переработки и сбыта (Sc. 2),

4. неприкосновенность частной жизни (Ск. 1),

5. разнообразие (Sc. 1 и Sc. 2),

6. Справедливость (Sc. 1 и Sc.2),

7. Визуальное сравнение (рис. 1 и 2).

В TSGM все метрики аккуратно организованы в tsgm.metrics Узнайте больше о деталях с помощью нашей подробной документации.

Теперь давайте начнем с примера кодирования, установив tsgm:pip install tsgm

Генерация синтетических данных. Далее мы импортируем tsgm и загружаем примерный набор данных. Тензор Xr теперь будет содержать 100 синусоидальных временных рядов или постоянных временных рядов (на основе целевого класса yr). Мы будем использовать ((Xr, yr)) в качестве реального (= исторического = исходного) набора данных. Xs содержит синтетические данные, сгенерированные вариационным автоэнкодером. (Примечание: мы используем только одну эпоху для демонстрации; увеличиваем количество эпох и проверяем сходимость обучения для практического применения).import numpy as np
import functools
import sklearn
import tensorflow as tf
from tensorflow import keras

import tsgm

n, n_ts, n_features = 100, 100, 20
vae_latent_dim = 8

# Load data that will be used as real
Xr, yr = tsgm.utils.gen_sine_vs_const_dataset(n, n_ts, n_features, max_value=2, const=1)
Xr = Xr.astype(np.float32)
yr = keras.utils.to_categorical(yr).astype(np.float32)
ys = yr # use real labels as synthetic labels

# Using real data generate synthetic time series dataset
scaler = tsgm.utils.TSFeatureWiseScaler()
scaled_data = scaler.fit_transform(Xr)
architecture = tsgm.models.zoo[«cvae_conv5»](n_ts, n_features, vae_latent_dim)
encoder, decoder = architecture.encoder, architecture.decoder
vae = tsgm.models.cvae.cBetaVAE(encoder, decoder, latent_dim=vae_latent_dim, temporal=False)
vae.compile(optimizer=keras.optimizers.Adam())

# Train VAE using historical data
vae.fit(scaled_data, yr, epochs=1, batch_size=64)
Xs, ys = vae.generate(ys)

d_real = tsgm.dataset.Dataset(Xr, yr)
d_syn = tsgm.dataset.Dataset(Xs, ys)

Сходство с реальными данными

Метрика расстояния

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

Чем меньше расстояние, тем ближе синтетические данные к реализму фактических данных. Теперь давайте определим набор статистических данных, которые послужат основой для нашей метрики расстояния. Методы tsgm.metrics.statistics.axis_*_s вычисляют * по заданной оси.statistics = [
functools.partial(tsgm.metrics.statistics.axis_max_s, axis=None),
functools.partial(tsgm.metrics.statistics.axis_min_s, axis=None),
functools.partial(tsgm.metrics.statistics.axis_max_s, axis=1),
functools.partial(tsgm.metrics.statistics.axis_min_s, axis=1)]

Двигаясь вперед, давайте установим метрику расстояния. Для простоты остановимся на евклидовой норме.discrepancy_func = lambda x, y: np.linalg.norm(x — y)

Собрав все это вместе, мы будем использовать объект tsgm.metrics.DistanceMetricdist_metric = tsgm.metrics.DistanceMetric(
statistics=statistics, discrepancy=discrepancy_func
)
print(dist_metric(d_real, d_syn))

Метрика MMD

Альтернативный подход предполагает сравнение синтетических и реальных распределений данных. В этом контексте использование метода максимального среднего расхождения (MMD) [3] оказывается удобным. MMD служит в качестве непараметрического двухвыборочного теста, позволяющего определить, взяты ли выборки из одного и того же распределения. С помощью эмпирических наблюдений мы определили метрику MMD как особенно удобный метод оценки схожести реальных данных.mmd_metric = tsgm.metrics.MMDMetric()
print(mmd_metric(Xr, Xs))

Дискриминационная метрика

При таком подходе модель обучается различать реальные и синтетические данные. В TSGM tsgm.metrics.DiscriminativeMetricоказывается ценным инструментом для этой цели. Эта метрика облегчает оценку того, насколько эффективно модель может различать реальные и синтетические наборы данных, предоставляя дополнительную перспективу сходства данных.# use LSTM classification model from TSGM zoo.
model = tsgm.models.zoo[«clf_cl_n»](
seq_len=Xr.shape[1], feat_dim=Xr.shape[2], output_dim=1).model
model.compile(
tf.keras.optimizers.Adam(),
tf.keras.losses.CategoricalCrossentropy(from_logits=False)
)

# use TSGM metric to measure the score
discr_metric = tsgm.metrics.DiscriminativeMetric()
print(
discr_metric(
d_hist=Xr, d_syn=Xs, model=model,
test_size=0.2, random_seed=42, n_epochs=10
)
)

Метрика согласованности

Далее мы переходим к метрике согласованности. Эта идея согласуется со сценарием 1, описанным выше. Здесь мы сосредоточимся на измерении согласованности набора последующих моделей. Рассмотрим более детально набор моделей М и оценщик E:M × D → R.

Чтобы оценить согласованность M на D и D*, мы измеряем p(m₁ ∼ m₂| m₁, m₂ ∈ M, D, D*), где m₁ ∼ m₂ означает m₁, согласующееся с m₂: «если m₁ превосходит m₂ там, где модели, обученные на D, то оно превосходит m₂ на D* и наоборот». Оценка этой вероятности включает в себя фиксацию конечного множества M и оценку моделей с использованием реальных данных, а также их раздельную оценку с использованием синтетических данных.

В TSGM первым шагом является определение набора оценщиков. Для этой цели мы будем использовать коллекцию LSTM-моделей, состоящую из одного-трех блоков LSTM.class EvaluatorConvLSTM():
»’
NB an oversimplified classifier, for educational purposes only.
»’

def __init__(self, model):
self._model = model

def evaluate(self, D: tsgm.dataset.Dataset, D_test: tsgm.dataset.Dataset) -> float:
X_train, y_train = D.Xy
X_test, y_test = D_test.Xy

self._model.fit(X_train, y_train)

y_pred = np.argmax(self._model.predict(X_test), 1)
print(self._model.predict(X_test).shape)
y_test = np.argmax(y_test, 1)
return sklearn.metrics.accuracy_score(y_pred, y_test)

# Define a set of models
seq_len, feat_dim, n_classes = *Xr.shape[1:], 2
models = [tsgm.models.zoo[«clf_cl_n»](seq_len, feat_dim, n_classes, n_conv_lstm_blocks=i) for i in range(1, 4)]
for m in models:
m.model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
evaluators = [EvaluatorConvLSTM(m.model) for m in models]

# Utilize the set of evaluators with ConsistencyMetric from tsgm
consistency_metric = tsgm.metrics.ConsistencyMetric(evaluators=evaluators)
print(consistency_metric(d_real, d_syn, d_real))

Производительность на переработке и сбыте

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

1. Дополнение реальных данных синтетическими.
Такой подход оказывается полезным в условиях ограниченности данных. Дополняя реальные данные сгенерированными аналогами, мы стремимся улучшить обучающую выборку для повышения производительности модели. Смотрите нашу публикацию в блоге об аугментации данных здесь [2].
2. Использование сгенерированных данных исключительно для последующего обучения модели.
В сценариях, где реальные данные ограничены и конфиденциальны, этот подход вступает в игру. В этом случае нисходящая модель обучается исключительно на сгенерированных данных, а затем оценивается на реальных данных.downstream_model = tsgm.models.zoo[«clf_cl_n»](seq_len, feat_dim, n_classes, n_conv_lstm_blocks=1).model
downstream_model.compile(loss=’binary_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])

evaluator = EvaluatorConvLSTM(downstream_model)

downstream_perf_metric = tsgm.metrics.DownstreamPerformanceMetric(evaluator)
print(downstream_perf_metric(d_real, d_syn, d_real))

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

Конфиденциальность: метрика атаки на вывод членства

Визуализация атаки с выводом членства.

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

«tsgm.metrics.PrivacyMembershipInferenceMetric» измеряет восприимчивость к атакам, связанным с выводом членства, используя синтетические данные. Пошаговая процедура оценки выглядит следующим образом:
1. Разделение данных. Разбить исторические данные на обучающий и контрольный наборы (обозначаются как Dt и Dh),
2. Обучение генеративной модели. Обучить генеративную модель на Dt и сгенерировать синтетический набор данных D*,
3. Обучение модели одноклассовой классификации (OCC). Обучите модель одноклассовой классификации (OCC) на синтетических данных D* и оцените ее на Dt и Dh,
4. Расчет целевого балла. В качестве целевой оценки используйте единицу минус точность модели OCC.

Этот процесс оценки дает представление о потенциальной уязвимости к атакам с целью вывода членства, использующим синтетические данные.

Давайте теперь представим модель злоумышленника. Для наглядности определим одноклассовый SVM-классификатор.class FlattenTSOneClassSVM:
def __init__(self, clf):
self._clf = clf

def fit(self, X):
X_fl = X.reshape(X.shape[0], -1)
self._clf.fit(X_fl)

def predict(self, X):
X_fl = X.reshape(X.shape[0], -1)
return self._clf.predict(X_fl)

attacker = FlattenTSOneClassSVM(sklearn.svm.OneClassSVM())
privacy_metric = tsgm.metrics.PrivacyMembershipInferenceMetric(
attacker=attacker
)

Теперь давайте определим тестовый набор и измерим метрику конфиденциальности:X_test, y_test = tsgm.utils.gen_sine_vs_const_dataset(10, 100, 20, max_value=2, const=1)
d_test = tsgm.dataset.Dataset(X_test, keras.utils.to_categorical(y_test))

# 1 indicates high privacy and 0 — low privacy.
privacy_metric(d_real, d_syn, d_test)

Разнообразие

С помощью этой метрики наша цель состоит в том, чтобы количественно оценить разнообразие синтетических данных. Рассмотрим изображение ниже, где красные точки представляют реальные данные, а синие точки означают синтетические данные. Какой вариант дает превосходный синтетический набор данных? Тот, что справа, кажется более благоприятным, но почему? Ответ кроется в его разнообразии, что делает его потенциально более универсальным и полезным. Однако одного разнообразия недостаточно; Очень важно учитывать другие метрики в тандеме, такие как расстояние или производительность на последующих этапах. В нашем исследовании мы проиллюстрируем эту концепцию с помощью энтропии.

Синтетические данные в 2D. А: недифференцированные синтетические данные; B: разнообразные синтетические данные.

spec_entropy = tsgm.metrics.EntropyMetric()
print(spec_entropy(Xr))
print(spec_entropy(Xs))

Справедливость

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

Возьмем, к примеру, равенство возможностей. Равенство возможностей служит метрикой справедливости, предназначенной для оценки того, предсказывает ли классификатор предпочтительный ярлык (тот, который дает преимущество или выгоду человеку) и данного атрибута этот предпочтительный ярлык одинаково хорошо для всех значений этого атрибута [6]. Этот показатель играет важную роль в обеспечении беспристрастности и равного отношения к различным атрибутивным значениям. Отличный пример этой метрики приведен в [6]: «Предположим, что Университет Глуббдубдриба принимает как лилипутов, так и бробдингнегцев на строгую математическую программу. Средние школы лилипутов предлагают обширную учебную программу математических классов, и подавляющее большинство учащихся имеют право на участие в университетской программе. В средних школах Бробдингнага вообще нет уроков математики, и в результате гораздо меньше их учеников имеют соответствующую квалификацию. Равенство возможностей обеспечивается для предпочтительного ярлыка «принятый» в отношении национальности (лилипут или бробдингнегианец), если квалифицированные студенты имеют равные шансы быть принятыми, независимо от того, являются ли они лилипутами или бробдингнагцами».

Качественный анализ

Для того, чтобы качественно оценить данные, удобно:
a. отбирать образцы и визуализировать отдельные образцы из синтетических и реальных данных,
b. строить встраивания сгенерированных образцов и визуализировать их с помощью, например, TSNE. Давайте проиллюстрируем (b) с TSGM:tsgm.utils.visualize_tsne_unlabeled(Xr, Xs, perplexity=10, markersize=20, alpha=0.5)

Визуализация исходных и синтетических данных временных рядов TSNE.

Цитата

Эта запись в блоге является частью проекта TSGM, в рамках которого мы создаем инструмент для улучшения конвейеров временных рядов за счет дополнения и генерации синтетических данных. Если вы нашли это полезным, взгляните на наш репозиторий и процитируйте статью о TSGM:@article{
nikitin2023tsgm,
title={TSGM: A Flexible Framework for Generative Modeling of Synthetic Time Series},
author={Nikitin, Alexander and Iannucci, Letizia and Kaski, Samuel},
journal={arXiv preprint arXiv:2305.11567},
year={2023}
}

Заключение

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

Ссылки:

[1] Никитин А., Ианнуччи Л., Каски С., 2023. TSGM: Гибкая среда для генеративного моделирования синтетических временных рядов. Препринт arXiv arXiv:2305.11567. Ссылка на Arxiv.
[2] Аугментации временных рядов, пост TowardsDataScience, https://medium.com/towards-data-science/time-series-augmentations-16237134b29b.
[3] Греттон А., Боргвардт К.М., Раш М.Д., Шёлькопф Б. и Смола А., 2012. Тест ядра с двумя выборками. Журнал исследований машинного обучения, 13(1), стр.723–773. Ссылка на JMLR.
[4] Вэнь, К., Сун, Л., Ян, Ф., Сун, Х., Гао, Дж., Ван, С. и Сюй, Х., 2020. Дополнение данных временных рядов для глубокого обучения: опрос. Препринт arXiv arXiv:2002.12478. Ссылка на Arxiv.
[5] Ваттенберг, М., Вьегас, Ф. и Хардт, М., 2016. Борьба с дискриминацией с помощью интеллектуального машинного обучения. Google Research, 17. Ссылка на Google Research.
[6] Глоссарий по машинному обучению: справедливость. Блог разработчиков Google.

Обнаружение аномалий временных рядов

Обнаружение аномалий автором с помощью DALL· Э 3

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

Введение в анализ временных рядов

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

Однако анализ временных рядов также сопряжен с проблемами и отличиями от невременного анализа данных. Одно из основных отличий заключается в том, что временные ряды зависят от времени, т.е. порядок и диапазон данных имеют значение и не могут быть проигнорированы или изменены. Еще одно отличие заключается в том, что временные ряды часто нестационарны, т.е. их статистические свойства (такие как среднее и дисперсия) изменяются со временем. Это затрудняет применение традиционных статистических методов, предполагающих стационарность данных.

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

Основные понятия анализа временных рядов

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

Временной ряд состоит из трех основных компонентов: даты, времени и характеристик. Дата и время указывают, когда было измерено значение переменной. Характеристики — это те самые переменные, которые мы хотим проанализировать. В нашем примере дата — это день месяца, погода — день недели, а характеристика — количество посетителей.

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

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

Третье требование — иметь четкое определение целей анализа, т.е. что мы хотим узнать из данных и как мы хотим их использовать. Цели анализа могут быть разными в зависимости от сценария использования и исследовательского вопроса. Например, мы можем захотеть проанализировать временной ряд, чтобы:

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

Общие сведения об аномалиях во временных рядах

Прежде чем мы рассмотрим, как обнаруживать аномалии во временных рядах, нам нужно понять, что такое аномалии и как они проявляются в данных. Аномалия — это значение или событие, которое значительно отклоняется от нормального тренда данных. Аномалии могут быть двух типов: пунктуальные и коллективные. Точечные аномалии — это изолированные значения, которые сильно отличаются от других значений во временном ряду. Коллективные аномалии — это группы значений, которые отличаются от остальных временных рядов.

Например, на следующем рисунке мы видим временной ряд, который записывает количество посетителей веб-сайта каждый день в течение месяца. Точечные аномалии обозначены красным цветом, а коллективные аномалии — синим.

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

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

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

На следующем рисунке показан пример временного ряда, который имеет тенденцию к росту, годовую сезонность и шум.

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

Требования к данным для анализа временных рядов

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

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

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

Дифференциация в анализе временных рядов

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

Дифференцирование заключается в вычитании предыдущего значения из каждого значения во временном ряду, в результате чего получается новый временной ряд, представляющий изменение данных с течением времени. Например, если у нас есть временная серия {x1, x2, x3, …}, то ее первое отличие равно {x2 — x1, x3 — x2, …}. Дифференциацию можно повторять несколько раз, добиваясь таким образом второго отличия, третьего различия и т.д. Дифференциация призвана убрать из временных рядов компоненты тренда и сезонности, которые являются основными причинами нестационарности. Фактически, если данные имеют тренд или сезонность, их значения будут коррелировать с предыдущими или последующими значениями. Вычитание этих значений уменьшает или устраняет эту корреляцию.

Например, мы можем увидеть временной ряд, который имеет тенденцию к увеличению и годовую сезонность. Первое его отличие убирает тренд, но не сезонность. Второе его отличие убирает как тренд, так и сезонность.

Дифференцирование лежит в основе одной из наиболее широко используемых моделей для анализа временных рядов и обнаружения аномалий: модели ARIMA.

Введение в модель ARIMA

Модель ARIMA является одной из наиболее широко используемых моделей для анализа временных рядов и обнаружения аномалий. Аббревиатура ARIMA расшифровывается как Autoregressive Integrated Moving Average (Авторегрессионная интегрированная скользящая средняя). Данная модель сочетает в себе три основных компонента:

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

Модель ARIMA имеет три основных параметра: pd и q. Параметр p указывает количество терминов авторегрессии, используемых в модели. Параметр d указывает, сколько раз временной ряд должен быть дифференцирован, чтобы сделать его стационарным. Параметр q указывает количество членов скользящей средней, используемых в модели. Например, в модели ARIMA(1,1,1) используются член авторегрессии, разность и член скользящего среднего.

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

  • Во-первых, нам нужно проверить, является ли временной ряд стационарным или нет. Мы можем использовать статистические тесты, такие как расширенный критерий Дики-Фуллера, чтобы проверить, являются ли среднее значение и дисперсия временных рядов постоянными с течением времени.
  • Во-вторых, нам нужно дифференцировать временной ряд до тех пор, пока он не станет стационарным. Мы можем использовать графики, такие как график автокорреляционных функций и частичных автокорреляционных функций, чтобы определить необходимое количество разностей.
  • В-третьих, необходимо оценить параметры модели ARIMA с помощью методов оптимизации, таких как метод максимального правдоподобия. Мы можем использовать критерии выбора модели, такие как информационный критерий Акаике или байесовский информационный критерий, для выбора оптимальных значений параметров p, d и q.
  • В-четвертых, нам необходимо валидировать модель ARIMA с помощью методов верификации, таких как тест Люнга-Бокса или тест Жака-Бера. Мы можем использовать графики, такие как график невязок или график прогноза, чтобы проверить, хорошо ли модель согласуется с данными и есть ли в данных какие-либо аномалии.
  • В-пятых, необходимо использовать модель ARIMA для описания основных характеристик временных рядов, прогнозирования будущих значений временных рядов и обнаружения аномалий во временных рядах. Мы можем использовать меры точности, такие как среднеквадратическая ошибка или средняя абсолютная ошибка, для оценки качества прогнозов и аномалий.

Обнаружение аномалий временных рядов

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

Порог для определения аномалий зависит от нескольких факторов, таких как уровень достоверности, распределение ошибок, частота данных и т. д. В общем случае для определения порога можно использовать понятие доверительного интервала. Доверительный интервал — это интервал, который содержит прогнозируемое значение с определенной вероятностью. Например, доверительный интервал 95 % означает, что прогнозируемое значение находится в этом диапазоне с вероятностью 95 %. Если наблюдаемое значение находится за пределами доверительного интервала, то это можно считать аномалией.

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

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

В этой статье мы рассмотрели, как использовать модель ARIMA для анализа временных рядов и обнаружения аномалий.

Заключение

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

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

Модели пространства состояний и фильтрация Калмана

Анализ временных рядов — это мощный инструмент для понимания и прогнозирования поведения данных, изменяющихся с течением времени. Модели пространства состояний и фильтрация Калмана являются передовыми методами, которые позволяют нам моделировать и оценивать базовые состояния системы на основе зашумленных наблюдений. В этом уроке мы изучим концепции моделей пространства состояний и фильтрации Калмана и реализуем их в Python.

Содержание

  1. Введение в модели пространства состояний
  2. Алгоритм фильтра Калмана
  3. Реализация моделей пространства состояний и фильтрации Калмана в Python
  4. Пример из практики: Прогнозирование цен на акции с использованием моделей пространства состояний и фильтрации Калмана
  5. Заключение

1. Введение в модели пространства состояний

Модели пространства состояний предоставляют гибкую основу для моделирования данных временных рядов. Они состоят из двух компонентов: уравнения состояния и уравнения наблюдения. Уравнение состояния описывает, как основные состояния системы изменяются с течением времени, в то время как уравнение наблюдения связывает наблюдаемые данные с базовыми состояниями.

Уравнение состояния можно представить следующим образом:x(t) = F(t) * x(t-1) + G(t) * u(t)

где x(t) — вектор состояния в момент времени t, F(t) — матрица перехода состояния, x(t-1) — вектор состояния t-1, G(t) — матрица управления и u(t) x(t) F(t)tx(t-1) вектор управления. G(t)

Уравнение наблюдения можно представить следующим образом:y(t) = H(t) * x(t) + v(t)

где y(t) — наблюдаемые данные в момент времени t, H(t) — матрица наблюдения, x(t) — вектор состояния в момент t t и v(t)y(t)tH(t) — шум наблюдения. x(t)

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

2. Алгоритм фильтра Калмана

Фильтр Калмана — это алгоритм, используемый для оценки базовых состояний системы на основе зашумленных наблюдений. Он работает рекурсивно, обновляя оценку состояния по мере поступления новых наблюдений.

Алгоритм фильтрации Калмана состоит из двух этапов: этапа прогнозирования и шага обновления.

Шаг прогнозирования

На этапе прогнозирования мы используем уравнение состояния для прогнозирования следующего состояния на основе оценки текущего состояния:x_hat(t|t-1) = F(t) * x_hat(t-1|t-1) + G(t) * u(t)
P(t|t-1) = F(t) * P(t-1|t-1) * F(t)^T + Q(t)

где x_hat(t|t-1) – оценка прогнозируемого состояния в момент времени t, x_hat(t-1|t-1) – оценка предыдущего состояния в момент времени t-1, P(P(t|t-1)) – ковариация предсказанного состояния в момент времени t, P(P(t-1|t-1) – ковариация предыдущего состояния в момент ttt-1 x_hat(t|t-1) и Q(t) – ковариация шума процесса. x_hat(t-1|t-1) t-1

Шаг обновления

На этапе обновления мы используем уравнение наблюдения для обновления оценки состояния на основе нового наблюдения:K(t) = P(t|t-1) * H(t)^T * (H(t) * P(t|t-1) * H(t)^T + R(t))^-1
x_hat(t|t) = x_hat(t|t-1) + K(t) * (y(t) — H(t) * x_hat(t|t-1))
P(t|t) = (I — K(t) * H(t)) * P(t|t-1)

где K(t)(t) — коэффициент усиления Калмана в момент времени t, R(t) — ковариация шума наблюдения, x_hat(t|t)) — обновленная оценка состояния в момент времени t, y(t) — новое наблюдение в момент tt R(t)I — тождественная матрица.t

Фильтр Калмана обеспечивает оптимальную оценку базовых состояний с учетом наблюдений и допущений модели. Он широко используется в различных областях, включая обработку сигналов, системы управления и финансы.

3. Реализация моделей пространства состояний и фильтрации Калмана в Python

Теперь, когда мы хорошо разобрались с моделями пространства состояний и алгоритмом фильтрации Калмана, давайте реализуем их на Python. Для численных вычислений мы будем использовать библиотеку numpy, а для визуализации данных — библиотеку matplotlib.

Установка необходимых библиотек

Прежде чем мы начнем, давайте установим необходимые библиотеки. Откройте терминал и выполните следующую команду:pip install numpy matplotlib

Импорт необходимых библиотек

После того, как библиотеки установлены, мы можем импортировать их в наш скрипт Python:import numpy as np
import matplotlib.pyplot as plt

Генерация синтетических данных

Для демонстрации реализации моделей пространства состояний и фильтрации Калмана сгенерируем некоторые синтетические данные. Создадим простую линейную систему с гауссовским шумом.np.random.seed(0)

# Define the number of time steps
T = 100

# Define the state transition matrix
F = np.array([[1]])

# Define the observation matrix
H = np.array([[1]])

# Define the process noise covariance
Q = np.array([[0.1]])

# Define the observation noise covariance
R = np.array([[1]])

# Define the initial state
x0 = np.array([0])

# Generate the true state and observations
x_true = np.zeros((T, 1))
y = np.zeros(T)

x_true[0] = x0
y[0] = H @ np.asarray(x_true[0]) + np.random.multivariate_normal(np.zeros((1,)), R)

for t in range(1, T):
x_true[t] = F @ x_true[t-1] + np.random.multivariate_normal(np.zeros((1,)), Q)
y[t] = H @ np.asarray(x_true[t]) + np.random.multivariate_normal(np.zeros((1,)), R)

Реализация фильтра Калмана

Теперь, когда у нас есть синтетические данные, давайте реализуем алгоритм фильтра Калмана для оценки базовых состояний.# Initialize the state estimate and covariance
x_hat = np.zeros((T, 1))
P = np.zeros((T, 1, 1))

x_hat[0] = x0
P[0] = Q

# Run the Kalman filter
for t in range(1, T):
# Prediction step
x_hat[t] = F @ x_hat[t-1]
P[t] = F @ P[t-1] @ F.T + Q

# Update step
K = P[t] @ H.T @ np.linalg.inv(H @ P[t] @ H.T + R)
x_hat[t] = x_hat[t] + K @ (y[t] — H @ x_hat[t])
P[t] = (np.eye(1) — K @ H) @ P[t]

Визуализация результатов

Наконец, давайте визуализируем истинные состояния, наблюдения и предполагаемые состояния с помощью фильтра Калмана.# Plot the true states, observations and estimated states
plt.figure(figsize=(10, 6))
plt.plot(range(T), x_true, label=’True States’)
plt.plot(range(T), y, label=’Observations’)
plt.plot(range(T), x_hat, label=’Estimated States’)
plt.xlabel(‘Time’)
plt.ylabel(‘State’)
plt.legend()
plt.show()

Plot 1
Рисунок 1: Истинные состояния, наблюдения и предполагаемые состояния

На рисунке 1 мы видим, что оцениваемые состояния точно соответствуют истинным состояниям, даже в присутствии шума.

4. Пример из практики: Прогнозирование цен на акции с использованием моделей пространства состояний и фильтрации Калмана

Теперь, когда у нас есть базовое понимание моделей пространства состояний и фильтрации Калмана, давайте применим эти методы к реальной проблеме: прогнозированию цен на акции.

Загрузка данных о ценах на акции

Для начала нам нужно загрузить данные о цене акций для конкретного актива. Для загрузки данных мы будем использовать библиотеку yfinance. Выполните следующий код для установки библиотеки:pip install yfinance

После установки библиотеки мы можем загрузить данные о ценах на акции, используя следующий код:import yfinance as yf

# Download stock price data
data = yf.download(‘JPM’, start=’2020-01-01′, end=’2023-11-30′)

Предварительная обработка данных

Прежде чем мы сможем применить модели пространства состояний и фильтрацию Калмана, нам нужно предварительно обработать данные о ценах на акции. Мы преобразуем данные в логарифмические доходы, которые больше подходят для моделирования финансовых временных рядов.# Compute log returns
data[‘Log Returns’] = np.log(data[‘Close’]).diff()

# Remove missing values
data = data.dropna()

Построение модели пространства состояний

Далее нам нужно построить модель пространства состояний для прогнозирования цен на акции. Для волатильности мы будем использовать простую модель с постоянным дрейфом и случайным блужданием.# Define the state transition matrix
F = np.array([[1]])

# Define the observation matrix
H = np.array([[1]])

# Define the process noise covariance
Q = np.array([[0.001]])

# Define the observation noise covariance
R = np.array([[0.01]])

# Define the initial state
x0 = np.array([0])

# Define the number of time steps
T = len(data)

# Generate the observations
y = data[‘Log Returns’].values.reshape(-1, 1)

Оценка состояний с помощью фильтра Калмана

Теперь, когда у нас есть модель пространства состояний и наблюдения, давайте оценим лежащие в их основе состояния с помощью фильтра Калмана.# Initialize the state estimate and covariance
x_hat = np.zeros((T, 1))
P = np.zeros((T, 1, 1))

x_hat[0] = x0
P[0] = Q

# Run the Kalman filter
for t in range(1, T):
# Prediction step
x_hat[t] = F @ x_hat[t-1]
P[t] = F @ P[t-1] @ F.T + Q

# Update step
K = P[t] @ H.T @ np.linalg.inv(H @ P[t] @ H.T + R)
x_hat[t] = x_hat[t] + K @ (y[t] — H @ x_hat[t])
P[t] = (np.eye(1) — K @ H) @ P[t]

Визуализация результатов

Наконец, давайте визуализируем истинную логарифмическую доходность, предполагаемую логарифмическую доходность и прогнозируемые цены акций.# Compute the estimated log returns
log_returns_hat = x_hat.flatten()

# Compute the predicted stock prices
prices_hat = np.exp(np.cumsum(log_returns_hat))

# Plot the true log returns, estimated log returns and predicted stock prices
plt.figure(figsize=(10, 6))
plt.plot(data.index, data[‘Log Returns’], label=’True Log Returns’)
plt.plot(data.index, log_returns_hat, label=’Estimated Log Returns’)
plt.plot(data.index, prices_hat, label=’Predicted Prices’)
plt.xlabel(‘Date’)
plt.ylabel(‘Log Returns / Prices’)
plt.legend()
plt.show()

Plot 2
Рисунок 2: Истинная логарифмическая доходность, расчетная логарифмическая доходность и прогнозируемые цены

На рисунке 2 мы видим, что предполагаемая логарифмическая доходность точно повторяет истинную логарифмическую доходность, а прогнозируемые цены следуют общей тенденции истинных цен.

5. Заключение

В этом учебном пособии мы рассмотрели концепции моделей пространства состояний и фильтрации Калмана для расширенного анализа временных рядов. Мы реализовали модели пространства состояний и алгоритм фильтра Калмана на Python и применили их к кейсу прогнозирования цен на акции.

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

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

VAR: векторная авторегрессионная модель во временных рядах

Модель векторной авторегрессии (VAR) — это тип многомерной модели временных рядов, которая фиксирует линейные взаимозависимости между несколькими сериями времени. VAR — это обобщение одномерной модели авторегрессии (AR) на несколько временных рядов.

Имейте в виду, что модель VAR отличается от модели множественной регрессии; Это похоже на одновременное прогнозирование нескольких целевых переменных.

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

Для этих двух переменных у нас будет два уравнения. Первое уравнение представляет показы рекламы a, которые состоят из коэффициента c11, умноженного на показы предыдущего месяца, плюс коэффициент c12, умноженного на посещаемость сайта за предыдущий месяц, плюс член ошибки. Второе уравнение имеет аналогичную структуру.

Это модель VAR(1), так как она учитывает только один предыдущий период времени. Теперь представим эти уравнения в векторной форме.

Давайте сделаем простую реализацию модели VAR.import pandas as pd

file_path = ‘data_var.csv’
data = pd.read_csv(file_path)
data.head()

import matplotlib.pyplot as plt

# Convert ‘Month’ to datetime for proper plotting
data[‘Month’] = pd.to_datetime(data[‘Month’])

# Plotting the data
plt.figure(figsize=(12, 6))
plt.plot(data[‘Month’], data[‘A’], label=’A’, marker=’o’)
plt.plot(data[‘Month’], data[‘B’], label=’B’, marker=’x’)

plt.title(‘Time Series Plot of A and B’)
plt.xlabel(‘Month’)
plt.ylabel(‘Values’)
plt.legend()
plt.grid(True)
plt.show()

График временных рядов a и b. Изображение автора.

Давайте проверим, являются ли тренды стационарными.from statsmodels.tsa.stattools import adfuller

# Function to perform Augmented Dickey-Fuller test
def adf_test(series, name):
result = adfuller(series, autolag=’AIC’)
print(f’Results of Dickey-Fuller Test for {name}:’)
print(f’Test Statistic: {result[0]}’)
print(f’p-value: {result[1]}’)
print(f’Number of Lags Used: {result[2]}’)
print(f’Number of Observations Used: {result[3]}’)
print(‘Critical Values:’)
for key, value in result[4].items():
print(f’ {key}: {value}’)
print(‘\n’)

# Apply ADF test on both columns A and B
adf_test(data[‘A’], ‘A’)
adf_test(data[‘B’], ‘B’)

«»»
Results of Dickey-Fuller Test for A:
Test Statistic: 2.496961840696511
p-value: 0.9990488643096954
Number of Lags Used: 11
Number of Observations Used: 48
Critical Values:
1%: -3.5745892596209488
5%: -2.9239543084490744
10%: -2.6000391840277777

Results of Dickey-Fuller Test for B:
Test Statistic: 0.6093665677144796
p-value: 0.9878442378995834
Number of Lags Used: 10
Number of Observations Used: 49
Critical Values:
1%: -3.5714715250448363
5%: -2.922629480573571
10%: -2.5993358475635153″»»

В обоих случаях p-значение значительно выше общего порога 0,05. Это говорит о том, что мы не можем отвергнуть нулевую гипотезу для обоих рядов, указывая на то, что и ряды «А», и «В» нестационарны.

Чтобы сделать данные временных рядов стационарными, мы можем применить дифференциацию, которая включает в себя вычитание предыдущего наблюдения из текущего.# Applying first differencing
data[‘A_diff’] = data[‘A’].diff()
data[‘B_diff’] = data[‘B’].diff()

# Removing the NaN values that result from differencing
data_diff = data.dropna()

Еще раз проверим стационарность:# Apply ADF test on the differenced columns A_diff and B_diff
adf_test(data_diff[‘A_diff’], ‘A_diff’)
adf_test(data_diff[‘B_diff’], ‘B_diff’)

«»»
Results of Dickey-Fuller Test for A_diff:
Test Statistic: -6.80748468232877
p-value: 2.1581659778142047e-09
Number of Lags Used: 10
Number of Observations Used: 48
Critical Values:
1%: -3.5745892596209488
5%: -2.9239543084490744
10%: -2.6000391840277777

Results of Dickey-Fuller Test for B_diff:
Test Statistic: -5.029348708232431
p-value: 1.9408721882426033e-05
Number of Lags Used: 9
Number of Observations Used: 49
Critical Values:
1%: -3.5714715250448363
5%: -2.922629480573571
10%: -2.5993358475635153
«»»

В обоих случаях p-значение значительно ниже общего порога 0,05, что указывает на то, что нулевая гипотеза о наличии единичного корня может быть отвергнута. Это означает, что обе разностные серии «A_diff» и «B_diff» являются стационарными.# Plotting the differenced data to visualize stationarity
plt.figure(figsize=(12, 6))

# Plotting A_diff
plt.subplot(2, 1, 1)
plt.plot(data_diff[‘Month’], data_diff[‘A_diff’], label=’A_diff’, color=’blue’, marker=’o’)
plt.title(‘Differenced Series A’)
plt.xlabel(‘Month’)
plt.ylabel(‘Differenced A’)
plt.grid(True)

# Plotting B_diff
plt.subplot(2, 1, 2)
plt.plot(data_diff[‘Month’], data_diff[‘B_diff’], label=’B_diff’, color=’green’, marker=’x’)
plt.title(‘Differenced Series B’)
plt.xlabel(‘Month’)
plt.ylabel(‘Differenced B’)
plt.grid(True)

# Adjust layout
plt.tight_layout()
plt.show()

Дифференцированная серия. Изображение автора.

Мы можем использовать statsmodels для построения модели VAR.from statsmodels.tsa.api import VAR

# Preparing the data for VAR model (using differenced data)
var_data = data_diff[[‘A_diff’, ‘B_diff’]]

# Determining the optimal lag order
model = VAR(var_data)

# Building the VAR model with the selected lag order
var_model = model.fit(15)

# Summary of the model
model_summary = var_model.summary()
model_summary

«»»
Summary of Regression Results
==================================
Model: VAR
Method: OLS
Date: Tue, 23, Jan, 2024
Time: 16:40:16
———————————————————————
No. of Equations: 2.00000 BIC: 9.37481
Nobs: 44.0000 HQIC: 7.79307
Log likelihood: -213.803 FPE: 1896.23
AIC: 6.86073 Det(Omega_mle): 652.641
———————————————————————
Results for equation A_diff
=============================================================================
coefficient std. error t-stat prob
——————————————————————————
const 1.676161 0.907616 1.847 0.065
L1.A_diff -0.202003 0.216916 -0.931 0.352
L1.B_diff -0.316071 0.106469 -2.969 0.003
L2.A_diff -0.586367 0.213523 -2.746 0.006
L2.B_diff -0.172112 0.129038 -1.334 0.182
L3.A_diff -0.067523 0.247604 -0.273 0.785
L3.B_diff -0.113934 0.136604 -0.834 0.404
L4.A_diff -0.237763 0.298493 -0.797 0.426
L4.B_diff -0.010448 0.124615 -0.084 0.933
L5.A_diff -0.231402 0.324532 -0.713 0.476
L5.B_diff 0.031771 0.123401 0.257 0.797
L6.A_diff -0.137343 0.327016 -0.420 0.674
L6.B_diff 0.067154 0.122940 0.546 0.585
L7.A_diff 0.018159 0.329243 0.055 0.956
L7.B_diff 0.042226 0.129893 0.325 0.745
L8.A_diff 0.262081 0.359466 0.729 0.466
L8.B_diff -0.014168 0.135055 -0.105 0.916
L9.A_diff 0.083046 0.325316 0.255 0.799
L9.B_diff -0.014975 0.131012 -0.114 0.909
L10.A_diff -0.006128 0.286993 -0.021 0.983
L10.B_diff 0.146481 0.148800 0.984 0.325
L11.A_diff -0.020594 0.233438 -0.088 0.930
L11.B_diff -0.116961 0.165070 -0.709 0.479
L12.A_diff 0.132956 0.196337 0.677 0.498
L12.B_diff 0.083259 0.167734 0.496 0.620
L13.A_diff 0.062767 0.183971 0.341 0.733
L13.B_diff -0.465888 0.187123 -2.490 0.013
L14.A_diff -0.136875 0.179916 -0.761 0.447
L14.B_diff -0.070832 0.166423 -0.426 0.670
L15.A_diff 0.166066 0.156232 1.063 0.288
L15.B_diff -0.520732 0.164354 -3.168 0.002
=============================================================================

Results for equation B_diff
=============================================================================
coefficient std. error t-stat prob
——————————————————————————
const 4.643934 2.199896 2.111 0.035
L1.A_diff -0.311568 0.525765 -0.593 0.553
L1.B_diff -0.713791 0.258061 -2.766 0.006
L2.A_diff 0.484014 0.517541 0.935 0.350
L2.B_diff -0.626711 0.312766 -2.004 0.045
L3.A_diff 1.522904 0.600147 2.538 0.011
L3.B_diff -0.337516 0.331103 -1.019 0.308
L4.A_diff 0.912135 0.723493 1.261 0.207
L4.B_diff -0.175041 0.302044 -0.580 0.562
L5.A_diff 0.703550 0.786605 0.894 0.371
L5.B_diff -0.041870 0.299101 -0.140 0.889
L6.A_diff 1.074106 0.792628 1.355 0.175
L6.B_diff -0.273198 0.297985 -0.917 0.359
L7.A_diff 1.462725 0.798025 1.833 0.067
L7.B_diff -0.343429 0.314838 -1.091 0.275
L8.A_diff 1.285859 0.871280 1.476 0.140
L8.B_diff -0.166339 0.327350 -0.508 0.611
L9.A_diff 1.019038 0.788506 1.292 0.196
L9.B_diff -0.639988 0.317549 -2.015 0.044
L10.A_diff 0.152215 0.695618 0.219 0.827
L10.B_diff -0.517679 0.360663 -1.435 0.151
L11.A_diff -0.047880 0.565811 -0.085 0.933
L11.B_diff -0.415805 0.400099 -1.039 0.299
L12.A_diff 0.291200 0.475884 0.612 0.541
L12.B_diff -0.633974 0.406557 -1.559 0.119
L13.A_diff 0.212173 0.445912 0.476 0.634
L13.B_diff -0.776003 0.453553 -1.711 0.087
L14.A_diff -0.100138 0.436083 -0.230 0.818
L14.B_diff -0.491335 0.403378 -1.218 0.223
L15.A_diff -0.261901 0.378677 -0.692 0.489
L15.B_diff -0.267472 0.398364 -0.671 0.502
=============================================================================

Correlation matrix of residuals
A_diff B_diff
A_diff 1.000000 0.166542
B_diff 0.166542 1.000000
«»»

Как видно, модель не назначает в качестве целевой переменной ни A, ни B. Вместо этого он создает модели для обоих, используя все объявленные лаги. Столбец ‘Prob’ представляет p-значение, где в идеале мы ищем значения, значительно меньшие, чем 0,05. Например, мы можем рассмотреть возможность использования лагов, указанных ниже для ‘A’.L1.B_diff -0.316071 0.106469 -2.969 0.003
L2.A_diff -0.586367 0.213523 -2.746 0.006
L13.B_diff -0.465888 0.187123 -2.490 0.013
L15.B_diff -0.520732 0.164354 -3.168 0.002

a_t = (-0,316071 * b_t-1)+(–0,586367 * a_t-2) + (-0,465888 * b_t-13) + (-0,520732 * a_b-15)

Наиболее важным аспектом моделей векторной авторегрессии (VAR) является их способность фиксировать динамические взаимосвязи между несколькими переменными временных рядов, обеспечивая всестороннюю информацию для прогнозирования и анализа политики.

Источники

https://www.statsmodels.org/stable/vector_ar.html

https://www.machinelearningplus.com/time-series/vector-autoregression-examples-python/

PyTimeTK — пакет анализа временных рядов

Что такое PyTimeTK

Анализ временных рядов имеет фундаментальное значение во многих областях, от бизнес-прогнозирования до научных исследований. Несмотря на то, что экосистема Python предлагает такие инструменты, как pandas, иногда они могут быть многословными и не оптимизированными для всех операций, особенно для сложных агрегаций и визуализаций на основе времени.

PytimeTK Сочетает в себе простоту использования и вычислительную эффективность, а также значительно упрощает процесс манипулирования и визуализации временных рядов. Используя серверную часть polars, вы можете получить прирост скорости от 3X до колоссальных 3500X, что является плюсом.

Установка

Пререквизиты — Убедитесь, что в вашей системе установлен Python 3.9 или более поздней версии.

Установите последнюю стабильную версию pytimetk с помощью pip: Последняя версия 0.2.0

pip install pytimetk

В качестве альтернативы вы можете установить разрабатываемую версию:

pip install git+https://github.com/business-science/pytimetk.git

Основы PyTimeTK

Обработка данных

Используйте следующие функции для обработки данных для таймсерий с помощью pytimetk

  1. summarize_by_time — Суммирование DataFrame или Groupby Object по времени. Эта функция полезна для выполнения статистической обработки набора данных с объектом groupby с определенной частотой во времени. Мы можем использовать несколько функций агрегирования, таких как ( Сумма, Медиана, Минимум, Максимум, Стандартное отклонение, Дисперсия, Первое значение в группе, Последнее значение в группе, Количество значений, Количество уникальных значений, Корреляция между значениями, Пользовательские lambda также могут быть использованы) на кадре данных с различными частотами, такими как (например, «D» для ежедневного или «MS» для начала месяца, — S: вторая частота — мин.: минутная частота — H: почасовая частота — D: ежедневная частота — W: еженедельная частота — M: частота в конце месяца — MS: частота начала месяца — Q: частота начала квартала — QS: частота начала квартала — QS: частота начала квартала — Y: частота на конец года — YS: частота начала года)

df.groupby(‘category_1’).summarize_by_time(
date_column = ‘order_date’,
value_column = [‘total_price’, ‘quantity’],
freq = ‘MS’,
agg_func = [‘sum’, ‘mean’, ‘median’,’min’,
(‘q25’, lambda x: np.quantile(x, 0.25)),
(‘q75’, lambda x: np.quantile(x, 0.75)),
‘max’,(‘range’,lambda x: x.max() — x.min())],
wide_format = False,
engine = ‘pandas’)

2. pad_by_time — Сделать нерегулярные временные ряды регулярными, добавив недостающие данные. Например, если у вас есть исторические данные по месяцам, в которых отсутствует несколько месяцев, используйте pad_by_time и дополните отсутствующие данные нулевыми значениями.df.groupby(‘category_1’).pad_by_time(
date_column = ‘order_date’,
freq = ‘W’,
end_date = df.order_date.max())

3. future_frame — Эта функция используется для расширения фрейма данных или объекта Groupby будущими датами на основе заданной длины, при необходимости связывая исходные данные. Это в основном полезно для подготовки будущего набора данных, который можно использовать для прогнозирования.df.groupby(‘category_2’).future_frame(
date_column = ‘order_date’,
length_out = 12 # Extend data for future 3 months )

Визуализация данных

plot_timeseries() — Создание графиков временных рядов с использованием различных механизмов построения графиков, таких как Plotnine, Matplotlib и Plotly.

  • Генерирует интерактивные сюжетные графики (отлично подходит для исследования и приложений с обтекаемым освещением)
  • Консолидирует 20+ строк plotnine/matpotlib и plotly кода
  • Хорошо масштабируется на множество временных рядов
  • Может быть преобразован из интерактивных графиков plotly в статические plotnine/matplotlib, изменив движок с plotly на plotnine или Matplotlib.
  • Группы могут быть добавлены с помощью pandas groupby()

df.groupby(‘id’).plot_timeseries(‘date’, ‘value’,
facet_ncol = 2, # 2-column faceted plot
facet_scales = «free_y»,
smooth_frac = 0.2, # Apply smoothing to the time series data
smooth_size = 2.0,
y_intercept = None,
x_axis_date_labels = «%Y»,
engine = ‘plotly’,
width = 600,
height = 500)

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

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

Pytimetk использует следующие методы для определения аномалий в данных временных рядов;

  1. Декомпозиция временных рядов:
  • Первым делом необходимо разложить временной ряд на несколько составляющих. Как правило, это трендсезонность и остаточные (или остаточные) компоненты.
  • Тренд представляет собой базовую закономерность или направление данных с течением времени. Сезонность фиксирует повторяющиеся закономерности или циклы в течение определенного периода, например ежедневного, еженедельного, ежемесячного и т. д.
  • Остаток (или остаток) — это то, что остается после того, как трендовые и сезонные компоненты были удалены из исходного временного ряда.

2. Генерация остатков:

  • После разложения извлекается оставшийся компонент. Эта составляющая отражает ту часть временного ряда, которая не может быть объяснена трендовой и сезонной составляющими.
  • Идея заключается в том, что в то время как тренд и сезонность представляют собой предсказуемые и, следовательно, «нормальные» паттерны, в остальном аномалии могут проявиться с наибольшей вероятностью.

Существует 2 распространенных метода сезонного разложения; STL и Twitter;

  • STL (Seasonal and Trend Decomposition) — это универсальный и надежный метод декомпозиции временных рядов. STL очень хорошо работает в условиях, когда присутствует долгосрочный тренд. Лессовый алгоритм обычно очень хорошо справляется с обнаружением тренда. Тем не менее, в условиях, когда сезонная составляющая доминирует над трендом, Twitter, как правило, работает лучше.
  • Метод Twitter аналогичен методу декомпозиции, используемому в пакете AnomalyDetection Twitter. Метод Twitter работает идентично STL для удаления сезонной составляющей. Основное отличие заключается в удалении тренда, которое выполняется путем удаления медианы данных, а не подгонки более сглаженной поверхности. Медиана хорошо работает, когда долгосрочный тренд менее доминирует, чем краткосрочная сезонная составляющая. Это связано с тем, что сглаживатель имеет тенденцию перегонять аномалии.

Обнаружение анамолии в pytimetk:

  1. Анамолизировать — обнаруживает аномалии в данных временных рядов либо для одного временного ряда, либо для нескольких временных рядов, сгруппированных по определенному столбцу. Эта функция использует параллельную обработку для ускорения вычислений для больших наборов данных с большим количеством групп временных рядов. Параллельная обработка сопряжена с накладными расходами и может быть не быстрее при работе с небольшими наборами данных. Чтобы использовать параллельную обработку, установите threads = -1, чтобы использовать все доступные процессоры.

# Anomalize the data
anomalize_df = tk.anomalize(
df, «date», «value»,method = «twitter»,
iqr_alpha = 0.10, # To determine the threshold for detecting outliers.
clean_alpha = 0.75, # To determine the threshold for cleaning the outliers
clean = «min_max», # specifies the method used to clean the anomalies
verbose = True, # To display additional info and progress during the execution )

Эта функция возвращает кадр данных с указанными ниже столбцами, где recomposed_l1 и l2 являются нижней и верхней границей перекомпонованного временного ряда.

2. Plot_Anomalies — Создает график аномалий в данных временных рядов с помощью Plotly, Matplotlib.anomalize_df.plot_anomalies(date_column = «date»,engine = «plotly»))

3. Plot_Anomalies_decomp — Эта функция принимает данные из функции anomalize() и возвращает график декомпозиции аномалии. Он возвращает график наблюдаемых, сезонных, тенденций и остатка. Мы также можем использовать Groupby, чтобы построить график по категориям.anomalize_df.plot_anomalies_decomp(«date», engine = ‘plotly’)

4. Plot_Anomalies_cleaned — Эта функция принимает данные из функции anomalize() и возвращает график очищенных аномалий, что означает, что мы можем просмотреть данные до и после удаления аномалийanomalize_df.plot_anomalies_cleaned(«date»)

Проектирование признаков

Добавление объектов в кадры данных временных рядов (дополнение) с помощью следующих функций из пакета pytimetk

  1. augment_timeseries_signature -Принимает DataFrame и столбец даты в качестве входных данных и возвращает исходный df с 29 различными объектами, основанными на дате и времени, добавленными в качестве новых столбцов с именем объекта, основанным на date_column.

2. augment_holiday_signature — Инженеры 4 различных праздничных функции с одной даты и времени для 137 стран.

3. augment_lags — Добавляет лаги к объекту Pandas DataFrame или DataFrameGroupBy.

4. augment_leads — добавляет лиды к объекту Pandas DataFrame или DataFrameGroupBy.

5. augment_diffs -Добавляет различия к объекту Pandas DataFrame или DataFrameGroupBy.

6. augment_rolling — Применение одной или нескольких функций последовательности и размеров окна на основе рядов к одному или нескольким столбцам DataFrame.

7. augment_rolling_apply — Применить к одному или нескольким подвижным функциям и размерам окон на основе DataFrame.

8. augment_expanding -Применение одной или нескольких расширяющихся функций на основе серий к одному или нескольким столбцам DataFrame.

9. augment_expanding_apply — Применение одной или нескольких расширяющих функций на основе DataFrame к одному или нескольким столбцам DataFrame.

10.augment_fourier. Добавляет преобразования Фурье к объекту Pandas DataFrame или DataFrameGroupBy.

11. augment_hilbert — Применить преобразование Гильберта к указанным столбцам DataFrame.

12. augment_wavelet — Применить преобразование Wavely к указанным столбцам DataFrame.

Сравнение с Pandas

Как видно из таблицы, pytimetk — это не только скорость; Это также упрощает кодовую базу. Например, summarize_by_time(), преобразует 6-строчную процедуру двойного цикла for в pandas в краткую 2-строчную операцию. А благодаря двигателю polars вы получаете результаты в 13,4 раза быстрее, чем pandas!

Аналогичным образом, plot_timeseries() значительно упрощает процесс построения графиков, инкапсулируя то, что обычно требует 16 строк кода matplotlib, в простую команду из 2 строк в pytimetk, без ущерба для настройки или качества. А с помощью движков plotly и plotnine вы можете создавать интерактивные графики и красивые статичные визуализации всего за несколько строк кода.

Для функций календаря pytimetk предлагает augment_timeseries_signature() который сокращает более 30 строк извлечения pandas dt. Для подвижных функций pytimetk предлагает augment_rolling() который в 10–3500 раз быстрее, чем pandas. Он также предлагает pad_by_time()) для заполнения пробелов в данных временных рядов и anomalize() для обнаружения и исправления аномалий в данных временных рядов.

Ссылка на репозиторий GitHub — https://github.com/sunkusowmyasree/PyTimeTK

Выводы

С помощью различных функций и методов pytimetk мы можем усовершенствовать наш аналитический подход к данным временных рядов.

  • Функции Pytimetk предоставляют невероятно мощные методы с очень простой структурой и минимальным кодом. Они были незаменимы при составлении высококачественного прогноза продаж, легко интегрированного со sklearn.
  • Этот пакет предлагает широкий спектр универсальных функций временных рядов, многие из которых могут помочь улучшить финансовый, фондовый, портфельный и инвестиционный анализ в Python
  • Все функции, связанные с Augument, помогают нам обогатить наш текущий набор данных, а также помогают нам в анализе с помощью вычислений с запаздыванием и опережением на основе признаков и скользящих вычислений.
  • Получите ценную информацию о производительности нашей окончательной модели, используя plot_timeseries для визуализации результатов.

Анализ временных рядов в машинном обучении

Хронология событий, приливы и отливы фондовых рынков, ритм времен года – мир, каким мы его знаем, богат закономерностями и последовательностями, которые разворачиваются с течением времени. Анализ временных рядов, сочетающий в себе статистические методы и методы машинного обучения, предлагает средства для анализа и интерпретации зависящих от времени наборов данных.

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

В этой статье мы углубимся в концепцию анализа временных рядов в машинном обучении.

Что такое анализ временных рядов?

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

Мы применяем анализ временных рядов в тех случаях, когда статистические методы неэффективны и не могут быть применены из-за ограничений модели.

Анализ временных рядов признает, что точки данных, собранные последовательно с течением времени, могут демонстрировать неотъемлемые закономерности или структуры, такие как автокорреляция, тренды или сезонные колебания, которые необходимо учитывать и включать в анализ.

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

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

Зачем нужен анализ временных рядов?

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

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

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

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

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

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

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

Что отличает данные временных рядов?

Данные временных рядов являются неизменяемыми из-за их последовательного характера. Обычно они записываются в виде новых записей, добавляемых к существующим данным, с сохранением порядка событий. В отличие от реляционных данных, которые часто являются изменяемыми и подвержены транзакционным обновлениям, данные временных рядов остаются неизменными и сохраняют свою историческую последовательность.

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

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

Хотя все события происходят в рамках времени, не все события обязательно зависят от времени. Данные временных рядов выходят за рамки хронологической последовательности; Он охватывает события, значение которых увеличивается с добавлением времени в качестве оси. Данные временных рядов могут существовать на различных уровнях детализации, охватывающих от микросекунд до наносекунд.

Какими качествами обладают данные временных рядов?

Данные временных рядов имеют отличительные характеристики, которые необходимо учитывать при их моделировании и анализе. К таким характеристикам относятся:

Зависимость от времени

Данные временных рядов по своей природе зависят от времени. Каждое наблюдение представляет собой точку данных в определенный момент времени, и последующие наблюдения находятся под влиянием предыдущих.

Тенденция

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

Сезонность

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

Шум

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

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

Данные временных рядов можно разделить на две категории: стационарные и нестационарные данные.

Стационарные данные

Данные стационарных временных рядов относятся к данным, в которых статистические свойства остаются постоянными с течением времени. В нем отсутствуют какие-либо заметные тенденции, сезонность или закономерности, которые могли бы вызвать существенные изменения в этих статистических свойствах. Вариативность набора данных в первую очередь объясняется случайной ошибкой.

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

Нестационарные данные

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

  • Годовые показатели продаж компании, переживающей устойчивый рост на протяжении многих лет, указывают на четкую тенденцию.
  • Ежемесячные показания температуры в городе, демонстрирующие сезонную закономерность по мере повышения и понижения температуры в зависимости от времени года.
  • Цены на акции нового стартапа, который пережил значительный рост в течение короткого периода, демонстрируя ярко выраженный восходящий тренд с течением времени.

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

Из каких этапов состоит анализ временных рядов?

Чтобы провести этот анализ, необходимо выполнить следующие действия:

1. Сбор и очистка данных

Соберите необходимые данные и убедитесь, что в них нет ошибок или пропущенных значений.

2. Визуализация на основе времени

Создавайте визуализации, отображающие взаимосвязь между временем и ключевыми объектами, представляющими интерес.

3. Оценка стационарности

Определите стационарность временного ряда, изучив его статистические свойства во времени.

4. Поисковый анализ

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

5. Разработка модели

Создавайте различные модели, такие как авторегрессия (AR), скользящая средняя (MA), авторегрессионная скользящая средняя (ARMA) и интегрированная скользящая средняя авторегрессии (ARIMA), чтобы зафиксировать динамику временных рядов.

6. Извлечение ценной информации из прогнозов

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

Предварительная обработка данных для анализа

Чтобы точно предсказать значения временного ряда, крайне важно исключить значения, которые выходят за пределы заданного диапазона и создают аномальные флуктуации в ряду. Например, годовой ценовой ряд на бензин колеблется от $0,99 до $1,05. Однако из-за дефицита предложения цена на несколько дней временно превысила $1,2. Такие флуктуации могут вносить неопределенность в прогнозные модели, и нет необходимости включать их в процесс моделирования. Чтобы решить эту проблему, мы можем использовать фильтры для удаления таких аномальных значений.

Вот некоторые часто используемые фильтры:

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

Выбор фильтра зависит от характеристик и конкретных требований анализа. Часто бывает полезно поэкспериментировать с различными фильтрами и размерами окон, чтобы найти наиболее подходящий подход для вашего конкретного набора данных.

Какие существуют типы анализа временных рядов в машинном обучении?

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

Модели машинного обучения, используемые в анализе временных рядов, включают:

  • Классификация

Этот подход направлен на идентификацию и присвоение категорий или меток данным на основе определенных критериев или характеристик.

  • Аппроксимация кривой

Этот метод включает в себя построение точек данных вдоль кривой для анализа и понимания взаимосвязей между переменными в данных.

  • Описательный анализ

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

  • Объяснительный анализ

Объяснительный анализ направлен на понимание данных и взаимосвязей между переменными, исследуя причинно-следственную динамику.

  • Поисковый анализ

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

  • Прогнозирование

Прогнозирование включает в себя прогнозирование будущих точек данных на основе исторических тенденций. Он использует исторические данные в качестве модели для будущих данных, что позволяет оценить потенциальные сценарии.

  • Анализ вмешательства

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

  • Сегментация

Сегментация включает в себя разделение данных на сегменты или подмножества для выявления базовых свойств или закономерностей в исходном источнике данных.

Продвинутые методы анализа временных рядов

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

Трансформаторы

Трансформаторы, первоначально разработанные для обработки естественного языка, были распространены на различные области, включая анализ временных рядов. Стандартный подход заключается в адаптации для этой цели архитектур на основе Transformer, таких как BERT, GPT или Transformer Encoder.

Процесс включает в себя следующие этапы:

  • предварительная обработка данных в токенизированные пары «ввод-вывод»;
  • кодирование и выбор подходящей архитектуры на основе Transformer, например, Transformer Encoder;
  • обучение модели соответствующими функциями потерь и валидация гиперпараметров;
  • оценка работоспособности модели на тестовой установке;
  • Обработка последовательностей переменной длины с помощью методов заполнения или маскирования.

Сезонное разложение

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

Модели экспоненциального сглаживания (Holt-Winters, ARIMA-ETS)

Модели экспоненциального сглаживания, такие как Holt-Winters и ARIMA-ETS (Error, Trend, Seasonality), являются мощными инструментами для прогнозирования временных рядов. Эти модели учитывают тенденцию и сезонность, присутствующие в данных, и обеспечивают надежные прогнозы. Модели Холта-Уинтерса особенно эффективны для учета сезонных колебаний, в то время как модели ARIMA-ETS работают с более сложными закономерностями.

Сети с длинной краткосрочной памятью (LSTM)

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

Оценка модели и метрики производительности

Оценка производительности моделей временных рядов имеет решающее значение для понимания их точности и надежности. Различные метрики производительности, такие как среднеквадратичная ошибка (MSE), средняя абсолютная ошибка (MAE) и среднеквадратичная ошибка (RMSE), помогают оценить эффективность моделей. Кроме того, графические методы, такие как анализ невязок, графики ошибок прогноза и квантиль-квантильные графики, дают представление о производительности модели и любых остаточных закономерностях.

Посмотрите это видео, чтобы узнать больше о прогнозировании в машинном обучении:

https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FAvG7czmeQfs%3Ffeature%3Doembed&display_name=YouTube&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DAvG7czmeQfs&image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FAvG7czmeQfs%2Fhqdefault.jpg&key=a19fcc184b9711e1b4764040d3dc5c07&type=text%2Fhtml&schema=youtube

Ограничения анализа временных рядов

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

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

Надежность предсказаний, как правило, уменьшается по мере того, как они заглядывают в будущее. Это становится очевидным, если учесть часто неточный характер 10-дневного прогноза погоды. Точно так же анализ временных рядов не может дать окончательных прогнозов на будущее, а скорее вероятности для конкретных результатов. Например, хотя мы не можем с уверенностью утверждать, что пользователь приложения для здоровья преодолеет более 10 000 шагов в определенное воскресенье, мы можем утверждать, что существует высокая вероятность или 95% уверенность, согласно историческим данным.

Инструменты, библиотеки и пакеты машинного обучения для анализа временных рядов

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

sktime

Sktime — это фреймворк Python, который фокусируется на анализе данных временных рядов. Его комплексные инструменты обеспечивают эффективную обработку, визуализацию и анализ данных временных рядов. Благодаря удобному дизайну и расширяемости, sktime облегчает бесшовную реализацию новых алгоритмов временных рядов.

Sktime расширяет scikit-learn API, охватывая все основные методы и инструменты для решения задач регрессии, прогнозирования и классификации временных рядов. Помимо специализированных алгоритмов машинного обучения, библиотека также включает в себя методы преобразования, специально разработанные для данных временных рядов. Эти уникальные особенности отличают Sktime от других существующих библиотек.

Дата и время

Datetime — это модуль Python, который облегчает работу с датами и временем, предоставляя ряд методов и функций. Этот модуль позволяет обрабатывать различные сценарии, включая представление и сравнение дат и времени, а также выполнение вычислений.

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

Цфреш

Tsfresh — это пакет Python, который автоматизирует вычисления широкого спектра функций. Этот пакет позволяет систематически извлекать значимые признаки из данных временных рядов.

Tsfresh включает в себя процедуру фильтрации, чтобы убедиться в релевантности извлеченных признаков. Эта процедура оценивает объяснительную силу и значимость каждого признака в контексте задач регрессии или классификации.

Пакет предлагает несколько расширенных функций временных рядов, в том числе:

  • Компоненты преобразования Фурье
  • Вейвлет-преобразование
  • Частичная автокорреляция

Статистикамодели

Statsmodels — это комплексный пакет Python, который предлагает набор классов и функций для оценки различных статистических моделей, проведения статистических тестов и статистического анализа данных.

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

Пмдарима

Pmdarima — это библиотека Python, предназначенная для статистического анализа данных временных рядов, построенных на модели ARIMA (AutoRegressive Integrated Moving Average). Он предлагает инструменты для анализа, прогнозирования и визуализации данных временных рядов. Кроме того, pmdarima предоставляет специализированный функционал для работы с сезонными данными, такой как тест на сезонность и инструмент для сезонной декомпозиции.

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

Pmdarima является удобной оболочкой для модели ARIMA, включающей функцию «auto», которая автоматически определяет наилучшие гиперпараметры (p, d, q) для модели ARIMA. К примечательным особенностям библиотеки можно отнести:

  • Функциональность, аналогичная возможности «auto.arima» в R.
  • Набор статистических тестов для оценки стационарности и сезонности во временных рядах.
  • Различные преобразователи и феатруизаторы, как эндогенные, так и экзогенные, включая преобразования Бокса-Кокса и Фурье.
  • Утилиты для операций, таких как дифференциация и обратная дифференциация.
  • Разнообразная коллекция встроенных наборов данных для создания прототипов и предоставления примеров.
  • Инструменты сезонной декомпозиции.
  • Утилиты для перекрестной проверки.

PyCaret

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

По сравнению с другими библиотеками машинного обучения с открытым исходным кодом, PyCaret выделяется как альтернатива low-code, которая может заменить длинные блоки кода всего несколькими строками. Такой оптимизированный подход обеспечивает более быстрое и эффективное проведение экспериментов. PyCaret — это оболочка Python для различных библиотек и фреймворков машинного обучения, включая scikit-learn, XGBoost, LightGBM, CatBoost, spaCy, Optuna, Hyperopt, Ray и т. д.

Несмотря на то, что PyCaret в первую очередь не ориентирован на прогнозирование временных рядов, он предлагает специальный модуль для этой цели. Несмотря на то, что модуль в настоящее время находится в режиме предварительной версии, вы можете опробовать его, установив PyCaret с тегом «-pre».

Модуль временных рядов PyCaret согласуется с существующим API и поставляется с различными функциональными возможностями. К ним относятся статистическое тестирование, обучение и выбор моделей (с помощью 30+ алгоритмов), анализ моделей, автоматизированная настройка гиперпараметров, протоколирование экспериментов и даже развертывание на облачных платформах.

Приложения для анализа временных рядов

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

Экономика и финансы

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

Сбытовой

Компании используют прогнозирование временных рядов для прогнозирования будущих продаж, спроса и доходов. Эти данные имеют решающее значение для составления бюджета, планирования и управления запасами.

Прогноз погоды

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

Здравоохранение

Моделирование временных рядов используется, среди прочего, в здравоохранении для прогнозирования вспышек заболеваний, результатов лечения пациентов и потребностей в медицинских ресурсах. Например, во время пандемии COVID-19 этот анализ использовался для прогнозирования распространения вируса и потребности в больничных ресурсах.

Производство энергии

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

Электросвязь

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

Транспорт

В транспортной отрасли анализ временных рядов используется для прогнозирования трафика, оптимизации маршрутов и планирования развития инфраструктуры.

Анализ социальных медиа

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

Наука об окружающей среде

Экологи и ученые-экологи могут отслеживать изменения в экосистемах с течением времени, предсказывая такие вещи, как размер популяции животных, уровень загрязнения или влияние изменения климата.

Производственный

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

Заключение

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

Срез временных рядов и извлечение ценовых шаблонов

Часть 1

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

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

Пример 1, Старший таймфрейм, цена биткоина от 20210101 до 20240401 (Три года дневных свечей).

Вертикальные пунктирные линии являются границами сегментов.

Наклонная зеленая линия дает вам вершины клиньев.

Красная наклонная линия дает вам нижнюю часть клиньев.

Обратите внимание, что эти линии не обязательно связаны временными рядами. Они просто задают общее направление сегментов.

Пример 2, младший таймфрейм, цена биткоина от 20240301 до 20240401 (месяц часовых свечей).

Вероятно, было бы контрпродуктивно объяснять алгоритм построчно, но общая идея такова:

  1. ‘compute_candles_stats‘ будет вычислять скользящие средние по свечам. Он будет определять «close_vs_ema_inflection», т.е. точки перегиба, когда цена закрытия пересекает выше или ниже скользящей средней. Обратите внимание, что скользящие средние рассчитываются над скользящим окном. Размер раздвижного окна, управляемого ‘sliding_window_ratio
  2. partition_sliding_window‘ — это место, где временной ряд будет разбит на сегменты

Первый проход заключается в сегментации временных рядов на основе точек перегиба.

Верхняя и нижняя точки в начале и конце канала инициализируются как верхняя и нижняя полосы кипения (управляется параметром ‘boillenger_std_multiples‘, по умолчанию 1)

Второй проход Для каждого сегмента он выполнит линейную регрессию по локальным максимумам и минимумам, используя ‘scipy.stats.linregress. Если стандартная ошибка нарушает параметр ‘linregress_stderr_threshold‘, он попытается рекурсивно разделить сегмент на два. Максимальная глубина рекурсии контролируется параметром ‘max_recur_depth’

Третий проход После того, как сегменты были вычислены, он перебирал их и пытался объединить. Логика такова: Смежные сегменты с одинаковыми уклонами (верхний и нижний) будут объединены.

Это контролируется ‘segment_consolidate_slope_ratio_threshold‘.

Последняя часть в ‘partition_sliding_window’ — ‘classify_segment‘.

Результатом ‘partition_sliding_window’ является список, который может быть отформатирован как Pandas DataFrame.

‘class’ дает вам классификацию сегмента:

a. Восходящая параллель

b. Восходящее сближение/расхождение

c. Боковой путь (Вы можете устанавливать уровни поддержки/сопротивления из боковых сегментов, а также из ATH/ATL в скользящем окне)

d. Падающая параллель

e. Падение сближение/расхождение

Идентификация сегмента как «клина» или «параллельного» канала контролируется параметром «segment_upper_vs_lower_slope_ratio_threshold»

Определение «бокового пути» — это движение цены менее чем на «sideway_price_condition_threshold» между началом и концом сегмента (по умолчанию 0,05 или 5%)

А при конвергенции/расхождении помните начальные/конечные уровни, инициализированные верхними/нижними полосами Боллинджера, поэтому волатильность будет определять форму сегмента.

Мотивы? Вместо того, чтобы скармливать свечи/цены/объемные временные ряды в бэктест или алгоритм, вы можете скармливать сегменты/ценовые паттерны. Можно ответить на вопросы: «Цена вообще пробивается после падающего сходящегося клина?»

На самом деле я протестировал это на истории.

Файлы входных данных:

BTCUSDT_candles_2024–03–01–00–00–00_2024–04–24–00–00–00_1h.csv https://gist.github.com/normanlmfung/6a657058fa14ff5ba250303c8df05e1e

BTCUSDT_candles_2021–01–01–00–00–00_2024–04–24–00–00–00_1d.csv https://gist.github.com/normanlmfung/4375b946f58e048494dc90eed0189128

Эти файлы данных были взяты из компонента ‘market_data_gizmo.fetch_candles‘, который я вам не предоставил. Он просто реализовал технику скользящего окна, используя библиотеку CCXT для получения свечей BTC с криптобирж. Вы можете получить свои рыночные данные любым способом, при условии, что они создают один и тот же формат csv. Или вы можете использовать это.

Обратите внимание на параметры ‘dt_start’, ‘dt_end’ (даты начала/окончания временных рядов), а также тикер + ts_candle_size определяет ожидаемое имя файла.

target_candle_file_name : str = f'{_ticker}_candles_{dt_start.strftime(«%Y-%m-%d-%H-%M-%S»)}_{dt_end.strftime(«%Y-%m-%d-%H-%M-%S»)}_{ts_candle_size}.csv’

Использование не должно быть слишком сложным, получите свечи в формате выше. «compute_candle_stats» будет оценивать точки перегиба, в которых цена закрытия пересекается выше/ниже EMA, которая используется в качестве первой части для сегментации временного ряда. Затем вызовите ‘partition_sliding_window‘, который содержит основную логику сегментации временных рядов.

Также, помимо точек перегиба, «compute_candle_stats» оценивает кучу других технических индикаторов, которые я использовал для других целей: SMA, EMA (и их наклоны), ATRпоказатель ХерстаRSI, MACDуровни Фибоначчи

Код

Пожалуйста, возьмите код из следующей частии, которая посвящена применению того, что мы обсуждали в этой статье, извините, он довольно длинный. Все это есть в Google Colab. Я надеюсь, что краткого обсуждения выше будет достаточно, чтобы вы поняли код. Пожалуйста, дайте мне знать, если кто-то хочет сделать этот проект с открытым исходным кодом и улучшить его.

Часть 2. Развенчиваем мифы: торговля на пробое

[Это не совет по инвестициям, проведите собственное исследование. Считай, что я здесь, чтобы солгать тебе.]

В этой статье мы попытаемся объяснить пробои в торговле с научной точки зрения. Эта статья является целью предыдущей статьи (Извлечение срезов временных рядов и ценовых паттернов). Стоит отметить, что я вижу, что другие, например, Code Trader, делали что-то подобное.

Если оглянуться вокруг, то недостатка в таких «шпаргалках» нет .

Далее разные трейдеры по-разному рисуют клинья. Например, март-апрель 2024 г. является ли это «нисходящим треугольником»? «Симметричный треугольник»?

Да и имеет ли это значение? Подумайте, «Бычий клин» считается бычьим продолжением? Нарисованный немного другой «Нисходящий треугольник» является «Медвежьим продолжением»?

Есть ли в этом какая-то правда? Можем ли мы подтвердить это статистикой? Или опровергнуть? Я подозреваю, что, когда цены сходятся, то, что обычно следует за этим, это падение вверх или вниз. Теперь, я думаю, у вас будет больше шансов сделать ставку на то, что он прорвется во время бычьего рынка и сломается во время медвежьего рынка.

Подход к тестированию на истории выглядит следующим образом. Мы бы торговали BTC.

Критерии поступления

Критерий 1: Торговля по тренду

Мы используем 90d EMA в качестве нашей путеводной звезды.

Если цена выше 90d EMA, мы считаем, что находимся в бычьем тренде. Мы бы только захотели.

Если цена ниже 90d EMA, мы считаем, что находимся в медвежьем тренде. Мы бы только шортили.

Критерий 2: Всякий раз, когда цена сходится, мы входим. Что такое «Конвергенция», проверяем «последний сегмент», смотрим, классифицируется ли он как один из:

  • rising_converging (Если выше 90d EMA)
  • falling_converging (Если EMA ниже 90d)
  • sideway_converging

Основываясь на критерии 1, мы открываем длинную или короткую позицию в зависимости от 90d EMA. И откуда берутся сегменты? Мы нарезаем временные ряды (скользящие цены) на сегменты с помощью методик (берем собственно код), описанных в предыдущей части.

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

  • Если вы используете подход агрегации свечей, вы предполагаете постоянную ширину сегмента
  • Вы не учитываете «конвергентные» или «параллельные» каналы

Вы найдете «Сегменты» в этом файле: BTCUSDT_lo candles_w_segments_2021–02–27–00–00–00_2024–04–24–00–00–00_1d.csv

Столбец «class» содержит классификацию сегментов

«start_dates» контрольная дата начала теста. «lo_how_many_candles_values» определяет основное скользящее окно, используемое в бэктестах.

lo_how_many_candles_values = [ (‘1h’, 24, 24*1150) ]

‘1h’ : Часовые свечи

«24» : Размер скользящего окна составляет 24 часовые свечи

«24*1150»: тестовый период составляет 24 часа x 1150, или 1150 дней.

Многие другие параметры, такие как «hi_how_many_candles_values» (определение скользящего окна старшего таймфрейма), «ema_short_slope_threshold_values», «rsi_upper_threshold_values» не используются для этого проекта. И заметьте, что «compute_candles_stats» вычисляет много вещей (показатель Херста, ATR, RSI, MACD… и т.д.) от свечей, которые на самом деле не используются в этом проекте (я использовал их для других сделок). Почему я храню их в коде? Тем не менее, вы можете легко расширить и добавить дополнительные сигналы в качестве критериев входа/выхода.

Критерий 3: Есть ли у нас наличные деньги?

«entry_percent_initial_cash_values» определяет условный ордер: 80% от первоначальной денежной суммы («initial_cash_values«). Напоминаем,

  • Мы не торгуем все большими и большими условными ордерами, зарабатывая деньги. Таким образом, прибыль не «реинвестируется»

target_order_notional = initial_cash * entry_percent_initial_cash/100

  • Стратегия остановится, если общий капитал (денежные средства + позиция, «gloabl_state.total_equity«) упадет ниже этого минимального номинала ордера.
  • В этом эксперименте мы торгуем только одним тикером и не учитываем такие вещи, как критерий Келли.

Критерии выхода

Критерии тейк-профита (TP): Всякий раз, когда цена пробивает верхнюю или нижнюю полосу Боллинджера.

Параметр, который управляет этим: boillenger_std_multiples_values

Критерии стоп-лосса (SL): «unurealized_pnl_live» оценивается с помощью:

  • candles low: Если вы сидите в длинной позиции
  • candles high: Если вы сидите на короткой позиции

Поэтому мы настроены пессимистично. Напротив, TP использует цену закрытия для оценки, она не является ни оптимистичной, ни пессимистичной.

Обратите внимание, что вы можете сделать это, так как это одноногая сделка. Для сделок с двумя ногами (например, statsarb) вы должны использовать цену закрытия как для TP, так и для SL (вместо максимумов или минимумов), так как максимумы на одной ноге, скорее всего, случаются в разное время с минимумами на второй ноге.

Параметр, который управляет этим, «sl_percent_values«: 3%

После выполнения теста на истории есть два ключевых выходных результата:

  1. Ищем выписку из сделки, файл «backtest_converging_wedge_strategy_BTC_20210301_trades.csv» — он взят из фрейма данных «pd_flattenned_trades». Отсюда мы строим кривую эквити. csv также очень полезен, так как вы можете углубиться в детали сделки и поместить на него сводную таблицу.

2. Отрывной лист из отчета Quantstats

Стратегический риск

Помните, наша Полярная звезда – это 90d EMA? Только вы

  • длинная при закрытии>90d EMA
  • короткая при закрытии<90d EMA

Таким образом, большая часть потерь будет приходиться на длинные входы, но когда рынок продолжает падать к 90d EMA. Наоборот. Пример: март-май 2024 BTC. Вот тогда следование за трендом будет кровоточить.

Давайте посмотрим на результат.

  1. Кривая эквити
  • В течение трех лет просадка вниз выглядит небольшой (когда вы увеличиваете масштаб, вы можете чувствовать себя по-другому, особенно если просадка происходит, когда вы начинаете торговать в реальном времени)
  • Флэтовые места — это когда стратегия простаивает (не торгует). В 2022 году мы делаем меньше записей, проверьте CSV сегмента.

2. Отрывной лист (Мы используем quantstats)

Распределение возвратов

Март 2021 г., декабрь 2021 г., вероятно, исказили распределение доходности, но можно ли с уверенностью предположить, что вы можете зарабатывать в среднем 2–3% в месяц? Также помните, что прибыль не реинвестируется, поэтому процентная доходность может выглядеть сдержанной в 2023–2024 годах. Откройте файл сделки и посмотрите на фактическую сумму прибыли в долларах.

3. Покопайтесь в торговом файле «backtest_converging_wedge_strategy_BTC_20210301_trades.csv»

Во-первых, мы попробуем разбить pnl на основе:

  • side: Это сторона закрытия сделки. Таким образом, если «side» = buy, это означает, что позиция является короткой позицией (т.е. она совершила короткий вход, когда цена ниже 90d EMA). Если «side» = sell, это означает, что позиция является длинной позицией (т.е. она совершила вход в длинную позицию, когда цена выше 90d EMA)
  • reason: TP (тейк-профит) или SL (стоп-лосс)
  • last_lo_candles_segment_class: Это классификация сегментов. Это «Последний сегмент» в точке, где стратегия вошла в систему.

Заметка

  • сторона = покупка (т.е. короткая позиция входит, когда цена<90d EMA)

Если вы входите с использованием «falling_converging», то по номиналу TP/SL = 5.69

  • сторона = продажа (т.е. вход в короткую позицию, когда цена >90d EMA)

Если вы входите с использованием «rising_converging«, то по номиналу TP/SL = 4.27

Если «sideway_converging«, то вы все равно зарабатываете, но TP/SL немного хуже = 2.67

90% попаданий (Черт возьми, ты должен быть богат?!)! Но обратите внимание, что при 3% SL вы платите больше за убыточную сделку, чем за прибыльную. Таким образом, вы не можете просто смотреть на коэффициент попадания.

Вам также нужно посмотреть на среднюю прибыль и убыток для прибыльных сделок по сравнению с убыточными сделками. В том виде, в котором мы торговали, нам нужно три тейк-профита для покрытия одного стоп-лосса.

Кроме того, я предлагаю вам сгруппироваться по «trade_day», чтобы увидеть, приходятся ли ваши убыточные дни обычно на конец месяца из-за таких вещей, как ВВПPCE или FOMC?

Таким образом, судя по тестированию на истории, он поддерживает использование «Восходящего треугольника» на бычьем рынке и «Нисходящего треугольника» на медвежьем рынке.

С другой стороны, я не буду открывать длинную позицию по «восходящему треугольнику» на медвежьем рынке. Или шортить «Нисходящий треугольник» на бычьем рынке. Я призываю вас изменить условия входа и повторно запустить бэк-тест, чтобы доказать это.

Юк, ты же не хочешь шортить во время бычьего ралли, и точка.

Раскрытие: Я не торговал по этой стратегии, мне нравятся более простые сделки, а также сделки, которые делают больше входов, закрывая pnl на более частой основе, даже для младшего Шарпа. Но большая часть кода для тестирования на истории была взята из другой стратегии, которой я занимаюсь в реальной торговле: и результаты бэк-тестов очень похожи на прибыль и убытки от реальной торговли.

Некоторые люди разделяют исторические данные на:

  • Обучающая выборка: Здесь вы найдете подходящие параметры стратегии
  • Проверочный набор: Отсюда вы определите, лжет ли вам тест на спину!

Это просто мои личные референсы, это сначала живая торговля небольшими суммами и сравнение на живой основе, live pnl vs back-тесты.

Это очень важно, так как тестирование на истории дает вам общее представление о том, как вы работаете в течение длительного периода времени, а также при различных рыночных режимах.

Заключительное слово

Есть много других гипотез, которые вы можете проверить. Какие-то из них выстоят, какие-то потерпят неудачу. То, что вы ищете, — это постоянство:

Если сигнал постоянно теряет деньги, это здорово! Так как вы должны просто торговать в противоположном направлении.

Сигнал, которым не стоит торговать: «Random Walks».

И что дальше?

  • По боковым клиньям, а также по глобальным минимумам/максимумам скользящего окна, можно сделать вывод об уровнях поддержки и сопротивления. Например, можем ли мы предположить, что если уровень поддержки пробит (скажем, на 1%?), мы можем с уверенностью предположить, что импульс унесет нас дальше вниз к следующему уровню поддержки? Идея заключается в том, что многие стоп-лоссы скрываются немного ниже уровней поддержки?

Но в итоге получилось так: мы быстро и тяжело вернулись к отметке 64,5 тысячи.

20240523 BTC
  • Неужели вы не можете заработать больше денег, просто разводя вилки? Когда вы проводите вилку, вы открываете длинную одну ногу и короткую другую, риск намного ниже. Почему бы просто не вилочку?

Вопрос к Вам

Уровень поддержки перетестирован один, два, три раза: делает ли это его сильнее (или слабее)? Как вы его тестируете?

Несколько часов спустя:

Это отскок дохлой кошки?

Подождите, это бычий флаг?! или нисходящий треугольник?

Код

«run_scenario» — это ядро логики тестирования на истории: именно здесь вы найдете цикл, который воспроизводит свечи, делает входы, фиксирует прибыль, стоп-лоссы.

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник

Источник