Skip to content

Commit f8bc06e

Browse files
committed
Update simulate and select_sample methods
1 parent 029309a commit f8bc06e

File tree

4 files changed

+78
-72
lines changed

4 files changed

+78
-72
lines changed

Project.toml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
2323
StatsAPI = "82ae8749-77ed-4fe6-ae5f-f523153014b0"
2424

2525
[compat]
26-
Distributions = "0.25"
27-
FillArrays = "0, 1"
28-
MultivariateStats = "0"
29-
Statistics = "1"
30-
StatsAPI = "1"
3126
julia = "1.9"
3227

3328
[extras]

src/DynamicFactorModels.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ using Distributed
1717
using LinearAlgebra
1818
using FillArrays
1919
using Random
20-
using Distributions
20+
using Distributions: MvNormal
2121

2222
using StatsAPI: StatisticalModel
2323

src/types.jl

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,13 @@ function poly(ε::SpatialMovingAverage)
155155
return I + Diagonal(spatial(ε)) * weights(ε)
156156
end
157157
end
158+
Base.copy::Simple) = Simple(copy(cov(Σ)))
159+
function Base.copy::SpatialAutoregression)
160+
SpatialAutoregression(copy(spatial(ε)), ε.ρmax, copy(weights(ε)), copy(cov(ε)))
161+
end
162+
function Base.copy::SpatialMovingAverage)
163+
SpatialMovingAverage(copy(spatial(ε)), ε.ρmax, copy(weights(ε)), copy(cov(ε)))
164+
end
158165

159166
# factor process
160167
"""
@@ -329,37 +336,37 @@ dynamics(F::AbstractFactorProcess) = F.ϕ
329336
dynamics(F::UnrestrictedUnitRoot) = I
330337
dynamics(F::NelsonSiegelUnitRoot) = I
331338
cov(F::AbstractFactorProcess) = F.Σ
332-
function copy(F::UnrestrictedStationaryIdentified)
339+
function Base.copy(F::UnrestrictedStationaryIdentified)
333340
Λ = copy(loadings(F))
334341
f = copy(factors(F))
335342
ϕ = copy(dynamics(F))
336343

337344
return UnrestrictedStationaryIdentified(Λ, f, ϕ)
338345
end
339-
function copy(F::UnrestrictedStationaryFull)
346+
function Base.copy(F::UnrestrictedStationaryFull)
340347
Λ = copy(loadings(F))
341348
f = copy(factors(F))
342349
ϕ = copy(dynamics(F))
343350
Σ = copy(cov(F))
344351

345352
return UnrestrictedStationaryFull(Λ, f, ϕ, Σ)
346353
end
347-
function copy(F::UnrestrictedUnitRoot)
354+
function Base.copy(F::UnrestrictedUnitRoot)
348355
Λ = copy(loadings(F))
349356
f = copy(factors(F))
350357
Σ = copy(cov(F))
351358

352359
return UnrestrictedUnitRoot(Λ, f, Σ)
353360
end
354-
function copy(F::NelsonSiegelStationary)
361+
function Base.copy(F::NelsonSiegelStationary)
355362
τ = copy(maturities(F))
356363
f = copy(factors(F))
357364
ϕ = copy(dynamics(F))
358365
Σ = copy(cov(F))
359366

360367
return NelsonSiegelStationary(decay(F), τ, f, ϕ, Σ)
361368
end
362-
function copy(F::NelsonSiegelUnitRoot)
369+
function Base.copy(F::NelsonSiegelUnitRoot)
363370
τ = copy(maturities(F))
364371
f = copy(factors(F))
365372
Σ = copy(cov(F))

src/utilities.jl

Lines changed: 65 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
#=
22
utilities.jl
33
4-
Provides a collection of utility tools for working with dynamic factor
5-
models, such as simulation, filtering, and smoothing.
4+
Provides a collection of utility tools for working with dynamic factor
5+
models, such as simulation, filtering, and smoothing.
66
77
@author: Quint Wiersma <q.wiersma@vu.nl>
88
@@ -16,10 +16,10 @@ Select a sample `sample` of the data from the dynamic factor model `model`.
1616
"""
1717
function select_sample(model::DynamicFactorModel, sample::AbstractUnitRange)
1818
μ = select_sample(mean(model), sample)
19-
ε = select_sample(errors(model), sample)
19+
ε = copy(errors(model))
2020
F = select_sample(process(model), sample)
2121

