Skip to content

Commit 09b4096

Browse files
author
andre_ramos
committed
format files
1 parent 4951ed8 commit 09b4096

16 files changed

Lines changed: 340 additions & 170 deletions

Project.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
88
GLMNet = "8d5ece8b-de18-5317-b113-243142960cc6"
99
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
1010
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
11-
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
1211
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1312
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
1413
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,14 @@ To reproduce the paper results, run the following experiments:
240240

241241
Evaluates SSL and SS (StateSpaceModels) benchmark models on M4 competition dataset across all granularities (Monthly, Quarterly, Daily, Hourly, Weekly, Yearly).
242242

243+
**Before running:** Add PyCall to your Julia environment:
244+
```julia
245+
using Pkg
246+
Pkg.add("PyCall")
247+
```
248+
249+
The script also requires Python packages (`statsmodels`, `numpy`) for the SS benchmark evaluation.
250+
243251
```shell
244252
julia paper_tests/m4_test/m4_test.jl
245253
```
@@ -254,8 +262,6 @@ Results are saved in:
254262
- `paper_tests/m4_test/results_SSL/` - SSL model results by granularity
255263
- `paper_tests/m4_test/metrics_results/` - Summary metrics and benchmark results
256264

257-
Note: Requires PyCall and Python packages (`statsmodels`, `numpy`) for the SS benchmark evaluation.
258-
259265
### Simulation Parameter Study
260266

261267
Compares SSL vs Kalman filter on simulated data.

