Реализация на Python

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

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

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

import numpy as np
import matplotlib.pyplot as plt

np.random.seed(1)

n = 100 # length

# generate random data
ts = np.zeros(n)
for i in range(n):
    if np.random.rand() < 0.2:
        ts[i] = np.random.randint(1, 10)

# plotting
plt.plot(ts)
plt.xlabel('Time')
plt.ylabel('Demand')
plt.title('Intermittent Demand Time Series')
plt.show()

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

import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA

# split data 
train_data = ts[:-25]
test_data = ts[-25:]

# build ARIMA model
model = ARIMA(train_data, order=(1, 1, 1))
model_fit = model.fit()
forecast = model_fit.forecast(steps=25)

# plot forecast vs actual
plt.plot(test_data, label="Actual")
plt.plot(forecast, label="Forecast")
plt.legend()
plt.show()

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

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

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

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

F(t+1) = α * Y(t) + (1-α) * F(t)

В этой формуле F(t+1) — это прогноз на следующий период времени, Y(t) — фактическое значение временного ряда в период времени t, а F(t) — прогноз на текущий период времени. Коэффициент сглаживания α представляет собой значение от 0 до 1, которое определяет вес, придаваемый самому последнему наблюдению.

# take the demands
z = ts[ts != 0]
print('Demand, z: ', z)
plt.plot(z)

Чтобы определить временной интервал между каждой продажей, мы можем использовать метод Numpy flatnonzero для получения порядковых номеров ненулевых элементов. Впоследствии мы можем использовать метод diff для вычисления разницы между этими значениями.

p_idx = np.flatnonzero(ts)
print('Index: ', p_idx)
p = np.diff(p_idx, prepend=-1)
print('Period, p: ', p)

"""
Index:  [ 2  3 21 26 27 30 34 39 41 42 43 47 48 49 54 59 66 73 75 76 82 83 89 92
 95 99]
Period, p:  [ 3  1 18  5  1  3  4  5  2  1  1  4  1  1  5  5  7  7  2  1  6  1  6  3
  3  4]
"""

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

alpha = 0.1
n = len(z)
z_hat = np.zeros(n)
p_hat = np.zeros(n)
z_hat[0] = z[0]
p_hat[0] = np.mean(p)

for i in range(1,n):
    z_hat[i] = alpha*z[i] + (1-alpha)*z_hat[i-1]
    p_hat[i] = alpha*p[i] + (1-alpha)*p_hat[i-1]
    
print('Smoothed Demand: ', np.round(z_hat,2))
print('Smoothed Period: ',np.round(p_hat,2))

"""
Smoothed Demand:  [6.   5.5  5.15 5.54 5.38 5.74 5.47 5.82 6.04 6.04 6.13 6.32 5.79 6.01
 6.01 5.91 5.52 5.36 5.63 5.77 5.39 5.55 5.89 5.51 5.85 5.87]
Smoothed Period:  [3.85 3.56 5.01 5.   4.6  4.44 4.4  4.46 4.21 3.89 3.6  3.64 3.38 3.14
 3.33 3.49 3.84 4.16 3.94 3.65 3.88 3.6  3.84 3.75 3.68 3.71]
"""

Прогнозирование:

y_hat = z_hat / p_hat
print('Forecast: ', np.round(y_hat, 2))

"""
Forecast:  [1.56 1.54 1.03 1.11 1.17 1.29 1.24 1.31 1.43 1.55 1.7  1.73 1.71 1.91
 1.81 1.69 1.43 1.29 1.43 1.58 1.39 1.54 1.54 1.47 1.59 1.58]
"""

Теперь у нас есть прогнозы. Затем мы вставим их на свои места в соответствии с индексами.

m = len(ts) + 1
forecast = np.empty(m)
forecast[:] = np.nan
forecast[p_idx+1] = y_hat

