## ----include = FALSE----------------------------------------------------------
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

## ----setup--------------------------------------------------------------------
library(bayesRecon)

## ----finTShier, fig.cap="**Figure 1**: financial time series hierarchy.", out.width = '80%', echo = FALSE----
knitr::include_graphics("img/finTS_hier.jpg")

## -----------------------------------------------------------------------------
# Hierarchy composed by 6 time series: 5 bottom and 1 upper
n_b <- 5
n_u <- 1
n <- n_b + n_u

A <- matrix(1, ncol = n_b, nrow = n_u) # aggregation matrix

# Actual values:
actuals <- data.frame(extr_mkt_events) # convert to data frame
# Base forecasts:
base_fc <- extr_mkt_events_basefc

N <- nrow(actuals) # number of days (3508)

# # If you want to run only N reconciliations (instead of 3508):
# N <- 200
# actuals <- actuals[1:N,]
# base_fc$mu <- base_fc$mu[1:N,]

## -----------------------------------------------------------------------------
# We need to save the mean and median of the reconciled distribution
# in order to compute the squared error and the absolute error:
rec_means <- matrix(NA, ncol = n, nrow = N)
rec_medians <- matrix(NA, ncol = n, nrow = N)

# We need to save the lower and upper quantiles of the reconciled distribution
# in order to compute the interval score:
rec_L <- matrix(NA, ncol = n, nrow = N)
rec_U <- matrix(NA, ncol = n, nrow = N)
int_cov <- 0.9 # use 90% interval
q1 <- (1 - int_cov) / 2
q2 <- (1 + int_cov) / 2

# Set the number of samples to draw from the reconciled distribution:
N_samples <- 1e4

start <- Sys.time()
for (j in 1:N) {
  # Base forecasts:
  base_fc_j <- c()
  for (i in 1:n) {
    base_fc_j[[i]] <- list(size = base_fc$size[[i]], mu = base_fc$mu[[j, i]])
  }

  # Reconcile via importance sampling:
  buis <- reconc_BUIS(A, base_fc_j, "params", "nbinom",
    num_samples = N_samples, seed = 42
  )
  samples_y <- rbind(buis$upper_rec_samples, buis$bottom_rec_samples)

  # Save mean, median, and lower and upper quantiles:
  rec_means[j, ] <- rowMeans(samples_y)
  rec_medians[j, ] <- apply(samples_y, 1, median)
  rec_L[j, ] <- apply(samples_y, 1, quantile, q1)
  rec_U[j, ] <- apply(samples_y, 1, quantile, q2)
}
stop <- Sys.time()
cat(
  "Computational time for ", N, " reconciliations: ",
  round(difftime(stop, start, units = "secs"), 2), "s"
)

## -----------------------------------------------------------------------------
base_means <- base_fc$mu
base_medians <- matrix(NA, ncol = n, nrow = N)
base_L <- matrix(NA, ncol = n, nrow = N)
base_U <- matrix(NA, ncol = n, nrow = N)

for (i in 1:n) {
  base_medians[, i] <- sapply(
    base_means[, i],
    function(mu) qnbinom(p = 0.5, size = base_fc$size[[i]], mu = mu)
  )
  base_L[, i] <- sapply(
    base_means[, i],
    function(mu) qnbinom(p = q1, size = base_fc$size[[i]], mu = mu)
  )
  base_U[, i] <- sapply(
    base_means[, i],
    function(mu) qnbinom(p = q2, size = base_fc$size[[i]], mu = mu)
  )
}

## -----------------------------------------------------------------------------
# Compute the squared errors
SE_base <- (base_means - actuals)^2
SE_rec <- (rec_means - actuals)^2

# Compute the absolute errors
AE_base <- abs(base_medians - actuals)
AE_rec <- abs(rec_medians - actuals)

# Define the function for the interval score
int_score <- function(l, u, actual, int_cov = 0.9) {
  is <- (u - l) +
    2 / (1 - int_cov) * (actual - u) * (actual > u) +
    2 / (1 - int_cov) * (l - actual) * (l > actual)
  return(is)
}

# Compute the interval scores
IS_base <- mapply(int_score, base_L, base_U, data.matrix(actuals))
IS_base <- matrix(IS_base, nrow = N)
IS_rec <- mapply(int_score, rec_L, rec_U, data.matrix(actuals))
IS_rec <- matrix(IS_rec, nrow = N)

