Открыть список
Как стать автором
Обновить
40.5
Рейтинг

Прогнозирование временных рядов методом рядов Фурье

Блог компании GlowByteМатематикаМашинное обучениеФизика
image
Привет, Хабр.

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

Внимание! Статья содержит множество формул.



Схематично данный метод можно проиллюстрировать следующей анимацией

image

План статьи



Тем, кто с мехмата/матфака хорошо знаком с такими понятиями, как временные ряды и ряды Фурье, можно пропустить первые три раздела данной статьи.

Временной ряд


Временной ряд – значения некоторой величины, измеренные через равные промежутки времени $\Delta t.$ Если принять $\Delta t = 1,$ начальное значение $t_{1} = 1, \; t_{i}=i,$где $i = 1, 2, ... n,$ тогда временной ряд можно записать в виде последовательности

$y_1, y_2, …, y_n, \;\;\;\;(1)$


где $y_k \in R.$ Примеры временных рядов: стоимость акции, температура воздуха, курс доллара и т.д.

Задача прогнозирования временных рядов


Задача прогнозирования временных рядов заключается в нахождении функции F:

$y_{n+d}=F(y_1, y_2, …, y_n, d), \;\;\;\;(2)$


где $d \in \{1, 2, ..., D\}$ – отсрочка прогноза, $D$ – горизонт прогнозирования.
Т.е. по известным значениям ряда из прошлого необходимо найти его значения в будущем.

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

Ряды Фурье


Пусть функция $f(x)$ непрерывна на отрезке $[a, b],$ последовательность функций

$φ_1(x), φ_2 (x), \ldots , φ_k(x), \ldots \;\;\;(3)$


является ортонормальной на $[a, b],$ т.е.

$$display$$\int_a^b φ_i(x) φ_j (x)dx= δ_{ik} = \begin{cases} 1 &\text{$i = k$}\\ 0 &\text{$i \neq k$} \end{cases}. \;\;\;(4)$$display$$


Обозначим

$a_k = \int_a^b f(x)\varphi_k(x)dx, k=1, 2, ... , \;\;\;(5)$


тогда ряд

$$display$$a_1φ_1 (x)+a_2φ_2 (x)+⋯+a_kφ_k (x)+⋯ \;\;\; (6)$$display$$


называется рядом Фурье. Коэффициенты (5) этого ряда называются коэффициентами Фурье функции f(x) по системе (3).
Ряд Фурье – разложение некоторой функции по полной системе ортонормированных функций (по некоторому базису).

Тригонометрические ряды Фурье
Если в качестве системы (3) взять ортонормированную на отрезке $[-\pi,\pi]$ систему функций

$\frac{1}{\sqrt{\pi}}, \frac{1}{\sqrt{\pi}}\cos(x), \frac{1}{\sqrt{\pi}}\sin (x), ..., \frac{1}{\sqrt{\pi}}\cos (kx), \frac{1}{\sqrt{\pi}}\sin (kx), ...,\;\;\;(7)$


то разложение произвольной функции f(x) по системе (7) в ряд Фурье на отрезке $[-\pi,\pi]$ имеет вид

$\frac{a_0}{2}+\sum_{k=1}^{\infty}a_k \cos⁡(kx)+b_k \sin⁡(kx), \;\;\; (8)$


где коэффициентыи $a_k, \; b_k$ имеют вид

$\begin{aligned} &a_{0} = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x)dx; \\&a_{k} = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x)\cos(kx)dx; \\ &b_{k} = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x)\sin(kx)dx.\end{aligned} \;\;\;(9)$


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

Теорема Дирихлe: Если функция f(x) задана на отрезке $[-\pi,\pi]$ и является на нем кусочно-непрерывной, кусочно-монотонной и ограниченной, то ее тригонометрический ряд Фурье сходится во всех точках отрезка. Если s(x) – сумма тригонометрического ряда Фурье функции f(x), то во всех точках непрерывности этой функции

$s(x) = f(x),$


А во всех точках разрыва

$s(x)=\frac{1}{2} (f(x-0)+f(x+0)).$