# Forward fill missing values with the previous forecast
for i in range(1,m):
    if np.isnan(forecast[i]):
        forecast[i] = forecast[i-1]


plt.plot(ts)
plt.plot(forecast)

Кростон (взято отсюда):

def get_croston(ts, alpha=0.1):
    """
    Perform Croston's method on a time series, ts, and return
    a forecast
    
    Parameters
    ----------
    ts : (N,) array_like
        1-D input array
    alpha : float
        Smoothing factor, `0 < alpha < 1`, default = 0.1
        
    Returns
    -------
    forecast : (N+1,) ndarray
        1-D array of forecasted values, forecast begins 1-step
        after the first non-zero demand in ts. Prefilled with np.nan.
    """
    
    # Initialise arrays for demand, z, and period, p. Starting
    # demand is the first non-zero demand value, starting period is
    # mean of all intervals
    ts_trim = np.trim_zeros(ts, 'f')
    n = len(ts_trim)
    z = np.zeros(n)
    p = np.zeros(n)
    p_idx = np.flatnonzero(ts)
    p_diff = np.diff(p_idx, prepend=-1)
    p[0] = np.mean(p_diff)
    z[0] = ts[p_idx[0]]

    # Perform Croston's method
    q = 1
    for i in range(1,n):
        if ts_trim[i] > 0:
            z[i] = alpha*ts_trim[i] + (1-alpha)*z[i-1]
            p[i] = alpha*q + (1-alpha)*p[i-1]
            q = 1
        else:
            z[i] = z[i-1]
            p[i] = p[i-1]
            q += 1 
    f = z / p
    nan_arr = [np.nan] * (len(ts)-n+1)
    return np.concatenate((nan_arr, f))

f_alpha_low = get_croston(ts, 0.05)
f_alpha_mid = get_croston(ts, 0.5)
f_alpha_high = get_croston(ts, 0.9)
plt.plot(ts)
plt.plot(f_alpha_low, label="0.05")
plt.plot(f_alpha_mid, label="0.5")
plt.plot(f_alpha_high, label="0.9")
plt.legend()

Во-первых, он инициализирует массивы для спроса z и периода p. Переменная ts_trim создается путем обрезки нулей в начале входного временного ряда ts, а ее длина сохраняется в n. Затем создается массив z с нулями, а массив аналогичного размера p также создается с нулями. Переменная p_idx хранит индексы ненулевых элементов в ts, а p_diff вычисляет интервалы времени между ненулевыми элементами. Первый элемент p устанавливается на среднее значение временных интервалов, а первый элемент z устанавливается на первый ненулевой элемент ts.

Затем он выполняет метод Кростона. Он перебирает временной ряд ts_trim, начиная со второго элемента, и проверяет, больше ли текущий элемент 0 (т. е. есть спрос) или нет. При наличии спроса значение прогноза z[i] обновляется с использованием коэффициента сглаживания alpha и предыдущего значения прогноза z[i-1]. Оценка периода p[i] также обновляется с использованием коэффициента сглаживания alpha и оценки предыдущего периода p[i-1], а q сбрасывается на 1. Если спрос отсутствует, значение прогноза z[i] и оценка периода p[i] не обновляются, а q увеличивается на 1. .

Этот последний блок кода вычисляет значения прогноза f путем деления значений прогноза спроса z на оценку периода p. Затем создается массив nan_arr с длиной, равной разнице между длинами ts и ts_trim плюс 1, и заполняется значениями NaN. Наконец, функция возвращает конкатенацию nan_arr и f.

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

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

Читать далее











Источники

https://www.pmorgan.com.au/tutorials/crostons-method/

https://www.pmorgan.com.au/tutorials/error-metrics-for-intermittent-demand-cfe%2C-pis%2C-msr/

https://forecastegy.com/posts/intermittent-time-series-forecasting-in-python/

https://towardsdatascience.com/croston-forecast-model-for-intermittent-demand-360287a17f5f