paper_tests/m4_test/evaluate_model.jl

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function evaluate_SSL(
2525
else
2626
ξ_threshold = 0
2727
end
28-
28+
2929
model = StateSpaceLearning.StructuralModel(
3030
normalized_y;
3131
level="stochastic",
@@ -38,7 +38,7 @@ function evaluate_SSL(
3838
ξ_threshold=ξ_threshold,
3939
ζ_threshold=param["ζ_threshold"],
4040
ω_threshold=param["ω_threshold"],
41-
ϕ_threshold=param["ϕ_threshold"]
41+
ϕ_threshold=param["ϕ_threshold"],
4242
)
4343

4444
if selection == "split"
@@ -78,26 +78,25 @@ function evaluate_SSL(
7878
return initialization_df, results_df
7979
end
8080

81-
8281
function evaluate_SS(input::Dict, m::Int, H::Int, frequency::Int)
8382
"""
8483
Evaluate statsmodels UnobservedComponents model using PyCall
8584
Similar to evaluate_ss in m4_test.py
8685
Requires PyCall to be imported in the calling scope
8786
"""
88-
87+
8988
y_train = input["train"]
9089
y_test = input["test"]
9190

9291
y_train_py = PyObject(y_train)
9392
y_test_py = PyObject(y_test)
9493
H_py = PyObject(H)
9594
frequency_py = PyObject(frequency)
96-
95+
9796
py"""
9897
import statsmodels.api as sm
9998
import numpy as np
100-
99+
101100
def evaluate_ss_py(y_train, y_test, H, frequency):
102101
# Prepare model components
103102
if frequency > 1:
@@ -146,21 +145,17 @@ function evaluate_SS(input::Dict, m::Int, H::Int, frequency::Int)
146145
147146
return forecast_values, simulation
148147
"""
149-
150-
prediction, scenarios = py"evaluate_ss_py"(
151-
y_train_py, y_test_py,
152-
H_py, frequency_py
153-
)
154-
148+
149+
prediction, scenarios = py"evaluate_ss_py"(y_train_py, y_test_py, H_py, frequency_py)
150+
155151
# Convert back to Julia arrays
156152
prediction = Vector{Float64}(prediction)
157153
scenarios = Matrix{Float64}(scenarios)
158-
154+
159155
# Calculate metrics
160-
mase = MASE(y_train, y_test, prediction; m = m)
156+
mase = MASE(y_train, y_test, prediction; m=m)
161157
smape = sMAPE(y_test, prediction)
162158
crps = CRPS(scenarios, y_test)
163-
159+
164160
return DataFrame([[mase], [smape], [crps]], [:MASE, :sMAPE, :CRPS])
165-
166-
end
161+
end

paper_tests/m4_test/m4_test.jl

Lines changed: 122 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,112 @@ include("metrics.jl")
3434
include("evaluate_model.jl")
3535
include("prepare_data.jl")
3636

37-
complete_dict_vec = Dict("M" => build_train_test_dict(df_train, df_test;name="M"),
38-
"D" => build_train_test_dict(df_train_daily, df_test_daily;name="D"),
39-
"Q" => build_train_test_dict(df_train_quarterly, df_test_quarterly;name="Q"),
40-
"H" => build_train_test_dict(df_train_hourly, df_test_hourly;name="H"),
41-
"W" => build_train_test_dict(df_train_weekly, df_test_weekly;name="W"),
42-
"Y" => build_train_test_dict(df_train_yearly, df_test_yearly;name="Y"),
37+
complete_dict_vec = Dict(
38+
"M" => build_train_test_dict(df_train, df_test; name="M"),
39+
"D" => build_train_test_dict(df_train_daily, df_test_daily; name="D"),
40+
"Q" => build_train_test_dict(df_train_quarterly, df_test_quarterly; name="Q"),
41+
"H" => build_train_test_dict(df_train_hourly, df_test_hourly; name="H"),
42+
"W" => build_train_test_dict(df_train_weekly, df_test_weekly; name="W"),
43+
"Y" => build_train_test_dict(df_train_yearly, df_test_yearly; name="Y"),
4344
)
4445

4546
parameters_dict = Dict(
46-
"M" => Dict("freq_seasonal" => 12, "cycle_period" => 0, "m" => 12, "ξ_threshold" => 1, "ζ_threshold" => 12, "ω_threshold" => 12, "ϕ_threshold" => 0, "seasonal" => "stochastic", "cycle" => "none", "sample_size" => 60, "name" => "MONTH", "H" => 18, "NAIVE_sMAPE" => 14.427, "NAIVE_MASE" => 1.063),
47-
"Q" => Dict("freq_seasonal" => 4, "cycle_period" => 0, "m" => 4, "ξ_threshold" => 1, "ζ_threshold" => 4, "ω_threshold" => 4, "ϕ_threshold" => 0, "seasonal" => "stochastic", "cycle" => "none", "sample_size" => "all", "name" => "QUARTERLY", "H" => 8, "NAIVE_sMAPE" => 11.012, "NAIVE_MASE" => 1.371),
48-
"D" => Dict("freq_seasonal" => 1, "cycle_period" => 0, "m" => 1, "ξ_threshold" => 1, "ζ_threshold" => 7, "ω_threshold" => 1, "ϕ_threshold" => 0, "seasonal" => "none", "cycle" => "none", "sample_size" => 90, "name" => "DAILY", "H" => 14, "NAIVE_sMAPE" => 3.045, "NAIVE_MASE" => 3.278),
49-
"W" => Dict("freq_seasonal" => 1, "cycle_period" => 0, "m" => 1, "ξ_threshold" => 1, "ζ_threshold" => 4, "ω_threshold" => 1, "ϕ_threshold" => 0, "seasonal" => "none", "cycle" => "none", "sample_size" => 104, "name" => "WEEKLY", "H" => 13, "NAIVE_sMAPE" => 9.161, "NAIVE_MASE" => 2.777),
50-
"Y" => Dict("freq_seasonal" => 1, "cycle_period" => 0, "m" => 1, "ξ_threshold" => 1, "ζ_threshold" => 2, "ω_threshold" => 1, "ϕ_threshold" => 0, "seasonal" => "none", "cycle" => "none", "sample_size" => "all", "name" => "YEARLY", "H" => 6, "NAIVE_sMAPE" => 16.342, "NAIVE_MASE" => 3.974),
51-
"H" => Dict("freq_seasonal" => 168, "cycle_period" => [24], "m" => 24, "ξ_threshold" => 1, "ζ_threshold" => 168, "ω_threshold" => 168, "ϕ_threshold" => 12, "seasonal" => "stochastic", "cycle" => "stochastic", "sample_size" => 720, "name" => "HOURLY", "H" => 48, "NAIVE_sMAPE" => 18.383, "NAIVE_MASE" => 2.395),
47+
"M" => Dict(
48+
"freq_seasonal" => 12,
49+
"cycle_period" => 0,
50+
"m" => 12,
51+
"ξ_threshold" => 1,
52+
"ζ_threshold" => 12,
53+
"ω_threshold" => 12,
54+
"ϕ_threshold" => 0,
55+
"seasonal" => "stochastic",
56+
"cycle" => "none",
57+
"sample_size" => 60,
58+
"name" => "MONTH",
59+
"H" => 18,
60+
"NAIVE_sMAPE" => 14.427,
61+
"NAIVE_MASE" => 1.063,
62+
),
63+
"Q" => Dict(
64+
"freq_seasonal" => 4,
65+
"cycle_period" => 0,
66+
"m" => 4,
67+
"ξ_threshold" => 1,
68+
"ζ_threshold" => 4,
69+
"ω_threshold" => 4,
70+
"ϕ_threshold" => 0,
71+
"seasonal" => "stochastic",
72+
"cycle" => "none",
73+
"sample_size" => "all",
74+
"name" => "QUARTERLY",
75+
"H" => 8,
76+
"NAIVE_sMAPE" => 11.012,
77+
"NAIVE_MASE" => 1.371,
78+
),
79+
"D" => Dict(
80+
"freq_seasonal" => 1,
81+
"cycle_period" => 0,
82+
"m" => 1,
83+
"ξ_threshold" => 1,
84+
"ζ_threshold" => 7,
85+
"ω_threshold" => 1,
86+
"ϕ_threshold" => 0,
87+
"seasonal" => "none",
88+
"cycle" => "none",
89+
"sample_size" => 90,
90+
"name" => "DAILY",
91+
"H" => 14,
92+
"NAIVE_sMAPE" => 3.045,
93+
"NAIVE_MASE" => 3.278,
94+
),
95+
"W" => Dict(
96+
"freq_seasonal" => 1,
97+
"cycle_period" => 0,
98+
"m" => 1,
99+
"ξ_threshold" => 1,
100+
"ζ_threshold" => 4,
101+
"ω_threshold" => 1,
102+
"ϕ_threshold" => 0,
103+
"seasonal" => "none",
104+
"cycle" => "none",
105+
"sample_size" => 104,
106+
"name" => "WEEKLY",
107+
"H" => 13,
108+
"NAIVE_sMAPE" => 9.161,
109+
"NAIVE_MASE" => 2.777,
110+
),
111+
"Y" => Dict(
112+
"freq_seasonal" => 1,
113+
"cycle_period" => 0,
114+
"m" => 1,
115+
"ξ_threshold" => 1,
116+
"ζ_threshold" => 2,
117+
"ω_threshold" => 1,
118+
"ϕ_threshold" => 0,
119+
"seasonal" => "none",
120+
"cycle" => "none",
121+
"sample_size" => "all",
122+
"name" => "YEARLY",
123+
"H" => 6,
124+
"NAIVE_sMAPE" => 16.342,
125+
"NAIVE_MASE" => 3.974,
126+
),
127+
"H" => Dict(
128+
"freq_seasonal" => 168,
129+
"cycle_period" => [24],
130+
"m" => 24,
131+
"ξ_threshold" => 1,
132+
"ζ_threshold" => 168,
133+
"ω_threshold" => 168,
134+
"ϕ_threshold" => 12,
135+
"seasonal" => "stochastic",
136+
"cycle" => "stochastic",
137+
"sample_size" => 720,
138+
"name" => "HOURLY",
139+
"H" => 48,
140+
"NAIVE_sMAPE" => 18.383,
141+
"NAIVE_MASE" => 2.395,
142+
),
52143
)
53144

54145
# Function to append results to CSV file
@@ -70,7 +161,7 @@ function run_config(
70161
selection::String,
71162
information_criteria::String,
72163
α::AbstractFloat,
73-
param::Dict
164+
param::Dict,
74165
)
75166
results_df = DataFrame()
76167
initialization_df = DataFrame()
@@ -93,7 +184,7 @@ function run_config(
93184
α,
94185
selection,
95186
information_criteria,
96-
param
187+
param,
97188
)
98189

99190
if i % clear_df_number == 0 || i == length(dict_vec)
@@ -177,7 +268,10 @@ function run_benchmark_model(dict_vec::Vector, param::Dict)
177268
"CRPS" => [crps],
178269
"Median CRPS" => [median_crps],
179270
)
180-
CSV.write("paper_tests/m4_test/metrics_results/BENCHMARK_$(param["name"])_SUMMARY.csv", summary_df)
271+
CSV.write(
272+
"paper_tests/m4_test/metrics_results/BENCHMARK_$(param["name"])_SUMMARY.csv",
273+
summary_df,
274+
)
181275
return results_df
182276
end
183277

@@ -189,21 +283,30 @@ function main()
189283
for selection in ["split", "fixed_alpha"]
190284
if selection == "fixed_alpha"
191285
information_criteria = "aic"
192-
alpha_set = [0.1, 0.3, 0.5, 0.7, 0.9, 1.0]
193-
else
286+
alpha_set = [0.1, 0.3, 0.5, 0.7, 0.9, 1.0]
287+
else
194288
information_criteria = "aic"
195289
alpha_set = [-1.0]
196290
end
197291
for α in alpha_set
198292
@info "Running configuration: Param=$(gran), Outlier=$(outlier), IC=$(information_criteria), α=$(α)"
199293
results_table = run_config(
200-
complete_dict_vec[gran], results_table, outlier, selection, information_criteria, α, parameters_dict[gran]
294+
complete_dict_vec[gran],
295+
results_table,
296+
outlier,
297+
selection,
298+
information_criteria,
299+
α,
300+
parameters_dict[gran],
201301
)
202302
end
203303
end
204304
end
205305
filename = parameters_dict[gran]["name"]
206-
CSV.write("paper_tests/m4_test/metrics_results/SSL_$(filename)_METRICS_RESULTS.csv", results_table)
306+
CSV.write(
307+
"paper_tests/m4_test/metrics_results/SSL_$(filename)_METRICS_RESULTS.csv",
308+
results_table,
309+
)
207310
@info "Running benchmark model for $(parameters_dict[gran]["name"])"
208311
run_benchmark_model(complete_dict_vec[gran], parameters_dict[gran])
209312
end

paper_tests/simulation_param/evaluate_models.jl

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,21 @@ function align_components!(
1919
end
2020

2121
function get_SSL_results(
22-
y_train::Vector{Fl},
23-
s::Int,
24-
μ_true, ν_true, γ_true,
25-
inf_criteria::String
22+
y_train::Vector{Fl}, s::Int, μ_true, ν_true, γ_true, inf_criteria::String
2623
) where {Fl<:AbstractFloat}
27-
2824
min_y = minimum(y_train)
2925
max_y = maximum(y_train)
3026
normy = (y_train .- min_y) ./ (max_y - min_y)
3127

3228
model = StateSpaceLearning.StructuralModel(
33-
normy;
34-
freq_seasonal=s,
35-
outlier=false,
36-
ξ_threshold=0,
37-
ζ_threshold=1,
38-
ω_threshold=1
29+
normy; freq_seasonal=s, outlier=false, ξ_threshold=0, ζ_threshold=1, ω_threshold=1
3930
)
4031
StateSpaceLearning.fit!(
4132
model;
4233
information_criteria=inf_criteria,
4334
ϵ=0.05,
4435
penalize_initial_states=true,
45-
α = 0.1
36+
α=0.1,
4637
)
4738

4839
μ_hat = model.output.decomposition["trend"] .* (max_y - min_y)
@@ -52,4 +43,4 @@ function get_SSL_results(
5243
align_components!(μ_hat, ν_hat, γ_hat, μ_true, ν_true, γ_true)
5344

5445
return μ_hat, ν_hat, γ_hat
55-
end
46+
end

0 commit comments

Comments
 (0)