22-
return DynamicFactorModel(data(model)[:,sample], μ, ε, F)
22+
return DynamicFactorModel(data(model)[:, sample], μ, ε, F)
2323
end
2424

2525
"""
@@ -28,78 +28,82 @@ end
2828
Select a sample `sample` of the data from the mean model `μ`.
2929
"""
3030
select_sample::ZeroMean, sample::AbstractUnitRange) = ZeroMean.type, μ.n)
31-
select_sample::Exogenous, sample::AbstractUnitRange) = Exogenous(regressors(μ)[:,sample], size(slopes(μ), 1))
32-
33-
"""
34-
select_sample(ε, sample) -> ε
35-
36-
Select a sample `sample` of the data from the error model `ε`.
37-
"""
38-
select_sample::Simple, sample::AbstractUnitRange) = Simple(size(resid(ε), 1), length(sample), type=eltype(resid(ε)))
39-
select_sample::SpatialAutoregression, sample::AbstractUnitRange) = SpatialAutoregression(size(resid(ε), 1), length(sample), copy(weights(ε)), spatial=length(spatial(ε)) == 1 ? :homo : :hetero, type=eltype(resid(ε)))
40-
select_sample::SpatialMovingAverage, sample::AbstractUnitRange) = SpatialMovingAverage(size(resid(ε), 1), length(sample), copy(weights(ε)), spatial=length(spatial(ε)) == 1 ? :homo : :hetero, type=eltype(resid(ε)))
31+
function select_sample::Exogenous, sample::AbstractUnitRange)
32+
Exogenous(regressors(μ)[:, sample], size(slopes(μ), 1))
33+
end
4134

4235
"""
4336
select_sample(F, sample) -> F
4437
4538
Select a sample `sample` of the data from the dynamic factor process `F`.
4639
"""
47-
select_sample(F::UnrestrictedStationaryIdentified, sample::AbstractUnitRange) = UnrestrictedStationary((size(loadings(F), 1), length(sample), size(F)), dependence=:identified, type=eltype(factors(F)))
48-
select_sample(F::UnrestrictedStationaryFull, sample::AbstractUnitRange) = UnrestrictedStationary((size(loadings(F), 1), length(sample), size(F)), dependence=:full, type=eltype(factors(F)))
49-
select_sample(F::UnrestrictedUnitRoot, sample::AbstractUnitRange) = UnrestrictedUnitRoot((size(loadings(F), 1), length(sample), size(F)), type=eltype(factors(F)))
50-
select_sample(F::NelsonSiegelStationary, sample::AbstractUnitRange) = NelsonSiegelStationary(length(sample), maturities(F), type=eltype(factors(F)))
51-
select_sample(F::NelsonSiegelUnitRoot, sample::AbstractUnitRange) = NelsonSiegelUnitRoot(length(sample), maturities(F), type=eltype(factors(F)))
40+
function select_sample(F::UnrestrictedStationaryIdentified, sample::AbstractUnitRange)
41+
UnrestrictedStationary((size(loadings(F), 1), length(sample), size(F)),
42+
dependence = :identified, type = eltype(factors(F)))
43+
end
44+
function select_sample(F::UnrestrictedStationaryFull, sample::AbstractUnitRange)
45+
UnrestrictedStationary((size(loadings(F), 1), length(sample), size(F)),
46+
dependence = :full, type = eltype(factors(F)))
47+
end
48+
function select_sample(F::UnrestrictedUnitRoot, sample::AbstractUnitRange)
49+
UnrestrictedUnitRoot((size(loadings(F), 1), length(sample), size(F)),
50+
type = eltype(factors(F)))
51+
end
52+
function select_sample(F::NelsonSiegelStationary, sample::AbstractUnitRange)
53+
NelsonSiegelStationary(length(sample), maturities(F), type = eltype(factors(F)))
54+
end
55+
function select_sample(F::NelsonSiegelUnitRoot, sample::AbstractUnitRange)
56+
NelsonSiegelUnitRoot(length(sample), maturities(F), type = eltype(factors(F)))
57+
end
5258

