Estimating and Forecasting with the koma Package

library(koma)

Overview

This vignette walks through a minimal end-to-end workflow: define a small system, prepare data, estimate, and forecast. For full syntax details, see the equation reference, and for time series handling see the ets vignette.

Define a small system

We start with four stochastic equations and two identities that mirror a small open economy. The interest rate, world GDP, and the exchange rate are treated as exogenous in this example. To keep the setup minimal, the GDP identity below uses fixed illustrative weights. For an example with time-varying weights computed from nominal series, see the Klein vignette.

equations <- "consumption ~ gdp + consumption.L(1) + interest_rate,
investment ~ gdp + investment.L(1) + interest_rate,
exports ~ world_gdp + exchange_rate + exports.L(1),
imports ~ gdp + exchange_rate + imports.L(1),
gdp == 0.55*consumption + 0.20*investment + 0.30*exports - 0.05*imports"

exogenous_variables <- c("interest_rate", "world_gdp", "exchange_rate")

Build the system

sys_eq <- system_of_equations(
    equations = equations,
    exogenous_variables = exogenous_variables
)

print(sys_eq)
#> 
#> ── System of Equations ─────────────────────────────────────────────────────────
#> consumption ~  constant + gdp + consumption.L(1) + interest_rate
#>  investment ~  constant + gdp + investment.L(1) + interest_rate
#>     exports ~  constant + world_gdp + exchange_rate + exports.L(1)
#>     imports ~  constant + gdp + exchange_rate + imports.L(1)
#>         gdp == 0.55 * consumption + 0.20 * investment + 0.30 * exports-0.05 * imports

Pick estimation and forecast ranges

We use the last year of the dataset as a short out-of-sample forecast period. For this introductory example, the identity already contains fixed numeric weights, so only estimation and forecast ranges are needed.

dates <- list(
    estimation = list(start = c(1996, 1), end = c(2019, 4)),
    forecast = list(start = c(2023, 1), end = c(2023, 4))
)

Prepare the data

We use the small_open_economy dataset, which is a list of ts objects. We’ll keep only the variables that appear in the system.

data("small_open_economy")
series <- unique(c(sys_eq$endogenous_variables, sys_eq$exogenous_variables))
ts_data <- small_open_economy[series]

If you pass ts objects directly, estimate() assumes they are already in rates (the form the model estimates on) and converts them with series_type = "rate", method = "none" (no transformation applied), emitting a warning that lists the affected series:

estimates <- estimate(ts_data, sys_eq, dates)
#> ! The following series are plain <ts> objects, not <koma_ts>: "consumption",
#>   "investment", "exports", "imports", "gdp", "interest_rate", "world_gdp",
#>   and "exchange_rate".
#> i They are assumed to already be in rates, the form the model estimates on,
#>   and are converted to <koma_ts> with `series_type = "rate"`, `method =
#>   "none"`. The values are used as-is; no rate/level transformation is
#>   applied.
#> i To convert a series from levels (e.g. a percentage or diff_log growth
#>   rate), wrap it first with `ets()` or `as_ets()`. See
#>   `vignette("koma-extended-timeseries")` for details.

Most of these series are actually in levels and need a diff_log transform to become growth rates, and interest_rate is a rate that needs no transform, so here we convert explicitly instead of relying on the rate/none default:

ts_data <- lapply(ts_data, function(x) {
    as_ets(x, series_type = "level", method = "diff_log")
})
ts_data$interest_rate <- as_ets(
    ts_data$interest_rate,
    series_type = "rate",
    method = "none"
)

Estimate the model

estimates <- estimate(
    ts_data,
    sys_eq,
    dates
)
#> 
#> ── Gibbs Sampler Settings ──────────────────────────────────────────────────────
#> ── System Wide Settings ──
#> • Number of draws (`ndraws`): 2000
#> • Burn-in ratio (`burnin_ratio`): 0.5
#> • Burn-in (`burnin`): 1000
#> • Store frequency (`nstore`): 1
#> • Number of saved draws (`nsave`): 1000
#> • Tau (`tau`): 1.1
#> 
#> 
#> ── Estimation ──────────────────────────────────────────────────────────────────
#> 
#> ── ⚠ MCMC Acceptance Probability Warnings ──────────────────────────────────────
#> • investment: 60.6%
#> • imports: 60.5%
#> 
#> ℹ Some acceptance probabilities are outside the recommended range (20%-60%).
#> Consider revising the equations, tuning each equation's tau, or adjusting your priors.

