#' Get and Separate All Specials of a Formula Object
#'
#' @param x A `formula` object.
#' @noRd
formula_specials <- function(x, original, family) {
xt <- terms(x, specials = formula_special_funs)
xt_specials <- attr(xt, "specials")[formula_special_funs]
xt_variables <- attr(xt, "variables")
xt_terms <- attr(xt, "term.labels")
specials <- list()
for (y in formula_special_funs) {
specials[[y]] <- onlyif(
!is.null(xt_specials[[y]]),
xt_variables[[xt_specials[[y]] + 1L]][[2L]]
)
}
special_vars <- unlist(xt_specials)
special_vars <- ifelse_(
!is.null(xt_specials$offset),
special_vars[!names(special_vars) %in% "offset"],
special_vars
)
x <- drop_terms(
termobj = xt,
dropx = get_special_term_indices(
special_vars,
xt_variables,
xt_terms
)
)
xt <- terms(x, specials = c("fixed", "varying", "random"))
xt_specials <- attr(xt, "specials")[c("fixed", "varying", "random")]
xt_variables <- attr(xt, "variables")
xt_terms <- attr(xt, "term.labels")
special_vars <- unlist(xt_specials)
fixed <- get_special_terms("fixed", xt_variables, xt_specials)
varying <- get_special_terms("varying", xt_variables, xt_specials)
random <- get_special_terms("random", xt_variables, xt_specials)
x <- drop_terms(
termobj = xt,
dropx = get_special_term_indices(
special_vars,
xt_variables,
xt_terms
)
)
form_terms <- formula_terms(x)
fixed$terms <- union(form_terms, fixed$terms)
fixed$icpt <- attr(xt, "intercept") || fixed$icpt
full_terms <- union(fixed$terms, union(varying$terms, random$terms))
any_icpt <- fixed$icpt || varying$icpt || random$icpt
stopifnot_(
!is_cumulative(family) || fixed$icpt || varying$icpt,
"A time-constant or a time-varying intercept must be specified
for a cumulative channel."
)
if (fixed$icpt && varying$icpt) {
warning_(
c(
"Both time-constant and time-varying intercept specified:",
`i` = "Defaulting to time-varying intercept."
)
)
fixed$icpt <- FALSE
}
if (length(full_terms) > 0L) {
x <- reformulate(
termlabels = full_terms,
response = xt_variables[[2L]],
intercept = 1L * any_icpt
)
} else {
y <- as.character(xt_variables[[2L]])
x <- ifelse_(
any_icpt,
as.formula(paste0(y, "~ 1")),
as.formula(paste0(y, "~ -1"))
)
}
xt <- formula_terms(x)
resp <- deparse1(formula_lhs(x))
list(
response = resp,
name = stan_name(resp),
formula = x,
family = family,
original = original,
specials = specials,
fixed = which(xt %in% fixed$terms),
has_fixed_intercept = as.logical(fixed$icpt),
varying = which(xt %in% varying$terms),
has_varying_intercept = as.logical(varying$icpt),
random = which(xt %in% random$terms),
has_random_intercept = as.logical(random$icpt)
)
}
#' Process Formulas for Deterministic Channels and Get Past Value Definitions
#'
#' @param x A `formula` object.
#' @noRd
formula_past <- function(formula) {
formula_str <- deparse1(formula)
rhs <- formula_rhs(formula)
past_def <- NULL
past_type <- NULL
if (length(rhs) > 1L && identical(deparse1(rhs[[1L]]), "|")) {
past_type <- deparse1(rhs[[3L]][[1L]])
stopifnot_(
past_type %in% c("init", "past"),
c(
"Invalid formula of a deterministic channel:",
`x` = "Unsupported definition {.var {past_type}}
on the right-hand side of {.fun |}."
)
)
stopifnot_(
identical(length(rhs[[3L]]), 2L),
"Invalid number of arguments supplied to {.fun {past_type}}
in {.var {formula_str}}."
)
past_def <- rhs[[3L]][[2L]]
formula[[3]] <- rhs[[2L]]
}
stopifnot_(
!grepl("fixed\\(.+\\)", formula_str, perl = TRUE),
c(
"The use of {.fun fixed} is not meaningful for deterministic channels:",
`x` = "Time-invariant definition was found in {.var {formula_str}}."
)
)
stopifnot_(
!grepl("varying\\(.+\\)", formula_str, perl = TRUE),
c(
"The use of {.fun varying} is not meaningful for deterministic channels:",
`x` = "Time-varying definition was found in {.var {formula_str}}."
)
)
stopifnot_(
!grepl("random\\(.+\\)", formula_str, perl = TRUE),
c(
"The use of {.fun random} is not meaningful for deterministic channels:",
`x` = "Random effect definition was found in {.var {formula_str}}."
)
)
list(
formula = formula,
specials = list(
past_type = past_type,
past = past_def,
rank = Inf
),
fixed = integer(0L),
varying = integer(0L),
random = integer(0L)
)
}
#' Process Response Variables for Deterministic Channels
#'
#' @param y \[`character(1)`] The response variable name.
#' @noRd
deterministic_response <- function(y) {
if (grepl("factor\\(.*\\)", y, perl = TRUE)) {
list(
type = "factor",
resp = gsub("factor\\((.*)\\)", "\\1", y, perl = TRUE)
)
} else if (grepl("numeric\\(.*\\)", y, perl = TRUE)) {
list(
type = "numeric",
resp = gsub("numeric\\((.*)\\)", "\\1", y, perl = TRUE)
)
} else if (grepl("integer\\(.*\\)", y, perl = TRUE)) {
list(
type = "integer",
resp = gsub("integer\\((.*)\\)", "\\1", y, perl = TRUE)
)
} else if (grepl("logical\\(.*\\)", y, perl = TRUE)) {
list(
type = "logical",
resp = gsub("logical\\((.*)\\)", "\\1", y, perl = TRUE)
)
} else {
warning_(
c(
"No type specified for deterministic channel {.var {y}}:",
`i` = "Assuming type is {.cls numeric}."
)
)
list(type = "numeric", resp = y)
}
}
#' Computes All Specials Defined in a Formula in the Context of the Data
#'
#' @param dformula \[`dformula`]\cr A model formula object
#' @param data \[`data.table`]\cr Data containing the variables used for
#' the special definitions in the formula.
#' @param check \[`logical(1)`]\cr Should the evaluated specials be checked
#' for validity?
#' @noRd
evaluate_specials <- function(dformula, data, check = TRUE) {
if (length(dformula$specials) == 0L) {
return(NULL)
}
out <- list()
y <- dformula$name
for (spec in formula_special_funs) {
spec_formula <- dformula$specials[[spec]]
if (!is.null(spec_formula)) {
spec_eval <- tryCatch(
data[, eval(spec_formula)],
error = function(e) e,
warning = function(w) w
)
stopifnot_(
!inherits(spec_eval, c("warning", "error")),
c(
"Unable to evaluate {.fun {spec}} for response variable {.var {y}}:",
`x` = spec_eval$message
)
)
if (check) {
do.call(paste0("check_", spec), args = list(y = y, x = spec_eval))
}
out[[spec]] <- spec_eval
}
}
out
}
#' Check that a number of trials definition is valid
#'
#' @param y \[`character(1)`]\cr Name of the response variable.
#' @param x \[`character(1)`]\cr Values of the trials specification.
#' @noRd
check_trials <- function(y, x) {
stopifnot_(
!inherits(x, "factor"),
c(
"Invalid {.fun trials} definition for response variable {.var {y}}:",
`x` = "Number of trials cannot be a {.cls factor}."
)
)
stopifnot_(
all(x >= 1.0) && all(x == as.integer(x)),
c(
"Invalid {.fun trials} definition for response variable {.var {y}}:",
`x` = "Number of trials must contain only positive integers."
)
)
}
#' Check that an offset definition is valid
#'
#' @param y \[`character(1)`]\cr Name of the response variable.
#' @param x \[`character(1)`]\cr Values of the offset specification.
#' @noRd
check_offset <- function(y, x) {
stopifnot_(
!inherits(x, "factor"),
c(
"Invalid {.fun offset} definition for response variable {.var {y}}:",
`x` = "Offset cannot be a {.cls factor}."
)
)
}
#' Retrieve the Corresponding Terms of A Special Component in a Formula
#'
#' @param type \[`character(1)`]\cr Either `"fixed"`, `"varying"` or `"random"`.
#' @param xt_variables \[`language()`]\cr A `"variables"` component of a
#' formula `terms` object.
#' @param xt_specials \[`list`]\cr A `"specials"` component of a
#' formula `terms` object.
#' @noRd
get_special_terms <- function(type, xt_variables, xt_specials) {
terms <- character(0L)
icpt <- FALSE
if (!is.null(xt_specials[[type]])) {
stopifnot_(
length(xt_specials[[type]]) == 1L,
"Multiple {.code {type}()} terms are not supported."
)
stopifnot_(
length(xt_variables[[xt_specials[[type]] + 1L]]) == 2L,
"A {.code {type}()} term must have a single formula argument."
)
# eval to ensure that form is a formula
form <- eval(xt_variables[[xt_specials[[type]] + 1L]][[2L]])
terms <- formula_terms(form)
icpt <- attr(terms(form), "intercept")
nested_specials <- c("fixed", "varying", "random")
xt <- terms(form, specials = nested_specials)
xt_specials <- attr(xt, "specials")[nested_specials]
for (spec in nested_specials) {
stopifnot_(
is.null(xt_specials[[spec]]),
paste0(
"A model formula must not contain nested ",
"{.code fixed()}, {.code varying()}, or {.code random()} terms."
)
)
}
}
list(terms = terms, icpt = icpt)
}
#' Retrieve the Corresponding Term of a Special Variable in a Formula
#'
#' @param special \[`integer()`]\cr A vector of special variable indices.
#' @param vars \[`language`]\cr
#' The `"variables"` attribute of a `terms` object.
#' @param term_labels \[`character()`]\cr
#' The `"term.labels"` attribute of a `terms` object.
#' @noRd
get_special_term_indices <- function(special, vars, term_labels) {
out <- integer(length(special))
for (i in seq_along(special)) {
v <- deparse1(vars[[special[i] + 1L]])
out[i] <- which(term_labels == v)
}
out
}
# Supported specials
formula_special_funs <- c(
"offset",
"trials"
)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.