Coder Social home page Coder Social logo

lampspuc / statespacemodels.jl Goto Github PK

View Code? Open in Web Editor NEW
265.0 9.0 27.0 9.2 MB

StateSpaceModels.jl is a Julia package for time-series analysis using state-space models.

Home Page: https://lampspuc.github.io/StateSpaceModels.jl/latest/

License: MIT License

Julia 100.00%
state-space-models time-series kalman-filter julia-language statistics forecasting econometrics sarima time-series-analysis unobserved-components

statespacemodels.jl's Introduction

StateSpaceModels.jl

Build Status Coverage Documentation
Build Status Codecov branch

StateSpaceModels.jl is a package for modeling, forecasting, and simulating time series in a state-space framework. Implementations were made based on the book "Time Series Analysis by State Space Methods" (2012) by James Durbin and Siem Jan Koopman. The notation of the variables in the code also follows the book.

Quickstart

import Pkg

Pkg.add("StateSpaceModels")

using StateSpaceModels

y = randn(100)

model = LocalLevel(y)

fit!(model)

print_results(model)

forecast(model, 10)

kf = kalman_filter(model)

v = get_innovations(kf)

ks = kalman_smoother(model)

alpha = get_smoothed_state(ks)

Features

Current features include:

  • Kalman filter and smoother
  • Maximum likelihood estimation
  • Forecasting and Monte Carlo simulation
  • User-defined models (user specifies the state-space system)
  • Several predefined models, including:
    • Exponential Smoothing (ETS, all the linear ones)
    • Unobserved components (local level, basic structural, ...)
    • SARIMA
    • Linear regression
    • Naive models
  • Completion of missing values
  • Diagnostics for the residuals of fitted models
  • Visualization recipes

Quick Examples

Fitting and forecasting

Quick example of different models fit and forecast for the air passengers time-series

using CSV
using DataFrames
using Plots
using StateSpaceModels

airp = CSV.File(StateSpaceModels.AIR_PASSENGERS) |> DataFrame
log_air_passengers = log.(airp.passengers)
steps_ahead = 30

# SARIMA
model_sarima = SARIMA(log_air_passengers; order = (0, 1, 1), seasonal_order = (0, 1, 1, 12))
fit!(model_sarima)
forec_sarima = forecast(model_sarima, steps_ahead)

# Unobserved Components
model_uc = UnobservedComponents(log_air_passengers; trend = "local linear trend", seasonal = "stochastic 12")
fit!(model_uc)
forec_uc = forecast(model_uc, steps_ahead)

# Exponential Smoothing
model_ets = ExponentialSmoothing(log_air_passengers; trend = true, seasonal = 12)
fit!(model_ets)
forec_ets = forecast(model_ets, steps_ahead)

# Naive model
model_naive = SeasonalNaive(log_air_passengers, 12)
fit!(model_naive)
forec_naive = forecast(model_naive, steps_ahead)

plt_sarima = plot(model_sarima, forec_sarima; title = "SARIMA", label = "");
plt_uc = plot(model_uc, forec_uc; title = "Unobserved components", label = "");
plt_ets = plot(model_ets, forec_ets; title = "Exponential smoothing", label = "");
plt_naive = plot(model_ets, forec_naive; title = "Seasonal Naive", label = "");

plot(plt_sarima, plt_uc, plt_ets, plt_naive; layout = (2, 2), size = (500, 500))

quick_example_airp

Automatic forecasting

Quick examples on automatic forecasting. When performing automatic forecasting users should provide the seasonal period if there is one.

model = auto_ets(log_air_passengers; seasonal = 12)
model = auto_arima(log_air_passengers; seasonal = 12)

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.

Citing StateSpaceModels.jl

If you use StateSpaceModels.jl in your work, we kindly ask you to cite the following paper:

@article{SaavedraBodinSouto2019,
  title={StateSpaceModels.jl: a Julia Package for Time-Series Analysis in a State-Space Framework},
  author={Raphael Saavedra and Guilherme Bodin and Mario Souto},
  journal={arXiv preprint arXiv:1908.01757},
  year={2019}
}

statespacemodels.jl's People

Contributors

azev77 avatar bgroenks96 avatar dietzemarina avatar guilhermebodin avatar iagochavarry avatar juliatagbot avatar juliohm avatar marinadietze avatar mariohsouto avatar pkofod avatar psrcloud avatar raphaelsaavedra avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

statespacemodels.jl's Issues

Change structures to Array{Type, 3}

This code

        y = collect(1:15.0)
        unimodel = lineartrendmodel(y)

        @test isa(unimodel, StateSpaceModels.StateSpaceModel)
        @test unimodel.mode == "time-invariant"

        ss = statespace(unimodel)

        @test isa(ss, StateSpaceModels.StateSpace)
        ss.state.alpha

returns this as output, it is a super annoying structure to deal with
image

Add HypothesisTest to run Diagnostics

Should we consider using HypothesisTests instead of creating our own version? This could be at least on the tests to assure we are doing the right thing.

Question about long structural cycles

Hi,

Thanks for the really cool module.

I have data that has long "seasonal" cycles, think of data being sampled each minute and each day shows cyclical changes. It seems that the larger I make the s parameter of the structural model the longer and longer it takes to fit.

For example, it can take a really long time for 1000 data-points and 288 specified for s. Is there some trick I can use to make things go faster?

Thanks,

Rusty

Conflicting method definition

Following method definition conflict occur.

Method definition vec(Number) in module FiniteDiff at /home/appleparan/.julia/packages/FiniteDiff/IPtYL/src/jacobians.jl:128 overwritten in module DiffEqDiffTools at /home/appleparan/.julia/packages/DiffEqDiffTools/3mm8U/src/jacobians.jl:114.
  ** incremental compilation may be fatally broken for this module **

It seems Optim package version limit in Project.toml makes this warning. Is there a reason keeping version limit in Project.toml?

PS: here is a dependencies of problematic packages.

  • Optim -> NLSolversBase -> FiniteDiff
  • Optim -> DiffEqDiffTools

Implement diagnostics

Diagnostics for the residuals:

  • Jarque-Bera test
  • Ljung-Box test
  • Homoscedasticity test

Release 0.3.0

Is there anything we should absolutely do before that?

To do list

  • Loglikelihood sum starting point
  • Parallelization when estimating seeds
  • StateSpaceModels module
  • Monte Carlo simulation
  • Multivariate modeling
  • Missing values in endogenous series
  • User-defined model
  • Cycles
  • Forecasting
  • Exact initialization

Update docs

Docs are currently out of date with the new change allowing any float type for the models and filters

Potential assertion error

