Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include changes from latest glmmTMB #933

Merged
merged 13 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Type: Package
Package: insight
Title: Easy Access to Model Information for Various Model Objects
Version: 0.20.4.4
Version: 0.20.4.5
Authors@R:
c(person(given = "Daniel",
family = "Lüdecke",
Expand Down Expand Up @@ -128,7 +128,7 @@ Suggests:
geoR,
ggeffects,
GLMMadaptive,
glmmTMB,
glmmTMB (>= 1.1.10),
glmtoolbox,
gmnl,
grDevices,
Expand Down Expand Up @@ -218,4 +218,3 @@ Config/testthat/edition: 3
Config/testthat/parallel: true
Config/Needs/website: easystats/easystatstemplate
Config/Needs/check: stan-dev/cmdstanr
Remotes: easystats/bayestestR, easystats/parameters
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

* Fixed rendering issue of the example in `?insight::display`.

* Fixed issues due to recent changes in the *glmmTMB* package.

# insight 0.20.4

## New supported models
Expand Down
43 changes: 32 additions & 11 deletions R/compute_variances.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.compute_variances <- function(model,

Check warning on line 1 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint / lint

file=R/compute_variances.R,line=1,col=1,[cyclocomp_linter] Reduce the cyclomatic complexity of this function from 57 to at most 40.

Check warning on line 1 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/compute_variances.R,line=1,col=1,[cyclocomp_linter] Reduce the cyclomatic complexity of this function from 57 to at most 40.
component,
name_fun = NULL,
name_full = NULL,
Expand Down Expand Up @@ -509,7 +509,7 @@
# fixed effects variance ------------------------------------------------------
#
# This is in line with Nakagawa et al. 2017, Suppl. 2
# see https://royalsocietypublishing.org/action/downloadSupplement?doi=10.1098%2Frsif.2017.0213&file=rsif20170213supp2.pdf

Check warning on line 512 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint / lint

file=R/compute_variances.R,line=512,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 122 characters.

Check warning on line 512 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/compute_variances.R,line=512,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 122 characters.
#
# However, package MuMIn differs and uses "fitted()" instead, leading to minor
# deviations
Expand Down Expand Up @@ -537,7 +537,7 @@
# variance associated with a random-effects term (Johnson 2014) --------------
#
# This is in line with Nakagawa et al. 2017, Suppl. 2, and package MuMIm
# see https://royalsocietypublishing.org/action/downloadSupplement?doi=10.1098%2Frsif.2017.0213&file=rsif20170213supp2.pdf

Check warning on line 540 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint / lint

file=R/compute_variances.R,line=540,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 122 characters.

Check warning on line 540 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/compute_variances.R,line=540,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 122 characters.
# ----------------------------------------------------------------------------
.compute_variance_random <- function(model, terms, mixed_effects_info) {
if (is.null(terms)) {
Expand Down Expand Up @@ -578,7 +578,7 @@
# (also call obersvation level variance).
#
# This is in line with Nakagawa et al. 2017, Suppl. 2, and package MuMIm
# see https://royalsocietypublishing.org/action/downloadSupplement?doi=10.1098%2Frsif.2017.0213&file=rsif20170213supp2.pdf

Check warning on line 581 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint / lint

file=R/compute_variances.R,line=581,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 122 characters.

Check warning on line 581 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/compute_variances.R,line=581,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 122 characters.
#
# We need:
# - the overdispersion parameter / sigma
Expand All @@ -593,7 +593,7 @@
faminfo,
model_null = NULL,
revar_null = NULL,
name,

Check warning on line 596 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint / lint

file=R/compute_variances.R,line=596,col=44,[function_argument_linter] Arguments without defaults should come before arguments with defaults.

Check warning on line 596 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/compute_variances.R,line=596,col=44,[function_argument_linter] Arguments without defaults should come before arguments with defaults.
approx_method = "lognormal",
model_component = NULL,
verbose = TRUE) {
Expand Down Expand Up @@ -774,7 +774,7 @@
# different methods, which are now also implemented here.
#
# This is in line with Nakagawa et al. 2017, Suppl. 2, and package MuMIm
# see https://royalsocietypublishing.org/action/downloadSupplement?doi=10.1098%2Frsif.2017.0213&file=rsif20170213supp2.pdf

Check warning on line 777 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/compute_variances.R,line=777,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 122 characters.
#
# There may be small deviations to Nakagawa et al. for the null-model, which
# despite being correctly re-formulated in "null_model()", returns slightly
Expand All @@ -790,7 +790,7 @@
sig,
model_null = NULL,
revar_null = NULL,
name,

Check warning on line 793 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/compute_variances.R,line=793,col=38,[function_argument_linter] Arguments without defaults should come before arguments with defaults.
approx_method = "lognormal",
model_component = NULL,
verbose = TRUE) {
Expand Down Expand Up @@ -848,6 +848,7 @@
nbinom = ,
nbinom1 = ,
nbinom2 = ,
nbinom12 = ,
negbinomial = ,
tweedie = ,
`negative binomial` = exp(mu + 0.5 * as.vector(revar_null)),
Expand Down Expand Up @@ -888,6 +889,7 @@
nbinom = ,
nbinom1 = ,
nbinom2 = ,
nbinom12 = ,
negbinomial = ,
`negative binomial` = ,
`zero-inflated negative binomial` = .variance_family_nbinom(model, mu, sig, faminfo),
Expand Down Expand Up @@ -924,6 +926,7 @@
nbinom = ,
nbinom1 = ,
nbinom2 = ,
nbinom12 = ,
quasipoisson = ,
negbinomial = ,
`negative binomial` = ,
Expand Down Expand Up @@ -967,6 +970,7 @@
nbinom = ,
nbinom1 = ,
nbinom2 = ,
nbinom12 = ,
negbinomial = ,
genpois = ,
`negative binomial` = ((1 / mu) + (1 / dispersion_param))^-1,
Expand All @@ -979,6 +983,7 @@
nbinom = ,
nbinom1 = ,
nbinom2 = ,
nbinom12 = ,
negbinomial = ,
genpois = ,
`negative binomial` = (1 / mu) + (1 / dispersion_param),
Expand Down Expand Up @@ -1069,12 +1074,8 @@
phi <- model@phi
p <- model@p - 2
} else {
if ("psi" %in% names(model$fit$par)) {
psi <- model$fit$par["psi"] # glmmmTMB >= 1.1.5
} else {
psi <- model$fit$par["thetaf"]
}
p <- unname(stats::plogis(psi) + 1)
check_if_installed("glmmTMB")
p <- unname(unlist(glmmTMB::family_params(model)))
}
phi * mu^p
}
Expand All @@ -1092,6 +1093,14 @@
return(rep(1e-16, length(mu)))
}
mu * (1 + sig)
} else if (identical(faminfo$family, "nbinom12")) {
# nbinom12-family from glmmTMB requires psi-parameter
if ("psi" %in% names(model$fit$par)) {
psi <- model$fit$par["psi"]
} else {
format_error("Could not extract psi-parameter for the distributional variance for nbinom12-family.")
}
stats::family(model)$variance(mu, sig, psi)
} else {
stats::family(model)$variance(mu, sig)
}
Expand All @@ -1104,19 +1113,31 @@
# ----------------------------------------------
.variance_zinb <- function(model, sig, faminfo, family_var) {
if (inherits(model, "glmmTMB")) {
if (identical(faminfo$family, "nbinom12")) {
# nbinom12-family from glmmTMB requires psi-parameter
if ("psi" %in% names(model$fit$par)) {
psi <- model$fit$par["psi"]
} else {
format_error("Could not extract psi-parameter for the distributional variance for nbinom12-family.")
}
}
v <- stats::family(model)$variance
# zi probability
p <- stats::predict(model, type = "zprob")
# mean of conditional distribution
mu <- stats::predict(model, type = "conditional")
# sigma
betad <- model$fit$par["betad"]
betadisp <- model$fit$par["betadisp"]
k <- switch(faminfo$family,
gaussian = exp(0.5 * betad),
Gamma = exp(-0.5 * betad),
exp(betad)
gaussian = exp(0.5 * betadisp),
Gamma = exp(-0.5 * betadisp),
exp(betadisp)
)
pvar <- (1 - p) * v(mu, k) + mu^2 * (p^2 + p)
if (identical(faminfo$family, "nbinom12")) {
pvar <- (1 - p) * v(mu, k, psi) + mu^2 * (p^2 + p)
} else {
pvar <- (1 - p) * v(mu, k) + mu^2 * (p^2 + p)
}
} else if (inherits(model, "MixMod")) {
v <- family_var
p <- stats::plogis(stats::predict(model, type_pred = "link", type = "zero_part"))
Expand Down Expand Up @@ -1215,7 +1236,7 @@
if (length(rs) > length(fe)) rs <- rs[seq_along(fe)]
if (length(fe) > length(rs)) fe <- fe[seq_along(rs)]

all(mapply(function(r, f) all(r %in% f), rs, fe, SIMPLIFY = TRUE))

Check warning on line 1239 in R/compute_variances.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/compute_variances.R,line=1239,col=7,[undesirable_function_linter] Avoid undesirable function "mapply".
}


Expand Down
19 changes: 17 additions & 2 deletions R/find_formula.R
Original file line number Diff line number Diff line change
Expand Up @@ -1117,6 +1117,7 @@ find_formula.glmmTMB <- function(x, verbose = TRUE, ...) {
f.zi <- stats::formula(x, component = "zi")
f.disp <- stats::formula(x, component = "disp")

# check for "empty" formulas
if (identical(safe_deparse(f.zi), "~0") || identical(safe_deparse(f.zi), "~1")) {
f.zi <- NULL
}
Expand All @@ -1125,7 +1126,7 @@ find_formula.glmmTMB <- function(x, verbose = TRUE, ...) {
f.disp <- NULL
}


# extract random parts of formula
f.random <- lapply(.findbars(f.cond), function(.x) {
f <- safe_deparse(.x)
stats::as.formula(paste0("~", f))
Expand All @@ -1147,16 +1148,30 @@ find_formula.glmmTMB <- function(x, verbose = TRUE, ...) {
f.zirandom <- f.zirandom[[1]]
}

f.disprandom <- lapply(.findbars(f.disp), function(.x) {
f <- safe_deparse(.x)
if (f == "NULL") {
return(NULL)
}
stats::as.formula(paste0("~", f))
})

if (length(f.disprandom) == 1L) {
f.disprandom <- f.disprandom[[1]]
}

# extract fixed effects parts
f.cond <- stats::as.formula(.get_fixed_effects(f.cond))
if (!is.null(f.zi)) f.zi <- stats::as.formula(.get_fixed_effects(f.zi))
if (!is.null(f.disp)) f.disp <- stats::as.formula(.get_fixed_effects(f.disp))

f <- compact_list(list(
conditional = f.cond,
random = f.random,
zero_inflated = f.zi,
zero_inflated_random = f.zirandom,
dispersion = f.disp
dispersion = f.disp,
dispersion_random = f.disprandom
))
.find_formula_return(f, verbose = verbose)
}
Expand Down
2 changes: 1 addition & 1 deletion R/format_table.R
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
#'
#' @note `options(insight_use_symbols = TRUE)` overrides the `use_symbols` argument
#' and always displays symbols, if possible.
#' @examplesIf require("rstanarm", warn.conflicts = FALSE) && require("parameters", , warn.conflicts = FALSE)
#' @examplesIf require("rstanarm", warn.conflicts = FALSE) && require("parameters", warn.conflicts = FALSE) && packageVersion("parameters") > "0.22.2"

Check warning on line 58 in R/format_table.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/format_table.R,line=58,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 150 characters.
#' format_table(head(iris), digits = 1)
#'
#' m <- lm(Sepal.Length ~ Species * Sepal.Width, data = iris)
Expand Down
8 changes: 4 additions & 4 deletions R/get_residuals.R
Original file line number Diff line number Diff line change
Expand Up @@ -285,11 +285,11 @@ print.insight_residuals <- function(x, ...) {
# mean of conditional distribution
mu <- stats::predict(model, type = "conditional")
# sigma
betad <- model$fit$par["betad"]
betadisp <- model$fit$par["betadisp"]
k <- switch(faminfo$family,
gaussian = exp(0.5 * betad),
Gamma = exp(-0.5 * betad),
exp(betad)
gaussian = exp(0.5 * betadisp),
Gamma = exp(-0.5 * betadisp),
exp(betadisp)
)
pvar <- (1 - p) * v(mu, k) + mu^2 * (p^2 + p)
pred <- stats::predict(model, type = "response") ## (1 - p) * mu
Expand Down
3 changes: 2 additions & 1 deletion R/get_variances.R
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
#' - `var.residual`, residual variance (sum of dispersion and distribution-specific/observation level variance)
#' - `var.distribution`, distribution-specific (or observation level) variance
#' - `var.dispersion`, variance due to additive dispersion
#' - `var.intercept`, the random-intercept-variance, or between-subject-variance (\ifelse{html}{\out{&tau;<sub>00</sub>}}{\eqn{\tau_{00}}})

Check warning on line 45 in R/get_variances.R

View workflow job for this annotation

GitHub Actions / lint-changed-files / lint-changed-files

file=R/get_variances.R,line=45,col=121,[line_length_linter] Lines should not be more than 120 characters. This line is 139 characters.
#' - `var.slope`, the random-slope-variance (\ifelse{html}{\out{&tau;<sub>11</sub>}}{\eqn{\tau_{11}}})
#' - `cor.slope_intercept`, the random-slope-intercept-correlation (\ifelse{html}{\out{&rho;<sub>01</sub>}}{\eqn{\rho_{01}}})
#' - `cor.slopes`, the correlation between random slopes (\ifelse{html}{\out{&rho;<sub>00</sub>}}{\eqn{\rho_{00}}})
Expand Down Expand Up @@ -135,7 +135,7 @@
#' - Bernoulli (logistic) regression
#' - Binomial regression (with other than binary outcomes)
#' - Poisson and Quasi-Poisson regression
#' - Negative binomial regression (including nbinom1 and nbinom2 families)
#' - Negative binomial regression (including nbinom1, nbinom2 and nbinom12 families)
#' - Gaussian regression (linear models)
#' - Gamma regression
#' - Tweedie regression
Expand All @@ -149,6 +149,7 @@
#' - Compound Poisson regression
#' - Generalized Poisson regression
#' - Log-normal regression
#' - Skew-normal regression
#'
#' Extracting variance components for models with zero-inflation part is not
#' straightforward, because it is not definitely clear how the distribution-specific
Expand Down
4 changes: 2 additions & 2 deletions R/utils_model_info.R
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
# poisson family --------

poisson_fam <-
fitfam %in% c("poisson", "quasipoisson", "genpois", "ziplss") |
fitfam %in% c("poisson", "quasipoisson", "genpois", "ziplss", "bell") |
grepl("poisson", fitfam_lower, fixed = TRUE)


Expand Down Expand Up @@ -345,7 +345,7 @@
dirichlet_fam || is.ordinal || zero.inf || is.censored || is.survival || is_binomtest ||
is.categorical || hurdle || is.multinomial || is_chi2test || is_proptest || is_xtab) {
linear_model <- FALSE
} else if (!(fitfam %in% c("Student's-t", "t Family", "gaussian", "Gaussian", "lognormal")) && !grepl("(\\st)$", fitfam)) {
} else if (!(fitfam %in% c("Student's-t", "t Family", "gaussian", "Gaussian", "lognormal", "skewnormal")) && !grepl("(\\st)$", fitfam)) {
linear_model <- FALSE
}
if (!linear_model && is.survival && fitfam == "gaussian") {
Expand Down
2 changes: 1 addition & 1 deletion man/format_table.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion man/get_variance.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions tests/testthat/test-r2_nakagawa_negbin_zi.R
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,19 @@ test_that("glmmTMB, Nbinom1 zero-inflated", {
expect_equal(out$R2_conditional, 0.6051817, tolerance = 1e-4, ignore_attr = TRUE)
expect_equal(out$R2_marginal, 0.5173316, tolerance = 1e-4, ignore_attr = TRUE)
})


test_that("glmmTMB, Nbinom12, zero-inflated", {
# results are in line with other nbinom families
skip_if_not_installed("glmmTMB", minimum_version = "1.1.10")
data(Salamanders, package = "glmmTMB")
m <- glmmTMB::glmmTMB(
count ~ mined + spp + (1 | site),
ziformula = ~mined,
family = glmmTMB::nbinom12(),
data = Salamanders, REML = TRUE
)
out <- performance::r2_nakagawa(m)
expect_equal(out$R2_conditional, 0.7857598, tolerance = 1e-4, ignore_attr = TRUE)
expect_equal(out$R2_marginal, 0.6742057, tolerance = 1e-4, ignore_attr = TRUE)
})
12 changes: 8 additions & 4 deletions tests/testthat/test-r2_nakagawa_tweedie.R
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,19 @@ test_that("cplm, tweedie", {
# results for glmmTMB are very close to cplm
m0 <- glmmTMB::glmmTMB(
Parasite ~ (1 | Population) + (1 | Container),
family = glmmTMB::tweedie(1),
family = glmmTMB::tweedie(),
start = list(psi = -12),
map = list(psi = factor(NA)),
data = DataAll
)
m <- glmmTMB::glmmTMB(
Parasite ~ Sex + Treatment + Habitat + (1 | Population) + (1 | Container),
family = glmmTMB::tweedie(1),
family = glmmTMB::tweedie(),
start = list(psi = -12),
map = list(psi = factor(NA)),
data = DataAll
)
out3 <- performance::r2_nakagawa(m, null_model = m0)
expect_equal(out$R2_conditional, out3$R2_conditional, tolerance = 1e-2, ignore_attr = TRUE)
expect_equal(out$R2_marginal, out3$R2_marginal, tolerance = 1e-2, ignore_attr = TRUE)
expect_equal(out$R2_conditional, out3$R2_conditional, tolerance = 1e-1, ignore_attr = TRUE)
expect_equal(out$R2_marginal, out3$R2_marginal, tolerance = 1e-1, ignore_attr = TRUE)
})
Loading