Skip to contents

The Bridge-to-MIDAS Spectrum

bridgr supports several ways to map higher-frequency observations into a lower-frequency target equation. Those choices span a continuum:

  • Bridge-style deterministic aggregation: "mean", "last", "sum", or fixed numeric weights.
  • Unrestricted mixed-frequency regression: "unrestricted".
  • Parametric MIDAS-style weighting: "expalmon", "beta", and "legendre".

This vignette uses a simple monthly-to-quarterly simulation so the differences between these approaches are easy to see.

A Small Monthly-to-Quarterly Example

n_quarters <- 40
quarter_index <- rep(seq_len(n_quarters), each = 3)
slot <- rep(1:3, times = n_quarters)
monthly_time <- seq(
  as.Date("2010-01-01"),
  by = "month",
  length.out = n_quarters * 3
)
monthly_indicator <- dplyr::tibble(
  time = monthly_time,
  value = 15 + quarter_index * 0.35 +
    ifelse(slot == 1, 0.8 * sin(quarter_index / 2), 0) +
    ifelse(slot == 2, -0.6 * cos(quarter_index / 3), 0) +
    ifelse(slot == 3, 0.7 * sin(quarter_index / 4 + 0.3), 0)
)

quarter_time <- monthly_time[seq(1, length(monthly_time), by = 3)]
quarter_target <- dplyr::tibble(
  time = quarter_time,
  value = 0.5 +
    vapply(
      seq_along(quarter_time),
      function(i) {
        block <- monthly_indicator$value[((i - 1) * 3 + 1):(i * 3)]
        0.2 * block[[1]] + 0.6 * block[[2]] + 0.2 * block[[3]]
      },
      numeric(1)
    ) +
    rep(c(0.1, -0.05, 0.08, -0.02), length.out = length(quarter_time))
)

The target is intentionally driven more by the middle month of each quarter, so we can see how the different aggregation schemes react. The monthly indicator also has slot-specific within-quarter movements, so the unrestricted specification can estimate three distinct monthly coefficients.

Deterministic Bridge Aggregation

mean_model <- bridge(
  target = quarter_target,
  indic = monthly_indicator,
  indic_predict = "last",
  indic_aggregators = "mean",
  h = 1
)

last_model <- bridge(
  target = quarter_target,
  indic = monthly_indicator,
  indic_predict = "last",
  indic_aggregators = "last",
  h = 1
)

summary(mean_model)
#> Bridge model summary
#> -----------------------------------
#> Target series: quarter_target
#> Target frequency: quarter
#> Forecast horizon: 1
#> Estimation rows: 40
#> Regressors: monthly_indicator
#> -----------------------------------
#> Target equation coefficients:
#>                   Estimate
#> (Intercept)          0.516
#> monthly_indicator    0.999
#> -----------------------------------
#> Indicator summary:
#>                   Frequency Predict Aggregation
#> monthly_indicator month     last    mean       
#> -----------------------------------
summary(last_model)
#> Bridge model summary
#> -----------------------------------
#> Target series: quarter_target
#> Target frequency: quarter
#> Forecast horizon: 1
#> Estimation rows: 40
#> Regressors: monthly_indicator
#> -----------------------------------
#> Target equation coefficients:
#>                   Estimate
#> (Intercept)          0.590
#> monthly_indicator    0.993
#> -----------------------------------
#> Indicator summary:
#>                   Frequency Predict Aggregation
#> monthly_indicator month     last    last       
#> -----------------------------------

These are classic bridge-model choices. They are easy to interpret and often work well when you want a transparent rule for within-period aggregation.

Unrestricted Mixed-Frequency Regression

unrestricted_model <- bridge(
  target = quarter_target,
  indic = monthly_indicator,
  indic_predict = "last",
  indic_aggregators = "unrestricted",
  h = 1
)

summary(unrestricted_model)
#> Bridge model summary
#> -----------------------------------
#> Target series: quarter_target
#> Target frequency: quarter
#> Forecast horizon: 1
#> Estimation rows: 40
#> Regressors: monthly_indicator_hf1, monthly_indicator_hf2, monthly_indicator_hf3
#> -----------------------------------
#> Target equation coefficients:
#>                       Estimate
#> (Intercept)              0.541
#> monthly_indicator_hf1    0.198
#> monthly_indicator_hf2    0.596
#> monthly_indicator_hf3    0.205
#> -----------------------------------
#> Indicator summary:
#>                   Frequency Predict Aggregation 
#> monthly_indicator month     last    unrestricted
#> -----------------------------------

