|
| 1 | +############################################################################### |
| 2 | +# Reconciliation with mixed-conditioning (Mix-Cond) |
| 3 | +############################################################################### |
| 4 | + |
| 5 | + |
| 6 | + |
| 7 | +#' @title Probabilistic Reconciliation of forecasts via mixed conditioning |
| 8 | +#' |
| 9 | +#' @description |
| 10 | +#' |
| 11 | +#' Uses the mixed conditioning algorithm to draw samples from the reconciled |
| 12 | +#' forecast distribution. |
| 13 | +#' |
| 14 | +#' @details |
| 15 | +#' |
| 16 | +#' The base (unreconciled) forecasts are passed with the parameters |
| 17 | +#' |
| 18 | +#' * `fc_bottom`: a list of length n_bottom where each element is either a pmf object (`bottom_in_type='pmf'`), |
| 19 | +#' a vector of samples (`bottom_in_type='samples'`) or the parameters (`bottom_in_type='params'`) of the |
| 20 | +#' parametric distribution specified in `distr`. |
| 21 | +#' * `fc_upper`: a list containing the parameters of the multivariate Gaussian distribution of the upper forecasts. |
| 22 | +#' The list must contain only the named elements `mu` (vector of length n_upper) and `Sigma` (n_upper x n_upper matrix) |
| 23 | +#' |
| 24 | +#' |
| 25 | +#' A PMF object is a numerical vector containing the probability mass function for a forecast. |
| 26 | +#' The first element of a PMF vector for the variable X always contains the probability P(X=0) and |
| 27 | +#' there is one element for each integer. |
| 28 | +#' The last element of the PMF corresponds to the probability of the last value in the support of X. |
| 29 | +#' See also \link{PMF.get_mean}, \link{PMF.get_var}, \link{PMF.sample}, \link{PMF.get_quantile}, \link{PMF.summary} for functions that handle PMF objects. |
| 30 | +#' |
| 31 | +#' |
| 32 | +#' A warnings is triggered if the intersection of the support for the reconciled uppers |
| 33 | +#' and the support of the bottom-up distribution is too small. In this case only |
| 34 | +#' few samples from the reconciled upper are kept. The warning reports the percentage |
| 35 | +#' of samples kept. |
| 36 | +#' |
| 37 | +#' Note that warnings are an indication that the base forecasts might have issues. Please check the base forecasts in case of warnings. |
| 38 | +#' |
| 39 | +#' @param S Summing matrix (n x n_bottom). return_pmf = TRUE, return_samples = FALSE, suppress_warnings = FALSE, seed = NULL |
| 40 | +#' @param fc_bottom A list containing the bottom base forecasts, see details. |
| 41 | +#' @param fc_upper A list containing the bottom base forecasts, see details. |
| 42 | +#' @param bottom_in_type A string with three possible values: |
| 43 | +#' |
| 44 | +#' * 'pmf' if the bottom base forecasts are in the form of pmf, see details; |
| 45 | +#' * 'samples' if the bottom base forecasts are in the form of samples; |
| 46 | +#' * 'params' if the bottom base forecasts are in the form of estimated parameters. |
| 47 | +#' |
| 48 | +#' @param distr A string describing the type of bottom base forecasts ('gaussian', 'poisson' or 'nbinom'). |
| 49 | +#' |
| 50 | +#' This is only used if `bottom_in_type=='params'`. |
| 51 | +#' |
| 52 | +#' @param num_samples Number of samples drawn from the reconciled distribution. |
| 53 | +#' @param num_resample Number of importance sampling resamples. |
| 54 | +#' @param our_sampler TO BE REMOVED AFTER THE TESTS. |
| 55 | +#' @param return_type The return type of the reconciled distributions. A string with three possible values: |
| 56 | +#' |
| 57 | +#' * 'pmf' returns a list containing reconciled pmf objects; |
| 58 | +#' * 'samples' returns a list containing reconciled samples; |
| 59 | +#' * 'all' returns a list with both pmf objects and samples. |
| 60 | +#' |
| 61 | +#' @param ... additional parameters to be passed to the smoothing functions for PMF objects. |
| 62 | +#' |
| 63 | +#' @param suppress_warnings Logical. If \code{TRUE}, no warnings about samples |
| 64 | +#' are triggered. If \code{FALSE}, warnings are generated. Default is \code{FALSE}. See Details. |
| 65 | +#' @param seed Seed for reproducibility. |
| 66 | +#' |
| 67 | +#' @return A list containing the reconciled forecasts. The list has the following named elements: |
| 68 | +#' |
| 69 | +#' * `bottom_reconciled`: a list containing the pmf, the samples (matrix n_bottom x `num_samples`) or both, depending on the value of `return_type`; |
| 70 | +#' * `upper_reconciled`: a list containing the pmf, the samples (matrix n_upper x `num_samples`) or both, depending on the value of `return_type`. |
| 71 | +#' |
| 72 | +#' @examples |
| 73 | +#' |
| 74 | +#' library(bayesRecon) |
| 75 | +#' |
| 76 | +#' # Consider a simple hierarchy with two bottom and one upper |
| 77 | +#' A <- matrix(c(1,1),nrow=1) |
| 78 | +#' S <- rbind(A,diag(nrow=2)) |
| 79 | +#' # The bottom forecasts are Poisson with lambda=15 |
| 80 | +#' lambda <- 15 |
| 81 | +#' n_tot <- 60 |
| 82 | +#' fc_bottom <- list() |
| 83 | +#' fc_bottom[[1]] <- apply(matrix(seq(0,n_tot)),MARGIN=1,FUN=function(x) dpois(x,lambda=lambda)) |
| 84 | +#' fc_bottom[[2]] <- apply(matrix(seq(0,n_tot)),MARGIN=1,FUN=function(x) dpois(x,lambda=lambda)) |
| 85 | +#' |
| 86 | +#' # The upper forecast is a Normal with mean 40 and std 5 |
| 87 | +#' fc_upper<- list(mu=40, Sigma=matrix(c(5^2))) |
| 88 | +#' |
| 89 | +#' # We can reconcile with reconc_TDcond |
| 90 | +#' res.mixCond <- reconc_MixCond(S, fc_bottom, fc_upper) |
| 91 | +#' |
| 92 | +#' # Note that the bottom distributions are slightly shifted to the right |
| 93 | +#' PMF.summary(res.mixCond$bottom_reconciled$pmf[[1]]) |
| 94 | +#' PMF.summary(fc_bottom[[1]]) |
| 95 | +#' |
| 96 | +#' PMF.summary(res.mixCond$bottom_reconciled$pmf[[2]]) |
| 97 | +#' PMF.summary(fc_bottom[[2]]) |
| 98 | +#' |
| 99 | +#' # The upper distribution is slightly shifted to the left |
| 100 | +#' PMF.summary(res.mixCond$upper_reconciled$pmf[[1]]) |
| 101 | +#' PMF.get_var(res.mixCond$upper_reconciled$pmf[[1]]) |
| 102 | +#' |
| 103 | +#' @references |
| 104 | +#' Corani, G., Azzimonti, D., Augusto, J.P.S.C., Zaffalon, M. (2021). *Probabilistic Reconciliation of Hierarchical Forecast via Bayes' Rule*. In: Hutter, F., Kersting, K., Lijffijt, J., Valera, I. (eds) Machine Learning and Knowledge Discovery in Databases. ECML PKDD 2020. Lecture Notes in Computer Science(), vol 12459. Springer, Cham. \doi{10.1007/978-3-030-67664-3_13}. |
| 105 | +#' |
| 106 | +#' Zambon, L., Agosto, A., Giudici, P., Corani, G. (2023). *Properties of the reconciled distributions for Gaussian and count forecasts*. \doi{10.48550/arXiv.2303.15135}. |
| 107 | +#' |
| 108 | +#' Zambon, L., Azzimonti, D., Rubattu, N., Corani, G. (2024). *Probabilistic reconciliation of mixed-type hierarchical time series* The 40th Conference on Uncertainty in Artificial Intelligence, accepted. |
| 109 | +#' |
| 110 | +#' @seealso [reconc_TDcond], [reconc_BUIS()] |
| 111 | +#' |
| 112 | +#' @export |
| 113 | +reconc_MixCond = function(S, fc_bottom, fc_upper, |
| 114 | + bottom_in_type = "pmf", distr = NULL, |
| 115 | + num_samples = 2e4, num_resample = 2e4, |
| 116 | + return_type = "pmf", |
| 117 | + ..., |
| 118 | + suppress_warnings = FALSE, seed = NULL,our_sampler=TRUE) { |
| 119 | + |
| 120 | + set.seed(seed) |
| 121 | + |
| 122 | + # Parameters for convolution |
| 123 | + # toll=1e-16 |
| 124 | + # Rtoll=1e-7 |
| 125 | + # smooth_bottom=TRUE |
| 126 | + # al_smooth=NULL |
| 127 | + # lap_smooth=FALSE |
| 128 | + |
| 129 | + # After testing the convolution parameters: |
| 130 | + # remove dots, remove comment above, and set the "best parameters" as default in |
| 131 | + # PMF.check_support and .TD_sampling |
| 132 | + |
| 133 | + # Check inputs |
| 134 | + .check_input_TD(S, fc_bottom, fc_upper, |
| 135 | + bottom_in_type, distr, |
| 136 | + return_type) |
| 137 | + |
| 138 | + # Get aggr. matrix A |
| 139 | + A = .get_A_from_S(S)$A |
| 140 | + n_u = nrow(A) |
| 141 | + n_b = ncol(A) |
| 142 | + |
| 143 | + # Prepare samples from the base bottom distribution |
| 144 | + if (bottom_in_type == "pmf") { |
| 145 | + B = lapply(fc_bottom, PMF.sample, N_samples=num_samples) |
| 146 | + B = do.call("cbind", B) # matrix of bottom samples (N_samples x n_bottom) |
| 147 | + } else if (bottom_in_type == "samples") { |
| 148 | + B = do.call("cbind", fc_bottom) |
| 149 | + } else if (bottom_in_type == "params") { |
| 150 | + L_pmf = lapply(fc_bottom, PMF.from_params, distr = distr) |
| 151 | + B = lapply(L_pmf, PMF.sample, N_samples=num_samples) |
| 152 | + B = do.call("cbind", B) # matrix of bottom samples (N_samples x n_bottom) |
| 153 | + } |
| 154 | + |
| 155 | + |
| 156 | + |
| 157 | + # Get mean and covariance matrix of the MVN upper base forecasts |
| 158 | + mu_u = fc_upper$mu |
| 159 | + Sigma_u = as.matrix(fc_upper$Sigma) |
| 160 | + |
| 161 | + # IS using MVN |
| 162 | + U = B %*% t(A) |
| 163 | + if(our_sampler){ |
| 164 | + weights = .MVN_density(x=U, mu = mu_u, Sigma = Sigma_u) |
| 165 | + }else{ |
| 166 | + weights = emdbook::dmvnorm(U, mu = mu_u, Sigma = Sigma_u) |
| 167 | + } |
| 168 | + |
| 169 | + |
| 170 | + check_weights.res = .check_weigths(weights) |
| 171 | + if (check_weights.res$warning & !suppress_warnings) { |
| 172 | + warning_msg = check_weights.res$warning_msg |
| 173 | + warning(warning_msg) |
| 174 | + } |
| 175 | + if(!(check_weights.res$warning & (1 %in% check_weights.res$warning_code))){ |
| 176 | + B = .resample(B, weights) |
| 177 | + } |
| 178 | + |
| 179 | + ESS = sum(weights)**2/sum(weights**2) |
| 180 | + |
| 181 | + B = .resample(B, weights, num_resample) |
| 182 | + B = t(B) |
| 183 | + U = A %*% B |
| 184 | + |
| 185 | + |
| 186 | + # Prepare output: include the marginal pmfs and/or the samples (depending on "return" inputs) |
| 187 | + out = list(bottom_reconciled=list(), upper_reconciled=list(), ESS = ESS) |
| 188 | + if (return_type %in% c('pmf', 'all')) { |
| 189 | + upper_pmf = lapply(1:n_u, function(i) PMF.from_samples(U[i,])) |
| 190 | + bottom_pmf = lapply(1:n_b, function(i) PMF.from_samples(B[i,])) |
| 191 | + |
| 192 | + out$bottom_reconciled$pmf = bottom_pmf |
| 193 | + out$upper_reconciled$pmf = upper_pmf |
| 194 | + |
| 195 | + } |
| 196 | + if (return_type %in% c('samples','all')) { |
| 197 | + |
| 198 | + out$bottom_reconciled$samples = B |
| 199 | + out$upper_reconciled$samples = U |
| 200 | + |
| 201 | + } |
| 202 | + |
| 203 | + return(out) |
| 204 | +} |
0 commit comments