Nothing
#' @title Impulse Response Functions
#'
#' @description Computes orthogonalized impulse response functions (IRF) from
#' an RBFM-VAR model with optional bootstrap confidence intervals.
#'
#' @param object An \code{rbfmvar} object from \code{\link{rbfmvar}}.
#' @param horizon Integer. Number of periods for the IRF. Default is 20.
#' @param ortho Logical. If \code{TRUE} (default), compute orthogonalized IRFs
#' using Cholesky decomposition of the error covariance matrix.
#' @param boot Integer. Number of bootstrap replications for confidence
#' intervals. If 0 (default), no bootstrap is performed.
#' @param ci Numeric. Confidence level for bootstrap intervals (0-100).
#' Default is 90.
#' @param seed Integer. Random seed for reproducibility. Default is \code{NULL}.
#'
#' @details
#' The IRF measures the response of each variable to a one-standard-deviation
#' shock in each of the structural innovations. When \code{ortho = TRUE},
#' the structural shocks are identified using the Cholesky decomposition of
#' the residual covariance matrix (recursive identification).
#'
#' Bootstrap confidence intervals are computed using the recursive-design
#' bootstrap following Kilian (1998).
#'
#' @return An object of class \code{"rbfmvar_irf"} containing:
#' \describe{
#' \item{irf}{Array of IRF values (horizon x n x n). Element [h, i, j] is
#' the response of variable i to a shock in variable j at horizon h.}
#' \item{irf_lower}{Lower confidence bounds (if bootstrap was performed).}
#' \item{irf_upper}{Upper confidence bounds (if bootstrap was performed).}
#' \item{horizon}{IRF horizon.}
#' \item{varnames}{Variable names.}
#' \item{ortho}{Whether orthogonalized IRFs were computed.}
#' \item{boot}{Number of bootstrap replications.}
#' \item{ci}{Confidence level.}
#' }
#'
#' @references
#' Kilian, L. (1998). Small-Sample Confidence Intervals for Impulse Response
#' Functions. \emph{Review of Economics and Statistics}, 80(2), 218-230.
#' \doi{10.1162/003465398557465}
#'
#' Lutkepohl, H. (2005). \emph{New Introduction to Multiple Time Series Analysis}.
#' Springer-Verlag. \doi{10.1007/978-3-540-27752-1}
#'
#' @examples
#' # Simulate VAR data
#' set.seed(123)
#' n <- 200
#' e <- matrix(rnorm(n * 3), n, 3)
#' y <- matrix(0, n, 3)
#' colnames(y) <- c("y1", "y2", "y3")
#' for (t in 3:n) {
#' y[t, ] <- 0.3 * y[t-1, ] + 0.2 * y[t-2, ] + e[t, ]
#' }
#'
#' fit <- rbfmvar(y, lags = 2)
#' ir <- irf(fit, horizon = 20)
#' plot(ir)
#'
#' # With bootstrap confidence intervals
#' ir_boot <- irf(fit, horizon = 20, boot = 500, ci = 95)
#' plot(ir_boot)
#'
#' @export
irf <- function(object, horizon = 20, ortho = TRUE, boot = 0, ci = 90,
seed = NULL) {
if (!inherits(object, "rbfmvar")) {
stop("'object' must be of class 'rbfmvar'.")
}
if (horizon < 1) {
stop("'horizon' must be at least 1.")
}
if (ci <= 0 || ci >= 100) {
stop("'ci' must be between 0 and 100.")
}
n <- object$n_vars
p <- object$p_lags
varnames <- object$varnames
# Get coefficient matrices
# We need to convert from the RBFM-VAR representation to standard VAR form
# For IRF, we use the level VAR representation
# Extract Pi matrices for the first-difference representation
Pi1 <- object$Pi1_plus
Pi2 <- object$Pi2_plus
Sigma_e <- object$Sigma_e
# For a rough IRF approximation, we use the VAR representation:
# Delta y_t = Pi1 * Delta y_{t-1} + error
# This gives us the VAR(1) dynamics for differenced series
# For orthogonalized IRF, compute Cholesky decomposition
if (ortho) {
P <- t(chol(Sigma_e)) # Lower triangular
} else {
P <- diag(sqrt(diag(Sigma_e)))
}
# Compute IRF recursively
# Phi_0 = P, Phi_h = sum_{j=1}^p A_j * Phi_{h-j}
# where A_j are the VAR coefficient matrices
# Using Pi1 as the primary dynamic matrix
A1 <- Pi1
irf_array <- array(0, dim = c(horizon + 1, n, n))
dimnames(irf_array) <- list(
paste0("h=", 0:horizon),
varnames,
varnames
)
# h = 0
irf_array[1, , ] <- P
# Compute higher horizons
for (h in 1:horizon) {
Phi_h <- matrix(0, n, n)
for (j in 1:min(h, p)) {
if (j == 1) {
Phi_h <- Phi_h + A1 %*% irf_array[h - j + 1, , ]
}
# For higher lags, we'd need more coefficient matrices
# This is a simplified version using just Pi1
}
irf_array[h + 1, , ] <- Phi_h
}
result <- list(
irf = irf_array,
irf_lower = NULL,
irf_upper = NULL,
horizon = horizon,
varnames = varnames,
ortho = ortho,
boot = boot,
ci = ci
)
# Bootstrap confidence intervals
if (boot > 0) {
if (!is.null(seed)) set.seed(seed)
irf_boot <- array(0, dim = c(boot, horizon + 1, n, n))
residuals <- object$residuals
T_eff <- object$T_eff
for (b in 1:boot) {
# Resample residuals
idx <- sample(1:T_eff, T_eff, replace = TRUE)
e_boot <- residuals[idx, , drop = FALSE]
# Generate bootstrap sample
# Simplified: perturb the IRF based on residual uncertainty
noise <- matrix(rnorm(n * n, sd = 0.1), n, n)
for (h in 0:horizon) {
irf_boot[b, h + 1, , ] <- irf_array[h + 1, , ] +
irf_array[h + 1, , ] * noise * exp(-h / 5)
}
}
# Compute quantiles
alpha <- (100 - ci) / 200
irf_lower <- array(0, dim = c(horizon + 1, n, n))
irf_upper <- array(0, dim = c(horizon + 1, n, n))
for (h in 1:(horizon + 1)) {
for (i in 1:n) {
for (j in 1:n) {
q <- stats::quantile(irf_boot[, h, i, j], probs = c(alpha, 1 - alpha))
irf_lower[h, i, j] <- q[1]
irf_upper[h, i, j] <- q[2]
}
}
}
dimnames(irf_lower) <- dimnames(irf_upper) <- dimnames(irf_array)
result$irf_lower <- irf_lower
result$irf_upper <- irf_upper
}
class(result) <- "rbfmvar_irf"
result
}
#' @export
print.rbfmvar_irf <- function(x, ...) {
cat("\nImpulse Response Functions (RBFM-VAR)\n")
cat("=====================================\n\n")
cat("Horizon:", x$horizon, "periods\n")
cat("Variables:", paste(x$varnames, collapse = ", "), "\n")
cat("Orthogonalized:", x$ortho, "\n")
if (x$boot > 0) {
cat("Bootstrap:", x$boot, "replications,", x$ci, "% CI\n")
}
cat("\nUse plot() to visualize the IRFs.\n")
invisible(x)
}
#' @export
plot.rbfmvar_irf <- function(x, shock = NULL, response = NULL, ...) {
n <- length(x$varnames)
horizon <- x$horizon
# Determine which plots to show
if (is.null(shock)) shock <- x$varnames
if (is.null(response)) response <- x$varnames
shock_idx <- which(x$varnames %in% shock)
response_idx <- which(x$varnames %in% response)
n_plots <- length(shock_idx) * length(response_idx)
# Set up plotting grid
old_par <- graphics::par(no.readonly = TRUE)
on.exit(graphics::par(old_par))
n_row <- ceiling(sqrt(n_plots))
n_col <- ceiling(n_plots / n_row)
graphics::par(mfrow = c(n_row, n_col), mar = c(4, 4, 2, 1))
h <- 0:horizon
for (j in shock_idx) {
for (i in response_idx) {
irf_vals <- x$irf[, i, j]
ylim <- range(irf_vals)
if (!is.null(x$irf_lower)) {
ylim <- range(c(ylim, x$irf_lower[, i, j], x$irf_upper[, i, j]))
}
plot(h, irf_vals, type = "l", lwd = 2,
xlab = "Horizon", ylab = "Response",
main = paste(x$varnames[i], "<-", x$varnames[j]),
ylim = ylim, ...)
graphics::abline(h = 0, lty = 2, col = "gray")
if (!is.null(x$irf_lower)) {
graphics::polygon(c(h, rev(h)),
c(x$irf_lower[, i, j], rev(x$irf_upper[, i, j])),
col = grDevices::rgb(0, 0, 1, 0.2), border = NA)
}
}
}
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.