"unrestricted" estimates one coefficient for each within-quarter monthly observation. In a monthly-on-quarterly example this means three separate regressors. This example keeps indic_predict = "last" so the comparison isolates the aggregation choice; combining "unrestricted" with indic_predict = "direct" gives a direct MIDAS-style alignment and is covered in the ragged-edge vignette.

Parametric MIDAS-Style Weighting

bridgr also supports parametric weighting rules that estimate the within-period shape from the data while keeping the number of free parameters small.

expalmon_model <- bridge(
  target = quarter_target,
  indic = monthly_indicator,
  indic_predict = "last",
  indic_aggregators = "expalmon",
  solver_options = list(seed = 123, n_starts = 1, maxiter = 100),
  h = 1
)

beta_model <- bridge(
  target = quarter_target,
  indic = monthly_indicator,
  indic_predict = "last",
  indic_aggregators = "beta",
  solver_options = list(
    seed = 123,
    n_starts = 1,
    maxiter = 100,
    start_values = c(2, 2)
  ),
  h = 1
)

legendre_model <- bridge(
  target = quarter_target,
  indic = monthly_indicator,
  indic_predict = "last",
  indic_aggregators = "legendre",
  solver_options = list(seed = 123, n_starts = 1, maxiter = 100),
  h = 1
)

The fitted object stores both the estimated weight profile and the underlying parametric coefficients.

indicator_id <- expalmon_model$indic_name[[1]]

dplyr::bind_rows(
  dplyr::tibble(
    model = "expalmon",
    month = seq_along(expalmon_model$parametric_weights[[indicator_id]]),
    weight = expalmon_model$parametric_weights[[indicator_id]]
  ),
  dplyr::tibble(
    model = "beta",
    month = seq_along(beta_model$parametric_weights[[indicator_id]]),
    weight = beta_model$parametric_weights[[indicator_id]]
  ),
  dplyr::tibble(
    model = "legendre",
    month = seq_along(legendre_model$parametric_weights[[indicator_id]]),
    weight = legendre_model$parametric_weights[[indicator_id]]
  )
)
#> # A tibble: 9 × 3
#>   model    month    weight
#>   <chr>    <int>     <dbl>
#> 1 expalmon     1 1.99 e- 1
#> 2 expalmon     2 5.97 e- 1
#> 3 expalmon     3 2.05 e- 1
#> 4 beta         1 8.88 e-16
#> 5 beta         2 1.000e+ 0
#> 6 beta         3 8.88 e-16
#> 7 legendre     1 1.99 e- 1
#> 8 legendre     2 5.97 e- 1
#> 9 legendre     3 2.05 e- 1

Forecast Comparison

All of these models share the same downstream interface.

dplyr::bind_rows(
  dplyr::tibble(
    model = "mean",
    forecast = as.numeric(forecast(mean_model)$mean)
  ),
  dplyr::tibble(
    model = "last",
    forecast = as.numeric(forecast(last_model)$mean)
  ),
  dplyr::tibble(
    model = "unrestricted",
    forecast = as.numeric(forecast(unrestricted_model)$mean)
  ),
  dplyr::tibble(
    model = "expalmon",
    forecast = as.numeric(forecast(expalmon_model)$mean)
  ),
  dplyr::tibble(
    model = "beta",
    forecast = as.numeric(forecast(beta_model)$mean)
  ),
  dplyr::tibble(
    model = "legendre",
    forecast = as.numeric(forecast(legendre_model)$mean)
  )
)
#> # A tibble: 6 × 2
#>   model        forecast
#>   <chr>           <dbl>
#> 1 mean             29.0
#> 2 last             28.9
#> 3 unrestricted     29.0
#> 4 expalmon         29.0
#> 5 beta             29.0
#> 6 legendre         29.0

Choosing an Aggregation Strategy

As a rough guide:

  • Use deterministic bridge aggregation when you want a transparent and stable nowcasting rule.
  • Use "unrestricted" when the frequency gap is small and you want each within-period observation to have its own coefficient.
  • Use "expalmon", "beta", or "legendre" when you want data-driven within-period weights but would like a more parsimonious parameterization than "unrestricted".
  • Use indic_predict = "direct" when you want direct MIDAS-style alignment based only on the latest observed high-frequency blocks.

The key design choice in bridgr is that all of these specifications share the same estimation, forecasting, and summary workflow. You can therefore move between bridge and MIDAS-style models without switching to a different API.