Skip to content

Commit f4a8d9c

Browse files
authored
Merge pull request #21 from K-Ibadullaev/KForecast
Multistep K-Forecast, changed description and input parameters for L …
2 parents 3aa03cc + eca57eb commit f4a8d9c

File tree

12 files changed

+5427
-140
lines changed

12 files changed

+5427
-140
lines changed

examples_and_guide/.ipynb_checkpoints/MSSA_guide-checkpoint.ipynb

Lines changed: 2055 additions & 0 deletions
Large diffs are not rendered by default.

examples_and_guide/.ipynb_checkpoints/SSA_guide-checkpoint.ipynb

Lines changed: 1886 additions & 0 deletions
Large diffs are not rendered by default.

examples_and_guide/MSSA_guide.ipynb

Lines changed: 145 additions & 32 deletions
Large diffs are not rendered by default.

examples_and_guide/SSA_guide.ipynb

Lines changed: 92 additions & 33 deletions
Large diffs are not rendered by default.

src/py_ssa_lib/.ipynb_checkpoints/MSSA-checkpoint.py

Lines changed: 460 additions & 0 deletions
Large diffs are not rendered by default.

src/py_ssa_lib/.ipynb_checkpoints/SSA-checkpoint.py

Lines changed: 495 additions & 0 deletions
Large diffs are not rendered by default.

src/py_ssa_lib/MSSA.py

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -338,51 +338,71 @@ def estimate_LRR(self, idx_components):
338338
else :
339339
print(nu_sq)
340340

341-
def L_Forecast(self, ts, M, idx_components, mode='forward'):
341+
def L_Forecast(self, ts, M, idx_components, return_full=True):
342342
"""
343-
Forecasts M values for a given time series using LRR
343+
Forecasts M values for a given time series using LRR and U space
344344
Parameters
345345
----------
346346
idx_components:list or numpy.arange of positive integer numbers, denotes the indices of elementary components
347347
ts: numpy.array, input time series
348348
M:int, number of values to forecast or estimate
349-
mode:str, forecasts M future values for S time series if mode is "forward",
349+
return_full:bool, if True returns original time series + M forecasted values,
350350
351351
352352
Returns
353353
-------
354-
y_pred: numpy.array, original time series + M forecasted values, or original time series, where the last M values are estimated
354+
y_pred: numpy.array, (original time series +) M forecasted values,
355355
"""
356356
R = self.estimate_LRR(idx_components)
357357
R = R.reshape(-1,1)
358-
if M<=0:
359-
y_pred = ts[:,:self.N]
360-
return y_pred
361-
362-
if mode == 'forward':
363-
y_pred = np.zeros((ts.shape[0],self.N+M))
364-
y_pred[:,:self.N] = ts[:,:self.N]
365-
366-
for m in range(0,M):
367-
y_pred[:,self.N+m] = (y_pred[:,self.N-self.L+m+1:y_pred.shape[1]-M+m]@R).flatten()
358+
359+
y_pred = np.zeros((min(ts.shape),self.N+M))
360+
y_pred[:,:self.N] = ts[:,:self.N]
368361

369-
#elif mode == 'retrospective':
370-
# This method is no longer supported
371-
#y_pred = np.zeros((ts.shape[0],self.N))
372-
#y_pred[:,:self.N-M] = ts[:,:self.N-M]
373-
374-
#for m in range(0,M):
375-
376-
#y_pred[:,self.N+m-M] = (y_pred[:,self.N-self.L+m+1-M:y_pred.shape[1]-M+m] @ R).flatten()
377-
378-
362+
for m in range(0,M):
363+
y_pred[:,self.N+m] = (y_pred[:,self.N-self.L+m+1:y_pred.shape[1]-M+m]@R).flatten()
364+
365+
if return_full:
366+
return y_pred
379367
else:
380-
381-
raise ValueError('Wrong type of the forecasting mode')
368+
return y_pred[:,-M:]
382369

370+
def K_forecast(self, ts, M, idx_components, return_full=True):
371+
""""
372+
Forecasts M values for a given time series using V eigenspace
373+
Parameters
374+
----------
375+
idx_components:list or numpy.arange of positive integer numbers, denotes the indices of elementary components
376+
ts: numpy.array, input time series
377+
M:int, number of values to forecast or estimate
378+
return_full:bool, if True returns original time series + M forecasted values,
379+
380+
381+
Returns
382+
-------
383+
y_pred: numpy.array, (original time series +) M forecasted values,
384+
"""
383385

386+
W = self.V[:,idx_components]
387+
W = W[[(self.K) * s for s in range(0, min(ts.shape))],: ]
388+
389+
Q = self.V[:,idx_components]
390+
Q = np.delete(Q,[(self.K) * s for s in range(0, min(ts.shape))],0 )
391+
392+
inverse_m = np.linalg.inv(np.eye(min(ts.shape)) - W @ W.T )
393+
394+
y_pred = np.zeros((min(ts.shape),self.N+M))
395+
y_pred[:,:self.N] = ts
396+
for m in range(0,M):
384397

