Time series regression with a fixed parameter path with jumps
In this example we explore the joint posterior in the time varying regression with data simulated from a fixed parameter path with jumps. A time-varying regression with dynamic shrinkage process prior is fitted to see if the model can capture the underlying parameter dynamics:
\[\begin{align*} y_t &= \boldsymbol{x}_t^\top \boldsymbol{\theta}_t + \varepsilon_t, \quad \varepsilon_t \sim N(0,\sigma_\varepsilon) \\ \boldsymbol{\theta}_t &= \boldsymbol{\theta}_{t-1} + \boldsymbol{\nu}_t, \quad \boldsymbol{\nu}_t \sim N\Big(\boldsymbol{0},\mathrm{Diag}(\exp(\boldsymbol{h}_t/2))\Big) \\ \boldsymbol{h}_t &= \boldsymbol{\mu} + \phi(\boldsymbol{h}_{t-1} -\boldsymbol{\mu}) + \boldsymbol{\eta}_t, \quad \boldsymbol{\eta}_t \sim Z(\alpha,\alpha, 0, \sigma_\eta) \\ \end{align*}\]
First we load the required packages and set some plotting parameters.:
using Plots, Distributions, LaTeXStrings, Random, Measures, LinearAlgebra, BandedMatrices
using ColorSchemes
using SMCsamplers, LogisticBetaDistribution
using DynamicGlobalLocalShrinkage
using Utils: quantile_multidim, ScaledInverseChiSq
using Utils: mvcolors as colors
gr(legend = :topleft, grid = false, color = colors[1], lw = 1, legendfontsize=12,
xtickfontsize=10, ytickfontsize=10, xguidefontsize=12, yguidefontsize=12,
titlefontsize = 14, markerstrokecolor = :auto)
Random.seed!(123);Simulate data from the model
T = 500;
p = 2; # Number of parameters, including intercept
σ²ₑ = 0.3^2;
X = [ones(T+1) randn(T+1, p-1)]; # Design matrix
y = zeros(T+1)
θ = zeros(T+1,p) # Store the regression parameters
for t in 2:(T+1)
θ[t,1] = sin(2π*t/T)
if t < T/3
θ[t,2] = 0
else
if t < ((2/3)*T)
θ[t,2] = -1
else
θ[t,2] = 1
end
end
y[t] = X[t,:] ⋅ θ[t,:] + rand(Normal(0, sqrt(σ²ₑ)))
end
y = y[2:end];
X = X[2:end,:];Plot the parameter evolution path of βₜ
plt = []
for j = 1:p
push!(plt, plot(θ[:,j], label = L"\theta_{%$(j-1)}", xlabel = "time, "*L"t",
ylabel = L"\theta_{%$(j-1)}", title = "Path of "*L"\theta_{%$(j-1)}",
color = colors[j], lw = 2))
end
plot(plt..., layout = (p,1), size = (800, 400), xguidefontsize = 12,
yguidefontsize = 12, titlefontsize=12, legend = nothing, margin = 5mm)Scatter plot of the data and the regression line.
nSnapshots = 5
timeSnapshots = round(Int,T/nSnapshots):round(Int,T/nSnapshots):T
gradcols = cgrad(:Blues, nSnapshots + 1; categorical = true, rev = false)[2:end]
xmin, xmax = extrema(X[:,2]);
pltline = scatter(X[:,2], y, marker_z = 1:T, xlabel = "x", ylabel = "y", markersize = 2,
color = :Blues, label = "", colorbar = false, markerstrokewidth = 0, legend = :left, legendfontsize = 10)
for (ind,t) = enumerate(timeSnapshots)
plot!([xmin,xmax], [[1,xmin] ⋅ θ[t,:],[1,xmax] ⋅ θ[t,:]],
color = gradcols[ind], lw = 2, label = L"t = %$t", xlabel = "x", ylabel = "y")
end
pltlineSet up the prior, model and algorithm settings
priorSettings = (
ϕ₀ = 0.5, κ₀ = 0.3, # Prior for ϕ ~ N(ϕ₀, κ₀²)
m₀ = -12.0, σ₀ = 3.0, # Prior for μ ~ N(m₀, σ₀²)
ν₀ = 3.0, ψ₀ = 1.0, # Prior for σ²ₙ ~ scaled inverse χ²(ν₀, ψ₀)
μ₀ = zeros(p), Σ₀ = 10*I(p),# Prior for βₜ at time t=0
νₑ = 3, ψ²ₑ = 0.3^2, # Prior for noise variance σ²ₑ ~ scaled inverse χ²(νₑ, ψ²ₑ)
);
modelSettings = (
α = 1/2, # First shape param in Z distribution
β = 1/2, # Second shape param in Z distribution
updateσₙ = false, # Update σ²ₙ in the Gibbs sampler, or set σₙ = 1
nMixComp = 10, # nComp in mixture approximation of log χ²₁. Only 5 or 10 supported.
);
algoSettings = (
nIter = 10000, # Number of iterations in the Gibbs sampler
nBurn = 3000, # Number of burn-in iterations
offsetMethod = eps(), # Offset for log-volatility
);Set up the Gibbs sampler
function GibbsSamplerTvRegDSP(y, X, priorSettings, modelSettings, algoSettings)
T = length(y)
ϕ₀, κ₀, m₀, σ₀, ν₀, ψ₀, μ₀, Σ₀, νₑ, ψ²ₑ = priorSettings
nIter, nBurn, offsetMethod = algoSettings
α, β, updateσₙ, nMixComp = modelSettings
# Approximate the log χ²₁ distribution with a mixture of normals
mixture = SetUpLogChi2Mixture(nMixComp) # Only 5 and 10 component supported
# Initial values
p = size(X,2) # only the errors follow a dynamic shrinkage process
S = zeros(Int, T, p) # Mixture allocation for logχ²₁ - this is updated first
μ = fill(m₀, p)
if updateσₙ
σ²ₙ = fill(ψ₀, p)
else
σ²ₙ = fill(1.0, p)
end
ϕ = fill(ϕ₀, p)
H = fill(m₀, T, p)
H̃ = H .- μ'
ξ = ones(T, p)
θ = zeros(T+1, p) # Regression coefficients evolution
Dᵩ = BandedMatrix(-1 => repeat([-ϕ[1]], T-1), 0 => Ones(T)) # Init D matrix for h_t
σ²ₑ = 1
# Storage
θpost = zeros(T+1, p, nIter) # Store regression coefficients
Hpost = zeros(T, p, nIter) # Store log-volatility evolution
ϕpost = zeros(p, nIter) # Store AR coefficients
σₙpost = zeros(p, nIter) # Store variance in log-volatility evolution
μpost = zeros(p, nIter) # Store mean in log-volatility evolution
σ²ₑpost = zeros(nIter) # Store measurement variance
offset = (offsetMethod == "kowal") ? eps()*ones(T,p) : fill(offsetMethod, T, p)
P = zeros(T, nMixComp) # storage for mixture component probabilities
# Set up state-space model
Σₑ = σ²ₑ
A = collect(I(p))
C = zeros(1,p,T)
for t in 1:T
C[:,:,t] = X[t,:]
end
B = 0.0
U = zeros(T,1)
Y = y; # Observations
for i in 1:(nBurn + nIter)
# Draw local level using the FFBS algorithm
Σᵥ = LogVol2Covs(H)
FFBS!(θ, U, Y, A, B, C, Σₑ, Σᵥ, μ₀, Σ₀)
# Update measurement variance
residuals = [y[t] - X[t,:] ⋅ θ[t,:] for t in 1:T]
σ²ₑ = rand(ScaledInverseChiSq(νₑ + T, (νₑ*ψ²ₑ + sum(residuals.^2))/(νₑ + T)))
# Update the log-volatility evolution
ν = diff(θ, dims = 1)
setOffset!(offset, ν, offsetMethod)
update_dsp!(ν, S, P, H, H̃, ξ, ϕ, μ, σ²ₙ, priorSettings, mixture, Dᵩ)
if i > nBurn
θpost[:, :, i - nBurn] = θ
Hpost[:, :, i - nBurn] = H
ϕpost[:, i - nBurn] = ϕ
σₙpost[:, i - nBurn] = σ²ₙ
μpost[:, i - nBurn] = μ
σ²ₑpost[i - nBurn] = σ²ₑ
end
end
return θpost, Hpost, ϕpost, σₙpost, μpost, σ²ₑpost
end;Run the Gibbs sampler
θpost, Hpost, ϕpost, σₙpost, μpost, σ²ₑpost = GibbsSamplerTvRegDSP(y, X,
priorSettings, modelSettings, algoSettings);Plot the posterior distributions of the static parameters
p1 = []
for j = 1:p
push!(p1, histogram(μpost[j,:], title = "posterior "*L"\mu_{%$(j)}",
color = colors[7], ylabel = "posterior density", normalize = true))
end
plot(p1..., layout = (1,p), size = (800,400), legend = nothing)
p2 = []
for j = 1:p
push!(p2, histogram(ϕpost[j,:], title = "posterior "*L"\phi_{%$(j)}",
color = colors[8], ylabel = "posterior density", normalize = true))
end
plot(p2..., layout = (1,p), size = (800,400), legend = nothing)
p3 = histogram(sqrt.(σ²ₑpost), title = "posterior "*L"\sigma_\varepsilon",
color = colors[9], ylabel = "posterior density", normalize = true)
vline!([sqrt(σ²ₑ)], color = :black, linestyle = :dash, label = "true", lw = 2)
plot(p1..., p2..., p3, layout = (3,p), size = (800,600), margin = 5mm, legend = nothing)Plot the posterior distribution of the regression parameter evolution
plt = []
for j = 1:p
stdev_quantiles = quantile_multidim(θpost[:,j,:], [0.05, 0.5, 0.9]; dims = 2)
p2 = plot(stdev_quantiles[:,2], xlabel = "time", title = L"\theta_{t%$(j-1)}",
color = colors[3], label = (j==1) ? "median" : "", lw = 2)
plot!(stdev_quantiles[:,2], label = "", fillrange = stdev_quantiles[:,1], lw = 0,
fillalpha = 0.3, color =:gray)
plot!(stdev_quantiles[:,2], xlabel = "time", label = (j==1) ? "95% C.I." : "",
fillrange = stdev_quantiles[:,3], lw = 0, fillalpha = 0.3, color =:gray)
plot!(θ[:,j], color = colors[1], label = (j==1) ? "true" : "", lw = 2)
push!(plt, p2)
end
plot(plt..., layout = (p,1), size = (800, 600), legend = :topright, margin = 5mm)Plot the posterior distribution of the log-volatility evolution
plt = []
for j = 1:p
stdev_quantiles = quantile_multidim(Hpost[:,j,:], [0.05, 0.5, 0.9]; dims = 2)
p2 = plot(stdev_quantiles[:,2], xlabel = "time", title = L"h_{t%$j}",
color = colors[3], label = "", lw = 2)
plot!(stdev_quantiles[:,2], label = "", fillrange = stdev_quantiles[:,1], lw = 0,
fillalpha = 0.3, color =:gray)
plot!(stdev_quantiles[:,2], xlabel = "time", label = "",
fillrange = stdev_quantiles[:,3], lw = 0, fillalpha = 0.3, color =:gray)
push!(plt, p2)
end
plot(plt..., layout = (p,1), size = (800, 400), legend = :topleft, margin = 5mm)This page was generated using Literate.jl.