Skip to content

Commit 01f19cf

Browse files
Merge pull request #71 from LAMPSPUC/add_xi_threshold
Add xi threshold and update dynamic_exog_coefs estimation
2 parents e2096de + 85fcef3 commit 01f19cf

File tree

6 files changed

+138
-65
lines changed

6 files changed

+138
-65
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,8 @@ longhorizon/
1515
test.jl
1616
test_ms.jl
1717
new/
18-
test2.jl
18+
test2.jl
19+
20+
src/boosting_estimation_procedure.jl
21+
plots/
22+
paper_tests/m4_test/evaluate_model2.jl

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "StateSpaceLearning"
22
uuid = "971c4b7c-2c4e-4bac-8525-e842df3cde7b"
33
authors = ["andreramosfc <[email protected]>"]
4-
version = "2.0.5"
4+
version = "2.0.6"
55

66
[deps]
77
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ simulation = simulate(model, 12, 1000) # Gets 1000 scenarios path of 12 steps ah
3636
* `freq_seasonal::Union{Int,Vector{Int}}`: Seasonal frequency or vector of frequencies (default: 12)
3737
* `cycle_period::Union{Union{Int,<:AbstractFloat},Vector{Int},Vector{<:AbstractFloat}}`: Cycle period or vector of periods (default: 0)
3838
* `outlier::Bool`: Include outlier component (default: true)
39+
* `ξ_threshold::Int`: Threshold for level innovations (default: 1)
3940
* `ζ_threshold::Int`: Threshold for slope innovations (default: 12)
4041
* `ω_threshold::Int`: Threshold for seasonal innovations (default: 12)
4142
* `ϕ_threshold::Int`: Threshold for cycle innovations (default: 12)

src/StateSpaceLearning.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export fit!,
1717
forecast,
1818
simulate,
1919
StructuralModel,
20+
BasicStructuralModel,
2021
plot_point_forecast,
2122
plot_scenarios,
2223
simulate_states

src/models/structural_model.jl

Lines changed: 114 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ mutable struct StructuralModel <: StateSpaceLearningModel
6565
freq_seasonal::Union{Int,Vector{Int}}
6666
cycle_period::Union{Union{Int,<:AbstractFloat},Vector{Int},Vector{<:AbstractFloat}}
6767
outlier::Bool
68+
ξ_threshold::Int
6869
ζ_threshold::Int
6970
ω_threshold::Int
7071
ϕ_threshold::Int
@@ -82,6 +83,7 @@ mutable struct StructuralModel <: StateSpaceLearningModel
8283
freq_seasonal::Union{Int,Vector{Int}}=12,
8384
cycle_period::Union{Union{Int,<:AbstractFloat},Vector{Int},Vector{<:AbstractFloat}}=0,
8485
outlier::Bool=true,
86+
ξ_threshold::Int=1,
8587
ζ_threshold::Int=12,
8688
ω_threshold::Int=12,
8789
ϕ_threshold::Int=12,
@@ -156,6 +158,7 @@ mutable struct StructuralModel <: StateSpaceLearningModel
156158
freq_seasonal,
157159
cycle_period,
158160
outlier,
161+
ξ_threshold,
159162
ζ_threshold,
160163
ω_threshold,
161164
ϕ_threshold,
@@ -180,6 +183,7 @@ mutable struct StructuralModel <: StateSpaceLearningModel
180183
freq_seasonal,
181184
cycle_period,
182185
outlier,
186+
ξ_threshold,
183187
ζ_threshold,
184188
ω_threshold,
185189
ϕ_threshold,
@@ -191,6 +195,45 @@ mutable struct StructuralModel <: StateSpaceLearningModel
191195
end
192196
end
193197