5359
"""
54-
simulate(F; burn=100, rng=Xoshiro()) -> sim
60+
simulate(F, S; rng = Xoshiro()) -> f
5561
56-
Simulate the dynamic factors from the dynamic factor process `F`
57-
with a burn-in period of `burn` using the random number generator `rng`.
62+
Simulate the dynamic factors from the dynamic factor process `F` `S` times using the random
63+
number generator `rng`.
5864
"""
59-
function simulate(F::AbstractFactorProcess; burn::Integer=100, rng::AbstractRNG=Xoshiro())
60-
# burn-in
61-
f_prev = rand(rng, dist(F))
62-
f_next = similar(f_prev)
63-
for _ = 1:burn
64-
f_next .= dynamics(F) * f_prev + rand(rng, dist(F))
65-
f_prev .= f_next
66-
end
67-
68-
# simulate data
69-
F_sim = copy(F)
70-
for (t, ft) pairs(eachcol(factors(F_sim)))
71-
if t == 1
72-
ft .= f_prev
65+
function simulate(F::AbstractFactorProcess, S::Integer; rng::AbstractRNG = Xoshiro())
66+
R = size(loadings(F), 2)
67+
f = similar(factors(F), R, S)
68+
dist = MvNormal(cov(F))
69+
for (s, fs) in pairs(eachcol(f))
70+
if s == 1
71+
# initial condition
72+
fs .= rand(rng, dist)
7373
else
74-
ft .= dynamics(F) * factors(F_sim)[:,t-1] + rand(rng, dist(F))
74+
fs .= dynamics(F) * f[:, s - 1] + rand(rng, dist)
7575
end
76+
7677
end
7778

78-
return F_sim
79+
return f
7980
end
8081

8182
"""
82-
simulate(ε; rng=Xoshiro()) -> sim
83+
simulate(ε, S; rng = Xoshiro()) -> e
8384
84-
Simulate from the error distribution `ε` using the random number generator
85-
`rng`.
85+
Simulate from the error distribution `ε` `S` times using the random number generator `rng`.
8686
"""
87-
simulate::Simple; rng::AbstractRNG=Xoshiro()) = Simple(rand(rng, dist(ε), size(resid(ε), 2)), MvNormal(Diagonal(var(ε))))
88-
function simulate::SpatialAutoregression; rng::AbstractRNG=Xoshiro())
89-
e_sim = similar(resid(ε))
90-
for et eachcol(e_sim)
91-
et .= poly(ε) \ rand(rng, dist(ε))
87+
function simulate::Simple, S::Integer; rng::AbstractRNG = Xoshiro())
88+
rand(rng, MvNormal(cov(ε)), S)
89+
end
90+
function simulate::SpatialAutoregression, S::Integer; rng::AbstractRNG = Xoshiro())
91+
e = rand(rng, MvNormal(cov(ε)), S)
92+
G = poly(ε)
93+
for es in eachcol(e)
94+
es .= G \ es
9295
end
9396

