|
a |
|
b/R/postestimation.R |
|
|
1 |
##============================================================================= |
|
|
2 |
## |
|
|
3 |
## Copyright (c) 2019 Marco Colombo |
|
|
4 |
## |
|
|
5 |
## Parts of the code are based on https://avehtari.github.io/bayes_R2/bayes_R2.html |
|
|
6 |
## Portions copyright (c) 2019 Aki Vehtari, Andrew Gelman, Ben Goodrich, Jonah Gabry |
|
|
7 |
## |
|
|
8 |
## Parts of the code are based on https://github.com/stan-dev/rstanarm |
|
|
9 |
## Portions copyright (c) 2015, 2016, 2017 Trustees of Columbia University |
|
|
10 |
## |
|
|
11 |
## This program is free software: you can redistribute it and/or modify |
|
|
12 |
## it under the terms of the GNU General Public License as published by |
|
|
13 |
## the Free Software Foundation, either version 3 of the License, or |
|
|
14 |
## (at your option) any later version. |
|
|
15 |
## |
|
|
16 |
## This program is distributed in the hope that it will be useful, |
|
|
17 |
## but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
|
18 |
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
|
19 |
## GNU General Public License for more details. |
|
|
20 |
## |
|
|
21 |
## You should have received a copy of the GNU General Public License |
|
|
22 |
## along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
|
23 |
## |
|
|
24 |
##============================================================================= |
|
|
25 |
|
|
|
26 |
|
|
|
27 |
#' Pointwise log-likelihood |
|
|
28 |
#' |
|
|
29 |
#' Compute the pointwise log-likelihood. |
|
|
30 |
#' |
|
|
31 |
#' @param object An object of class `hsstan`. |
|
|
32 |
#' @param newdata Optional data frame to use to evaluate the log-likelihood. |
|
|
33 |
#' If `NULL` (default), the model matrix is used. |
|
|
34 |
#' @param ... Currently ignored. |
|
|
35 |
#' |
|
|
36 |
#' @return |
|
|
37 |
#' A matrix of size `S` by `N`, where `S` is number of draws from the posterior |
|
|
38 |
#' distribution, and `N` is the number of data points. |
|
|
39 |
#' |
|
|
40 |
#' @examples |
|
|
41 |
#' \dontshow{utils::example("hsstan", echo=FALSE)} |
|
|
42 |
#' # continued from ?hsstan |
|
|
43 |
#' log_lik(hs.biom) |
|
|
44 |
#' |
|
|
45 |
#' @importFrom rstantools log_lik |
|
|
46 |
#' @importFrom stats rbinom rnorm |
|
|
47 |
#' @method log_lik hsstan |
|
|
48 |
#' @aliases log_lik |
|
|
49 |
#' @export log_lik |
|
|
50 |
#' @export |
|
|
51 |
log_lik.hsstan <- function(object, newdata=NULL, ...) { |
|
|
52 |
if (is.null(newdata)) |
|
|
53 |
newdata <- object$data |
|
|
54 |
y <- validate.outcome(newdata[[object$model.terms$outcome]]) |
|
|
55 |
mu <- posterior_linpred(object, newdata, transform=TRUE) |
|
|
56 |
if (!is.logistic(object)) |
|
|
57 |
sigma <- as.matrix(object$stanfit, pars="sigma") |
|
|
58 |
llkfun <- ifelse(is.logistic(object), |
|
|
59 |
function(x) stats::dbinom(y[x], 1, mu[, x], log=TRUE), |
|
|
60 |
function(x) stats::dnorm(y[x], mu[, x], sigma, log=TRUE)) |
|
|
61 |
llk <- cbind(sapply(1:ncol(mu), llkfun)) |
|
|
62 |
return(llk) |
|
|
63 |
} |
|
|
64 |
|
|
|
65 |
#' Posterior uncertainty intervals |
|
|
66 |
#' |
|
|
67 |
#' Compute posterior uncertainty intervals for `hsstan` objects. |
|
|
68 |
#' |
|
|
69 |
#' @param object An object of class `hsstan`. |
|
|
70 |
#' @param pars Names of parameters for which posterior intervals should be |
|
|
71 |
#' returned, which can be specified as regular expressions. If `NULL` |
|
|
72 |
#' (default) then this refers to the set of predictors used in the model. |
|
|
73 |
#' @param prob A value between 0 and 1 indicating the desired probability |
|
|
74 |
#' to be covered by the uncertainty intervals (0.95, by default). |
|
|
75 |
#' @param ... Currently ignored. |
|
|
76 |
#' |
|
|
77 |
#' @return |
|
|
78 |
#' A matrix with lower and upper interval bounds as columns and as many rows |
|
|
79 |
#' as selected parameters. |
|
|
80 |
#' |
|
|
81 |
#' @examples |
|
|
82 |
#' \dontshow{utils::example("hsstan", echo=FALSE)} |
|
|
83 |
#' # continued from ?hsstan |
|
|
84 |
#' posterior_interval(hs.biom) |
|
|
85 |
#' |
|
|
86 |
#' @importFrom rstantools posterior_interval |
|
|
87 |
#' @method posterior_interval hsstan |
|
|
88 |
#' @aliases posterior_interval |
|
|
89 |
#' @export posterior_interval |
|
|
90 |
#' @export |
|
|
91 |
posterior_interval.hsstan <- function(object, pars=NULL, prob=0.95, ...) { |
|
|
92 |
validate.samples(object) |
|
|
93 |
validate.probability(prob) |
|
|
94 |
pars <- get.pars(object, pars) |
|
|
95 |
post.matrix <- as.matrix(object$stanfit, pars=pars) |
|
|
96 |
rstantools::posterior_interval(post.matrix, prob=prob) |
|
|
97 |
} |
|
|
98 |
|
|
|
99 |
#' Posterior distribution of the linear predictor |
|
|
100 |
#' |
|
|
101 |
#' Extract the posterior draws of the linear predictor, possibly transformed |
|
|
102 |
#' by the inverse-link function. |
|
|
103 |
#' |
|
|
104 |
#' @param object An object of class `hsstan`. |
|
|
105 |
#' @param transform Whether the linear predictor should be transformed using |
|
|
106 |
#' the inverse-link function (`FALSE` by default). |
|
|
107 |
#' @param newdata Optional data frame containing the variables to use to |
|
|
108 |
#' predict. If `NULL` (default), the model matrix is used. If |
|
|
109 |
#' specified, its continuous variables should be standardized, since |
|
|
110 |
#' the model coefficients are learnt on standardized data. |
|
|
111 |
#' @param ... Currently ignored. |
|
|
112 |
#' |
|
|
113 |
#' @return |
|
|
114 |
#' A matrix of size `S` by `N`, where `S` is the number of draws from the |
|
|
115 |
#' posterior distribution of the (transformed) linear predictor, and `N` is |
|
|
116 |
#' the number of data points. |
|
|
117 |
#' |
|
|
118 |
#' @examples |
|
|
119 |
#' \dontshow{utils::example("hsstan", echo=FALSE)} |
|
|
120 |
#' # continued from ?hsstan |
|
|
121 |
#' posterior_linpred(hs.biom) |
|
|
122 |
#' |
|
|
123 |
#' @importFrom rstantools posterior_linpred |
|
|
124 |
#' @method posterior_linpred hsstan |
|
|
125 |
#' @aliases posterior_linpred |
|
|
126 |
#' @export posterior_linpred |
|
|
127 |
#' @export |
|
|
128 |
posterior_linpred.hsstan <- function(object, transform=FALSE, |
|
|
129 |
newdata=NULL, ...) { |
|
|
130 |
validate.samples(object) |
|
|
131 |
newdata <- validate.newdata(object, newdata) |
|
|
132 |
pars <- grep("^beta_", object$stanfit@model_pars, value=TRUE) |
|
|
133 |
post.matrix <- as.matrix(object$stanfit, pars=pars) |
|
|
134 |
linear.predictor <- multiplyABt(post.matrix, newdata) |
|
|
135 |
if (!transform) |
|
|
136 |
return(linear.predictor) |
|
|
137 |
return(object$family$linkinv(linear.predictor)) |
|
|
138 |
} |
|
|
139 |
|
|
|
140 |
#' Posterior predictive distribution |
|
|
141 |
#' |
|
|
142 |
#' Draw from the posterior predictive distribution of the outcome. |
|
|
143 |
#' |
|
|
144 |
#' @param object An object of class `hsstan`. |
|
|
145 |
#' @param newdata Optional data frame containing the variables to use to |
|
|
146 |
#' predict. If `NULL` (default), the model matrix is used. If |
|
|
147 |
#' specified, its continuous variables should be standardized, since |
|
|
148 |
#' the model coefficients are learnt on standardized data. |
|
|
149 |
#' @param nsamples A positive integer indicating the number of posterior samples |
|
|
150 |
#' to use. If `NULL` (default) all samples are used. |
|
|
151 |
#' @param seed Optional integer defining the seed for the pseudo-random number |
|
|
152 |
#' generator. |
|
|
153 |
#' @param ... Currently ignored. |
|
|
154 |
#' |
|
|
155 |
#' @return |
|
|
156 |
#' A matrix of size `S` by `N`, where `S` is the number of simulations from |
|
|
157 |
#' the posterior predictive distribution, and `N` is the number of data points. |
|
|
158 |
#' |
|
|
159 |
#' @examples |
|
|
160 |
#' \dontshow{utils::example("hsstan", echo=FALSE)} |
|
|
161 |
#' # continued from ?hsstan |
|
|
162 |
#' posterior_predict(hs.biom) |
|
|
163 |
#' |
|
|
164 |
#' @importFrom rstantools posterior_predict |
|
|
165 |
#' @importFrom stats rbinom rnorm |
|
|
166 |
#' @method posterior_predict hsstan |
|
|
167 |
#' @aliases posterior_predict |
|
|
168 |
#' @export posterior_predict |
|
|
169 |
#' @export |
|
|
170 |
posterior_predict.hsstan <- function(object, newdata=NULL, nsamples=NULL, |
|
|
171 |
seed=NULL, ...) { |
|
|
172 |
validate.samples(object) |
|
|
173 |
if (!is.null(nsamples)) |
|
|
174 |
validate.positive.scalar(nsamples, "nsamples", int=TRUE) |
|
|
175 |
|
|
|
176 |
## extract a random subset of the posterior samples |
|
|
177 |
if (!is.null(seed)) |
|
|
178 |
set.seed(seed) |
|
|
179 |
num.samples <- nsamples(object) |
|
|
180 |
samp <- sample(num.samples, min(num.samples, nsamples)) |
|
|
181 |
nsamples <- length(samp) |
|
|
182 |
|
|
|
183 |
## generate the posterior predictions |
|
|
184 |
mu <- posterior_linpred(object, newdata, transform=TRUE)[samp, , drop=FALSE] |
|
|
185 |
nobs <- ncol(mu) |
|
|
186 |
if (is.logistic(object)) |
|
|
187 |
pp <- t(sapply(1:nsamples, function(z) rbinom(nobs, 1, mu[z, ]))) |
|
|
188 |
else { |
|
|
189 |
sigma <- as.matrix(object$stanfit, pars="sigma")[samp, , drop=FALSE] |
|
|
190 |
pp <- t(sapply(1:nsamples, function(z) rnorm(nobs, mu[z, ], sigma[z]))) |
|
|
191 |
} |
|
|
192 |
return(pp) |
|
|
193 |
} |
|
|
194 |
|
|
|
195 |
#' Posterior measures of performance |
|
|
196 |
#' |
|
|
197 |
#' Compute the log-likelihood and a relevant measure of performance (R-squared |
|
|
198 |
#' or AUC) from the posterior samples. |
|
|
199 |
#' |
|
|
200 |
#' @param obj An object of class `hsstan` or `kfold`. |
|
|
201 |
#' @param prob Width of the posterior interval (0.95, by default). It is |
|
|
202 |
#' ignored if `summary=FALSE`. |
|
|
203 |
#' @param sub.idx Vector of indices of observations in the dataset to be used |
|
|
204 |
#' in computing the performance measures. If `NULL` (default), all |
|
|
205 |
#' observations in the dataset are used. |
|
|
206 |
#' @param summary Whether a summary of the distribution of the performance |
|
|
207 |
#' measure should be returned rather than the pointwise values |
|
|
208 |
#' (`TRUE` by default). |
|
|
209 |
#' @param cores Number of cores to use for parallelization (the value of |
|
|
210 |
#' `options("mc.cores")` by default). |
|
|
211 |
#' |
|
|
212 |
#' @return |
|
|
213 |
#' The mean, standard deviation and posterior interval of the performance |
|
|
214 |
#' measure (R-squared or AUC) if `summary=TRUE`, or a vector of values |
|
|
215 |
#' of the performance measure with length equal to the size of the posterior |
|
|
216 |
#' sample if `summary=FALSE`. Attribute `type` reports whether the performance |
|
|
217 |
#' measures are cross-validated or not. If `sub.idx` is not `NULL`, attribute |
|
|
218 |
#' `subset` reports the index of observations used in the computations. |
|
|
219 |
#' |
|
|
220 |
#' @examples |
|
|
221 |
#' \donttest{ |
|
|
222 |
#' \dontshow{utils::example("hsstan", echo=FALSE)} |
|
|
223 |
#' # continued from ?hsstan |
|
|
224 |
#' posterior_performance(hs.biom, cores=1) |
|
|
225 |
#' } |
|
|
226 |
#' |
|
|
227 |
#' @export |
|
|
228 |
posterior_performance <- function(obj, prob=0.95, sub.idx=NULL, summary=TRUE, |
|
|
229 |
cores=getOption("mc.cores", 1)) { |
|
|
230 |
if (inherits(obj, "hsstan")) { |
|
|
231 |
obj <- list(fits=array(list(fit=obj, test.idx=1:nrow(obj$data)), c(1, 2)), |
|
|
232 |
data=obj$data) |
|
|
233 |
colnames(obj$fits) <- c("fit", "test.idx") |
|
|
234 |
} else if (inherits(obj, c("kfold", "loo"))) { |
|
|
235 |
if (is.null(obj[["fits"]])) |
|
|
236 |
stop("No fitted models found, run 'kfold' with store.fits=TRUE.") |
|
|
237 |
} else |
|
|
238 |
stop("Not an 'hsstan' or 'kfold' object.") |
|
|
239 |
|
|
|
240 |
if (is.null(sub.idx)) { |
|
|
241 |
sub.idx <- 1:nrow(obj$data) |
|
|
242 |
used.subset <- FALSE |
|
|
243 |
} else { |
|
|
244 |
validate.indices(sub.idx, nrow(obj$data), "sub.idx") |
|
|
245 |
sub.idx <- sort(sub.idx) |
|
|
246 |
used.subset <- length(sub.idx) < nrow(obj$data) |
|
|
247 |
} |
|
|
248 |
|
|
|
249 |
validate.samples(obj$fits[[1]]) |
|
|
250 |
validate.probability(prob) |
|
|
251 |
logistic <- is.logistic(obj$fits[[1]]) |
|
|
252 |
num.folds <- nrow(obj$fits) |
|
|
253 |
|
|
|
254 |
## loop over the folds |
|
|
255 |
y <- mu <- llk <- NULL |
|
|
256 |
for (fold in 1:num.folds) { |
|
|
257 |
hs <- obj$fits[[fold]] |
|
|
258 |
test.idx <- intersect(obj$fits[, "test.idx"][[fold]], sub.idx) |
|
|
259 |
if (length(test.idx) == 0) |
|
|
260 |
next |
|
|
261 |
newdata <- obj$data[test.idx, ] |
|
|
262 |
y <- c(y, obj$data[test.idx, hs$model.terms$outcome]) |
|
|
263 |
mu <- cbind(mu, posterior_linpred(hs, newdata=newdata, transform=TRUE)) |
|
|
264 |
llk <- cbind(llk, log_lik(hs, newdata=newdata)) |
|
|
265 |
} |
|
|
266 |
|
|
|
267 |
if (used.subset && logistic && length(unique(y)) != 2) |
|
|
268 |
stop("'sub.idx' must contain both outcome classes.") |
|
|
269 |
|
|
|
270 |
if (logistic) { |
|
|
271 |
par.auc <- function(i) |
|
|
272 |
as.numeric(pROC::roc(y, mu[i, ], direction="<", quiet=TRUE)$auc) |
|
|
273 |
if (.Platform$OS.type != "windows") { |
|
|
274 |
out <- parallel::mclapply(X=1:nrow(mu), mc.cores=cores, |
|
|
275 |
mc.preschedule=TRUE, FUN=par.auc) |
|
|
276 |
} else { # windows |
|
|
277 |
cl <- parallel::makePSOCKcluster(cores) |
|
|
278 |
on.exit(parallel::stopCluster(cl)) |
|
|
279 |
out <- parallel::parLapply(X=1:nrow(mu), cl=cl, fun=par.auc) |
|
|
280 |
} |
|
|
281 |
} else { |
|
|
282 |
out <- pmax(fastCor(y, mu), 0)^2 |
|
|
283 |
} |
|
|
284 |
|
|
|
285 |
out <- cbind(perf=unlist(out), llk=rowSums(llk)) |
|
|
286 |
colnames(out)[1] <- ifelse(logistic, "auc", "r2") |
|
|
287 |
if (summary) |
|
|
288 |
out <- posterior_summary(out, prob) |
|
|
289 |
attr(out, "type") <- paste0(if(num.folds == 1) "non ", "cross-validated") |
|
|
290 |
if (used.subset) |
|
|
291 |
attr(out, "subset") <- sub.idx |
|
|
292 |
return(out) |
|
|
293 |
} |
|
|
294 |
|
|
|
295 |
#' Predictive information criteria for Bayesian models |
|
|
296 |
#' |
|
|
297 |
#' Compute an efficient approximate leave-one-out cross-validation |
|
|
298 |
#' using Pareto smoothed importance sampling (PSIS-LOO), or the widely |
|
|
299 |
#' applicable information criterion (WAIC), also known as the Watanabe-Akaike |
|
|
300 |
#' information criterion. |
|
|
301 |
#' |
|
|
302 |
#' @param x An object of class `hsstan`. |
|
|
303 |
#' @param cores Number of cores used for parallelisation (the value of |
|
|
304 |
#' `options("mc.cores")` by default). |
|
|
305 |
#' @param ... Currently ignored. |
|
|
306 |
#' |
|
|
307 |
#' @return |
|
|
308 |
#' A `loo` object. |
|
|
309 |
#' |
|
|
310 |
#' @examples |
|
|
311 |
#' \dontshow{utils::example("hsstan", echo=FALSE)} |
|
|
312 |
#' \dontshow{oldopts <- options(mc.cores=2)} |
|
|
313 |
#' # continued from ?hsstan |
|
|
314 |
#' loo(hs.biom) |
|
|
315 |
#' waic(hs.biom) |
|
|
316 |
#' \dontshow{options(oldopts)} |
|
|
317 |
#' |
|
|
318 |
#' @importFrom loo loo |
|
|
319 |
#' @method loo hsstan |
|
|
320 |
#' @aliases loo |
|
|
321 |
#' @export loo |
|
|
322 |
#' @export |
|
|
323 |
loo.hsstan <- function(x, cores=getOption("mc.cores"), ...) { |
|
|
324 |
validate.samples(x) |
|
|
325 |
llk <- log_lik(x) |
|
|
326 |
chain.id <- rep(1:ncol(x$stanfit), each=nrow(x$stanfit)) |
|
|
327 |
r.eff <- loo::relative_eff(exp(llk), chain_id=chain.id, cores=cores) |
|
|
328 |
loo <- suppressWarnings(loo::loo(llk, r_eff=r.eff, cores=cores)) |
|
|
329 |
return(loo) |
|
|
330 |
} |
|
|
331 |
|
|
|
332 |
#' @rdname loo.hsstan |
|
|
333 |
#' @importFrom loo waic |
|
|
334 |
#' @method waic hsstan |
|
|
335 |
#' @aliases waic |
|
|
336 |
#' @export waic |
|
|
337 |
#' @export |
|
|
338 |
waic.hsstan <- function(x, cores=getOption("mc.cores"), ...) { |
|
|
339 |
validate.samples(x) |
|
|
340 |
llk <- log_lik(x) |
|
|
341 |
waic <- suppressWarnings(loo::waic(llk, cores=cores)) |
|
|
342 |
return(waic) |
|
|
343 |
} |
|
|
344 |
|
|
|
345 |
#' Bayesian and LOO-adjusted R-squared |
|
|
346 |
#' |
|
|
347 |
#' Compute the Bayesian and the LOO-adjusted R-squared from the posterior |
|
|
348 |
#' samples. For Bayesian R-squared it uses the modelled residual variance |
|
|
349 |
#' (rather than the variance of the posterior distribution of the residuals). |
|
|
350 |
#' The LOO-adjusted R-squared uses Pareto smoothed importance sampling LOO |
|
|
351 |
#' residuals and Bayesian bootstrap. |
|
|
352 |
#' |
|
|
353 |
#' @param object An object of class `hsstan`. |
|
|
354 |
#' @param prob Width of the posterior interval (0.95, by default). It is |
|
|
355 |
#' ignored if `summary=FALSE`. |
|
|
356 |
#' @param summary Whether a summary of the distribution of the R-squared |
|
|
357 |
#' should be returned rather than the pointwise values (`TRUE` by |
|
|
358 |
#' default). |
|
|
359 |
#' @param ... Currently ignored. |
|
|
360 |
#' |
|
|
361 |
#' @return |
|
|
362 |
#' The mean, standard deviation and posterior interval of R-squared if |
|
|
363 |
#' `summary=TRUE`, or a vector of R-squared values with length equal to |
|
|
364 |
#' the size of the posterior sample if `summary=FALSE`. |
|
|
365 |
#' |
|
|
366 |
#' @references |
|
|
367 |
#' Andrew Gelman, Ben Goodrich, Jonah Gabry and Aki Vehtari (2019), |
|
|
368 |
#' R-squared for Bayesian regression models, |
|
|
369 |
#' _The American Statistician_, 73 (3), 307-309. |
|
|
370 |
#' \doi{10.1080/00031305.2018.1549100} |
|
|
371 |
#' |
|
|
372 |
#' Aki Vehtari, Andrew Gelman, Ben Goodrich and Jonah Gabry (2019), |
|
|
373 |
#' Bayesian R2 and LOO-R2. |
|
|
374 |
#' \url{https://avehtari.github.io/bayes_R2/bayes_R2.html} |
|
|
375 |
#' |
|
|
376 |
#' @examples |
|
|
377 |
#' \dontshow{utils::example("hsstan", echo=FALSE)} |
|
|
378 |
#' \dontshow{oldopts <- options(mc.cores=2)} |
|
|
379 |
#' # continued from ?hsstan |
|
|
380 |
#' bayes_R2(hs.biom) |
|
|
381 |
#' loo_R2(hs.biom) |
|
|
382 |
#' \dontshow{options(oldopts)} |
|
|
383 |
#' |
|
|
384 |
#' @importFrom rstantools bayes_R2 |
|
|
385 |
#' @method bayes_R2 hsstan |
|
|
386 |
#' @aliases bayes_R2 |
|
|
387 |
#' @export bayes_R2 |
|
|
388 |
#' @export |
|
|
389 |
bayes_R2.hsstan <- function(object, prob=0.95, summary=TRUE, ...) { |
|
|
390 |
validate.samples(object) |
|
|
391 |
validate.probability(prob) |
|
|
392 |
mu <- posterior_linpred(object, transform=TRUE) |
|
|
393 |
var.mu <- apply(mu, 1, stats::var) |
|
|
394 |
if (is.logistic(object)) |
|
|
395 |
sigma2 <- rowMeans(mu * (1 - mu)) |
|
|
396 |
else |
|
|
397 |
sigma2 <- drop(as.matrix(object$stanfit, pars="sigma"))^2 |
|
|
398 |
R2 <- var.mu / (var.mu + sigma2) |
|
|
399 |
if (summary) |
|
|
400 |
R2 <- vector.summary(R2, prob) |
|
|
401 |
return(R2) |
|
|
402 |
} |
|
|
403 |
|
|
|
404 |
#' @rdname bayes_R2.hsstan |
|
|
405 |
#' @importFrom rstantools loo_R2 |
|
|
406 |
#' @method loo_R2 hsstan |
|
|
407 |
#' @aliases loo_R2 |
|
|
408 |
#' @export loo_R2 |
|
|
409 |
#' @export |
|
|
410 |
loo_R2.hsstan <- function(object, prob=0.95, summary=TRUE, ...) { |
|
|
411 |
validate.samples(object) |
|
|
412 |
validate.probability(prob) |
|
|
413 |
y <- object$data[[object$model.terms$outcome]] |
|
|
414 |
mu <- posterior_linpred(object, transform=TRUE) |
|
|
415 |
ll <- log_lik(object) |
|
|
416 |
S <- nrow(mu) |
|
|
417 |
N <- ncol(mu) |
|
|
418 |
chains <- object$stanfit@sim$chains |
|
|
419 |
r.eff <- loo::relative_eff(exp(ll), chain_id=rep(1:chains, each=S / chains)) |
|
|
420 |
psis <- suppressWarnings(loo::psis(-ll, r_eff=r.eff)) |
|
|
421 |
mu.loo <- loo::E_loo(mu, psis, log_ratios=-ll)$value |
|
|
422 |
err.loo <- mu.loo - y |
|
|
423 |
|
|
|
424 |
## set the random seed as the seed used in the first chain and ensure |
|
|
425 |
## the old RNG state is restored on exit |
|
|
426 |
rng.state.old <- .Random.seed |
|
|
427 |
on.exit(assign(".Random.seed", rng.state.old, envir=.GlobalEnv)) |
|
|
428 |
set.seed(object$stanfit@stan_args[[1]]$seed) |
|
|
429 |
|
|
|
430 |
## dirichlet weights for bayesian bootstrap |
|
|
431 |
exp.draws <- matrix(stats::rexp(S * N, rate=1), nrow=S, ncol=N) |
|
|
432 |
dw <- exp.draws / rowSums(exp.draws) |
|
|
433 |
|
|
|
434 |
var.y <- (rowSums(sweep(dw, 2, y^2, FUN = "*")) - |
|
|
435 |
rowSums(sweep(dw, 2, y, FUN = "*"))^2) * (N / (N - 1)) |
|
|
436 |
var.err.loo <- (rowSums(sweep(dw, 2, err.loo^2, FUN = "*")) - |
|
|
437 |
rowSums(sweep(dw, 2, err.loo, FUN = "*")^2)) * (N / (N - 1)) |
|
|
438 |
|
|
|
439 |
R2 <- 1 - var.err.loo / var.y |
|
|
440 |
R2[R2 < -1] <- -1 |
|
|
441 |
R2[R2 > 1] <- 1 |
|
|
442 |
|
|
|
443 |
if (summary) |
|
|
444 |
R2 <- vector.summary(R2, prob) |
|
|
445 |
return(R2) |
|
|
446 |
} |