Nothing
as_constructor = function(x) UseMethod("as_constructor")
#' @export
as_constructor.default = function(x) identity
#' @export
as_constructor.factor = function(x) {
x_levels = levels(x)
x_is_ordered = is.ordered(x)
function(x) factor(x, levels = seq_along(x_levels), labels = x_levels, ordered = x_is_ordered)
}
#' @export
as_constructor.character = function(x) {
x_levels = levels(as.factor(x))
function(x) x_levels[x]
}
#' @export
as_constructor.logical = function(x) as.logical
#' @export
apply_prototypes = function(...) {
.Deprecated("recover_types", package = "tidybayes") # nocov
recover_types(...) # nocov
}
#' Decorate a model fit or sample with data types recovered from the input data
#'
#' Decorate a Bayesian model fit or a sample from it with types for
#' variable and dimension data types. Meant to be used before calling
#' [spread_draws()] or [gather_draws()] so that the values returned by
#' those functions are translated back into useful data types.
#'
#' Each argument in `...` specifies a list or data.frame. The `model`
#' is decorated with a list of constructors that can convert a numeric column
#' into the data types in the lists in `...`.
#'
#' Then, when [spread_draws()] or [gather_draws()] is called on the decorated
#' `model`, each list entry with the same name as the variable or a dimension
#' in `variable_spec` is a used as a prototype for that variable or dimension ---
#' i.e., its type is taken to be the expected type of that variable or dimension.
#' Those types are used to translate numeric values of variables back into
#' useful values (for example, levels of a factor).
#'
#' The most common use of `recover_types` is to automatically translate
#' dimensions of a variable that correspond to levels of a factor in the original data back into
#' levels of that factor. The simplest way to do this is to pass in the data
#' frame from which the original data came.
#'
#' Supported types of prototypes are factor, ordered, and logical. For example:
#'
#' \itemize{ \item if `prototypes$v` is a factor, the v column in the
#' returned draws is translated into a factor using `factor(v,
#' labels=levels(prototypes$v), ordered=is.ordered(prototypes$v))`. \item if
#' `prototypes$v` is a logical, the v column is translated into a logical
#' using `as.logical(v)`. }
#'
#' Additional data types can be supported by providing a custom implementation
#' of the generic function `as_constructor`.
#'
#' @template param-model
#' @param ... Lists (or data frames) providing data prototypes used to convert
#' columns returned by [spread_draws()] and [gather_draws()] back into useful data types.
#' See *Details*.
#' @return A decorated version of `model`.
#' @author Matthew Kay
#' @aliases apply_prototypes
#' @seealso [spread_draws()], [gather_draws()], [compose_data()].
#' @keywords manip
#' @examples
#' \dontrun{
#'
#' library(dplyr)
#' library(magrittr)
#' library(rstan)
#'
#' # Here's an example dataset with a categorical predictor (`condition`) with several levels:
#' set.seed(5)
#' n = 10
#' n_condition = 5
#' ABC = tibble(
#' condition = factor(rep(c("A","B","C","D","E"), n)),
#' response = rnorm(n * 5, c(0,1,2,1,-1), 0.5)
#' )
#'
#' # We'll fit the following model to it:
#' stan_code = "
#' data {
#' int<lower=1> n;
#' int<lower=1> n_condition;
#' int<lower=1, upper=n_condition> condition[n];
#' real response[n];
#' }
#' parameters {
#' real overall_mean;
#' vector[n_condition] condition_zoffset;
#' real<lower=0> response_sd;
#' real<lower=0> condition_mean_sd;
#' }
#' transformed parameters {
#' vector[n_condition] condition_mean;
#' condition_mean = overall_mean + condition_zoffset * condition_mean_sd;
#' }
#' model {
#' response_sd ~ cauchy(0, 1); // => half-cauchy(0, 1)
#' condition_mean_sd ~ cauchy(0, 1); // => half-cauchy(0, 1)
#' overall_mean ~ normal(0, 5);
#'
#' //=> condition_mean ~ normal(overall_mean, condition_mean_sd)
#' condition_zoffset ~ normal(0, 1);
#'
#' for (i in 1:n) {
#' response[i] ~ normal(condition_mean[condition[i]], response_sd);
#' }
#' }
#' "
#'
#' m = stan(model_code = stan_code, data = compose_data(ABC), control = list(adapt_delta=0.99),
#' # 1 chain / few iterations just so example runs quickly
#' # do not use in practice
#' chains = 1, iter = 500)
#'
#' # without using recover_types(), the `condition` column returned by spread_draws()
#' # will be an integer:
#' m %>%
#' spread_draws(condition_mean[condition]) %>%
#' median_qi()
#'
#' # If we apply recover_types() first, subsequent calls to other tidybayes functions will
#' # automatically back-convert factors so that they are labeled with their original levels
#' # (assuming the same name is used)
#' m %<>% recover_types(ABC)
#'
#' # now the `condition` column with be a factor with levels "A", "B", "C", ...
#' m %>%
#' spread_draws(condition_mean[condition]) %>%
#' median_qi()
#'
#' }
#' @export
recover_types = function(model, ...) {
if (!is.list(attr(model, "tidybayes_constructors"))) {
attr(model, "tidybayes_constructors") = list()
}
character_variables = character()
for (prototypes in list(...)) {
if (!is.list(prototypes)) {
cli::cli_abort(c(
"All arguments to {.help recover_types} (except the model) must be lists or data frames.",
">" = "Did you mean to do something like {.code recover_types(model, list(...))}?"
))
}
#we iterate this way instead of building a list directly
#so that existing names are overwritten
for (variable_name in names(prototypes)) {
variable = prototypes[[variable_name]]
if (is.character(variable)) {
character_variables = c(character_variables, variable_name)
}
attr(model, "tidybayes_constructors")[[variable_name]] = as_constructor(variable)
}
}
# deprecation warning for character variables
if (length(character_variables) > 0) {
cli::cli_warn(c(
"It is no longer recommended to use character vectors with {.help recover_types}, as the intended order of the levels of the variable is ambiguous.",
"i" = "The following character vectors were provided to {.fun recover_types}: {.var {character_variables}}",
">" = "Instead of using character vectors, convert the variable to a {.fun factor}
{.emph before} passing the data into your model, then pass the {.emph same}
factor variable to {.fun recover_types} after the model is fit. This will
ensure that the level order matches exactly when variable indices are
extracted from the model."
))
}
model
}
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.