From e684b9466868d85194a8e4f96bc4d59a30215246 Mon Sep 17 00:00:00 2001 From: andreramosfc Date: Wed, 22 Nov 2023 10:39:28 -0300 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9Dfirst=20documentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 106 +++++++++- docs/assets/quick_example_airp.PNG | Bin 0 -> 36816 bytes docs/assets/quick_example_completion_airp.PNG | Bin 0 -> 52470 bytes src/StateSpaceLearning.jl | 41 +++- src/estimation_procedure/adalasso.jl | 23 ++- src/estimation_procedure/estimation_utils.jl | 46 +++++ .../information_criteria.jl | 17 ++ src/estimation_procedure/lasso.jl | 58 ++++++ src/model_utils.jl | 183 ++++++++++++++++++ src/structs.jl | 20 ++ src/utils.jl | 35 ++++ 11 files changed, 524 insertions(+), 5 deletions(-) create mode 100644 docs/assets/quick_example_airp.PNG create mode 100644 docs/assets/quick_example_completion_airp.PNG diff --git a/README.md b/README.md index 6926778..1f6c138 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,107 @@ # StateSpaceLearning -[![ci](https://github.com/LAMPSPUC/StateSpaceLearning/actions/workflows/ci.yml/badge.svg)](https://github.com/LAMPSPUC/StateSpaceLearning/actions/workflows/ci.yml) +| **Build Status** | **Coverage** | +|:-----------------:|:-----------------:| +| [![ci](https://github.com/LAMPSPUC/StateSpaceLearning/actions/workflows/ci.yml/badge.svg)](https://github.com/LAMPSPUC/StateSpaceLearning/actions/workflows/ci.yml) | [![codecov](https://codecov.io/gh/LAMPSPUC/StateSpaceLearning/graph/badge.svg?token=VDpuXvPSI2)](https://codecov.io/gh/LAMPSPUC/StateSpaceLearning) | -[![codecov](https://codecov.io/gh/LAMPSPUC/StateSpaceLearning/graph/badge.svg?token=VDpuXvPSI2)](https://codecov.io/gh/LAMPSPUC/StateSpaceLearning) -StateSpaceLearning.jl is a package for modeling and forecasting time series in a high-dimension regression framework. \ No newline at end of file +StateSpaceLearning.jl is a package for modeling and forecasting time series in a high-dimension regression framework. + +## Quickstart + +```julia +using StateSpaceLearning + +y = randn(100) + +#Fit Model +output = StateSpaceLearning.fit_model(y) + +#Main output options +model_type = output.model_type # State Space Equivalent model utilized in the estimation (default = Basic Structural). +X = output.X # High Dimension Regression utilized in the estimation. +coefs = output.coefs # High Dimension Regression coefficients estimated in the estimation. +ϵ = output.ϵ # Residuals of the model. +fitted = output.fitted # Fit in Sample of the model. +components = output.components # Dictionary containing information about each component of the model, each component has the keys: "Values" (The value of the component in each timestamp) , "Coefs" (The coefficients estimated for each element of the component) and "Indexes" (The indexes of the elements of the component in the high dimension regression "X"). +residuals_variances = output.residuals_variances # Dictionary containing the estimated variances for the innovations components (that is the information that can be utilized to initialize the state space model). +s = ouotput.s # The seasonal frequency utilized in the model (default = 12). +T = output.T # The length of the original time series. +outlier = output.outlier # Boolean indicating the presence of outlier component (default = false). +valid_indexes = output.valid_indexes # Vector containing valid indexes of the time series (non valid indexes represent NaN values in the time series). +stabilize_ζ = output.stabilize_ζ # Stabilize_ζ parameter (default = 0). A non 0 value for this parameter might be important in terms of forecast for some time series to lead to more stable predictions (we recommend stabilize_ζ = 11 for monthly series). + +#Forecast +prediction = StateSpaceLearning.forecast(output, 12) #Gets a 12 steps ahead prediction + +``` + +## Features + +Current features include: +* Estimation +* Components decomposition +* Forecasting +* Completion of missing values +* Predefined models, including: + * Basic Structural" + * Local Linear Trend + * Local Level + +## Quick Examples + +### Fitting and forecasting +Quick example of fit and forecast for the air passengers time-series. + +```julia +using CSV +using DataFrames +using Plots +using StateSpaceModels + +airp = CSV.File(StateSpaceModels.AIR_PASSENGERS) |> DataFrame +log_air_passengers = log.(airp.passengers) +steps_ahead = 30 + +output = StateSpaceLearning.fit_model(log_air_passengers) +prediction_raw = StateSpaceLearning.forecast(output, steps_ahead) +prediction = exp.(prediction_raw) + +plot(airp.passengers, w=2 , color = "Black", lab = "Historical", legend = :outerbottom) +plot!(vcat(ones(output.T).*NaN, prediction), lab = "Forcast", w=2, color = "blue") +``` +![quick_example_airp](./docs/assets/quick_example_airp.png) + +### Completion of missing values +Quick example of completion of missing values for the air passengers time-series (artificial NaN values are added to the original time-series). + +```julia +using CSV +using DataFrames +using Plots +using StateSpaceModels + +airp = CSV.File(StateSpaceModels.AIR_PASSENGERS) |> DataFrame +airpassengers = (airp.passengers).*1.0 +log_air_passengers = log.(airpassengers) +steps_ahead = 30 + +log_air_passengers[60:72] .= NaN + +output = StateSpaceLearning.fit_model(log_air_passengers) + +fitted_completed_missing_values = ones(144).*NaN; fitted_completed_missing_values[60:72] = exp.(output.fitted[60:72]) +real_removed_valued = ones(144).*NaN; real_removed_valued[60:72] = deepcopy(airpassengers[60:72]) +airpassengers[60:72] .= NaN + +plot(airpassengers, w=2 , color = "Black", lab = "Historical", legend = :outerbottom) +plot!(real_removed_valued, lab = "Real Removed Values", w=2, color = "red") +plot!(fitted_completed_missing_values, lab = "Fit in Sample completed values", w=2, color = "blue") + +``` +![quick_example_completion_airp](./docs/assets/quick_example_completion_airp.png) + +## Contributing + +* PRs such as adding new models and fixing bugs are very welcome! +* For nontrivial changes, you'll probably want to first discuss the changes via issue. \ No newline at end of file diff --git a/docs/assets/quick_example_airp.PNG b/docs/assets/quick_example_airp.PNG new file mode 100644 index 0000000000000000000000000000000000000000..960b350ab1e9c1c9a5aca528a55c4209d810f54b GIT binary patch literal 36816 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 literal 0 HcmV?d00001 diff --git a/docs/assets/quick_example_completion_airp.PNG b/docs/assets/quick_example_completion_airp.PNG new file mode 100644 index 0000000000000000000000000000000000000000..c4bbbea04b14bbd5a14a094e5f61f5a67ceaa3dc GIT binary patch literal 52470 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 YO?6wdOyINA;IERM)iKmA*0c%z9{?WO(*OVf literal 0 HcmV?d00001 diff --git a/src/StateSpaceLearning.jl b/src/StateSpaceLearning.jl index 59561de..24586a1 100644 --- a/src/StateSpaceLearning.jl +++ b/src/StateSpaceLearning.jl @@ -16,6 +16,31 @@ include("utils.jl") export fit_model, forecast +""" + fit_model(y::Vector{Fl}; model_type::String="Basic Structural", Exogenous_X::Union{Matrix{Fl}, Missing}=missing, + estimation_procedure::String="AdaLasso", s::Int64=12, outlier::Bool=false, stabilize_ζ::Int64=0, + α::Float64=0.1, hyperparameter_selection::String="aic", adalasso_coef::Float64=0.1, + select_exogenous::Bool=true)::Output where Fl + + Fits the StateSpaceLearning model using specified parameters and estimation procedures. + + # Arguments + - `y::Vector{Fl}`: Vector of data. + - `model_type::String`: Type of model (default: "Basic Structural"). + - `Exogenous_X::Union{Matrix{Fl}, Missing}`: Exogenous variables matrix (default: missing). + - `estimation_procedure::String`: Estimation procedure (default: "AdaLasso"). + - `s::Int64`: Seasonal period (default: 12). + - `outlier::Bool`: Flag for considering outlier component (default: false). + - `stabilize_ζ::Int64`: Stabilize_ζ parameter (default: 0). + - `α::Float64`: Elastic net control factor between ridge (α=0) and lasso (α=1) (default: 0.1). + - `hyperparameter_selection::String`: Information criteria method for hyperparameter selection (default: "aic"). + - `adalasso_coef::Float64`: AdaLasso adjustment coefficient (default: 0.1). + - `select_exogenous::Bool`: Flag to select exogenous variables. When false the penalty factor for these variables will be set to 0 (default: true). + + # Returns + - `Output`: Output object containing model information, coefficients, residuals, etc. + +""" function fit_model(y::Vector{Fl}; model_type::String="Basic Structural", Exogenous_X::Union{Matrix{Fl}, Missing}=missing, estimation_procedure::String="AdaLasso", s::Int64=12, outlier::Bool=false, stabilize_ζ::Int64=0, α::Float64=0.1, hyperparameter_selection::String="aic", adalasso_coef::Float64=0.1, select_exogenous::Bool=true)::Output where Fl @@ -25,7 +50,7 @@ function fit_model(y::Vector{Fl}; model_type::String="Basic Structural", Exogeno @assert (model_type in AVAILABLE_MODELS) "Unavailable Model" @assert (estimation_procedure in AVAILABLE_ESTIMATION_PROCEDURES) "Unavailable estimation procedure" @assert (hyperparameter_selection in AVAILABLE_HYPERPARAMETER_SELECTION) "Unavailable hyperparameter selection method" - @assert 0 < α <= 1 "α must be in (0, 1], Lasso.jl cannot handle α = 0" + @assert 0 <= α <= 1 "α must be in (0, 1], Lasso.jl cannot handle α = 0" valid_indexes = findall(i -> !isnan(i), y) estimation_y = y[valid_indexes] @@ -48,6 +73,20 @@ function fit_model(y::Vector{Fl}; model_type::String="Basic Structural", Exogeno return Output(model_type, X, coefs, ϵ, fitted, components, residuals_variances, s, T, outlier, valid_indexes, stabilize_ζ) end +""" + forecast(output::Output, steps_ahead::Int64; Exogenous_Forecast::Union{Matrix{Fl}, Missing}=missing)::Vector{Float64} where Fl + + Returns the forecast for a given number of steps ahead using the provided StateSpaceLearning output and exogenous forecast data. + + # Arguments + - `output::Output`: Output object obtained from model fitting. + - `steps_ahead::Int64`: Number of steps ahead for forecasting. + - `Exogenous_Forecast::Union{Matrix{Fl}, Missing}`: Exogenous variables forecast (default: missing). + + # Returns + - `Vector{Float64}`: Vector containing forecasted values. + +""" function forecast(output::Output, steps_ahead::Int64; Exogenous_Forecast::Union{Matrix{Fl}, Missing}=missing)::Vector{Float64} where Fl @assert steps_ahead > 0 "steps_ahead must be a positive integer" Exogenous_Forecast = ismissing(Exogenous_Forecast) ? zeros(steps_ahead, 0) : Exogenous_Forecast diff --git a/src/estimation_procedure/adalasso.jl b/src/estimation_procedure/adalasso.jl index 73fec27..0793468 100644 --- a/src/estimation_procedure/adalasso.jl +++ b/src/estimation_procedure/adalasso.jl @@ -1,3 +1,24 @@ +""" + fit_adalasso(Estimation_X::Matrix{Tl}, estimation_y::Vector{Fl}, α::Float64, + hyperparameter_selection::String, + components_indexes::Dict{String, Vector{Int64}}, + adalasso_coef::Float64, select_exogenous::Bool)::Tuple{Vector{Float64}, Vector{Float64}} where {Tl, Fl} + + Fits an Adaptive Lasso (AdaLasso) regression model to the provided data and returns coefficients and residuals. + + # Arguments + - `Estimation_X::Matrix{Tl}`: Matrix of predictors for estimation. + - `estimation_y::Vector{Fl}`: Vector of response values for estimation. + - `α::Float64`: Elastic net control factor between ridge (α=0) and lasso (α=1) (default: 0.1). + - `hyperparameter_selection::String`: Information Criteria method for hyperparameter selection (default: aic). + - `components_indexes::Dict{String, Vector{Int64}}`: Dictionary containing indexes for different components. + - `adalasso_coef::Float64`: AdaLasso adjustment coefficient (default: 0.1). + - `select_exogenous::Bool`: Flag for selecting exogenous variables. When false the penalty factor for these variables will be set to 0. + + # Returns + - `Tuple{Vector{Float64}, Vector{Float64}}`: Tuple containing coefficients and residuals of the fitted AdaLasso model. + +""" function fit_adalasso(Estimation_X::Matrix{Tl}, estimation_y::Vector{Fl}, α::Float64, hyperparameter_selection::String, components_indexes::Dict{String, Vector{Int64}}, @@ -12,7 +33,7 @@ function fit_adalasso(Estimation_X::Matrix{Tl}, estimation_y::Vector{Fl}, α::Fl if key != "initial_states" && key != "μ₁" component = components_indexes[key] if key != "Exogenous_X" && key != "o" && !(key in ["ν₁", "γ₁"]) - κ = count(i -> i == 0, coefs[component]) < 1 ? 0 : std(coefs[component]) + κ = count(i -> i != 0, coefs[component]) < 1 ? 0 : std(coefs[component]) penalty_factor[component .- 1] .= (1 / (κ + adalasso_coef)) else penalty_factor[component .- 1] = (1 ./ (abs.(coefs[component]) .+ adalasso_coef)) diff --git a/src/estimation_procedure/estimation_utils.jl b/src/estimation_procedure/estimation_utils.jl index 86cbb9d..e8c3531 100644 --- a/src/estimation_procedure/estimation_utils.jl +++ b/src/estimation_procedure/estimation_utils.jl @@ -1,3 +1,15 @@ +""" + get_dummy_indexes(Exogenous_X::Matrix{Fl}) where {Fl} + + Identifies and returns the indexes of columns in the exogenous matrix that contain dummy variables. + + # Arguments + - `Exogenous_X::Matrix{Fl}`: Exogenous variables matrix. + + # Returns + - `Vector{Int}`: Vector containing the indexes of columns with dummy variables. + +""" function get_dummy_indexes(Exogenous_X::Matrix{Fl}) where{Fl} T, p = size(Exogenous_X) @@ -12,6 +24,19 @@ function get_dummy_indexes(Exogenous_X::Matrix{Fl}) where{Fl} return dummy_indexes end +""" + get_outlier_duplicate_columns(Estimation_X::Matrix{Tl}, components_indexes::Dict{String, Vector{Int64}}) where{Tl} + + Identifies and returns the indexes of outlier columns that are duplicates of dummy variables in the exogenous matrix. + + # Arguments + - `Estimation_X::Matrix{Tl}`: Matrix used for estimation. + - `components_indexes::Dict{String, Vector{Int64}}`: Dictionary containing indexes for different components. + + # Returns + - `Vector{Int}`: Vector containing the indexes of outlier columns that are duplicates of dummy variables in the exogenous matrix. + +""" function get_outlier_duplicate_columns(Estimation_X::Matrix{Tl}, components_indexes::Dict{String, Vector{Int64}}) where{Tl} o_indexes = components_indexes["o"] exogenous_indexes = components_indexes["Exogenous_X"] @@ -21,6 +46,27 @@ function get_outlier_duplicate_columns(Estimation_X::Matrix{Tl}, components_inde return o_indexes[dummy_indexes] .- 1 end +""" + fit_estimation_procedure(estimation_procedure::String, Estimation_X::Matrix{Tl}, estimation_y::Vector{Fl}, α::Float64, + hyperparameter_selection::String, components_indexes::Dict{String, Vector{Int64}}, + adalasso_coef::Float64, select_exogenous::Bool)::Tuple{Vector{Float64}, Vector{Float64}} where {Tl, Fl} + + Fits the specified estimation procedure (currently Lasso or AdaLasso) to the provided data and returns coefficients and residuals. + + # Arguments + - `estimation_procedure::String`: The chosen estimation procedure (either "Lasso" or "AdaLasso"). + - `Estimation_X::Matrix{Tl}`: Matrix of predictors for estimation. + - `estimation_y::Vector{Fl}`: Vector of response values for estimation. + - `α::Float64`: Elastic net control factor between ridge (α=0) and lasso (α=1) (default: 0.1). + - `hyperparameter_selection::String`: Information Criteria method for hyperparameter selection (default: aic). + - `components_indexes::Dict{String, Vector{Int64}}`: Dictionary containing indexes for different components. + - `adalasso_coef::Float64`: AdaLasso adjustment coefficient (default: 0.1). + - `select_exogenous::Bool`: Flag for selecting exogenous variables. When false the penalty factor for these variables will be set to 0. + + # Returns + - `Tuple{Vector{Float64}, Vector{Float64}}`: Tuple containing coefficients and residuals of the fitted estimation procedure. + +""" function fit_estimation_procedure(estimation_procedure::String, Estimation_X::Matrix{Tl}, estimation_y::Vector{Fl}, α::Float64, hyperparameter_selection::String, components_indexes::Dict{String, Vector{Int64}}, adalasso_coef::Float64, select_exogenous::Bool)::Tuple{Vector{Float64}, Vector{Float64}} where {Tl, Fl} diff --git a/src/estimation_procedure/information_criteria.jl b/src/estimation_procedure/information_criteria.jl index f8be846..b1b4f43 100644 --- a/src/estimation_procedure/information_criteria.jl +++ b/src/estimation_procedure/information_criteria.jl @@ -1,3 +1,20 @@ +""" + get_information(T::Int64, K::Int64, ϵ::Vector{Float64}; + hyperparameter_selection::String = "bic", p::Int64 = 0)::Float64 + + Calculates information criterion value based on the provided parameters and residuals. + + # Arguments + - `T::Int64`: Number of observations. + - `K::Int64`: Number of selected predictors. + - `ϵ::Vector{Float64}`: Vector of residuals. + - `hyperparameter_selection::String`: Method for hyperparameter selection (default: "bic"). + - `p::Int64`: Number of total predictors (default: 0). + + # Returns + - `Float64`: Information criterion value. + +""" function get_information(T::Int64, K::Int64, ϵ::Vector{Float64}; hyperparameter_selection::String = "bic", p::Int64 = 0)::Float64 if hyperparameter_selection == "bic" return T*log(var(ϵ)) + K*log(T) diff --git a/src/estimation_procedure/lasso.jl b/src/estimation_procedure/lasso.jl index 8863d43..16852a7 100644 --- a/src/estimation_procedure/lasso.jl +++ b/src/estimation_procedure/lasso.jl @@ -1,3 +1,20 @@ +""" + get_path_information_criteria(model::GLMNetPath, Estimation_X::Matrix{Tl}, estimation_y::Vector{Fl}, + hyperparameter_selection::String; intercept::Bool = true)::Tuple{Vector{Float64}, Vector{Float64}} where {Tl, Fl} + + Calculates the information criteria along the regularization path of a GLMNet model and returns coefficients and residuals of the best model based on the selected information criteria. + + # Arguments + - `model::GLMNetPath`: Fitted GLMNetPath model object. + - `Estimation_X::Matrix{Tl}`: Matrix of predictors for estimation. + - `estimation_y::Vector{Fl}`: Vector of response values for estimation. + - `hyperparameter_selection::String`: Information Criteria method for hyperparameter selection. + - `intercept::Bool`: Flag for intercept inclusion in the model (default: true). + + # Returns + - `Tuple{Vector{Float64}, Vector{Float64}}`: Tuple containing coefficients and residuals of the best model. + +""" function get_path_information_criteria(model::GLMNetPath, Estimation_X::Matrix{Tl}, estimation_y::Vector{Fl}, hyperparameter_selection::String; intercept::Bool = true)::Tuple{Vector{Float64}, Vector{Float64}} where {Tl, Fl} path_size = length(model.lambda) T, p = size(Estimation_X) @@ -18,11 +35,52 @@ function get_path_information_criteria(model::GLMNetPath, Estimation_X::Matrix{T return coefs, ϵ end +""" + fit_glmnet(Estimation_X::Matrix{Tl}, estimation_y::Vector{Fl}, α::Float64; + hyperparameter_selection::String = "aic", + penalty_factor::Vector{Float64}=ones(size(Estimation_X,2) - 1), + intercept::Bool = intercept)::Tuple{Vector{Float64}, Vector{Float64}} where {Tl, Fl} + + Fits a GLMNet model to the provided data and returns coefficients and residuals based on selected criteria. + + # Arguments + - `Estimation_X::Matrix{Tl}`: Matrix of predictors for estimation. + - `estimation_y::Vector{Fl}`: Vector of response values for estimation. + - `α::Float64`: Elastic net control factor between ridge (α=0) and lasso (α=1) (default: 0.1). + - `hyperparameter_selection::String`: Information Criteria method for hyperparameter selection (default: aic). + - `penalty_factor::Vector{Float64}`: Penalty factors for each predictor (default: ones(size(Estimation_X, 2) - 1)). + - `intercept::Bool`: Flag for intercept inclusion in the model (default: true). + + # Returns + - `Tuple{Vector{Float64}, Vector{Float64}}`: Tuple containing coefficients and residuals of the best model. + +""" function fit_glmnet(Estimation_X::Matrix{Tl}, estimation_y::Vector{Fl}, α::Float64; hyperparameter_selection::String = "aic", penalty_factor::Vector{Float64}=ones(size(Estimation_X,2) - 1), intercept::Bool = intercept)::Tuple{Vector{Float64}, Vector{Float64}} where {Tl, Fl} model = glmnet(Estimation_X, estimation_y, alpha = α, penalty_factor = penalty_factor, intercept = intercept, dfmax=size(Estimation_X, 2), lambda_min_ratio=0.001) return get_path_information_criteria(model, Estimation_X, estimation_y, hyperparameter_selection; intercept = intercept) end +""" + fit_lasso(Estimation_X::Matrix{Tl}, estimation_y::Vector{Fl}, α::Float64, hyperparameter_selection::String, + select_exogenous::Bool, components_indexes::Dict{String, Vector{Int64}}; + penalty_factor::Vector{Float64}=ones(size(Estimation_X,2) - 1), intercept::Bool = true)::Tuple{Vector{Float64}, Vector{Float64}} where {Tl, Fl} + + Fits a Lasso regression model to the provided data and returns coefficients and residuals based on selected criteria. + + # Arguments + - `Estimation_X::Matrix{Tl}`: Matrix of predictors for estimation. + - `estimation_y::Vector{Fl}`: Vector of response values for estimation. + - `α::Float64`: Elastic net control factor between ridge (α=0) and lasso (α=1) (default: 0.1). + - `hyperparameter_selection::String`: Information Criteria method for hyperparameter selection (default: aic). + - `select_exogenous::Bool`: Flag for selecting exogenous variables. When false the penalty factor for these variables will be set to 0. + - `components_indexes::Dict{String, Vector{Int64}}`: Dictionary containing indexes for different components. + - `penalty_factor::Vector{Float64}`: Penalty factors for each predictor (default: ones(size(Estimation_X, 2) - 1)). + - `intercept::Bool`: Flag for intercept inclusion in the model (default: true). + + # Returns + - `Tuple{Vector{Float64}, Vector{Float64}}`: Tuple containing coefficients and residuals of the fitted Lasso model. + +""" function fit_lasso(Estimation_X::Matrix{Tl}, estimation_y::Vector{Fl}, α::Float64, hyperparameter_selection::String, select_exogenous::Bool, components_indexes::Dict{String, Vector{Int64}}; penalty_factor::Vector{Float64}=ones(size(Estimation_X,2) - 1), intercept::Bool = true)::Tuple{Vector{Float64}, Vector{Float64}} where {Tl, Fl} outlier_duplicate_columns = get_outlier_duplicate_columns(Estimation_X, components_indexes) diff --git a/src/model_utils.jl b/src/model_utils.jl index 959e3f2..a7ef5f5 100644 --- a/src/model_utils.jl +++ b/src/model_utils.jl @@ -1,11 +1,74 @@ +""" + ξ_size(T::Int64)::Int64 + + Calculates the size of ξ innovation matrix based on the input T. + + # Arguments + - `T::Int64`: Length of the original time series. + + # Returns + - `Int64`: Size of ξ calculated from T. + +""" ξ_size(T::Int64)::Int64 = T-2 +""" +ζ_size(T::Int64, stabilize_ζ::Int64)::Int64 + + Calculates the size of ζ innovation matrix based on the input T. + + # Arguments + - `T::Int64`: Length of the original time series. + - `stabilize_ζ::Int64`: Stabilize parameter ζ. + + # Returns + - `Int64`: Size of ζ calculated from T. + +""" ζ_size(T::Int64, stabilize_ζ::Int64)::Int64 = T-stabilize_ζ-1 +""" +ω_size(T::Int64, s::Int64)::Int64 + + Calculates the size of ω innovation matrix based on the input T. + + # Arguments + - `T::Int64`: Length of the original time series. + - `s::Int64`: Seasonal period. + + # Returns + - `Int64`: Size of ω calculated from T. + +""" ω_size(T::Int64, s::Int64)::Int64 = T-s +""" +o_size(T::Int64)::Int64 + + Calculates the size of outlier matrix based on the input T. + + # Arguments + - `T::Int64`: Length of the original time series. + + # Returns + - `Int64`: Size of o calculated from T. + +""" o_size(T::Int64)::Int64 = T +""" + create_ξ(T::Int64, steps_ahead::Int64)::Matrix + + Creates a matrix of innovations ξ based on the input sizes, and the desired steps ahead (this is necessary for the forecast function) + + # Arguments + - `T::Int64`: Length of the original time series. + - `steps_ahead::Int64`: Number of steps ahead (for estimation purposes this should be set at 0). + + # Returns + - `Matrix`: Matrix of innovations ξ constructed based on the input sizes. + +""" function create_ξ(T::Int64, steps_ahead::Int64)::Matrix ξ_matrix = Matrix{Float64}(undef, T+steps_ahead, ξ_size(T)) for t in 1:T+steps_ahead @@ -15,6 +78,20 @@ function create_ξ(T::Int64, steps_ahead::Int64)::Matrix return ξ_matrix end +""" +create_ζ(T::Int64, steps_ahead::Int64, stabilize_ζ::Int64)::Matrix + + Creates a matrix of innovations ζ based on the input sizes, and the desired steps ahead (this is necessary for the forecast function). + + # Arguments + - `T::Int64`: Length of the original time series. + - `steps_ahead::Int64`: Number of steps ahead (for estimation purposes this should be set at 0). + - `stabilize_ζ::Int64`: Stabilize parameter ζ. + + # Returns + - `Matrix`: Matrix of innovations ζ constructed based on the input sizes. + +""" function create_ζ(T::Int64, steps_ahead::Int64, stabilize_ζ::Int64)::Matrix ζ_matrix = Matrix{Float64}(undef, T+steps_ahead, ζ_size(T, stabilize_ζ) + stabilize_ζ) for t in 1:T+steps_ahead @@ -23,6 +100,20 @@ function create_ζ(T::Int64, steps_ahead::Int64, stabilize_ζ::Int64)::Matrix return ζ_matrix[:, 1:end-stabilize_ζ] end +""" +create_ω(T::Int64, s::Int64, steps_ahead::Int64)::Matrix + + Creates a matrix of innovations ω based on the input sizes, and the desired steps ahead (this is necessary for the forecast function). + + # Arguments + - `T::Int64`: Length of the original time series. + - `s::Int64`: Seasonal period. + - `steps_ahead::Int64`: Number of steps ahead (for estimation purposes this should be set at 0). + + # Returns + - `Matrix`: Matrix of innovations ω constructed based on the input sizes. + +""" function create_ω(T::Int64, s::Int64, steps_ahead::Int64)::Matrix ω_matrix_size = ω_size(T, s) ω_matrix = Matrix{Float64}(undef, T+steps_ahead, ω_matrix_size) @@ -38,10 +129,38 @@ function create_ω(T::Int64, s::Int64, steps_ahead::Int64)::Matrix return ω_matrix end +""" +create_o_matrix(T::Int64, steps_ahead::Int64)::Matrix + + Creates a matrix of outliers based on the input sizes, and the desired steps ahead (this is necessary for the forecast function). + + # Arguments + - `T::Int64`: Length of the original time series. + - `steps_ahead::Int64`: Number of steps ahead (for estimation purposes this should be set at 0). + + # Returns + - `Matrix`: Matrix of outliers constructed based on the input sizes. + +""" function create_o_matrix(T::Int64, steps_ahead::Int64)::Matrix return vcat(Matrix(1.0 * I, T, T), zeros(steps_ahead, T)) end +""" + create_initial_states_Matrix(T::Int64, s::Int64, steps_ahead::Int64, model_type::String)::Matrix + + Creates an initial states matrix based on the input parameters. + + # Arguments + - `T::Int64`: Length of the original time series. + - `s::Int64`: Seasonal period. + - `steps_ahead::Int64`: Number of steps ahead. + - `model_type::String`: Type of model. + + # Returns + - `Matrix`: Initial states matrix constructed based on the input parameters. + +""" function create_initial_states_Matrix(T::Int64, s::Int64, steps_ahead::Int64, model_type::String)::Matrix μ₀_coefs = ones(T+steps_ahead) ν₀_coefs = collect(1:T+steps_ahead) @@ -61,6 +180,25 @@ function create_initial_states_Matrix(T::Int64, s::Int64, steps_ahead::Int64, mo end +""" + create_X(model_type::String, T::Int64, s::Int64, Exogenous_X::Matrix{Fl}, outlier::Bool, stabilize_ζ::Int64, + steps_ahead::Int64=0, Exogenous_Forecast::Matrix{Fl}=zeros(steps_ahead, size(Exogenous_X, 2))) where Fl + + Creates the StateSpaceLearning matrix X based on the model type and input parameters. + + # Arguments + - `model_type::String`: Type of model. + - `T::Int64`: Length of the original time series. + - `s::Int64`: Seasonal period. + - `Exogenous_X::Matrix{Fl}`: Exogenous variables matrix. + - `outlier::Bool`: Flag for considering outlier component. + - `stabilize_ζ::Int64`: Stabilize parameter for ζ matrix. + - `steps_ahead::Int64`: Number of steps ahead (default: 0). + - `Exogenous_Forecast::Matrix{Fl}`: Exogenous variables forecast matrix (default: zeros). + + # Returns + - `Matrix`: StateSpaceLearning matrix X constructed based on the input parameters. +""" function create_X(model_type::String, T::Int64, s::Int64, Exogenous_X::Matrix{Fl}, outlier::Bool, stabilize_ζ::Int64, steps_ahead::Int64=0, Exogenous_Forecast::Matrix{Fl}=zeros(steps_ahead, size(Exogenous_X, 2))) where Fl @@ -80,6 +218,23 @@ function create_X(model_type::String, T::Int64, s::Int64, Exogenous_X::Matrix{Fl end +""" + get_components_indexes(T::Int64, s::Int64, Exogenous_X::Matrix{Fl}, outlier::Bool, model_type::String, stabilize_ζ::Int64)::Dict where Fl + + Generates indexes dict for different components based on the model type and input parameters. + + # Arguments + - `T::Int64`: Length of the original time series. + - `s::Int64`: Seasonal period. + - `Exogenous_X::Matrix{Fl}`: Exogenous variables matrix. + - `outlier::Bool`: Flag for considering outlier component. + - `model_type::String`: Type of model. + - `stabilize_ζ::Int64`: Stabilize parameter for ζ matrix. + + # Returns + - `Dict`: Dictionary containing the corresponding indexes for each component of the model. + +""" function get_components_indexes(T::Int64, s::Int64, Exogenous_X::Matrix{Fl}, outlier::Bool, model_type::String, stabilize_ζ::Int64)::Dict where Fl μ₁_indexes = [1] ν₁_indexes = model_type in ["Local Linear Trend", "Basic Structural"] ? [2] : Int64[] @@ -110,6 +265,20 @@ function get_components_indexes(T::Int64, s::Int64, Exogenous_X::Matrix{Fl}, out "Exogenous_X" => exogenous_indexes, "initial_states" => initial_states_indexes) end +""" + get_variances(ϵ::Vector{Fl}, coefs::Vector{Fl}, components_indexes::Dict{String, Vector{Int64}})::Dict where Fl + + Calculates variances for each innovation component based on input data. + + # Arguments + - `ϵ::Vector{Fl}`: Vector of residuals. + - `coefs::Vector{Fl}`: Vector of coefficients. + - `components_indexes::Dict{String, Vector{Int64}}`: Dictionary containing indexes for different components. + + # Returns + - `Dict`: Dictionary containing variances for each innovation component. + +""" function get_variances(ϵ::Vector{Fl}, coefs::Vector{Fl}, components_indexes::Dict{String, Vector{Int64}})::Dict where Fl variances = Dict() @@ -120,6 +289,20 @@ function get_variances(ϵ::Vector{Fl}, coefs::Vector{Fl}, components_indexes::Di return variances end +""" + forecast_model(output::Output, steps_ahead::Int64, Exogenous_Forecast::Matrix{Fl})::Vector{Float64} where Fl + + Returns the forecast for a given number of steps ahead using the provided StateSpaceLearning output and exogenous forecast data. + + # Arguments + - `output::Output`: Output object obtained from model fitting. + - `steps_ahead::Int64`: Number of steps ahead for forecasting. + - `Exogenous_Forecast::Matrix{Fl}`: Exogenous forecast matrix. + + # Returns + - `Vector{Float64}`: Vector containing forecasted values. + +""" function forecast_model(output::Output, steps_ahead::Int64, Exogenous_Forecast::Matrix{Fl})::Vector{Float64} where Fl @assert output.model_type in AVAILABLE_MODELS "Unavailable Model" Exogenous_X = output.X[:, output.components["Exogenous_X"]["Indexes"]] diff --git a/src/structs.jl b/src/structs.jl index 2d6755f..1f797c5 100644 --- a/src/structs.jl +++ b/src/structs.jl @@ -1,3 +1,23 @@ +""" + mutable struct Output + + A mutable struct to store various components and results of a model estimation. + + # Fields + - `model_type::String`: Type of the model. + - `X::Matrix`: StateSpaceLearning Matrix data used in the model. + - `coefs::Vector`: Coefficients obtained from the model. + - `ϵ::Vector`: Residuals of the model. + - `fitted::Vector`: Fitted values from the model. + - `components::Dict`: Dictionary containing different components. + - `residuals_variances::Dict`: Dictionary storing variances of residuals for different components. + - `s::Int64`: Integer representing a parameter 's'. + - `T::Int64`: Integer representing a parameter 'T'. + - `outlier::Bool`: Boolean indicating the presence of outlier component. + - `valid_indexes::Vector{Int64}`: Vector containing valid indexes of the time series. + - `stabilize_ζ::Int64`: Stabilize_ζ parameter. + +""" mutable struct Output model_type::String X::Matrix diff --git a/src/utils.jl b/src/utils.jl index def4369..a6e2609 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1,3 +1,20 @@ +""" + build_components(X::Matrix{Tl}, coefs::Vector{Float64}, components_indexes::Dict{String, Vector{Int64}}) -> Dict where Tl + + Constructs components dict containing values, indexes and coefficients for each component. + + # Arguments + - X::Matrix{Tl}: Input matrix. + - coefs::Vector{Float64}: Coefficients. + - components_indexes::Dict{String, Vector{Int64}}: Dictionary mapping component names to their indexes. + + # Returns + - components::Dict: Dictionary containing components, each represented by a dictionary with keys: + - "Coefs": Coefficients for the component. + - "Indexes": Indexes associated with the component. + - "Values": Values computed from `X` and component coefficients. + +""" function build_components(X::Matrix{Tl}, coefs::Vector{Float64}, components_indexes::Dict{String, Vector{Int64}})::Dict where Tl components = Dict() for key in keys(components_indexes) @@ -10,6 +27,24 @@ function build_components(X::Matrix{Tl}, coefs::Vector{Float64}, components_inde return components end +""" + build_complete_variables(estimation_ϵ::Vector{Float64}, coefs::Vector{Float64}, X::Matrix{Tl}, valid_indexes::Vector{Int64}, T::Int64) -> Tuple{Vector{Float64}, Vector{Float64}} where Tl + + Builds complete residuals and fit in sample. Residuals will contain nan values for non valid indexes. Fit in Sample will be a vector of fitted values computed from input data and coefficients (non valid indexes will also be calculated via interpolation). + + # Arguments + - `estimation_ϵ::Vector{Float64}`: Vector of estimation errors. + - `coefs::Vector{Float64}`: Coefficients. + - `X::Matrix{Tl}`: Input matrix. + - `valid_indexes::Vector{Int64}`: Valid indexes. + - `T::Int64`: Length of the original time series. + + # Returns + - Tuple containing: + - `ϵ::Vector{Float64}`: Vector containing NaN values filled with estimation errors at valid indexes. + - `fitted::Vector{Float64}`: Vector of fitted values computed from input data and coefficients. + +""" function build_complete_variables(estimation_ϵ::Vector{Float64}, coefs::Vector{Float64}, X::Matrix{Tl}, valid_indexes::Vector{Int64}, T::Int64)::Tuple{Vector{Float64}, Vector{Float64}} where Tl ϵ = fill(NaN, T); ϵ[valid_indexes] = estimation_ϵ fitted = X*coefs