|
6 | 6 | #' |
7 | 7 | #' @param x Fitted model of class `merMod`, `glmmTMB`, `glm`, or `glm.nb` |
8 | 8 | #' (package **MASS**), or an object returned by `simulate_residuals()`. |
| 9 | +#' @param residual_type Character, indicating the type of residuals to be used |
| 10 | +#' for overdispersion tests. For mixed models, the default is `"simulated"`, |
| 11 | +#' which uses simulated residuals. These are based on [`simulate_residuals()`], |
| 12 | +#' using the **DHARMa** package. For `glm`, the default is `"simulated"` for |
| 13 | +#' bernoulli, binomial and negative-binomial models. Set `residual_type = "normal"` |
| 14 | +#' to use |
9 | 15 | #' |
10 | 16 | #' @inheritParams check_zeroinflation |
11 | 17 | #' |
@@ -102,8 +108,12 @@ plot.check_overdisp <- function(x, ...) { |
102 | 108 | model <- .safe(get(obj_name, envir = globalenv())) |
103 | 109 | } |
104 | 110 | } |
| 111 | + |
| 112 | + # detect residual type |
| 113 | + residual_type <- ifelse(isTRUE(attr(x, "simulated")), "simulated", NULL) |
| 114 | + |
105 | 115 | if (!is.null(model)) { |
106 | | - x <- .model_diagnostic_overdispersion(model, ...) |
| 116 | + x <- .model_diagnostic_overdispersion(model, residual_type = residual_type, ...) |
107 | 117 | class(x) <- c("see_check_overdisp", "data.frame") |
108 | 118 | attr(x, "colors") <- list(...)$colors |
109 | 119 | attr(x, "line_size") <- list(...)$size_line |
@@ -171,17 +181,34 @@ print.check_overdisp <- function(x, digits = 3, ...) { |
171 | 181 |
|
172 | 182 | # Overdispersion for classical models ----------------------------- |
173 | 183 |
|
| 184 | +#' @rdname check_overdispersion |
174 | 185 | #' @export |
175 | | -check_overdispersion.glm <- function(x, verbose = TRUE, ...) { |
| 186 | +check_overdispersion.glm <- function(x, residual_type = NULL, verbose = TRUE, ...) { |
176 | 187 | # model info |
177 | 188 | info <- insight::model_info(x) |
178 | 189 | obj_name <- insight::safe_deparse_symbol(substitute(x)) |
179 | 190 |
|
180 | | - # for certain distributions, simulated residuals are more accurate |
181 | | - use_simulated <- info$is_bernoulli || |
182 | | - info$is_binomial || |
183 | | - (!info$is_count && !info$is_binomial) || |
184 | | - info$is_negbin |
| 191 | + if (is.null(residual_type) || identical(residual_type, "simulated")) { |
| 192 | + # for certain distributions, simulated residuals are more accurate |
| 193 | + use_simulated <- info$is_bernoulli || |
| 194 | + info$is_binomial || |
| 195 | + (!info$is_count && !info$is_binomial) || |
| 196 | + info$is_negbin |
| 197 | + } else { |
| 198 | + use_simulated <- FALSE |
| 199 | + } |
| 200 | + |
| 201 | + # catch models/families not supported by DHARMa - we need to add more |
| 202 | + # exceptions here as they appear, but for now, `check_model()` also |
| 203 | + # automatically falls back to normal Q-Q plot for all models not supported |
| 204 | + # by DHARMa |
| 205 | + if ( |
| 206 | + info$family %in% |
| 207 | + c("quasipoisson", "quasibinomial") || |
| 208 | + !requireNamespace("DHARMa", quietly = TRUE) |
| 209 | + ) { |
| 210 | + use_simulated <- FALSE |
| 211 | + } |
185 | 212 |
|
186 | 213 | # model classes not supported in DHARMa |
187 | 214 | not_supported <- c("fixest", "glmx") |
@@ -260,20 +287,18 @@ check_overdispersion.model_fit <- check_overdispersion.poissonmfx |
260 | 287 | # Overdispersion for mixed models --------------------------- |
261 | 288 |
|
262 | 289 | #' @export |
263 | | -check_overdispersion.merMod <- function(x, ...) { |
| 290 | +check_overdispersion.merMod <- function(x, residual_type = NULL, ...) { |
264 | 291 | # for certain distributions, simulated residuals are more accurate |
265 | 292 | info <- insight::model_info(x) |
266 | 293 | obj_name <- insight::safe_deparse_symbol(substitute(x)) |
267 | 294 |
|
268 | | - # for certain distributions, simulated residuals are more accurate |
269 | | - use_simulated <- info$family == "genpois" || |
270 | | - info$is_zero_inflated || |
271 | | - info$is_bernoulli || |
272 | | - info$is_binomial || |
273 | | - (!info$is_count && !info$is_binomial) || |
274 | | - info$is_negbin # nolint |
275 | | - |
276 | | - if (use_simulated) { |
| 295 | + # validate argument |
| 296 | + if (!is.null(residual_type)) { |
| 297 | + insight::validate_argument(residual_type, c("normal", "simulated")) |
| 298 | + } |
| 299 | + |
| 300 | + # always use simulated residuals by default |
| 301 | + if (is.null(residual_type) || identical(residual_type, "simulated")) { |
277 | 302 | return(check_overdispersion(simulate_residuals(x, ...), object_name = obj_name, ...)) |
278 | 303 | } |
279 | 304 |
|
@@ -341,6 +366,7 @@ check_overdispersion.performance_simres <- function(x, alternative = "two.sided" |
341 | 366 |
|
342 | 367 | class(out) <- c("check_overdisp", "see_check_overdisp") |
343 | 368 | attr(out, "object_name") <- obj_name |
| 369 | + attr(out, "simulated") <- TRUE |
344 | 370 |
|
345 | 371 | out |
346 | 372 | } |
|
0 commit comments