Horseshoe BVAR — R cross-check¶

Vector autoregressions, Part 12 (R)¶

Validates horseshoe_python.ipynb. There is no maintained horseshoe-regression package on this R toolchain, so — as with the sign-restriction and heteroskedasticity notebooks — we implement the same Makalic-Schmidt (2016) auxiliary-variable Gibbs from scratch in base R and run it on one equation of the VAR (the unemployment equation). It should reproduce the Python result: almost everything shrunk to zero, with the equation's own first lag the one coefficient that escapes.

Data: largesv_data.csv — 10 stationary FRED-QD macro series, quarterly 1960–2019.

In [1]:
.libPaths('C:/Users/user/R/win-library/4.6')
d <- read.csv('largesv_data.csv', row.names=1); Y <- as.matrix(d); m <- ncol(Y); p <- 2; n <- nrow(Y)
Yt <- Y[(p+1):n, ]
X <- cbind(1, Y[p:(n-1), ], Y[(p-1):(n-2), ])                       # [const, lag1(10), lag2(10)]
colnames(X) <- c('const', paste0(colnames(Y), '.L1'), paste0(colnames(Y), '.L2'))
rig <- function(a, b) 1/rgamma(1, a, rate=b)                        # inverse-gamma(shape, rate)

hs_eq <- function(y, X, ndraw=2000, burn=1200) {
  k <- ncol(X); nn <- length(y); XtX <- crossprod(X)
  beta <- rep(0,k); lam2 <- rep(1,k); nu <- rep(1,k); tau2 <- 1; xi <- 1; sig2 <- var(y)
  ks <- matrix(0, ndraw, k)
  for (it in 1:(ndraw+burn)) {
    dd <- lam2*tau2; dd[1] <- 1e6
    Prec <- XtX/sig2 + diag(1/(dd*sig2)); L <- chol(Prec)
    mu <- backsolve(L, forwardsolve(t(L), crossprod(X, y)/sig2))
    beta <- mu + backsolve(L, rnorm(k)); b2 <- beta^2
    for (j in 2:k) { lam2[j] <- rig(1, 1/nu[j] + b2[j]/(2*tau2*sig2)); nu[j] <- rig(1, 1+1/lam2[j]) }
    tau2 <- rig(k/2, 1/xi + sum(b2[-1]/lam2[-1])/(2*sig2)); xi <- rig(1, 1+1/tau2)
    r <- y - X%*%beta; sig2 <- rig((nn+k-1)/2, (sum(r^2)+sum(b2[-1]/(lam2[-1]*tau2)))/2)
    if (it>burn) ks[it-burn, ] <- 1/(1+lam2*tau2)
  }
  colMeans(ks)
}
set.seed(1); kap <- hs_eq(Yt[, 'unrate'], X)
cat(sprintf('unemployment equation: %.0f%% of coefficients killed (kappa>0.9)\n', 100*mean(kap[-1] > 0.9)))
cat('coefficients that ESCAPE shrinkage (kappa < 0.4):\n')
esc <- which(kap < 0.4 & seq_along(kap) > 1); for (j in esc) cat(sprintf('  %-12s kappa %.2f\n', colnames(X)[j], kap[j]))
cat('cross-check -> Python horseshoe: the unemployment own-lag (unrate.L1) escapes; the rest are killed.\n')
unemployment equation: 85% of coefficients killed (kappa>0.9)
coefficients that ESCAPE shrinkage (kappa < 0.4):
  unrate.L1    kappa 0.08
cross-check -> Python horseshoe: the unemployment own-lag (unrate.L1) escapes; the rest are killed.

Same adaptive selection¶

  • Base-R reproduces the horseshoe. The from-scratch Makalic-Schmidt Gibbs on the unemployment equation shrinks the overwhelming majority of coefficients to zero and leaves unrate.L1 — the equation's own first lag, i.e. the persistence of unemployment — as the coefficient that escapes, exactly as the Python horseshoe VAR found.
  • Two independent implementations of the global-local sampler agree on which dynamics are real, confirming the adaptive selection is a property of the prior and the data, not the code.

References¶

  • Carvalho, C. M., Polson, N. G. & Scott, J. G. (2010). The horseshoe estimator for sparse signals. Biometrika 97, 465–480.
  • Makalic, E. & Schmidt, D. F. (2016). A simple sampler for the horseshoe estimator. IEEE Signal Processing Letters 23, 179–182.