Кроме этого,

$s(\pi) = s(-\pi) = \frac{1}{2}(f(\pi-0) + f(-\pi+0)).$


Среди всех тригонометрических многочленов

$$display$$\frac{α_0}{2}+\sum_{k=1}^{N}α_k\cos⁡(kx)+β_k\sin⁡(kx)$$display$$


с заданным N частичная сумма ряда Фурье

$S_N(x) = \frac{a_0}{2}+\sum_{k=1}^{N}a_k\cos⁡(kx)+b_k\sin⁡(kx)$


дает наилучшую (в метрике  пространства функций с интегрируемым квадратом на отрезке $[-\pi,\pi]$) аппроксимацию функции f(x) [3]. Именно это утверждение является основой для представления функции, значения которой являются исследуемым временным рядом с некоторой периодичностью, в виде частной суммы тригонометрического ряда Фурье.
Пусть f(t) – функция с интегрируемым квадратом на отрезке $[-l, l].$ Замена $x = \frac{\pi t}{l}, \; x \in [-\pi, \pi], \; t = \frac{lx}{\pi}$ переводит функцию в

$\widetilde{f}(x) = f\Bigr(\frac{lx}{\pi}\Bigl), \;\; x\in[-\pi, \pi]$


Для этой функции, заданной на отрезке $[-l, l],$ ряд Фурье имеет вид

$\frac{a_0}{2}+\sum_{k=1}^{N}a_k\cos⁡\Bigl(\frac{\pi kt}{l}\Bigr)+b_k\sin⁡\Bigl(\frac{\pi kt}{l}\Bigr), \;\;\; (10)$


где

$\begin{aligned} &a_{k} = \frac{1}{l} \int_{-l}^{l} f(t)\cos\Bigl(\frac{\pi kt}{l}\Bigr)dt; \;\; k=0,1,2...\,, \\ &b_{k} = \frac{1}{l} \int_{-l}^{l} f(t)\sin\Bigl(\frac{\pi kt}{l}\Bigr)dt, \;\; k=1, 2, 3...\, .\end{aligned}$



Матрица задержек. Прогнозирование временных рядов на один шаг


Дан временной ряд, содержащий m значений:

$x_1, x_2,…, x_m.$


Нужно спрогнозировать значение $x_{m+1}.$
Одним из методов прогнозирования на один шаг является метод построения матрицы задержек. Суть метода заключается в следующем [4-5]: выбирается некоторое число p – величина задержек, подбирается зависимость значения $x_{m+1}$ ряда от p предыдущих значений:

$x_{k+1}=f(x_{k-p+1}, \ldots, x_{k-1}, x_k).$


Вид зависимости, как правило, выбирается линейный

$x_{k+1}=A_p+A_{p-1} x_{k-p+1}+⋯+A_1 x_{k-1}+A_0x_k. \;\;\;(11)$


Для определения коэффициентов $A_0, A_1, ..., A_{p-1}$ строится матрица задержек:

$\quad \begin{pmatrix} x_1 & x_{p+1} & ... &x_{m-p+1} \\ x_2 & x_{p+2} & ... &x_{m-p+2} \\ ... & ... & ... &... \\ x_p & x_{2p} & ... &x_{m} \end{pmatrix} \quad \;\;\;(12)$


Если m не кратно p то можно отбросить несколько первых членов ряда. В последней строке выбирается множество S – так называемое множество «ближайших соседей» к значению $x_m.$ Количество элементов

$|S| = w = 2p+1.$


Коэффициенты $A_0, A_1, \ldots, A_p$ можно найти методом наименьших квадратов или каким-либо другим способом.

$\Phi(A_0, A_1, \ldots, A_{p}) = \sum_{k \in I}(x_k-A_p-A_{p-1}x_{k-p}-\ldots-A_1x_{k-2}-A_0x_{k-1})^2\rightarrow \min \;\;\; (13)$


где

$I=\{i_1+1,i_{2}+1,…,i_{w}+1\}.$


Из необходимого условия минимума

