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

  1. Обработка пропущенных значений
  2. Удалить тренд
  3. Удалить сезонность
  4. Проверить на стационарность и при необходимости сделать стационарным
  5. Нормализация данных
  6. Удалить выбросы
  7. Сглаживание данных

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

Удалить сезонность

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

# Import library
import statsmodels.api as sm

# Decompose data by selecting the appropiate frequency
decomp = sm.tsa.seasonal_decompose(
  df['Monthly beer production'], period=12)
decomp_plot = decomp.plot()

# Plot outcome
plt.xlabel('Date')
decomp_plot .set_figheight(10)
decomp_plot .set_figwidth(10)
plt.show()

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

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

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

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

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

# Calculate each year's average
monthly_average = df_beer.groupby(df_beer.index.month).mean()
mapped_monthly_average = df_beer.index.map(
    lambda x: monthly_average.loc[x.month])

# Standardize each year's average
df_beer = df_beer / mapped_monthly_average

# Plot outcome
df_beer.plot(figsize=(15,5))
plt.xlabel('Date')
plt.ylabel('Standardized beer production rate')
plt.grid()
plt.show()

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

Проверить на стационарность

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

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

Есть несколько статистических тестов, которые можно использовать для проверки стационарности временного ряда. Но сначала нам нужно представить, что такое единичный корень. Под единичным корнем понимается значение параметра в авторегрессионной (AR) модели, равное единице. Два основных теста для проверки стационарности:

  • Расширенный тест Дики-Фуллера (ADF). Он используется для проверки наличия единичного корня в данных временного ряда, что является распространенной причиной нестационарности. Нулевая гипотеза (H₀) состоит в том, что данные нестационарны, а отклонение этой гипотезы указывает на то, что данные стационарны.
  • Тест Квятковского-Филлипса-Шмидта-Шина. Этот тест используется для проверки наличия тенденции в данных, которая также может вызывать нестационарность. Нулевая гипотеза (H₀) заключается в том, что данные стационарны, и отклонение этой гипотезы указывает на наличие тренда.

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

Давайте выполним оба теста, используя функции из библиотеки statsmodels:

# Import required libraries
from statsmodels.tsa.stattools import adfuller, kpss

# Perform ADF test
result = adfuller(df_beer)
print('ADF test:\tp-value: {:.3f}'.format(result[1]))

# Perform KPSS test
result = kpss(df_beer)
print('KPSS test:\tp-value: {:.3f}'.format(result[1]))

Для теста KPSS нам нужно проверить, превышает ли p-значение уровень значимости. В данном случае он ниже 0,05, поэтому по этому тесту он не является стационарным.

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

# Apply one more differencing to the data
df_beer = df_beer.diff()[1:]

# Perform ADF test
result = adfuller(df_beer)
print('ADF test:\tp-value: {:.3f}'.format(result[1]))

# Perform KPSS test
result = kpss(df_beer)
print('KPSS test:\tp-value: {:.3f}'.format(result[1]))

Нормализация данных

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

  1. Min-Max Scaler: этот масштабатор масштабирует каждую функцию в заданном диапазоне, обычно от 0 до 1. Это полезно, когда вы хотите сохранить форму исходного распределения, но настроить масштаб данных. .
  2. Стандартный масштабатор: этот масштабатор масштабирует данные, чтобы иметь нулевое среднее значение и единичную дисперсию. Это эквивалентно вычислению z-оценки данных. Это полезно, когда распределение данных не является нормальным, и вы хотите нормализовать его до стандартного распределения Гаусса.
  3. Надежный скейлер: этот скейлер устойчив к выбросам в данных и масштабирует данные до IQR (межквартильный диапазон). Это полезно, когда у вас есть данные с экстремальными значениями, которые могут исказить процесс масштабирования.
  4. Max-Abs Scaler: этот масштабатор масштабирует каждую функцию до максимального абсолютного значения этой функции. Это полезно, когда данные сосредоточены вокруг нуля, и вы хотите сохранить направление и знак каждого объекта.

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

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

# Calculate mean and standard deviation
mean = df_beer.mean()
std = df_beer.std()

# Normalize data
df_beer = (df_beer - mean) / std

Мы также можем добиться того же результата с помощью функции scikit-learn:

# Import library
from sklearn.preprocessing import StandardScaler

# Create a StandardScaler object
scaler = StandardScaler()

# Convert data to numpy array and reshape
array_beer = df_beer.values.reshape(-1, 1)

# Fit the scaler to the data and transform it
data_scaled = scaler.fit_transform(array_beer)

# Convert back to pandas Series
df_beer = pd.Series(data_scaled.flatten(), 
                    index=df_beer.index, name='Month')

Оба результата будут одинаковыми. Два соображения:

  • Если вы выберете подход scikit-learn, вы сможете легко протестировать другие методы, просто заменив StandardScaler на MinMaxScaler, MaxAbsScaler или RobustScaler.
  • Убедитесь, что вы сохраняете значения среднего значения и стандартного отклонения в первом подходе и объект скейлера во втором, чтобы иметь возможность вернуться к значениям производства пива после получения прогноза от модели.

Мы можем отобразить, как выглядят нормализованные данные:

Наконец-то мы можем увидеть, как наши данные стационарны и нормализованы. Теперь мы на шаг ближе к тому, чтобы начать строить нашу модель!

Смотрите все части серии очистки данных:

  1. Пропущенные значения и детрендирование
  2. Убрать сезонность и нормализовать данные
  3. Удаление выбросов
  4. Сглаживание данных

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

В следующей статье мы увидим, как мы можем проверять и удалять выбросы!

Подпишитесь на DDIntel Здесь.

Посетите наш сайт здесь: https://www.datadriveninvestor.com

Присоединяйтесь к нашей сети здесь: https://datadriveninvestor.com/collaborate