print(estimates)
#> 
#> ── Estimates ───────────────────────────────────────────────────────────────────
#> consumption ~  0.35 - 0.02 * gdp  +  0.06 * consumption.L(1)  +  0.04 * interest_rate
#>  investment ~  - 0.28  +  1.96 * gdp - 0.02 * investment.L(1) - 0.14 * interest_rate
#>     exports ~  - 0.11  +  2.98 * world_gdp  +  0.28 * exchange_rate - 0.28 * exports.L(1)
#>     imports ~  0.04  +  2.15 * gdp - 0.18 * exchange_rate - 0.14 * imports.L(1)
#>         gdp == 0.55 * consumption  +  0.20 * investment  +  0.30 * exports - 0.05 * imports
summary(estimates)
#> 
#> ==============================================================================
#>                   consumption    investment     exports         imports       
#> ------------------------------------------------------------------------------
#> constant            0.35          -0.28          -0.11            0.04        
#>                   [ 0.27; 0.43]  [-0.63; 0.05]  [-0.66;  0.49]  [-0.42;  0.46]
#> consumption.L(1)    0.06                                                      
#>                   [-0.11; 0.23]                                               
#> interest_rate       0.04          -0.14                                       
#>                   [ 0.00; 0.08]  [-0.35; 0.06]                                
#> gdp                -0.02           1.96                           2.15        
#>                   [-0.11; 0.07]  [ 1.39; 2.47]                  [ 1.41;  2.92]
#> investment.L(1)                   -0.02                                       
#>                                  [-0.18; 0.14]                                
#> exports.L(1)                                     -0.28                        
#>                                                 [-0.43; -0.12]                
#> world_gdp                                         2.98                        
#>                                                 [ 2.16;  3.81]                
#> exchange_rate                                     0.28           -0.18        
#>                                                 [ 0.12;  0.45]  [-0.31; -0.06]
#> imports.L(1)                                                     -0.14        
#>                                                                 [-0.33;  0.04]
#> ==============================================================================
#> Posterior mean (90% credible interval: [5.0%, 95.0%])
#> Estimation period: 1996 Q1 - 2019 Q4

Forecast and inspect

Before forecasting, truncate endogenous series so they end in the quarter before the forecast start date.

estimates$ts_data[sys_eq$endogenous_variables] <-
    lapply(sys_eq$endogenous_variables, function(x) {
        stats::window(estimates$ts_data[[x]], end = c(2022, 4))
    })
forecasts <- forecast(estimates, dates)
#> 
#> ── Forecast ────────────────────────────────────────────────────────────────────
print(forecasts)
#> <koma_ts>
#> attributes:
#>   series_type: list[8]
#>   method: list[8]
#>   anker: list[8]
#> 
#> series:
#>         consumption investment exports imports    gdp interest_rate world_gdp
#> 2023 Q1      0.3945     1.0631  1.3003  1.5752 0.7409        1.1009    0.4827
#> 2023 Q2      0.4366     0.0987  0.3017  0.8643 0.3072        1.5227    0.4083
#> 2023 Q3      0.4390     0.3966  0.5795  1.2400 0.4326        1.7075    0.4259
#> 2023 Q4      0.4460     0.1396  0.3448  0.6953 0.3419        1.7006    0.2906
#>         exchange_rate
#> 2023 Q1        0.9217
#> 2023 Q2       -1.3863
#> 2023 Q3       -1.7869
#> 2023 Q4       -0.7502