$\frac{\partial \Phi}{\partial A_i}=0, \;\;\;i=1,2,\ldots,p$


получаем систему уравнений для нахождения коэффициентов

$A_0, A_1, \ldots, A_p.$

Величина p выбирается эмпирически.
Можно воспользоваться матричной записью выражения (13)

$\parallel x - XA \parallel \rightarrow \min$


и вспомнить аналитическое решение данной задачи:

$A^{*} = (X^{T}X)^{-1}X^Tx$


Подставляя полученный вектор $A^*$ в выражение (11), получаем прогноз для значения ряда $x_{m+1}.$
Далее метод прогнозирования на один шаг будет использоваться для прогнозирования коэффициентов Фурье на следующий период.

Долгосрочное прогнозирование временных рядов с помощью рядов Фурье


Постоянная длина периода


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

$f_1,f_2,….,f_s \;\;\;(14)$


при этом для значений последовательности (14) наблюдается определенная периодичность. Для исследования временного ряда (14) с постоянной периодичностью (например, продажи какого-либо товара по неделям) предлагается [1-2] использовать тригонометрические ряды Фурье. В этом случае весь набор наблюдений s можно разбить на m периодов длины l+1, а массив измерений (14) можно представить в виде матрицы

$\quad \begin{pmatrix} f_{10} & f_{11} & ... &f_{1l-1} &f_{1l} \\ ... & ... & ... &... &... \\ f_{m0} & f_{m1} & ... &f_{ml-1} &f_{ml} \end{pmatrix} \quad\;\;\; (15)$


размерности $(l+1) \times m,$ в которой каждая строка $(f_{i0}, f_{i1}, \ldots, f_{il-1},f_{il})$ является значениями показателя i-го периода (например, i-й недели).
Значения $f_{it}, \; t=0,1,\ldots, l$ объявляются значениями некоторой функции $f_i(t),$ где аргумент $t \in [0; l].$ Функцию $f_i(t)$ предлагается заменить частной суммой тригонометрического ряда Фурье по косинусам [6].

$$display$$\begin{aligned} &f_{i}(t)\approx y_i(t), \\ y_i(t) = \frac{a_0^i}{2}+&\sum_{k=1}^{N}a_k^i\cos⁡\Bigl(\frac{\pi kt}{l}\Bigr). \end{aligned} \;\;\;(16)$$display$$


Таким образом, получаем для каждого i-го периода частичную сумму ряда Фурье со своими коэффициентами $a_0^i, a_1^i, \ldots, a_N^i,$ для нахождения которых можно воспользоваться несколькими способами.

1 метод. Минимизация квадратичной ошибки

$\Phi( a_0^i, a_1^i, \ldots,a_N^i )=\sum_{t=0}^{l}(f_{it}-y_{it} )^2→min$


где $y_{it}= y_{i}(t), \;i=1, \ldots, m.$ Из условия минимума

$\frac{\partial \Phi}{\partial a_k^i}=0, \;\;\;k=0,1,\ldots,N$


получаем систему уравнений для нахождения коэффициентов $a_0^i, a_1^i, \ldots, a_N^i.$
Количество слагаемых N в этом случае нужно выбирать не более длины периода l.

2 метод. Непосредственно из формулы
Если выбрать N = l, то из формул (16) для определения коэффициентов $a_0^i, a_1^i, \ldots, a_N^i$ получаем систему уравнений

$\frac{a_0^i}{2}+\sum_{k=1}^{N}a_k^i\cos⁡\Bigl(\frac{\pi kt}{l}\Bigr) = f_{it}, \;\;t=0,1,\ldots,l. \;\;\;(17)$



3 метод. Численное интегрирование
Вспомним, что

$a_k^i=\frac{2}{l}\int_{-l}^{l}f_i(t)\cos \Bigl(\frac{k \pi t}{l} \Bigr)dt; \;\; k=0,1,2,...\,.$


Если длина периода l>20, то для нахождения коэффициентов $a_0^i, a_1^i, \ldots, a_N^i$ можно воспользоваться формулами численного интегрирования, при этом достаточно формулы трапеций.