198+
"""
199+
Create a Basic Structural Model.
200+
201+
# Arguments
202+
- `y::Union{Vector,Matrix}`: Time series to be modeled.
203+
- `level::String="stochastic"`: Level component of the model.
204+
- `slope::String="stochastic"`: Slope component of the model.
205+
- `seasonal::String="stochastic"`: Seasonal component of the model.
206+
- `freq_seasonal::Union{Int,Vector{Int}}=12`: Seasonal period of the model.
207+
208+
# Returns
209+
- `StructuralModel`: Basic Structural Model.
210+
"""
211+
function BasicStructuralModel(
212+
y::Union{Vector,Matrix};
213+
level::String="stochastic",
214+
slope::String="stochastic",
215+
seasonal::String="stochastic",
216+
freq_seasonal::Union{Int,Vector{Int}}=12,
217+
)
218+
return StructuralModel(
219+
y;
220+
level=level,
221+
slope=slope,
222+
seasonal=seasonal,
223+
cycle="none",
224+
freq_seasonal=freq_seasonal,
225+
cycle_period=0,
226+
outlier=false,
227+
ξ_threshold=0,
228+
ζ_threshold=0,
229+
ω_threshold=0,
230+
ϕ_threshold=0,
231+
stochastic_start=1,
232+
exog=zeros(length(y), 0),
233+
dynamic_exog_coefs=nothing,
234+
)
235+
end
236+
194237
"""
195238
ξ_size(T::Int, stochastic_start::Int)::Int
196239
@@ -204,7 +247,8 @@ end
204247
- `Int`: Size of ξ calculated from T.
205248
206249
"""
207-
ξ_size(T::Int, stochastic_start::Int)::Int = T - max(2, stochastic_start)
250+
ξ_size(T::Int, ξ_threshold::Int, stochastic_start::Int)::Int =
251+
T - max(2, stochastic_start) + 1 - ξ_threshold
208252