## -----------------------------------------------------------------------------
SS_AE <- (AE_base - AE_rec) / (AE_base + AE_rec) * 2
SS_SE <- (SE_base - SE_rec) / (SE_base + SE_rec) * 2
SS_IS <- (IS_base - IS_rec) / (IS_base + IS_rec) * 2
SS_AE[is.na(SS_AE)] <- 0
SS_SE[is.na(SS_SE)] <- 0
SS_IS[is.na(SS_IS)] <- 0

mean_skill_scores <- c(
  round(colMeans(SS_IS), 2),
  round(colMeans(SS_SE), 2),
  round(colMeans(SS_AE), 2)
)
mean_skill_scores <- data.frame(t(matrix(mean_skill_scores, nrow = n)))
colnames(mean_skill_scores) <- names(actuals)
rownames(mean_skill_scores) <- c("Interval score", "Squared error", "Absolute error")
knitr::kable(mean_skill_scores)

## -----------------------------------------------------------------------------
# Now we draw a larger number of samples:
N_samples <- 1e5

### Example of concordant-shift effect
j <- 124
base_fc_j <- c()
for (i in 1:n) {
  base_fc_j[[i]] <- list(
    size = extr_mkt_events_basefc$size[[i]],
    mu = extr_mkt_events_basefc$mu[[j, i]]
  )
}
# Reconcile
buis <- reconc_BUIS(A, base_fc_j, "params", "nbinom",
  num_samples = N_samples, seed = 42
)
samples_y <- rbind(buis$upper_rec_samples, buis$bottom_rec_samples)

# The reconciled mean is lower than both the base and bottom-up means:
means <- c(
  round(extr_mkt_events_basefc$mu[[j, 1]], 2),
  round(sum(extr_mkt_events_basefc$mu[j, 2:n]), 2),
  round(mean(samples_y[1, ]), 2)
)
col_names <- c("Base upper mean", "Bottom-up upper mean", "Reconciled upper mean")
knitr::kable(matrix(means, nrow = 1), col.names = col_names)

### Example of combination effect
j <- 1700
base_fc_j <- c()
for (i in 1:n) {
  base_fc_j[[i]] <- list(
    size = extr_mkt_events_basefc$size[[i]],
    mu = extr_mkt_events_basefc$mu[[j, i]]
  )
}
# Reconcile
buis <- reconc_BUIS(A, base_fc_j, "params", "nbinom",
  num_samples = N_samples, seed = 42
)
samples_y <- rbind(buis$upper_rec_samples, buis$bottom_rec_samples)

# The reconciled mean is between the base and the bottom-up mean:
means <- c(
  round(extr_mkt_events_basefc$mu[[j, 1]], 2),
  round(sum(extr_mkt_events_basefc$mu[j, 2:n]), 2),
  round(mean(samples_y[1, ]), 2)
)
col_names <- c("Base upper mean", "Bottom-up upper mean", "Reconciled upper mean")
knitr::kable(matrix(means, nrow = 1), col.names = col_names)

## -----------------------------------------------------------------------------
j <- 2308
base_fc_j <- c()
for (i in 1:n) {
  base_fc_j[[i]] <- list(
    size = extr_mkt_events_basefc$size[[i]],
    mu = extr_mkt_events_basefc$mu[[j, i]]
  )
}
# Reconcile
buis <- reconc_BUIS(A, base_fc_j, "params", "nbinom", num_samples = N_samples, seed = 42)
samples_y <- rbind(buis$upper_rec_samples, buis$bottom_rec_samples)

# Compute the variance of the base and reconciled bottom forecasts
base_bottom_var <- mapply(
  function(mu, size) var(rnbinom(n = 1e5, size = size, mu = mu)),
  extr_mkt_events_basefc$mu[j, 2:n],
  extr_mkt_events_basefc$size[2:n]
)
rec_bottom_var <- apply(samples_y[2:n, ], MARGIN = 1, var)

# The reconciled variance is greater than the base variance:
bottom_var <- rbind(base_bottom_var, rec_bottom_var)
rownames(bottom_var) <- c("var base", "var reconc")
knitr::kable(round(bottom_var, 2))