These functions are missing @assertion macros to avoid matricesandcto have different lenghts fromy`

    function StateSpaceModel(y::VecOrMat{Typ}, Z::Array{Typ, 3}, T::Matrix{Typ}, R::Matrix{Typ}, 
                             d::Matrix{Typ}, c::Matrix{Typ}, H::Matrix{Typ}, Q::Matrix{Typ}) where Typ <: Real

        # Convert y to a Matrix
        y = ensure_is_matrix(y)
        # Build StateSpaceDimensions
        dim = build_ss_dim(y, Z, T, R)
        return new{Typ}(y, Z, T, R, d, c, H, Q, dim, find_missing_observations(y), "time-variant")
    end

    function StateSpaceModel(y::VecOrMat{Typ}, Z::Matrix{Typ}, T::Matrix{Typ}, R::Matrix{Typ}, 
                             d::Matrix{Typ}, c::Matrix{Typ}, H::Matrix{Typ}, Q::Matrix{Typ}) where Typ <: Real

        # Convert y to a Matrix
        y = ensure_is_matrix(y)
        # Build StateSpaceDimensions
        dim = build_ss_dim(y, Z, T, R)
        Zvar = Array{Typ, 3}(undef, dim.p, dim.m, dim.n)
        for t in 1:dim.n, i in axes(Z, 1), j in axes(Z, 2)
            Zvar[i, j, t] = Z[i, j]
        end

        return new{Typ}(y, Zvar, T, R, d, c, H, Q, dim, find_missing_observations(y), "time-invariant")
    end

Function to update just the Kalman filter

Maybe it is good to have a function to just update the Kalman filter. My sketch:

function update_kalman_filter(model::StateSpaceModel{Typ}, filter::FilterOutput, model_test::StateSpaceModel{Typ}; tol::Typ = Typ(1e-5)) where Typ
    time_invariant = model.mode == "time-invariant"

    # Predictive state and its covariance
    a = cat(filter.a, Matrix{Typ}(undef, model_test.dim.n, model.dim.m), dims=1)
    P = cat(filter.P, Array{Typ, 3}(undef, model.dim.m, model.dim.m, model_test.dim.n+1), dims=3)
    att = cat(filter.att, Matrix{Typ}(undef, model_test.dim.n, model.dim.m), dims=1)
    Ptt = cat(filter.Ptt, Array{Typ, 3}(undef, model.dim.m, model.dim.m, model_test.dim.n), dims=3)

    # Innovation and its covariance
    v = cat(filter.v, Matrix{Typ}(undef, model_test.dim.n, model.dim.p), dims=1)
    F = cat(filter.F, Array{Typ, 3}(undef, model.dim.p, model.dim.p, model_test.dim.n) , dims=3)

    # Kalman gain
    K = Array{Typ, 3}(undef, model.dim.m, model.dim.p, model.dim.n + model_test.dim.n)
    
    # Auxiliary structures
    ZP = Array{Typ, 2}(undef, model.dim.p, model.dim.m)
    P_Ztransp_invF = Array{Typ, 2}(undef, model.dim.m, model.dim.p)
    RQR = Array{Typ, 2}(undef, model.dim.m, model.dim.m)

    # Steady state initialization
    steadystate = filter.steadystate
    tsteady     = filter.tsteady

    # Initial state: big Kappa initialization
    StateSpaceModels.fill_a1!(a)
    StateSpaceModels.fill_P1!(P; bigkappa = Typ(1e6))
    y = vcat(model.y, model_test.y)
    d = vcat(model.d, model_test.d)
    Z = cat(model.Z, model_test.Z,dims=3)
    c = vcat(model.c, model_test.c)
    
    StateSpaceModels.update_ZP!(ZP, Z, P, filter.tsteady) # ZP = Z[:, :, t] * P[:, :, t]
    StateSpaceModels.update_F!(F, ZP, Z, model.H, filter.tsteady) # F_t = Z_t * P_t * Z_t + H
    StateSpaceModels.update_P_Ztransp_Finv!(P_Ztransp_invF, ZP, F, filter.tsteady) # P_Ztransp_invF   = ZP' * invF(F, t)
    StateSpaceModels.update_K!(K, P_Ztransp_invF, model.T, filter.tsteady) # K_t = T * P_t * Z_t * F^-1_t
    
    for t_ = 1:model_test.dim.n
        t = model.dim.n+t_-1
        if t in model_test.missing_observations
            steadystate  = false
            v[t, :]      = fill(NaN, model.dim.p)
            F[:, :, t]   = fill(NaN, (model.dim.p, model.dim.p))
            StateSpaceModels.update_att!(att, a, t) # attt = a_t
            StateSpaceModels.update_Ptt!(Ptt, P, t) # Pttt = P_t
            StateSpaceModels.update_a!(a, att, model.T, c, t) # a_t+1 = T * attt + c_t
            StateSpaceModels.update_P!(P, model.T, Ptt, RQR, t) # P_t+1 = T * Pttt * T' + RQR'
        elseif steadystate
            StateSpaceModels.update_v!(v, y, Z, d, a, t) # v_t = y_t - Z_t * a_t - d_t
            StateSpaceModels.repeat_matrix_t_plus_1!(F, t-1) # F[:, :, t]   = F[:, :, t-1]
            StateSpaceModels.repeat_matrix_t_plus_1!(K, t-1) # K[:, :, t]   = K[:, :, t-1]
            StateSpaceModels.update_att!(att, a, P_Ztransp_invF, v, t) # attt = a_t + P_t * Z_t * F^-1_t * v_t
            StateSpaceModels.repeat_matrix_t_plus_1!(Ptt, t-1) # Pttt = Pttt-1
            StateSpaceModels.update_a!(a, att, model.T, c, t) # a_t+1 = T * attt + c_t
            StateSpaceModels.repeat_matrix_t_plus_1!(P, t) # P__+1 = P_t
        else
            StateSpaceModels.update_v!(v, y, Z, d, a, t) # v_t = y_t - Z_t * a_t - d_t
            StateSpaceModels.update_ZP!(ZP, Z, P, t) # ZP = Z[:, :, t] * P[:, :, t]
            StateSpaceModels.update_F!(F, ZP, Z, model.H, t) # F_t = Z_t * P_t * Z_t + H
            StateSpaceModels.update_P_Ztransp_Finv!(P_Ztransp_invF, ZP, F, t) # P_Ztransp_invF   = ZP' * invF(F, t)
            StateSpaceModels.update_K!(K, P_Ztransp_invF, model.T, t) # K_t = T * P_t * Z_t * F^-1_t
            StateSpaceModels.update_att!(att, a, P_Ztransp_invF, v, t) # attt = a_t + P_t * Z_t * F^-1_t * v_t
            StateSpaceModels.update_Ptt!(Ptt, P, P_Ztransp_invF, ZP, t) # Pttt = P_t - P_t * Z_t' * F^-1_t * Z_t * P_t
            StateSpaceModels.update_a!(a, att, model.T, c, t) # a_t+1 = T * attt + c_t
            StateSpaceModels.update_P!(P, model.T, Ptt, RQR, t) # P_t+1 = T * Pttt * T' + RQR'
            if time_invariant && StateSpaceModels.check_steady_state(P, t, tol)
                steadystate = true
                tsteady     = t
            end
        end
    end
    # Return the auxiliary filter structre
    return KalmanFilter(a[model.dim.n+1:end,:], att[model.dim.n+1:end,:], v, P, Ptt, F, steadystate, tsteady, K)
end

Add examples

  • Section 8.6 from Koopman book has an example of Diebold-Li
  • Examples from PyFlux to compare results: Nile River, DAR, US log-GDP
  • Example with NaN to estimate a parameter in Z or T

Shift between forecast and simulation scenarions in 0.4 branch

From the "Basic Structural Model" test in the refactor-to-0.4 branch:

julia> forec.expected_value
10-element Array{Array{Float64,1},1}:
 [6.083167302258863]
 [6.1946408652357725]
 [6.2159346905082975]
 [6.224800298528862]
 [6.342665144161216]
 [6.478336552034039]
 [6.4752275105634185]
 [6.305246285595047]
 [6.2049769753007]
 [6.068300933194807]

julia> expected_value_of_scenarios(scenarios)
10-element Array{Array{Float64,1},1}:
 [6.125259198552738]
 [6.082992264095608]
 [6.19469004566561]
 [6.215805308795272]
 [6.224534124065003]
 [6.342838593417967]
 [6.478180596529146]
 [6.475235575886917]
 [6.305295124889548]
 [6.205057985402772]

There is a clear shift of one time period between both.

Note that this should be caught in the tests, but they're currently using atol = 1.0 which is super large. We should use rtol = 1e-3 or even lower for this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.