209253
"""
210254
ζ_size(T::Int, ζ_threshold::Int, stochastic_start::Int)::Int
@@ -273,27 +317,28 @@ o_size(T::Int, stochastic_start::Int)::Int = T - max(1, stochastic_start) + 1
273317
(2 * (T - max(2, stochastic_start) + 1) - (max(1, ϕ_threshold) * 2))
274318

275319
"""
276-
create_ξ(T::Int, stochastic_start::Int)::Matrix
320+
create_ξ(T::Int, ξ_threshold::Int, stochastic_start::Int)::Matrix
277321
278322
Creates a matrix of innovations ξ based on the input sizes, and the desired steps ahead (this is necessary for the forecast function)
279323
280324
# Arguments
281325
- `T::Int`: Length of the original time series.
326+
- `ξ_threshold::Int`: Stabilize parameter ξ.
282327
- `stochastic_start::Int`: parameter to set at which time stamp the stochastic component starts.
283328
284329
# Returns
285330
- `Matrix`: Matrix of innovations ξ constructed based on the input sizes.
286331
287332
"""
288-
function create_ξ(T::Int, stochastic_start::Int)::Matrix
333+
function create_ξ(T::Int, ξ_threshold::Int, stochastic_start::Int)::Matrix
289334
stochastic_start = max(2, stochastic_start)
290335
ξ_matrix = zeros(T, T - stochastic_start + 1)
291336
ones_indexes = findall(
292337
I -> Tuple(I)[1] - (stochastic_start - 2) > Tuple(I)[2],
293-
CartesianIndices((T, T - stochastic_start)),
338+
CartesianIndices((T, T - stochastic_start + 1)),
294339
)
295340
ξ_matrix[ones_indexes] .= 1
296-
return ξ_matrix[:, 1:(end - 1)]
341+
return ξ_matrix[:, 1:(end - ξ_threshold)]
297342
end
298343

299344
"""
@@ -524,6 +569,7 @@ create_dynamic_exog_coefs_matrix(dynamic_exog_coefs::Vector{<:Tuple}, T::Int,ζ_
524569
function create_dynamic_exog_coefs_matrix(
525570
dynamic_exog_coefs::Vector{<:Tuple},
526571
T::Int,
572+
ξ_threshold::Int,
527573
ζ_threshold::Int,
528574
ω_threshold::Int,
529575
ϕ_threshold::Int,
@@ -537,7 +583,7 @@ function create_dynamic_exog_coefs_matrix(
537583
nothing
538584
else
539585
state_components_dict["level"] = hcat(
540-
ones(T, 1), create_ξ(T, stochastic_start)
586+
ones(T, 1), create_ξ(T, ξ_threshold, stochastic_start)
541587
)
542588
end
543589
key_name = "level"
@@ -588,6 +634,7 @@ create_forecast_dynamic_exog_coefs_matrix(dynamic_exog_coefs::Vector{<:Tuple}, T
588634
- `dynamic_exog_coefs::Vector{<:Tuple}`: Vector of tuples containing the combination components.
589635
- `T::Int`: Length of the original time series.
590636
- `steps_ahead::Int`: Steps ahead.
637+
- `ξ_threshold::Int`: Stabilize parameter ξ.
591638
- `ζ_threshold::Int`: Stabilize parameter ζ.
592639
- `ω_threshold::Int`: Stabilize parameter ω.
593640
- `ϕ_threshold::Int`: Stabilize parameter ϕ.
@@ -600,6 +647,7 @@ function create_forecast_dynamic_exog_coefs_matrix(
600647
dynamic_exog_coefs::Vector{<:Tuple},
601648
T::Int,
602649
steps_ahead::Int,
650+
ξ_threshold::Int,
603651
ζ_threshold::Int,
604652
ω_threshold::Int,
605653
ϕ_threshold::Int,
@@ -613,7 +661,8 @@ function create_forecast_dynamic_exog_coefs_matrix(
613661
nothing
614662
else
615663
state_components_dict["level"] = hcat(
616-
ones(T + steps_ahead, 1), create_ξ(T + steps_ahead, stochastic_start)
664+
ones(T + steps_ahead, 1),
665+
create_ξ(T + steps_ahead, ξ_threshold, stochastic_start),
617666
)[
618667
(end - steps_ahead + 1):end, 1:combination[4]
619668
]
@@ -680,6 +729,7 @@ create_X(
680729
freq_seasonal::Union{Int,Vector{Int}},
681730
cycle_period::Union{Int,Vector{Int}},
682731
outlier::Bool,
732+
ξ_threshold::Int,
683733
ζ_threshold::Int,
684734
ω_threshold::Int,
685735
ϕ_threshold::Int,
@@ -701,6 +751,7 @@ create_X(
701751
- `freq_seasonal::Union{Int, Vector{Int}}`: Seasonal period.
702752
- `cycle_period::Union{Int,Vector{Int}}`: Cycle period.
703753
- `outlier::Bool`: Flag for considering outlier component.
754+
- `ξ_threshold::Int`: Stabilize parameter ξ.
704755
- `ζ_threshold::Int`: Stabilize parameter ζ.
705756
- `ω_threshold::Int`: Stabilize parameter ω.
706757
- `ϕ_threshold::Int`: Stabilize parameter ϕ.
@@ -722,6 +773,7 @@ function create_X(
722773
freq_seasonal::Union{Int,Vector{Int}},
723774
cycle_period::Union{Union{Int,<:AbstractFloat},Vector{Int},Vector{<:AbstractFloat}},
724775
outlier::Bool,
776+
ξ_threshold::Int,
725777
ζ_threshold::Int,
726778
ω_threshold::Int,
727779
ϕ_threshold::Int,
@@ -732,7 +784,7 @@ function create_X(
732784
T = size(exog, 1)
733785

734786
ξ_matrix = if stochastic_level
735-
create_ξ(T, stochastic_start)
787+
create_ξ(T, ξ_threshold, stochastic_start)
736788
else
737789
zeros(T, 0)
738790
end
@@ -770,6 +822,7 @@ function create_X(
770822
create_dynamic_exog_coefs_matrix(
771823
dynamic_exog_coefs,
772824
T,
825+
ξ_threshold,
773826
ζ_threshold,
774827
ω_threshold,
775828
ϕ_threshold,
@@ -847,7 +900,9 @@ function get_components_indexes(model::StructuralModel)::Dict
847900

848901
if model.stochastic_level
849902
ξ_indexes = collect(
850-
(FINAL_INDEX + 1):(FINAL_INDEX + ξ_size(T, model.stochastic_start))
903+
(FINAL_INDEX + 1):(FINAL_INDEX + ξ_size(
904+
T, model.ξ_threshold, model.stochastic_start
905+
)),
851906
)
852907
FINAL_INDEX += length(ξ_indexes)
853908
else
@@ -908,7 +963,6 @@ function get_components_indexes(model::StructuralModel)::Dict
908963
FINAL_INDEX += length(exogenous_indexes)
909964

910965
dynamic_exog_coefs_indexes = collect((FINAL_INDEX + 1):size(model.X, 2))
911-
FINAL_INDEX += length(dynamic_exog_coefs_indexes)
912966

913967
components_indexes_dict = Dict(
914968
"μ1" => μ1_indexes,
@@ -940,6 +994,43 @@ function get_components_indexes(model::StructuralModel)::Dict
940994
end
941995
end
942996

997+
if !isnothing(model.dynamic_exog_coefs)
998+
for i in eachindex(model.dynamic_exog_coefs)
999+
if model.dynamic_exog_coefs[i][2] == "level"
1000+
components_indexes_dict["dynamic_exog_coefs_$(i)"] = collect(
1001+
(FINAL_INDEX + 1):(FINAL_INDEX + 1 + ξ_size(
1002+
T, model.ξ_threshold, model.stochastic_start
1003+
)),
1004+
)
1005+
FINAL_INDEX += length(components_indexes_dict["dynamic_exog_coefs_$(i)"])
1006+
elseif model.dynamic_exog_coefs[i][2] == "slope"
1007+
components_indexes_dict["dynamic_exog_coefs_$(i)"] = collect(
1008+
(FINAL_INDEX + 1):(FINAL_INDEX + 1 + ζ_size(
1009+
T, model.ζ_threshold, model.stochastic_start
1010+
)),
1011+
)
1012+
FINAL_INDEX += length(components_indexes_dict["dynamic_exog_coefs_$(i)"])
1013+
elseif model.dynamic_exog_coefs[i][2] == "seasonal"
1014+
components_indexes_dict["dynamic_exog_coefs_$(i)"] = collect(
1015+
(FINAL_INDEX + 1):(FINAL_INDEX + model.dynamic_exog_coefs[i][3] + ω_size(
1016+
T,
1017+
model.dynamic_exog_coefs[i][3],
1018+
model.ω_threshold,
1019+
model.stochastic_start,
1020+
)),
1021+
)
1022+
FINAL_INDEX += length(components_indexes_dict["dynamic_exog_coefs_$(i)"])
1023+
elseif model.dynamic_exog_coefs[i][2] == "cycle"
1024+
components_indexes_dict["dynamic_exog_coefs_$(i)"] = collect(
1025+
(FINAL_INDEX + 1):(FINAL_INDEX + 2 + ϕ_size(
1026+
T, model.ϕ_threshold, model.stochastic_start
1027+
)),
1028+
)
1029+
FINAL_INDEX += length(components_indexes_dict["dynamic_exog_coefs_$(i)"])
1030+
end
1031+
end
1032+
end
1033+
9431034
return components_indexes_dict
9441035
end
9451036

@@ -979,47 +1070,6 @@ function get_variances(
9791070
return variances
9801071
end
9811072

982-
"""
983-
get_variances(
984-
model::StructuralModel,
985-
ε::Vector{Vector{Fl}},
986-
coefs::Vector{Vector{Tl}},
987-
components_indexes::Dict{String,Vector{Int}},
988-
)::Vector{Dict} where {Fl, Tl}
989-
990-
Calculates variances for each innovation component and for the residuals.
991-
992-
# Arguments
993-
- `model::StructuralModel`: StructuralModel object.
994-
- `ε::Vector{Vector{Fl}}`: Vector of residuals.
995-
- `coefs::Vector{Vector{Fl}}`: Vector of coefficients.
996-
- `components_indexes::Dict{String, Vector{Int}}`: Dictionary containing indexes for different components.
997-
998-
# Returns
999-
- `Vector{Dict}`: Dictionary containing variances for each innovation component.
1000-
1001-
"""
1002-
function get_variances(
1003-
model::StructuralModel,
1004-
ε::Vector{Vector{Fl}},
1005-
coefs::Vector{Vector{Tl}},
1006-
components_indexes::Dict{String,Vector{Int}},
1007-
)::Vector{Dict} where {Fl,Tl}
1008-
model_innovations = get_model_innovations(model)
1009-
1010-
variances_vec = Dict[]
1011-
1012-
for i in eachindex(coefs)
1013-
variances = Dict()
1014-
for component in model_innovations
1015-
variances[component] = var(coefs[i][components_indexes[component]])
1016-
end
1017-
variances["ε"] = var(ε[i])
1018-
push!(variances_vec, variances)
1019-
end
1020-
return variances_vec
1021-
end
1022-
10231073
"""
10241074
get_model_innovations(model::StructuralModel)::Vector
10251075
@@ -1053,6 +1103,12 @@ function get_model_innovations(model::StructuralModel)
10531103
push!(model_innovations, "ϕ_$i")
10541104
end
10551105
end
1106+
1107+
if !isnothing(model.dynamic_exog_coefs)
1108+
for i in eachindex(model.dynamic_exog_coefs)
1109+
push!(model_innovations, "dynamic_exog_coefs_$i")
1110+
end
1111+
end
10561112
return model_innovations
10571113
end
10581114

@@ -1083,7 +1139,11 @@ function get_trend_decomposition(
10831139
end
10841140

10851141
if model.stochastic_level && !isempty(components["ξ"]["Coefs"])
1086-
ξ = vcat(zeros(max(2, model.stochastic_start) - 1), components["ξ"]["Coefs"], 0.0)
1142+
ξ = vcat(
1143+
zeros(max(2, model.stochastic_start) - 1),
1144+
components["ξ"]["Coefs"],
1145+
zeros(model.ξ_threshold),
1146+
)
10871147
@assert length(ξ) == T
10881148
else
10891149
ξ = zeros(AbstractFloat, T)
@@ -1572,7 +1632,7 @@ function forecast_dynamic_exog_coefs(
15721632
dynamic_exog_coefs = Vector{Tuple}(undef, length(model.dynamic_exog_coefs))
15731633
for i in eachindex(model.dynamic_exog_coefs)
15741634
if model.dynamic_exog_coefs[i][2] == "level"
1575-
n_coefs = 1 + ξ_size(T, model.stochastic_start)
1635+
n_coefs = 1 + ξ_size(T, model.ξ_threshold, model.stochastic_start)
15761636
extra_param = ""
15771637
elseif model.dynamic_exog_coefs[i][2] == "slope"
15781638
n_coefs = 1 + ζ_size(T, model.ζ_threshold, model.stochastic_start)
@@ -1601,6 +1661,7 @@ function forecast_dynamic_exog_coefs(
16011661
dynamic_exog_coefs,
16021662
T,
16031663
steps_ahead,
1664+
model.ξ_threshold,
16041665
model.ζ_threshold,
16051666
model.ω_threshold,
16061667
model.ϕ_threshold,

0 commit comments

Comments
 (0)