Skip to content

Commit bb82f48

Browse files
Show the influential-observations plot in check_model() for glmmTMB models (#974)
Co-authored-by: Daniel <mail@danielluedecke.de>
1 parent f058b58 commit bb82f48

8 files changed

Lines changed: 844 additions & 56 deletions

File tree

‎DESCRIPTION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Type: Package
22
Package: performance
33
Title: Assessment of Regression Models Performance
4-
Version: 0.18.2.17
4+
Version: 0.18.2.18
55
Authors@R:
66
c(person(given = "Daniel",
77
family = "Lüdecke",

‎NEWS.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@
77

88
## Changes
99

10+
* `check_model()` now shows the plot of influential observations for models
11+
from package *glmmTMB* with a gaussian, poisson, binomial, Gamma, or `nbinom2`
12+
family, and without zero-inflation or a dispersion formula. The plot uses hat
13+
values and a one-step approximation of Cook's distance, so the model is not
14+
refit. For other *glmmTMB* models, the plot is skipped, and with
15+
`verbose = TRUE` a message names the reason. For the same models,
16+
`plot(check_outliers())` shows this plot with the Cook's distances from the
17+
refits (#95).
18+
1019
* `check_outliers()` now computes the default threshold for Cook's distance as
1120
`qf(0.5, p, n - p)`, as the documentation states. Here `p` is the number of
1221
fixed effects of the conditional model, and `n` is the number of

‎R/check_model.R‎

Lines changed: 55 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@
126126
#' This plot is used to identify influential observations. If any points in this
127127
#' plot fall outside of Cook’s distance (the dashed lines) then it is considered
128128
#' an influential observation. See [`check_outliers()`] for further details.
129+
#' For models from package *glmmTMB*, this plot uses a one-step approximation
130+
#' of Cook's distance from the hat values, while [`check_outliers()`] computes
131+
#' Cook's distance by refitting the model without each observation.
129132
#'
130133
#' @section Multicollinearity:
131134
#' This plot checks for potential collinearity among predictors. In a nutshell,
@@ -949,24 +952,17 @@ check_model.DHARMa <- check_model.performance_simres
949952

950953
# outliers --------------
951954
if (any(c("all", "outliers") %in% check)) {
952-
# glmmTMB needs one refit per row for Cook's distance, which is too slow here
953955
if (inherits(model, "glmmTMB")) {
954-
if (verbose) {
955-
insight::format_alert(paste(
956-
"The check for influential observations is skipped for models of class",
957-
"`glmmTMB`, because Cook's distance needs one model refit per observation.",
958-
"Use `check_outliers()` to compute it."
959-
))
960-
}
956+
dat$INFLUENTIAL <- .check_model_influential_glmmTMB(model, verbose)
961957
} else {
962958
dat$OUTLIERS <- .safe(check_outliers(model, method = "cook"))
959+
if (is.null(dat$OUTLIERS)) {
960+
threshold <- NULL
961+
} else {
962+
threshold <- attributes(dat$OUTLIERS)$threshold$cook
963+
}
964+
dat$INFLUENTIAL <- .safe(.model_diagnostic_outlier(model, threshold = threshold))
963965
}
964-
if (is.null(dat$OUTLIERS)) {
965-
threshold <- NULL
966-
} else {
967-
threshold <- attributes(dat$OUTLIERS)$threshold$cook
968-
}
969-
dat$INFLUENTIAL <- .safe(.model_diagnostic_outlier(model, threshold = threshold))
970966
}
971967

972968
# posterior predictive checks --------------
@@ -1023,24 +1019,17 @@ check_model.DHARMa <- check_model.performance_simres
10231019

10241020
# outliers --------------
10251021
if (any(c("all", "outliers") %in% check)) {
1026-
# glmmTMB needs one refit per row for Cook's distance, which is too slow here
10271022
if (inherits(model, "glmmTMB")) {
1028-
if (verbose) {
1029-
insight::format_alert(paste(
1030-
"The check for influential observations is skipped for models of class",
1031-
"`glmmTMB`, because Cook's distance needs one model refit per observation.",
1032-
"Use `check_outliers()` to compute it."
1033-
))
1034-
}
1023+
dat$INFLUENTIAL <- .check_model_influential_glmmTMB(model, verbose)
10351024
} else {
10361025
dat$OUTLIERS <- .safe(check_outliers(model, method = "cook"))
1026+
if (is.null(dat$OUTLIERS)) {
1027+
threshold <- NULL
1028+
} else {
1029+
threshold <- attributes(dat$OUTLIERS)$threshold$cook
1030+
}
1031+
dat$INFLUENTIAL <- .safe(.model_diagnostic_outlier(model, threshold = threshold))
10371032
}
1038-
if (is.null(dat$OUTLIERS)) {
1039-
threshold <- NULL
1040-
} else {
1041-
threshold <- attributes(dat$OUTLIERS)$threshold$cook
1042-
}
1043-
dat$INFLUENTIAL <- .safe(.model_diagnostic_outlier(model, threshold = threshold))
10441033
}
10451034

10461035
# posterior predictive checks --------------
@@ -1068,6 +1057,44 @@ check_model.DHARMa <- check_model.performance_simres
10681057
}
10691058

10701059

1060+
# data for the influential observations panel of glmmTMB models ----------
1061+
1062+
# Cook's distance from `check_outliers()` needs one refit per row for glmmTMB,
1063+
# so the panel uses hat values and a one-step Cook's distance, with the
1064+
# threshold that `check_outliers()` uses
1065+
.check_model_influential_glmmTMB <- function(model, verbose = TRUE) {
1066+
unsupported <- .safe(
1067+
.glmmTMB_influence_unsupported(model),
1068+
"a structure that cannot be read"
1069+
)
1070+
if (!is.null(unsupported)) {
1071+
if (verbose) {
1072+
insight::format_alert(paste0(
1073+
"The check for influential observations is skipped for this `glmmTMB` ",
1074+
"model, because hat values are not available for models with ",
1075+
unsupported,
1076+
"."
1077+
))
1078+
}
1079+
return(NULL)
1080+
}
1081+
# without the threshold of `check_outliers()`, for example when n - p < 1,
1082+
# the contour lines would not match it
1083+
threshold <- .safe(.check_outliers_cook_threshold(model))
1084+
out <- NULL
1085+
if (!is.null(threshold)) {
1086+
out <- .safe(.model_diagnostic_outlier(model, threshold = threshold))
1087+
}
1088+
if (is.null(out) && verbose) {
1089+
insight::format_alert(paste(
1090+
"The check for influential observations is skipped for this `glmmTMB`",
1091+
"model, because its hat values and Cook's distances could not be computed."
1092+
))
1093+
}
1094+
out
1095+
}
1096+
1097+
10711098
# compile plots for checks of Bayesian models ------------------------
10721099

10731100
.check_assumptions_stan <- function(model, ...) {

0 commit comments

Comments
 (0)