После того, как коэффициенты найдены, строится прогноз для следующего периода по формуле

$\begin{aligned} y_{m+1}(t) = &\frac{a_0^{m+1}}{2}+\sum_{k=1}^{N}a_k^{m+1}\cos \Bigl(\frac{\pi kt}{l}\Bigr), \\ &t=1,2,...,l. \end{aligned}\;\;\; (18) $



Переменная длина периода


Пусть теперь длина периода не является постоянной (например, количество дней в месяце). Пусть l – длина i-го периода, i=1, 2, ..., m. Для каждого периода i имеем последовательность значений

$f_{i0},f_{i1},….,f_{il_i}$


Пусть вновь $f_{it} = f_i(t), \; t \in [0,l_i].$ Выполним замену переменной

$x=\frac{2\pi t}{l_i}-\pi, \;\; x \in [-\pi, \pi].$


Обозначим

$\begin{aligned} \widetilde{f}_i(x)&=f_i\Bigl(\frac{l_i}{2\pi}(x+\pi)\Bigr), \;\;x\in [-\pi, \pi], \\ &x_t=\frac{2\pi t}{l_i}-\pi, \;\; t=1,2,...,l_i. \end{aligned}$


Тогда $\widetilde{f}_i(x_t) = f_i(t)=f_{it}.$ Заменим $\widetilde{f}_i(x_t)$ на стандартном промежутке $[-\pi, \pi]$ частной суммой тригонометрического ряда Фурье

$$display$$\begin{aligned} &\widetilde{f}_{i}(x)\approx y_i(x), \\ y_i(t) = \frac{a_0^i}{2}+\sum_{k=1}^{N}&a_k^i\cos⁡(kx)+b_k^i\sin⁡(kx), \end{aligned} \;\;\;(19)$$display$$


где

$\begin{aligned} &a_{0}^i = \frac{1}{\pi} \int_{-\pi}^{\pi} \widetilde{f}_i(x)dx; \\&a_{k}^i = \frac{1}{\pi} \int_{-\pi}^{\pi} \widetilde{f}_i(x)\cos(kx)dx; \\ &b_{k}^i = \frac{1}{\pi} \int_{-\pi}^{\pi} \widetilde{f}_i(x)\sin(kx)dx.\end{aligned}$


Коэффициенты $a_0^i, a_1^i, \ldots, a_N^i, b_0^i, b_1^i, \ldots, b_N^i$ так же, как и для случая постоянных значений длин периодов, можно найти из условия минимизации квадратичной ошибки

$\Phi( a_0^i, a_1^i, \ldots,a_N^i, b_0^i, b_1^i, \ldots, b_N^i)=\sum_{t=0}^{l}(f_{it}-y_{it} )^2→\min \;\;\;(20)$


где $y_{it} = y_{i}(t), \; i=1,\ldots, m.$ Количество слагаемых N в этом случае нужно выбирать не более половины длины периода l. Также можно посчитать коэффициенты непосредственно из формул (19), либо, при больших длинах периода, можно воспользоваться формулами трапеций.
Таким образом, способ нахождения коэффициентов Фурье для каждого периода зависит от постоянства длин периодов и от их длины. Значения найденных коэффициентов в зависимости от номера периода рассматриваем как временные ряды, которые нужно спрогнозировать на один следующий шаг.

Коэффициенты ряда Фурье

$a_{0}^{m+1},a_1^{m+1}, \ldots,a_N^{m+1},b_{0}^{m+1},b_1^{m+1}, \ldots,b_N^{m+1}$


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

$$display$$\begin{aligned} y_{m+1}(t) = &\frac{a_0^{m+1}}{2}+\sum_{k=1}^{N}a_k^{m+1}\cos⁡(kx)+b_k^{m+1}\sin⁡(kx), \\ &x_t=\frac{2\pi t}{l_{m+1}}-\pi, \;\; t=1,2,...,l_{m+1}. \end{aligned}\;\;\; (21)$$display$$