rate(forecasts$mean$gdp)
#> <koma_ts>
#> attributes:
#>   series_type:  chr "rate"
#>   method:  chr "diff_log"
#>   anker:  num [1:2] 191669 2023
#> 
#> series:
#>           Qtr1      Qtr2      Qtr3      Qtr4
#> 2023 0.7409365 0.3071687 0.4326168 0.3418610
level(forecasts$mean$gdp)
#> <koma_ts>
#> attributes:
#>   series_type:  chr "level"
#>   method:  chr "diff_log"
#> 
#> series:
#>          Qtr1     Qtr2     Qtr3     Qtr4
#> 2022                            191668.9
#> 2023 193094.3 193688.3 194528.1 195194.2

You can also summarize forecast horizons with mean/median and quantiles:

summary(forecasts)
#> =========================================
#> consumption  Mean   Median  5%      95%  
#> -----------------------------------------
#> 2023 Q1      0.395   0.393  -0.042  0.825
#> 2023 Q2      0.437   0.432   0.047  0.843
#> 2023 Q3      0.439   0.444   0.031  0.861
#> 2023 Q4      0.446   0.441   0.047  0.857
#> =========================================
#> 
#> ========================================
#> investment  Mean   Median  5%      95%  
#> ----------------------------------------
#> 2023 Q1     1.063   1.031   -3.83  5.757
#> 2023 Q2     0.099    0.18  -4.864  4.811
#> 2023 Q3     0.397   0.361  -4.473   5.34
#> 2023 Q4      0.14   0.105  -4.763   5.09
#> ========================================
#> 
#> =====================================
#> exports  Mean   Median  5%      95%  
#> -------------------------------------
#> 2023 Q1    1.3   1.334  -2.211  5.034
#> 2023 Q2  0.302   0.325  -3.736  4.052
#> 2023 Q3  0.579   0.642  -3.165  4.368
#> 2023 Q4  0.345   0.397  -3.642  4.246
#> =====================================
#> 
#> =====================================
#> imports  Mean   Median  5%      95%  
#> -------------------------------------
#> 2023 Q1  1.575    1.54   -2.55  5.923
#> 2023 Q2  0.864     0.9  -3.844  5.227
#> 2023 Q3   1.24   1.209  -3.267   5.63
#> 2023 Q4  0.695   0.787  -3.727  5.147
#> =====================================
#> 
#> =====================================
#> gdp      Mean   Median  5%      95%  
#> -------------------------------------
#> 2023 Q1  0.741   0.776  -0.996  2.415
#> 2023 Q2  0.307   0.326  -1.483  2.109
#> 2023 Q3  0.433   0.437  -1.314  2.073
#> 2023 Q4  0.342   0.354  -1.449  2.155
#> =====================================
#> 
#> ==========================================
#> interest_rate  Mean   Median  5%     95%  
#> ------------------------------------------
#> 2023 Q1        1.101   1.101  1.101  1.101
#> 2023 Q2        1.523   1.523  1.523  1.523
#> 2023 Q3        1.708   1.708  1.708  1.708
#> 2023 Q4        1.701   1.701  1.701  1.701
#> ==========================================
#> 
#> ======================================
#> world_gdp  Mean   Median  5%     95%  
#> --------------------------------------
#> 2023 Q1    0.483   0.483  0.483  0.483
#> 2023 Q2    0.408   0.408  0.408  0.408
#> 2023 Q3    0.426   0.426  0.426  0.426
#> 2023 Q4    0.291   0.291  0.291  0.291
#> ======================================
#> 
#> =============================================
#> exchange_rate  Mean    Median  5%      95%   
#> ---------------------------------------------
#> 2023 Q1         0.922   0.922   0.922   0.922
#> 2023 Q2        -1.386  -1.386  -1.386  -1.386
#> 2023 Q3        -1.787  -1.787  -1.787  -1.787
#> 2023 Q4         -0.75   -0.75   -0.75   -0.75
#> =============================================
#> 
#> Mean, Median, Quantiles
summary(forecasts, variables = "gdp", horizon = 2)
#> =====================================
#> gdp      Mean   Median  5%      95%  
#> -------------------------------------
#> 2023 Q1  0.741   0.776  -0.996  2.415
#> 2023 Q2  0.307   0.326  -1.483  2.109
#> =====================================
#> 
#> Mean, Median, Quantiles
if (requireNamespace("plotly", quietly = TRUE)) {
    plot(forecasts, variables = c("gdp", "consumption"))
}