Skip to content

Commit af046dc

Browse files
committed
Added reconc_mixCond function, MVN_density function, tests, documentation and examples for reconc_mixCond.
1 parent 2ded7c7 commit af046dc

8 files changed

+449
-2
lines changed

NAMESPACE

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export(PMF.summary)
88
export(get_reconc_matrices)
99
export(reconc_BUIS)
1010
export(reconc_MCMC)
11+
export(reconc_MixCond)
1112
export(reconc_TDcond)
1213
export(reconc_gaussian)
1314
export(schaferStrimmer_cov)

R/reconc_MixCond.R

+204
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
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+
}

R/reconc_TDcond.R

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@
168168
#'
169169
#' 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.
170170
#'
171-
#' @seealso [reconc_BUIS()]
171+
#' @seealso [reconc_MixCond], [reconc_BUIS()]
172172
#'
173173
#' @export
174174
reconc_TDcond = function(S, fc_bottom, fc_upper,

R/utils.R

+26
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,32 @@
276276
return(samples)
277277
}
278278

279+
# Compute the MVN density
280+
.MVN_density <- function(x, mu, Sigma) {
281+
n = length(mu)
282+
if (any(dim(Sigma) != c(n,n))) {
283+
stop("Dimension of mu and Sigma are not compatible!")
284+
}
285+
.check_cov(Sigma, "Sigma")
286+
287+
if(is.vector(x)){
288+
x <- matrix(x, ncol=length(x))
289+
}
290+
291+
if (is.matrix(x)) {
292+
mu <- matrix(rep(mu, nrow(x)), ncol = n, byrow = TRUE)
293+
}
294+
295+
chol_S <- tryCatch(base::chol(Sigma), error = function(e) e)
296+
tmp <- backsolve(chol_S, t(x - mu), transpose = TRUE)
297+
rss <- colSums(tmp^2)
298+
299+
logval <- -sum(log(diag(chol_S))) - 0.5 * n * log(2 *pi) - 0.5 * rss
300+
301+
return(exp(logval))
302+
}
303+
304+
279305
################################################################################
280306
# Miscellaneous
281307

man/reconc_MixCond.Rd

+135
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/reconc_TDcond.Rd

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)