Nothing
#' Draws from the Posterior Predictive Distribution
#'
#' Compute posterior draws of the posterior predictive distribution. Can be
#' performed for the data used to fit the model (posterior predictive checks) or
#' for new data. By definition, these draws have higher variance than draws
#' of the expected value of the posterior predictive distribution computed by
#' \code{\link{posterior_epred.brmsfit}}. This is because the residual error
#' is incorporated in \code{posterior_predict}. However, the estimated means of
#' both methods averaged across draws should be very similar.
#'
#' @inheritParams prepare_predictions
#' @param object An object of class \code{brmsfit}.
#' @param re.form Alias of \code{re_formula}.
#' @param transform (Deprecated) A function or a character string naming
#' a function to be applied on the predicted responses
#' before summary statistics are computed.
#' @param negative_rt Only relevant for Wiener diffusion models.
#' A flag indicating whether response times of responses
#' on the lower boundary should be returned as negative values.
#' This allows to distinguish responses on the upper and
#' lower boundary. Defaults to \code{FALSE}.
#' @param sort Logical. Only relevant for time series models.
#' Indicating whether to return predicted values in the original
#' order (\code{FALSE}; default) or in the order of the
#' time series (\code{TRUE}).
#' @param ntrys Parameter used in rejection sampling
#' for truncated discrete models only
#' (defaults to \code{5}). See Details for more information.
#' @param cores Number of cores (defaults to \code{1}). On non-Windows systems,
#' this argument can be set globally via the \code{mc.cores} option.
#' @param ... Further arguments passed to \code{\link{prepare_predictions}}
#' that control several aspects of data validation and prediction.
#'
#' @return An \code{array} of draws. In univariate models,
#' the output is as an S x N matrix, where S is the number of posterior
#' draws and N is the number of observations. In multivariate models, an
#' additional dimension is added to the output which indexes along the
#' different response variables.
#'
#' @template details-newdata-na
#' @template details-allow_new_levels
#' @details For truncated discrete models only: In the absence of any general
#' algorithm to sample from truncated discrete distributions, rejection
#' sampling is applied in this special case. This means that values are
#' sampled until a value lies within the defined truncation boundaries. In
#' practice, this procedure may be rather slow (especially in \R). Thus, we
#' try to do approximate rejection sampling by sampling each value
#' \code{ntrys} times and then select a valid value. If all values are
#' invalid, the closest boundary is used, instead. If there are more than a
#' few of these pathological cases, a warning will occur suggesting to
#' increase argument \code{ntrys}.
#'
#' @examples
#' \dontrun{
#' ## fit a model
#' fit <- brm(time | cens(censored) ~ age + sex + (1 + age || patient),
#' data = kidney, family = "exponential", init = "0")
#'
#' ## predicted responses
#' pp <- posterior_predict(fit)
#' str(pp)
#'
#' ## predicted responses excluding the group-level effect of age
#' pp <- posterior_predict(fit, re_formula = ~ (1 | patient))
#' str(pp)
#'
#' ## predicted responses of patient 1 for new data
#' newdata <- data.frame(
#' sex = factor(c("male", "female")),
#' age = c(20, 50),
#' patient = c(1, 1)
#' )
#' pp <- posterior_predict(fit, newdata = newdata)
#' str(pp)
#' }
#'
#' @aliases posterior_predict
#' @method posterior_predict brmsfit
#' @importFrom rstantools posterior_predict
#' @export
#' @export posterior_predict
posterior_predict.brmsfit <- function(
object, newdata = NULL, re_formula = NULL, re.form = NULL,
transform = NULL, resp = NULL, negative_rt = FALSE,
ndraws = NULL, draw_ids = NULL, sort = FALSE, ntrys = 5,
cores = NULL, ...
) {
cl <- match.call()
if ("re.form" %in% names(cl) && !missing(re.form)) {
re_formula <- re.form
}
contains_draws(object)
object <- restructure(object)
prep <- prepare_predictions(
object, newdata = newdata, re_formula = re_formula, resp = resp,
ndraws = ndraws, draw_ids = draw_ids, check_response = FALSE, ...
)
posterior_predict(
prep, transform = transform, sort = sort, ntrys = ntrys,
negative_rt = negative_rt, cores = cores, summary = FALSE
)
}
#' @export
posterior_predict.mvbrmsprep <- function(object, ...) {
if (length(object$mvpars$rescor)) {
object$mvpars$Mu <- get_Mu(object)
object$mvpars$Sigma <- get_Sigma(object)
out <- posterior_predict.brmsprep(object, ...)
} else {
out <- lapply(object$resps, posterior_predict, ...)
along <- ifelse(length(out) > 1L, 3, 2)
out <- do_call(abind, c(out, along = along))
}
out
}
#' @export
posterior_predict.brmsprep <- function(object, transform = NULL, sort = FALSE,
summary = FALSE, robust = FALSE,
probs = c(0.025, 0.975),
cores = NULL, ...) {
summary <- as_one_logical(summary)
cores <- validate_cores_post_processing(cores)
if (is.customfamily(object$family)) {
# ensure that the method can be found during parallel execution
object$family$posterior_predict <-
custom_family_method(object$family, "posterior_predict")
}
for (nlp in names(object$nlpars)) {
object$nlpars[[nlp]] <- get_nlpar(object, nlpar = nlp)
}
for (dp in names(object$dpars)) {
object$dpars[[dp]] <- get_dpar(object, dpar = dp)
}
pp_fun <- paste0("posterior_predict_", object$family$fun)
pp_fun <- get(pp_fun, asNamespace("brms"))
N <- choose_N(object)
out <- plapply(seq_len(N), pp_fun, cores = cores, prep = object, ...)
if (grepl("_mv$", object$family$fun)) {
out <- do_call(abind, c(out, along = 3))
out <- aperm(out, perm = c(1, 3, 2))
dimnames(out)[[3]] <- names(object$resps)
} else if (has_multicol(object$family)) {
out <- do_call(abind, c(out, along = 3))
out <- aperm(out, perm = c(1, 3, 2))
dimnames(out)[[3]] <- object$cats
} else {
out <- do_call(cbind, out)
}
colnames(out) <- rownames(out) <- NULL
if (use_int(object$family)) {
out <- check_discrete_trunc_bounds(
out, lb = object$data$lb, ub = object$data$ub
)
}
out <- reorder_obs(out, object$old_order, sort = sort)
# transform predicted response draws before summarizing them
if (!is.null(transform)) {
# deprecated as of brms 2.12.3
warning2("Argument 'transform' is deprecated ",
"and will be removed in the future.")
out <- do_call(transform, list(out))
}
attr(out, "levels") <- object$cats
if (summary) {
# only for compatibility with the 'predict' method
if (is_ordinal(object$family)) {
levels <- seq_len(max(object$data$nthres) + 1)
out <- posterior_table(out, levels = levels)
} else if (is_categorical(object$family)) {
levels <- seq_len(object$data$ncat)
out <- posterior_table(out, levels = levels)
} else {
out <- posterior_summary(out, probs = probs, robust = robust)
}
}
out
}
#' Draws from the Posterior Predictive Distribution
#'
#' This method is an alias of \code{\link{posterior_predict.brmsfit}}
#' with additional arguments for obtaining summaries of the computed draws.
#'
#' @inheritParams posterior_predict.brmsfit
#' @param summary Should summary statistics be returned
#' instead of the raw values? Default is \code{TRUE}.
#' @param robust If \code{FALSE} (the default) the mean is used as
#' the measure of central tendency and the standard deviation as
#' the measure of variability. If \code{TRUE}, the median and the
#' median absolute deviation (MAD) are applied instead.
#' Only used if \code{summary} is \code{TRUE}.
#' @param probs The percentiles to be computed by the \code{quantile}
#' function. Only used if \code{summary} is \code{TRUE}.
#'
#' @return An \code{array} of predicted response values.
#' If \code{summary = FALSE} the output resembles those of
#' \code{\link{posterior_predict.brmsfit}}.
#'
#' If \code{summary = TRUE} the output depends on the family: For categorical
#' and ordinal families, the output is an N x C matrix, where N is the number
#' of observations, C is the number of categories, and the values are
#' predicted category probabilities. For all other families, the output is a N
#' x E matrix where E = \code{2 + length(probs)} is the number of summary
#' statistics: The \code{Estimate} column contains point estimates (either
#' mean or median depending on argument \code{robust}), while the
#' \code{Est.Error} column contains uncertainty estimates (either standard
#' deviation or median absolute deviation depending on argument
#' \code{robust}). The remaining columns starting with \code{Q} contain
#' quantile estimates as specified via argument \code{probs}.
#'
#' @seealso \code{\link{posterior_predict.brmsfit}}
#'
#' @examples
#' \dontrun{
#' ## fit a model
#' fit <- brm(time | cens(censored) ~ age + sex + (1 + age || patient),
#' data = kidney, family = "exponential", init = "0")
#'
#' ## predicted responses
#' pp <- predict(fit)
#' head(pp)
#'
#' ## predicted responses excluding the group-level effect of age
#' pp <- predict(fit, re_formula = ~ (1 | patient))
#' head(pp)
#'
#' ## predicted responses of patient 1 for new data
#' newdata <- data.frame(
#' sex = factor(c("male", "female")),
#' age = c(20, 50),
#' patient = c(1, 1)
#' )
#' predict(fit, newdata = newdata)
#' }
#'
#' @export
predict.brmsfit <- function(object, newdata = NULL, re_formula = NULL,
transform = NULL, resp = NULL,
negative_rt = FALSE, ndraws = NULL, draw_ids = NULL,
sort = FALSE, ntrys = 5, cores = NULL, summary = TRUE,
robust = FALSE, probs = c(0.025, 0.975), ...) {
contains_draws(object)
object <- restructure(object)
prep <- prepare_predictions(
object, newdata = newdata, re_formula = re_formula, resp = resp,
ndraws = ndraws, draw_ids = draw_ids, check_response = FALSE, ...
)
posterior_predict(
prep, transform = transform, ntrys = ntrys, negative_rt = negative_rt,
sort = sort, cores = cores, summary = summary, robust = robust,
probs = probs
)
}
#' Predictive Intervals
#'
#' Compute intervals from the posterior predictive distribution.
#'
#' @aliases predictive_interval
#'
#' @param object An \R object of class \code{brmsfit}.
#' @param prob A number p (0 < p < 1) indicating the desired probability mass to
#' include in the intervals. Defaults to \code{0.9}.
#' @param ... Further arguments passed to \code{\link{posterior_predict}}.
#'
#' @return A matrix with 2 columns for the lower and upper bounds of the
#' intervals, respectively, and as many rows as observations being predicted.
#'
#' @examples
#' \dontrun{
#' fit <- brm(count ~ zBase, data = epilepsy, family = poisson())
#' predictive_interval(fit)
#' }
#'
#' @importFrom rstantools predictive_interval
#' @export predictive_interval
#' @export
predictive_interval.brmsfit <- function(object, prob = 0.9, ...) {
out <- posterior_predict(object, ...)
predictive_interval(out, prob = prob)
}
# validate method name to obtain posterior predictions
# @param method name of the method
# @return validated name of the method
validate_pp_method <- function(method) {
method <- as_one_character(method)
if (method %in% c("posterior_predict", "predict", "pp")) {
method <- "posterior_predict"
} else if (method %in% c("posterior_epred", "fitted", "pp_expect")) {
method <- "posterior_epred"
} else if (method %in% c("posterior_linpred")) {
method <- "posterior_linpred"
} else if (method %in% c("predictive_error", "residuals")) {
method <- "predictive_error"
} else {
stop2("Posterior predictive method '", method, "' it not supported.")
}
method
}
# ------------------- family specific posterior_predict methods ---------------------
# All posterior_predict_<family> functions have the same arguments structure
# @param i index of the observatio for which to compute pp values
# @param prep A named list returned by prepare_predictions containing
# all required data and posterior draws
# @param ... ignored arguments
# @param A vector of length prep$ndraws containing draws
# from the posterior predictive distribution
posterior_predict_gaussian <- function(i, prep, ntrys = 5, ...) {
mu <- get_dpar(prep, "mu", i = i)
sigma <- get_dpar(prep, "sigma", i = i)
sigma <- add_sigma_se(sigma, prep, i = i)
rcontinuous(
n = prep$ndraws, dist = "norm",
mean = mu, sd = sigma,
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_student <- function(i, prep, ntrys = 5, ...) {
nu <- get_dpar(prep, "nu", i = i)
mu <- get_dpar(prep, "mu", i = i)
sigma <- get_dpar(prep, "sigma", i = i)
sigma <- add_sigma_se(sigma, prep, i = i)
rcontinuous(
n = prep$ndraws, dist = "student_t",
df = nu, mu = mu, sigma = sigma,
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_lognormal <- function(i, prep, ntrys = 5, ...) {
rcontinuous(
n = prep$ndraws, dist = "lnorm",
meanlog = get_dpar(prep, "mu", i = i),
sdlog = get_dpar(prep, "sigma", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_shifted_lognormal <- function(i, prep, ntrys = 5, ...) {
rcontinuous(
n = prep$ndraws, dist = "shifted_lnorm",
meanlog = get_dpar(prep, "mu", i = i),
sdlog = get_dpar(prep, "sigma", i = i),
shift = get_dpar(prep, "ndt", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_skew_normal <- function(i, prep, ntrys = 5, ...) {
mu <- get_dpar(prep, "mu", i = i)
sigma <- get_dpar(prep, "sigma", i = i)
sigma <- add_sigma_se(sigma, prep, i = i)
alpha <- get_dpar(prep, "alpha", i = i)
rcontinuous(
n = prep$ndraws, dist = "skew_normal",
mu = mu, sigma = sigma, alpha = alpha,
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_gaussian_mv <- function(i, prep, ...) {
Mu <- get_Mu(prep, i = i)
Sigma <- get_Sigma(prep, i = i)
.predict <- function(s) {
rmulti_normal(1, mu = Mu[s, ], Sigma = Sigma[s, , ])
}
rblapply(seq_len(prep$ndraws), .predict)
}
posterior_predict_student_mv <- function(i, prep, ...) {
nu <- get_dpar(prep, "nu", i = i)
Mu <- get_Mu(prep, i = i)
Sigma <- get_Sigma(prep, i = i)
.predict <- function(s) {
rmulti_student_t(1, df = nu[s], mu = Mu[s, ], Sigma = Sigma[s, , ])
}
rblapply(seq_len(prep$ndraws), .predict)
}
posterior_predict_gaussian_time <- function(i, prep, ...) {
obs <- with(prep$ac, begin_tg[i]:end_tg[i])
Jtime <- prep$ac$Jtime_tg[i, ]
mu <- as.matrix(get_dpar(prep, "mu", i = obs))
Sigma <- get_cov_matrix_ac(prep, obs, Jtime = Jtime)
.predict <- function(s) {
rmulti_normal(1, mu = mu[s, ], Sigma = Sigma[s, , ])
}
rblapply(seq_len(prep$ndraws), .predict)
}
posterior_predict_student_time <- function(i, prep, ...) {
obs <- with(prep$ac, begin_tg[i]:end_tg[i])
Jtime <- prep$ac$Jtime_tg[i, ]
nu <- as.matrix(get_dpar(prep, "nu", i = obs))
mu <- as.matrix(get_dpar(prep, "mu", i = obs))
Sigma <- get_cov_matrix_ac(prep, obs, Jtime = Jtime)
.predict <- function(s) {
rmulti_student_t(1, df = nu[s, ], mu = mu[s, ], Sigma = Sigma[s, , ])
}
rblapply(seq_len(prep$ndraws), .predict)
}
posterior_predict_gaussian_lagsar <- function(i, prep, ...) {
stopifnot(i == 1)
.predict <- function(s) {
M_new <- with(prep, diag(nobs) - ac$lagsar[s] * ac$Msar)
mu <- as.numeric(solve(M_new) %*% mu[s, ])
Sigma <- solve(crossprod(M_new)) * sigma[s]^2
rmulti_normal(1, mu = mu, Sigma = Sigma)
}
mu <- get_dpar(prep, "mu")
sigma <- get_dpar(prep, "sigma")
rblapply(seq_len(prep$ndraws), .predict)
}
posterior_predict_student_lagsar <- function(i, prep, ...) {
stopifnot(i == 1)
.predict <- function(s) {
M_new <- with(prep, diag(nobs) - ac$lagsar[s] * ac$Msar)
mu <- as.numeric(solve(M_new) %*% mu[s, ])
Sigma <- solve(crossprod(M_new)) * sigma[s]^2
rmulti_student_t(1, df = nu[s], mu = mu, Sigma = Sigma)
}
mu <- get_dpar(prep, "mu")
sigma <- get_dpar(prep, "sigma")
nu <- get_dpar(prep, "nu")
rblapply(seq_len(prep$ndraws), .predict)
}
posterior_predict_gaussian_errorsar <- function(i, prep, ...) {
stopifnot(i == 1)
.predict <- function(s) {
M_new <- with(prep, diag(nobs) - ac$errorsar[s] * ac$Msar)
Sigma <- solve(crossprod(M_new)) * sigma[s]^2
rmulti_normal(1, mu = mu[s, ], Sigma = Sigma)
}
mu <- get_dpar(prep, "mu")
sigma <- get_dpar(prep, "sigma")
rblapply(seq_len(prep$ndraws), .predict)
}
posterior_predict_student_errorsar <- function(i, prep, ...) {
stopifnot(i == 1)
.predict <- function(s) {
M_new <- with(prep, diag(nobs) - ac$errorsar[s] * ac$Msar)
Sigma <- solve(crossprod(M_new)) * sigma[s]^2
rmulti_student_t(1, df = nu[s], mu = mu[s, ], Sigma = Sigma)
}
mu <- get_dpar(prep, "mu")
sigma <- get_dpar(prep, "sigma")
nu <- get_dpar(prep, "nu")
rblapply(seq_len(prep$ndraws), .predict)
}
posterior_predict_gaussian_fcor <- function(i, prep, ...) {
stopifnot(i == 1)
mu <- as.matrix(get_dpar(prep, "mu"))
Sigma <- get_cov_matrix_ac(prep)
.predict <- function(s) {
rmulti_normal(1, mu = mu[s, ], Sigma = Sigma[s, , ])
}
rblapply(seq_len(prep$ndraws), .predict)
}
posterior_predict_student_fcor <- function(i, prep, ...) {
stopifnot(i == 1)
nu <- as.matrix(get_dpar(prep, "nu"))
mu <- as.matrix(get_dpar(prep, "mu"))
Sigma <- get_cov_matrix_ac(prep)
.predict <- function(s) {
rmulti_student_t(1, df = nu[s, ], mu = mu[s, ], Sigma = Sigma[s, , ])
}
rblapply(seq_len(prep$ndraws), .predict)
}
posterior_predict_binomial <- function(i, prep, ntrys = 5, ...) {
rdiscrete(
n = prep$ndraws, dist = "binom",
size = prep$data$trials[i],
prob = get_dpar(prep, "mu", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_beta_binomial <- function(i, prep, ntrys = 5, ...) {
rdiscrete(
n = prep$ndraws, dist = "beta_binomial",
size = prep$data$trials[i],
mu = get_dpar(prep, "mu", i = i),
phi = get_dpar(prep, "phi", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_bernoulli <- function(i, prep, ...) {
mu <- get_dpar(prep, "mu", i = i)
rbinom(length(mu), size = 1, prob = mu)
}
posterior_predict_poisson <- function(i, prep, ntrys = 5, ...) {
mu <- get_dpar(prep, "mu", i)
mu <- multiply_dpar_rate_denom(mu, prep, i = i)
rdiscrete(
n = prep$ndraws, dist = "pois", lambda = mu,
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_negbinomial <- function(i, prep, ntrys = 5, ...) {
mu <- get_dpar(prep, "mu", i)
mu <- multiply_dpar_rate_denom(mu, prep, i = i)
shape <- get_dpar(prep, "shape", i)
shape <- multiply_dpar_rate_denom(shape, prep, i = i)
rdiscrete(
n = prep$ndraws, dist = "nbinom",
mu = mu, size = shape,
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_negbinomial2 <- function(i, prep, ntrys = 5, ...) {
mu <- get_dpar(prep, "mu", i)
mu <- multiply_dpar_rate_denom(mu, prep, i = i)
sigma <- get_dpar(prep, "sigma", i)
shape <- multiply_dpar_rate_denom(1 / sigma, prep, i = i)
rdiscrete(
n = prep$ndraws, dist = "nbinom",
mu = mu, size = shape,
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_geometric <- function(i, prep, ntrys = 5, ...) {
mu <- get_dpar(prep, "mu", i)
mu <- multiply_dpar_rate_denom(mu, prep, i = i)
shape <- 1
shape <- multiply_dpar_rate_denom(shape, prep, i = i)
rdiscrete(
n = prep$ndraws, dist = "nbinom",
mu = mu, size = shape,
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_discrete_weibull <- function(i, prep, ntrys = 5, ...) {
rdiscrete(
n = prep$ndraws, dist = "discrete_weibull",
mu = get_dpar(prep, "mu", i = i),
shape = get_dpar(prep, "shape", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_com_poisson <- function(i, prep, ntrys = 5, ...) {
rdiscrete(
n = prep$ndraws, dist = "com_poisson",
mu = get_dpar(prep, "mu", i = i),
shape = get_dpar(prep, "shape", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_exponential <- function(i, prep, ntrys = 5, ...) {
rcontinuous(
n = prep$ndraws, dist = "exp",
rate = 1 / get_dpar(prep, "mu", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_gamma <- function(i, prep, ntrys = 5, ...) {
shape <- get_dpar(prep, "shape", i = i)
scale <- get_dpar(prep, "mu", i = i) / shape
rcontinuous(
n = prep$ndraws, dist = "gamma",
shape = shape, scale = scale,
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_weibull <- function(i, prep, ntrys = 5, ...) {
shape <- get_dpar(prep, "shape", i = i)
scale <- get_dpar(prep, "mu", i = i) / gamma(1 + 1 / shape)
rcontinuous(
n = prep$ndraws, dist = "weibull",
shape = shape, scale = scale,
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_frechet <- function(i, prep, ntrys = 5, ...) {
nu <- get_dpar(prep, "nu", i = i)
scale <- get_dpar(prep, "mu", i = i) / gamma(1 - 1 / nu)
rcontinuous(
n = prep$ndraws, dist = "frechet",
scale = scale, shape = nu,
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_gen_extreme_value <- function(i, prep, ntrys = 5, ...) {
rcontinuous(
n = prep$ndraws, dist = "gen_extreme_value",
sigma = get_dpar(prep, "sigma", i = i),
xi = get_dpar(prep, "xi", i = i),
mu = get_dpar(prep, "mu", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_inverse.gaussian <- function(i, prep, ntrys = 5, ...) {
rcontinuous(
n = prep$ndraws, dist = "inv_gaussian",
mu = get_dpar(prep, "mu", i = i),
shape = get_dpar(prep, "shape", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_exgaussian <- function(i, prep, ntrys = 5, ...) {
rcontinuous(
n = prep$ndraws, dist = "exgaussian",
mu = get_dpar(prep, "mu", i = i),
sigma = get_dpar(prep, "sigma", i = i),
beta = get_dpar(prep, "beta", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_wiener <- function(i, prep, negative_rt = FALSE, ntrys = 5,
...) {
out <- rcontinuous(
n = 1, dist = "wiener",
delta = get_dpar(prep, "mu", i = i),
alpha = get_dpar(prep, "bs", i = i),
tau = get_dpar(prep, "ndt", i = i),
beta = get_dpar(prep, "bias", i = i),
types = if (negative_rt) c("q", "resp") else "q",
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
if (negative_rt) {
# code lower bound responses as negative RTs
out <- out[["q"]] * ifelse(out[["resp"]], 1, -1)
}
out
}
posterior_predict_beta <- function(i, prep, ntrys = 5, ...) {
mu <- get_dpar(prep, "mu", i = i)
phi <- get_dpar(prep, "phi", i = i)
rcontinuous(
n = prep$ndraws, dist = "beta",
shape1 = mu * phi, shape2 = (1 - mu) * phi,
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_von_mises <- function(i, prep, ntrys = 5, ...) {
rcontinuous(
n = prep$ndraws, dist = "von_mises",
mu = get_dpar(prep, "mu", i = i),
kappa = get_dpar(prep, "kappa", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_asym_laplace <- function(i, prep, ntrys = 5, ...) {
rcontinuous(
n = prep$ndraws, dist = "asym_laplace",
mu = get_dpar(prep, "mu", i = i),
sigma = get_dpar(prep, "sigma", i = i),
quantile = get_dpar(prep, "quantile", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
}
posterior_predict_zero_inflated_asym_laplace <- function(i, prep, ntrys = 5,
...) {
zi <- get_dpar(prep, "zi", i = i)
tmp <- runif(prep$ndraws, 0, 1)
ifelse(
tmp < zi, 0,
rcontinuous(
n = prep$ndraws, dist = "asym_laplace",
mu = get_dpar(prep, "mu", i = i),
sigma = get_dpar(prep, "sigma", i = i),
quantile = get_dpar(prep, "quantile", i = i),
lb = prep$data$lb[i], ub = prep$data$ub[i],
ntrys = ntrys
)
)
}
posterior_predict_cox <- function(i, prep, ...) {
stop2("Cannot sample from the posterior predictive ",
"distribution for family 'cox'.")
}
posterior_predict_hurdle_poisson <- function(i, prep, ...) {
# hu is the bernoulli hurdle parameter
hu <- get_dpar(prep, "hu", i = i)
lambda <- get_dpar(prep, "mu", i = i)
ndraws <- prep$ndraws
# compare with hu to incorporate the hurdle process
tmp <- runif(ndraws, 0, 1)
# sample from a truncated poisson distribution
# by adjusting lambda and adding 1
t = -log(1 - runif(ndraws) * (1 - exp(-lambda)))
ifelse(tmp < hu, 0, rpois(ndraws, lambda = lambda - t) + 1)
}
posterior_predict_hurdle_negbinomial <- function(i, prep, ...) {
hu <- get_dpar(prep, "hu", i = i)
mu <- get_dpar(prep, "mu", i = i)
ndraws <- prep$ndraws
tmp <- runif(ndraws, 0, 1)
# sample from an approximate(!) truncated negbinomial distribution
# by adjusting mu and adding 1
t = -log(1 - runif(ndraws) * (1 - exp(-mu)))
shape <- get_dpar(prep, "shape", i = i)
ifelse(tmp < hu, 0, rnbinom(ndraws, mu = mu - t, size = shape) + 1)
}
posterior_predict_hurdle_gamma <- function(i, prep, ...) {
hu <- get_dpar(prep, "hu", i = i)
shape <- get_dpar(prep, "shape", i = i)
scale <- get_dpar(prep, "mu", i = i) / shape
ndraws <- prep$ndraws
tmp <- runif(ndraws, 0, 1)
ifelse(tmp < hu, 0, rgamma(ndraws, shape = shape, scale = scale))
}
posterior_predict_hurdle_lognormal <- function(i, prep, ...) {
hu <- get_dpar(prep, "hu", i = i)
mu <- get_dpar(prep, "mu", i = i)
sigma <- get_dpar(prep, "sigma", i = i)
ndraws <- prep$ndraws
tmp <- runif(ndraws, 0, 1)
ifelse(tmp < hu, 0, rlnorm(ndraws, meanlog = mu, sdlog = sigma))
}
posterior_predict_hurdle_cumulative <- function(i, prep, ...) {
mu <- get_dpar(prep, "mu", i = i)
hu <- get_dpar(prep, "hu", i = i)
disc <- get_dpar(prep, "disc", i = i)
thres <- subset_thres(prep)
nthres <- NCOL(thres)
ndraws <- prep$ndraws
p <- pordinal(
seq_len(nthres + 1L),
eta = mu,
disc = disc,
thres = thres,
family = "cumulative",
link = prep$family$link
)
tmp <- runif(ndraws, 0, 1)
ifelse(
tmp < hu, 0L,
first_greater(p, target = runif(prep$ndraws, min = 0, max = 1))
)
}
posterior_predict_zero_inflated_beta <- function(i, prep, ...) {
zi <- get_dpar(prep, "zi", i = i)
mu <- get_dpar(prep, "mu", i = i)
phi <- get_dpar(prep, "phi", i = i)
tmp <- runif(prep$ndraws, 0, 1)
ifelse(
tmp < zi, 0,
rbeta(prep$ndraws, shape1 = mu * phi, shape2 = (1 - mu) * phi)
)
}
posterior_predict_zero_one_inflated_beta <- function(i, prep, ...) {
zoi <- get_dpar(prep, "zoi", i)
coi <- get_dpar(prep, "coi", i)
mu <- get_dpar(prep, "mu", i = i)
phi <- get_dpar(prep, "phi", i = i)
tmp <- runif(prep$ndraws, 0, 1)
one_or_zero <- runif(prep$ndraws, 0, 1)
ifelse(tmp < zoi,
ifelse(one_or_zero < coi, 1, 0),
rbeta(prep$ndraws, shape1 = mu * phi, shape2 = (1 - mu) * phi)
)
}
posterior_predict_zero_inflated_poisson <- function(i, prep, ...) {
# zi is the bernoulli zero-inflation parameter
zi <- get_dpar(prep, "zi", i = i)
lambda <- get_dpar(prep, "mu", i = i)
ndraws <- prep$ndraws
# compare with zi to incorporate the zero-inflation process
tmp <- runif(ndraws, 0, 1)
ifelse(tmp < zi, 0L, rpois(ndraws, lambda = lambda))
}
posterior_predict_zero_inflated_negbinomial <- function(i, prep, ...) {
zi <- get_dpar(prep, "zi", i = i)
mu <- get_dpar(prep, "mu", i = i)
shape <- get_dpar(prep, "shape", i = i)
ndraws <- prep$ndraws
tmp <- runif(ndraws, 0, 1)
ifelse(tmp < zi, 0L, rnbinom(ndraws, mu = mu, size = shape))
}
posterior_predict_zero_inflated_binomial <- function(i, prep, ...) {
zi <- get_dpar(prep, "zi", i = i)
trials <- prep$data$trials[i]
prob <- get_dpar(prep, "mu", i = i)
ndraws <- prep$ndraws
tmp <- runif(ndraws, 0, 1)
ifelse(tmp < zi, 0L, rbinom(ndraws, size = trials, prob = prob))
}
posterior_predict_zero_inflated_beta_binomial <- function(i, prep, ...) {
zi <- get_dpar(prep, "zi", i = i)
trials <- prep$data$trials[i]
mu <- get_dpar(prep, "mu", i = i)
phi <- get_dpar(prep, "phi", i = i)
ndraws <- prep$ndraws
draws <- rbeta_binomial(ndraws, size = trials, mu = mu, phi = phi)
tmp <- runif(ndraws, 0, 1)
draws[tmp < zi] <- 0L
draws
}
posterior_predict_categorical <- function(i, prep, ...) {
eta <- get_Mu(prep, i = i)
eta <- insert_refcat(eta, refcat = prep$refcat)
p <- pcategorical(seq_len(prep$data$ncat), eta = eta)
first_greater(p, target = runif(prep$ndraws, min = 0, max = 1))
}
posterior_predict_multinomial <- function(i, prep, ...) {
eta <- get_Mu(prep, i = i)
eta <- insert_refcat(eta, refcat = prep$refcat)
p <- dcategorical(seq_len(prep$data$ncat), eta = eta)
size <- prep$data$trials[i]
rblapply(seq_rows(p), function(s) t(rmultinom(1, size, p[s, ])))
}
posterior_predict_dirichlet <- function(i, prep, ...) {
eta <- get_Mu(prep, i = i)
eta <- insert_refcat(eta, refcat = prep$refcat)
phi <- get_dpar(prep, "phi", i = i)
cats <- seq_len(prep$data$ncat)
alpha <- dcategorical(cats, eta = eta) * phi
rdirichlet(prep$ndraws, alpha = alpha)
}
posterior_predict_dirichlet2 <- function(i, prep, ...) {
mu <- get_Mu(prep, i = i)
rdirichlet(prep$ndraws, alpha = mu)
}
posterior_predict_logistic_normal <- function(i, prep, ...) {
mu <- get_Mu(prep, i = i)
Sigma <- get_Sigma(prep, i = i, cor_name = "lncor")
.predict <- function(s) {
rlogistic_normal(1, mu = mu[s, ], Sigma = Sigma[s, , ],
refcat = prep$refcat)
}
rblapply(seq_len(prep$ndraws), .predict)
}
posterior_predict_cumulative <- function(i, prep, ...) {
posterior_predict_ordinal(i = i, prep = prep)
}
posterior_predict_sratio <- function(i, prep, ...) {
posterior_predict_ordinal(i = i, prep = prep)
}
posterior_predict_cratio <- function(i, prep, ...) {
posterior_predict_ordinal(i = i, prep = prep)
}
posterior_predict_acat <- function(i, prep, ...) {
posterior_predict_ordinal(i = i, prep = prep)
}
posterior_predict_ordinal <- function(i, prep, ...) {
thres <- subset_thres(prep, i)
nthres <- NCOL(thres)
p <- pordinal(
seq_len(nthres + 1),
eta = get_dpar(prep, "mu", i = i),
disc = get_dpar(prep, "disc", i = i),
thres = thres,
family = prep$family$family,
link = prep$family$link
)
first_greater(p, target = runif(prep$ndraws, min = 0, max = 1))
}
posterior_predict_custom <- function(i, prep, ...) {
custom_family_method(prep$family, "posterior_predict")(i, prep, ...)
}
posterior_predict_mixture <- function(i, prep, ...) {
families <- family_names(prep$family)
theta <- get_theta(prep, i = i)
smix <- sample_mixture_ids(theta)
out <- rep(NA, prep$ndraws)
for (j in seq_along(families)) {
draw_ids <- which(smix == j)
if (length(draw_ids)) {
pp_fun <- paste0("posterior_predict_", families[j])
pp_fun <- get(pp_fun, asNamespace("brms"))
tmp_prep <- pseudo_prep_for_mixture(prep, j, draw_ids)
out[draw_ids] <- pp_fun(i, tmp_prep, ...)
}
}
out
}
# ------------ predict helper-functions ----------------------
# random numbers from (possibly truncated) continuous distributions
# @param n number of random values to generate
# @param dist name of a distribution for which the functions
# p<dist>, q<dist>, and r<dist> are available
# @param ... additional arguments passed to the distribution functions
# @param ntrys number of trys in rejection sampling for truncated models
# @return vector of random values prep from the distribution
rcontinuous <- function(n, dist, ..., lb = NULL, ub = NULL, ntrys = 5) {
args <- list(...)
if (is.null(lb) && is.null(ub)) {
# sample as usual
rdist <- paste0("r", dist)
out <- do_call(rdist, c(list(n), args))
} else {
# sample from truncated distribution
pdist <- paste0("p", dist)
qdist <- paste0("q", dist)
if (!exists(pdist, mode = "function") || !exists(qdist, mode = "function")) {
# use rejection sampling as CDF or quantile function are not available
out <- rdiscrete(n, dist, ..., lb = lb, ub = ub, ntrys = ntrys)
} else {
if (is.null(lb)) lb <- -Inf
if (is.null(ub)) ub <- Inf
plb <- do_call(pdist, c(list(lb), args))
pub <- do_call(pdist, c(list(ub), args))
out <- runif(n, min = plb, max = pub)
out <- do_call(qdist, c(list(out), args))
# infinite values may be caused by numerical imprecision
out[out %in% c(-Inf, Inf)] <- NA
}
}
out
}
# random numbers from (possibly truncated) discrete distributions
# currently rejection sampling is used for truncated distributions
# @param n number of random values to generate
# @param dist name of a distribution for which the functions
# p<dist>, q<dist>, and r<dist> are available
# @param ... additional arguments passed to the distribution functions
# @param lb optional lower truncation bound
# @param ub optional upper truncation bound
# @param ntrys number of trys in rejection sampling for truncated models
# @return a vector of random values draws from the distribution
rdiscrete <- function(n, dist, ..., lb = NULL, ub = NULL, ntrys = 5) {
args <- list(...)
rdist <- paste0("r", dist)
if (is.null(lb) && is.null(ub)) {
# sample as usual
out <- do_call(rdist, c(list(n), args))
} else {
# sample from truncated distribution via rejection sampling
if (is.null(lb)) lb <- -Inf
if (is.null(ub)) ub <- Inf
out <- vector("list", ntrys)
for (i in seq_along(out)) {
# loop of the trys to prevent a mismatch between 'n'
# and length of the parameter vectors passed as arguments
out[[i]] <- as.vector(do_call(rdist, c(list(n), args)))
}
out <- do_call(cbind, out)
out <- apply(out, 1, extract_valid_sample, lb = lb, ub = ub)
}
out
}
# sample from the IDs of the mixture components
sample_mixture_ids <- function(theta) {
stopifnot(is.matrix(theta))
mix_comp <- seq_cols(theta)
ulapply(seq_rows(theta), function(s)
sample(mix_comp, 1, prob = theta[s, ])
)
}
# extract the first valid predicted value per Stan sample per observation
# @param x draws to be check against truncation boundaries
# @param lb vector of lower bounds
# @param ub vector of upper bound
# @return a valid truncated sample or else the closest boundary
extract_valid_sample <- function(x, lb, ub) {
valid <- match(TRUE, x >= lb & x <= ub)
if (is.na(valid)) {
# no valid truncated value found
# set sample to lb or ub
# 1e-10 is only to identify the invalid draws later on
out <- ifelse(max(x) < lb, lb - 1e-10, ub + 1e-10)
} else {
out <- x[valid]
}
out
}
# check for invalid predictions of truncated discrete models
# @param x matrix of predicted values
# @param lb optional lower truncation bound
# @param ub optional upper truncation bound
# @param thres threshold (in %) of invalid values at which to warn the user
# @return rounded values of 'x'
check_discrete_trunc_bounds <- function(x, lb = NULL, ub = NULL, thres = 0.01) {
if (is.null(lb) && is.null(ub)) {
return(x)
}
if (is.null(lb)) lb <- -Inf
if (is.null(ub)) ub <- Inf
thres <- as_one_numeric(thres)
# ensure correct comparison with vector bounds
y <- as.vector(t(x))
pct_invalid <- mean(y < lb | y > ub, na.rm = TRUE)
if (pct_invalid >= thres) {
warning2(
round(pct_invalid * 100), "% of all predicted values ",
"were invalid. Increasing argument 'ntrys' may help."
)
}
round(x)
}
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.