385-
return y_pred
398+
Z = y_pred[:, self.N-self.K+m+1: self.N+m].flatten()
399+
y_pred[:,self.N + m] = inverse_m @ W @ Q.T @ Z
400+
401+
if return_full:
402+
return y_pred
403+
else:
404+
return y_pred[:,-M:]
405+
386406

387407
def estimate_ESPRIT(self, idx_components=[0], decompose_rho_omega=False):
388408

src/py_ssa_lib/SSA.py

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -373,75 +373,73 @@ def estimate_LRR(self, idx_components):
373373
return R
374374
else :
375375
print(nu_sq)
376-
377-
def L_Forecast(self, ts, M, idx_components, mode='forward'):
378-
"""
379-
Forecasts M values for a given time series using LRR (L-Forecasting)
376+
377+
def K_forecast(self, ts, idx_components, M, return_full=True):
378+
""""
379+
Forecasts M values for a given time series using V eigenspace
380380
Parameters
381381
----------
382382
idx_components:list or numpy.arange of positive integer numbers, denotes the indices of elementary components
383-
ts: numpy.array, input time series
383+
ts: numpy.array, single input time series . Check that its dimension is 1xN, or apply np.reshape(1,-1) on the input array
384384
M:int, number of values to forecast or estimate
385-
mode:str, forecasts M future values for S time series if mode is "forward"
385+
return_full:bool, if True returns original time series + M forecasted values,
386+
387+
386388
Returns
387389
-------
388-
y_pred: numpy.array, original time series + M forecasted values, or original time series, where the last M values are estimated
390+
y_pred: numpy.array, (original time series +) M forecasted values,
389391
"""
390-
R = self.estimate_LRR(idx_components)
391-
R = R.reshape(-1,1)
392-
if M<=0:
393-
y_pred = ts[:self.N]
394-
return y_pred
395-
396-
if mode == 'forward':
397-
y_pred = np.zeros((1,self.N+M))
398-
y_pred[0,:self.N] = ts[:self.N]
399-
for m in range(0,M):
400-
401-
y_pred[:,self.N+m] = (y_pred[:,self.N-self.L+m+1:y_pred.shape[0]-M+m-1]) @ R
402-
#elif mode == 'retrospective':
403-
#this is an deprecated method
404-
#y_pred = np.zeros((1,self.N))
405-
#y_pred[0,:self.N-M] = ts[:self.N-M]
406-
#for m in range(0,M):
407-
408-
#y_pred[:,self.N+m-M] = (y_pred[:,self.N-self.L+m+1-M:y_pred.shape[0]-M+m-1]) @ R
392+
W = self.V[:,idx_components]
393+
W = W[[(self.K) * s for s in range(0, min(ts.shape))]]
409394

410-
411-
else:
412-
413-
raise ValueError('Wrong type of the forecasting mode')
414-
395+
Q = self.V[:,idx_components]
396+
Q = np.delete(Q,[(self.K) * s for s in range(0, min(ts.shape))],0 )
397+
398+
inverse_m = np.linalg.inv(np.eye(min(ts.shape)) - W @ W.T )
415399

400+
y_pred = np.zeros((min(ts.shape),self.N+M))
401+
y_pred[:,:self.N] = ts
402+
for m in range(0,M):
416403

417-
return y_pred
404+
Z = y_pred[:, self.N-self.K+m+1: self.N+m].flatten()
405+
y_pred[:,self.N + m] = inverse_m @ W @ Q.T @ Z
418406

419-
def K_Forecast(model, ts, M, idx_components):
407+
if return_full:
408+
return y_pred
409+
else:
410+
return y_pred[:,-M:]
411+
412+
def L_Forecast(self, ts, M, idx_components, return_full=True):
420413
"""
421-
Forecasts M values for a given time series using K-Forecasting
414+
Forecasts M values for a given time series using LRR and U space
422415
Parameters
423416
----------
424417
idx_components:list or numpy.arange of positive integer numbers, denotes the indices of elementary components
425418
ts: numpy.array, input time series
426419
M:int, number of values to forecast or estimate
420+
return_full:bool, if True returns original time series + M forecasted values,
427421
422+
428423
Returns
429424
-------
430-
Z: numpy.array, original time series + M forecasted values, or original time series, where the last M values are estimated
425+
y_pred: numpy.array, (original time series +) M forecasted values,
431426
"""
432-
427+
R = self.estimate_LRR(idx_components)
428+
R = R.reshape(-1,1)
433429

434-
print(Warning("This function is under development! \nIt might contain some errors due to indexing of the signal space!"))
435-
Q = np.delete(model.V[:, idx_components], model.K-1, axis=0)
436-
W = model.V[model.K-1, idx_components].reshape(1,-1)
437-
Inverse = np.linalg.inv(np.eye(1) - W @ W.T)
438-
Z = ts[-model.K+1:]
439-
Z = np.append(Z, np.zeros((M))).reshape(-1,1)
440-
430+
y_pred = np.zeros((1,self.N+M))
431+
y_pred[0,:self.N] = ts[:self.N]
441432
for m in range(0,M):
442-
Z[-M+m,:] = Inverse @ W @ Q.T @ Z[-M+m - model.K+1:-M + m,:]
443-
444-
return Z.T
433+
434+
y_pred[:,self.N+m] = (y_pred[:,self.N-self.L+m+1:y_pred.shape[0]-M+m-1]) @ R
435+
436+
437+
if return_full:
438+
return y_pred
439+
else:
440+
return y_pred[:,-M:]
441+
442+
445443

446444
def estimate_ESPRIT(self, idx_components=[0], decompose_rho_omega=False):
447445
"""
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import sys
2+
import py_ssa_lib
3+
import pytest
4+
import pandas as pd
5+
import numpy as np
6+
import matplotlib.pyplot as plt
7+
import scipy as sp
8+
from sklearn.utils.extmath import randomized_svd
9+
10+
from py_ssa_lib.MSSA import MSSA
11+
from py_ssa_lib.datasets.load_energy_consumption_df import load_energy_consumption_df
12+
13+
14+
def test_mssa_init():
15+
16+
17+
mssa_test = MSSA(Verbose=True)
18+
19+
assert mssa_test.Verbose == True
20+
21+
22+
def test_mssa_fit():
23+
24+
i_start_ts = 2
25+
decomposition = "svd"
26+
#decomposition = "rand_svd"
27+
window_s = 7
28+
demo_df = load_energy_consumption_df()
29+
30+
mssa_test = MSSA(Verbose=True)
31+
mssa_test.fit(df=demo_df,decomposition=decomposition, idx_start_ts=i_start_ts, L=window_s)
32+
33+
assert (int(mssa_test.L) == window_s)
34+
assert (mssa_test.decomposition == decomposition )
35+
assert (int(mssa_test.N) == demo_df.iloc[:,i_start_ts:].shape[1])
36+
37+
def test_mssa_lrr():
38+
39+
i_start_ts = 2
40+
decomposition = "svd"
41+
42+
window_s = 7
43+
demo_df = load_energy_consumption_df()
44+
45+
mssa_test = MSSA()
46+
mssa_test.fit(df=demo_df,decomposition=decomposition, idx_start_ts=i_start_ts, L=window_s)
47+
ts_array = mssa_test.reconstruct_ts(idx_chosen_components=[0],return_as_df=False)
48+
M_ = 2
49+
fact_lrr = np.array([0.1756382, 0.1845678, 0.1981957, 0.2129944, 0.2277441, 0.2433179])
50+
51+
assert np.allclose(mssa_test.estimate_LRR( idx_components=[0]) , fact_lrr)==True
52+
53+
54+
55+
def test_mssa_L_Forecast():
56+
ts_index = 0
57+
i_start_ts = 2
58+
decomposition = "svd"
59+
60+
window_s = 7
61+
demo_df = load_energy_consumption_df()
62+
63+
mssa_test = MSSA()
64+
mssa_test.fit(df=demo_df,decomposition=decomposition, idx_start_ts=i_start_ts,L=window_s)
65+
ts_array = mssa_test.reconstruct_ts(idx_chosen_components=[0],return_as_df=False)
66+
M_ = 2
67+
68+
fact_lrr = np.array([0.1756382, 0.1845678, 0.1981957, 0.2129944, 0.2277441, 0.2433179])
69+
fact_L_forecast = np.array([
70+
[15.38531, 16.306364],
71+
[18.77296, 19.915842],
72+
[19.89430, 21.064436],
73+
[83.41526, 89.702892],
74+
[ 5.63838, 5.986697]
75+
])
76+
77+
assert np.allclose(mssa_test.estimate_LRR( idx_components=[0]) , fact_lrr )==True
78+
assert np.allclose(
79+
mssa_test.L_Forecast( ts_array, M=M_, idx_components=[0])[:,-M_:], fact_L_forecast ) == True
80+
81+
def test_mssa_estimate_ESPRIT():
82+
83+
i_start_ts = 2
84+
decomposition = "svd"
85+
86+
window_s = 7
87+
demo_df = load_energy_consumption_df()
88+
89+
mssa_test = MSSA()
90+
mssa_test.fit(df=demo_df,decomposition=decomposition, idx_start_ts=i_start_ts, L=window_s)
91+
ts_array = mssa_test.reconstruct_ts(idx_chosen_components=[0],return_as_df=False)
92+
M_ = 2
93+
fact_esprit =np.array([
94+
1.5373144+0.j, 1.0207511+0.j,
95+
0.91938+0.8687121j, 0.91938-0.8687121j,
96+
-0.7997768+0.84053236j, -0.7997768-0.84053236j
97+
])
98+
assert np.allclose(mssa_test.estimate_ESPRIT(idx_components=np.arange(mssa_test.d-1), decompose_rho_omega=False), fact_esprit) ==True
99+
100+
101+
102+

0 commit comments

Comments
 (0)