Резюмируем: для долгосрочного прогнозирования временных рядов

  1. Наблюдения каждого периода (каждой недели или каждого месяца) $ f_{it}, \; t=0,1,\ldots,l$ объявляются значениями некоторой функции $f_{i}(t)$
  2. Функция $f_{i}(t)$ заменяется частной суммой ряда Фурье
  3. Из условия минимума квадратичной ошибки (20) или каким-либо другим способом находятся коэффициенты ряда Фурье для известных периодов
  4. Найденные коэффициенты рассматриваются как временные ряды в зависимости от номера периода
  5. Для каждого коэффициента строится своя матрица задержек и делается прогноз этого коэффициента на следующий период
  6. По спрогнозированным коэффициентам Фурье по формуле (21) находятся значения неизвестной функции (временного ряда) для следующего периода


Немного кода


Попробуем применить данный алгоритм на реальных данных. Возьмем с сайта Росстата среднюю зарплату в России по месяцам за период с 2013-го по 2018-й г.

Код
import math
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import mean_absolute_error, mean_absolute_percentage_error
sns.set()

data = pd.read_excel('mean_salary.xlsx')
data


image

Преобразуем данные в удобный формат в виде ряда и построим график

Код
#сюда положим ряд, составленный из средних зп
salary_series = pd.Series()

for _, row in data.drop('year', axis=1).iterrows():
    salary_series = salary_series.append(pd.Series(row.values))

#используем даты в качестве индексов
salary_series.index = pd.date_range('2013-01-01', freq='M', periods=6*12)

#построим график
salary_series.plot(figsize=(12, 6), linewidth=2, fontsize=13)
plt.title('Среднемесячная ЗП в России', fontsize=15)
plt.xlabel('Дата', fontsize=14)
plt.ylabel('Среднемесячная ЗП, руб.', fontsize=14)
plt.show()


image

Разделим данные на тренировочный (2013 — 2017 гг.) и тестовый (2018 г.) наборы.

train_series = salary_series[salary_series.index.year<2018]
test_series = salary_series[salary_series.index.year==2018]

Перейдем к реализации алгоритма. Наш временной ряд состоит из 5-ти периодов одинаковой длины (12 месяцев), поэтому будем представлять неизвестную функцию в виде формулы (16). Для поиска коэффициентов $a^i_k$ из формул (17) получаются системы уравнений

$\quad \begin{pmatrix} \frac{1}{2} & 1 & 1 & ... & 1 \\ \frac{1}{2} & \cos\Bigl( \frac{\pi}{l} \Bigr) & \cos\Bigl( \frac{2\pi}{l} \Bigr) & ... &\cos\Bigl( \frac{N\pi}{l} \Bigr) \\ \frac{1}{2} & \cos\Bigl(\frac{2\pi}{l} \Bigr) & \cos\Bigl(\frac{4\pi}{l} \Bigr) & ... &\cos\Bigl( \frac{2N\pi}{l} \Bigr) \\ ... & ... & ... &... \\ \frac{1}{2}& \cos(\pi) & \cos(2 \pi)& ... &\cos(N \pi) \end{pmatrix} \quad \begin{pmatrix} a^i_0 \\ a^i_1 \\ a^i_2\\... \\ a^i_N \end{pmatrix} = \begin{pmatrix} f^i_0 \\ f^i_1 \\ f^i_2\\... \\ f^i_N \end{pmatrix}$


Код
def cos(k, t, l):
    """
    Вспомогательная функция косинуса
    """
    return math.cos(math.pi*k*t/l)


def get_matrix_and_vector(period_i: np.ndarray) -> (np.ndarray, np.ndarray):
    
    """
    Возвращает матрицу и вектор свободных членов для нахождения коэффициентов Фурье для i-го периода.
    
    period_i - наблюдения i-го периода
    """
    
    l = len(period_i) - 1
    N = l
        
    y = np.empty((0,))
    matrix = np.empty((0, N+1))
    
    for t in range(0, l+1):
        #первое значение в каждой строке 1/2 -- множитель перед коэффициентом a_0
        row = np.array([.5])
        
        for k in range(1, N+1):
            row = np.append(row, cos(k, t, l))

        row = np.reshape(row, (1, N+1))
        matrix = np.append(matrix, row, axis=0)
        y = np.append(y, period_i[t])
 
    return matrix, y


