|
126 | 126 | #' This plot is used to identify influential observations. If any points in this |
127 | 127 | #' plot fall outside of Cook’s distance (the dashed lines) then it is considered |
128 | 128 | #' 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. |
129 | 132 | #' |
130 | 133 | #' @section Multicollinearity: |
131 | 134 | #' This plot checks for potential collinearity among predictors. In a nutshell, |
@@ -949,24 +952,17 @@ check_model.DHARMa <- check_model.performance_simres |
949 | 952 |
|
950 | 953 | # outliers -------------- |
951 | 954 | if (any(c("all", "outliers") %in% check)) { |
952 | | - # glmmTMB needs one refit per row for Cook's distance, which is too slow here |
953 | 955 | 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) |
961 | 957 | } else { |
962 | 958 | 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)) |
963 | 965 | } |
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)) |
970 | 966 | } |
971 | 967 |
|
972 | 968 | # posterior predictive checks -------------- |
@@ -1023,24 +1019,17 @@ check_model.DHARMa <- check_model.performance_simres |
1023 | 1019 |
|
1024 | 1020 | # outliers -------------- |
1025 | 1021 | if (any(c("all", "outliers") %in% check)) { |
1026 | | - # glmmTMB needs one refit per row for Cook's distance, which is too slow here |
1027 | 1022 | 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) |
1035 | 1024 | } else { |
1036 | 1025 | 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)) |
1037 | 1032 | } |
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)) |
1044 | 1033 | } |
1045 | 1034 |
|
1046 | 1035 | # posterior predictive checks -------------- |
@@ -1068,6 +1057,44 @@ check_model.DHARMa <- check_model.performance_simres |
1068 | 1057 | } |
1069 | 1058 |
|
1070 | 1059 |
|
| 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 | + |
1071 | 1098 | # compile plots for checks of Bayesian models ------------------------ |
1072 | 1099 |
|
1073 | 1100 | .check_assumptions_stan <- function(model, ...) { |
|
0 commit comments