94-
return SpatialAutoregression(e_sim, MvNormal(Diagonal(var(ε))), copy(spatial(ε)), ε.ρ_max, copy(weights(ε)))
97+
return e
9598
end
96-
function simulate::SpatialMovingAverage; rng::AbstractRNG=Xoshiro())
97-
e_sim = similar(resid(ε))
98-
for et eachcol(e_sim)
99-
mul!(et, poly(ε), rand(rng, dist(ε)))
99+
function simulate::SpatialMovingAverage, S::Integer; rng::AbstractRNG = Xoshiro())
100+
e = rand(rng, MvNormal(cov(ε)), S)
101+
G = poly(ε)
102+
for es in eachcol(e)
103+
es .= G * es
100104
end
101105

102-
return SpatialMovingAverage(e_sim, MvNormal(Diagonal(var(ε))), copy(spatial(ε)), ε.ρ_max, copy(weights(ε)))
106+
return e
103107
end
104108

105109
"""
@@ -110,15 +114,15 @@ following the approach of Jungbacker and Koopman (2015).
110114
"""
111115
function collapse(model::DynamicFactorModel)
112116
# active factors
113-
active = [!all(iszero, λ) for λ eachcol(loadings(model))]
117+
active = [!all(iszero, λ) for λ in eachcol(loadings(model))]
114118

115119
# collapsing matrix
116120
H = cov(model)
117121
if all(active)
118122
C = (H \ loadings(model))' * loadings(model)
119123
Z_basis = (C \ loadings(model)')'
120124
else
121-
Z_basis = loadings(model)[:,active]
125+
Z_basis = loadings(model)[:, active]
122126
end
123127
A_low = (H \ Z_basis)'
124128

@@ -136,7 +140,7 @@ function state_space(model::DynamicFactorModel)
136140
Ty = eltype(data(model))
137141

138142
# active factors
139-
active = [!all(iszero, λ) for λ eachcol(loadings(model))]
143+
active = [!all(iszero, λ) for λ in eachcol(loadings(model))]
140144

141145
# collapsing matrix
142146
if any(active)
@@ -182,7 +186,7 @@ function _filter(y, Z, d, H, T, Q, a1, P1)
182186
P[1] = P1
183187

184188
# filter
185-
for (t, yt) pairs(eachcol(y))
189+
for (t, yt) in pairs(eachcol(y))
186190
# forecast error
187191
v[t] = yt - Z * a[t] - view(d, :, t)
188192
F[t] = Z * P[t] * Z' + H
@@ -192,8 +196,8 @@ function _filter(y, Z, d, H, T, Q, a1, P1)
192196

193197
# prediction
194198
if t < n
195-
a[t+1] = T * a[t] + K[t] * v[t]
196-
P[t+1] = T * P[t] * (T - K[t] * Z)' + Q
199+
a[t + 1] = T * a[t] + K[t] * v[t]
200+
P[t + 1] = T * P[t] * (T - K[t] * Z)' + Q
197201
end
198202
end
199203

@@ -242,7 +246,7 @@ function smoother(model::DynamicFactorModel)
242246
L = similar(P1)
243247

244248
# smoother
245-
for t reverse(eachindex(a))
249+
for t in reverse(eachindex(a))
246250
L .= T - K[t] * Z
247251

248252
# backward recursion
@@ -252,7 +256,7 @@ function smoother(model::DynamicFactorModel)
252256
# smoothing
253257
α̂[t] = a[t] + P[t] * r
254258
V[t] = P[t] - P[t] * N * P[t]
255-
t > 1 && (Γ[t-1] = I - P[t] * N)
259+
t > 1 && (Γ[t - 1] = I - P[t] * N)
256260
t < length(a) && (Γ[t] *= L * P[t])
257261
end
258262

@@ -265,4 +269,4 @@ end
265269
Forecast the mean model `μ` `periods` ahead.
266270
"""
267271
forecast::AbstractMeanSpecification, periods::Integer) = error("forecast for $(Base.typename(typeof(μ)).wrapper) not implemented.")
268-
forecast::ZeroMean, periods::Integer) = Zeros.type, μ.n, periods)
272+
forecast::ZeroMean, periods::Integer) = Zeros.type, μ.n, periods)

0 commit comments

Comments
 (0)