def solve_system(M: np.ndarray, 
                 b: np.ndarray) -> np.ndarray:
    
    """
    Решает систему линейных уравнений
    M - основная матрица системы
    b - столбец свободных членов
    """
    
    assert np.linalg.det(M) != 0
    return np.linalg.solve(M, b)


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

Код
def get_matrix_from_series(input_series: pd.Series, 
                           m: int, 
                           l: int):
    """
    Преобразует входной ряд в матрицу, 
    где каждая i-я строка -- наблюдения для i-го периода
    
    input_series -- входной ряд
    m -- количество периодов
    l - длина периода
    """
    
    return input_series.values.reshape(m, l)


def get_delay_matrix(input_vector: np.ndarray, 
                     p: int = 1) -> np.ndarray:
    """
    Строит матрицу задержек по входному вектору и величине задержек
    input_vector - входной вектор
    p - величина задержек
    """
    
    input_vector_copy = np.copy(input_vector)
    
    m = input_vector_copy.shape[0] % p
    
    #если длина ряда не кратна p, то удаляем несколько первых значений ряда
    if m != 0:
        input_vector_copy = np.delete(input_vector_copy, range(m))
    
    #определяем размерность матрицы зарежек
    row_dim = input_vector_copy.shape[0] // p
    col_dim = p
    
    #строим матрицу
    delay_matrix = np.resize(input_vector_copy, 
                             new_shape=(row_dim, col_dim)).T    
    
    return delay_matrix


def find_nearest(row: np.ndarray, 
                 p: int) -> set:
    """
    Возвращает индексы ближайших соседей для последнего элемента строки
    row - входная строка
    p - величина задержек
    """
    
    #количество соседей
    neighbors_cnt = 2 * p + 1
    
    last_element = row[-1]
    all_neighbors = row[:-1]

    #находим индексы ближайших соседей
    idx = set(np.argsort(np.abs(all_neighbors-last_element))[:neighbors_cnt])
        
    return idx


def predict_by_one_step(input_vector: np.ndarray, 
                        p: int = 1) -> float :
    """
    Прогнозирование на один шаг с помощью аналитического решения
    input_vector - входной вектор
    p - величина задержек
    """
    
    delay_matrix = get_delay_matrix(input_vector, p)
    last_row = delay_matrix[-1,:]
    nearest_neighbors_indexes = find_nearest(last_row, p)
    
    y = np.empty((0,))
    X = np.empty((0, p+1))
    for index in nearest_neighbors_indexes:
        y = np.append(y, delay_matrix[0, index+1])
        row = np.append(np.array([1]), delay_matrix[:, index])
        row = np.reshape(row, (1, p+1))
        X = np.append(X, row, axis=0)
    
    coef = np.dot(np.dot(np.linalg.inv(np.dot(X.T, X)), X.T), y)
    prediction = sum(np.append(np.array([1]), delay_matrix[:, -1]) * coef)
    
    return prediction


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

Код
def get_new_fourier_coefs(periods: np.ndarray, 
                          p: int = 1) -> list:
    
    """
    Возвращает коэффициенты Фурье для неизвестного периода
    periods - матрица наблюдений для известных периодов, где i-я строчка -- наблюдения i-го периода
    p - величина задержек
    
    """
    
    #список с предсказанными на след. период коэффициентами
    new_coefs = []
    
    #матрица с коэффициентами Фурье за все периоды. Строки -- коэфициенты за период
    coefs_for_all_periods = []
    
    for period in periods:
        
        X, y = get_matrix_and_vector(period)
        
        #находим коэффициенты Фурье, как решение системы линейных уравнений
        fourier_coef_for_period = solve_system(X, y)
        
        coefs_for_all_periods.append(fourier_coef_for_period)
    
    coefs_for_all_periods = np.array(coefs_for_all_periods)
    
    #Прогноз каждого коэффициента Фурье a_k для неизвестного периода
    #Каждый коэф. Фурье рассматривается как временной ряд, который прогнозируется на один шаг
    for i in range(coefs_for_all_periods.shape[1]):
        coef_for_next_period = predict_by_one_step(coefs_for_all_periods[:, i], p=p)
        new_coefs.append(coef_for_next_period)

    return new_coefs


def predict_next_period(new_coefs: list, 
                        l: int):
    """
    Прогнозирует временной ряд на неизвестный период
    
    new_coefs - коэффициенты Фурье для следующего периода
    l - длина периода
    """
    
    new_period = []
    for t in range(0, l):
        s = new_coefs[0] / 2
        for k in range(1, len(new_coefs)):
            s += new_coefs[k]*cos(k, t, l=l-1)

        new_period.append(s)
    
    return new_period


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

Код
m = 5 #количество периодов в train выборке
l = 12 #длина периода
p = 1 #величина задержек

matrix = get_matrix_from_series(train_series, m, l)
new_coefs = get_new_fourier_coefs(matrix, p)
test_pred = predict_next_period(new_coefs, l)
test_pred = pd.Series(test_pred, index=test_series.index)

#ошибка прогноза
mae = round(mean_absolute_error(test_pred, test_series), 2)
mape = round(mean_absolute_percentage_error(test_series, test_pred), 3)


#построим график
test_pred.plot(figsize=(12, 6), linewidth=3, 
               fontsize=13, label='Прогноз')

test_series.plot(figsize=(12, 6), linewidth=3, 
                 fontsize=13, label='Факт')

plt.legend(fontsize=15)
plt.text(test_series.index[4], 55000, f'Mean absolute error = {mae}', fontsize=15)
plt.text(test_series.index[4], 54000, f'Mean absolute percentage error = {mape}', fontsize=15)
plt.title('Сравнение прогноза и факта среднемесячной ЗП в РФ на 2018-й год', fontsize=15)
plt.xlabel('Дата', fontsize=14)
plt.ylabel('Среднемесячная ЗП, руб.', fontsize=14)
plt.show()


image

Весь код и данные доступны по ссылке на гитхабе.

Список литературы


  1. П.А. Гатин,  В.Н. Семенова, Исследование циклических временных рядов с переменной цикличностью методом рядов Фурье, Вестник ДИТИ, 2018. – № 1(15). – С.91-96
  2. П.А. Гатин,  В.Н. Семенова, Об одном подходе к исследованию временных рядов с постоянной цикличностью, ДИТИ НИЯУ МИФИ, 2017. – С. 48-51
  3. А.Н. Колмогоров, С.В. Фомин – Элементы теории функций и функционального анализа
  4. В.Н. Афанасьев, М.М. Юзбашев, Анализ временных рядов и прогнозирование – М.: Финансы и статистика, Инфра-М, 2010. – 320 c.
  5. А.Ю. Лоскутов, О.Л. Котляров, И.А. Истомин, Д.И. Журавлев. Проблемы нелинейной динамики. III. Локальные методы прогнозирования временных рядов. Вестн. Моск. ун-та, сеp. Физ.-астр., 2002, No6, c.3–21.
  6. О.С. Амосов, О.С., Муллер Н.В. Исследование временных рядов с применением методов фрактального и вейвлет анализа, Интернет-журнал «НАУКОВЕДЕНИЕ», Выпуск 3
Теги:data sciencemachine learningaifourierвременные рядыtimeseriesпрогнозирование
Хабы: Блог компании GlowByte Математика Машинное обучение Физика
Всего голосов 8: ↑8 и ↓0 +8
Просмотры5.5K

Похожие публикации

Лучшие публикации за сутки

Информация

Дата основания
2002
Местоположение
Россия
Сайт
glowbyteconsulting.com
Численность
1 001–5 000 человек
Дата регистрации
Представитель
IraMefedova

Блог на Хабре