Skip to content

Commit 9d90a73

Browse files
authored
Add support for group-level estimates from coxme (#1087)
* Add support for group-level estimates from coxme * fix * refactor * use insight * fix * fix * as.data.frame method * ... * fixes * ... * fix * add test * seed * add test * fix * fix * update news * wordlist * fix test * fix * Update DESCRIPTION * fix * comment * fix * test * fix warning * fix
1 parent bde23a9 commit 9d90a73

14 files changed

+384
-541
lines changed

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Type: Package
22
Package: parameters
33
Title: Processing of Model Parameters
4-
Version: 0.27.0.3
4+
Version: 0.27.0.5
55
Authors@R:
66
c(person(given = "Daniel",
77
family = "Lüdecke",

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Generated by roxygen2: do not edit by hand
22

3+
S3method(as.data.frame,VarCorr.coxme)
34
S3method(as.data.frame,VarCorr.lme)
45
S3method(as.double,n_clusters)
56
S3method(as.double,n_factors)
@@ -225,6 +226,7 @@ S3method(model_parameters,clmm2)
225226
S3method(model_parameters,coeftest)
226227
S3method(model_parameters,compare.loo)
227228
S3method(model_parameters,comparisons)
229+
S3method(model_parameters,coxme)
228230
S3method(model_parameters,cpglmm)
229231
S3method(model_parameters,data.frame)
230232
S3method(model_parameters,dbscan)

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
generic `display()` method can be used, too, which has a `format` argument that
1717
also supports `"tt"` for `tinytable`.
1818

19+
* Improved support for *coxme* models in `model_parameters()`. Random effects
20+
and group level estimates are now returned as well.
21+
1922
## Bug fixes
2023

2124
* Fixed issue with models of class `selection` with multiple outcomes.

R/extract_random_parameters.R

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,57 @@
122122
.extract_random_parameters.MixMod <- function(model, ...) {
123123
NULL
124124
}
125+
126+
127+
.extract_random_parameters.coxme <- function(model, ci = NULL, effects = "random", ...) {
128+
insight::check_if_installed(c("lme4", "coxme"))
129+
130+
# extract random effects
131+
re_grp <- insight::find_random(model, split_nested = TRUE, flatten = TRUE)
132+
133+
do.call(rbind, lapply(re_grp, function(grp) {
134+
# coerce to data frame
135+
out <- as.data.frame(lme4::ranef(model)[[grp]], stringsAsFactors = FALSE)
136+
colnames(out)[1] <- "(Intercept)"
137+
138+
# reshape, to have a long format
139+
out$.grp <- unique(insight::get_data(model)[[grp]])
140+
out <- datawizard::reshape_longer(out, select = -".grp")
141+
out$grpvar <- grp
142+
143+
# rename columns
144+
colnames(out) <- c("Level", "Parameter", "Coefficient", "Group")
145+
out$SE <- NA
146+
147+
# re-order columns
148+
out <- out[c("Group", "Parameter", "Level", "Coefficient", "SE")]
149+
out$Parameter[out$Parameter == "Intercept"] <- "(Intercept)"
150+
151+
# sort
152+
out <- datawizard::data_arrange(out, c("Group", "Parameter", "Level"))
153+
154+
# coerce to character
155+
out$Parameter <- as.character(out$Parameter)
156+
out$Level <- as.character(out$Level)
157+
out$Group <- as.character(out$Group)
158+
out$Effects <- "random"
159+
160+
out$CI_low <- out$CI_high <- NA
161+
ci_cols <- c("CI_low", "CI_high")
162+
163+
stat_column <- gsub("-statistic", "", insight::find_statistic(model), fixed = TRUE)
164+
165+
# to match rbind
166+
out[[stat_column]] <- NA
167+
out$df_error <- NA
168+
out$p <- NA
169+
170+
out <- out[c("Parameter", "Level", "Coefficient", "SE", ci_cols, stat_column, "df_error", "p", "Effects", "Group")]
171+
172+
if (effects == "random") {
173+
out[c(stat_column, "df_error", "p")] <- NULL
174+
}
175+
176+
out
177+
}))
178+
}

R/extract_random_variances.R

Lines changed: 53 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,18 @@
159159
ci_random = NULL,
160160
verbose = FALSE,
161161
...) {
162-
varcorr <- .get_variance_information(model, component)
163-
if (!inherits(model, "lme")) {
162+
# special handling for lme objects
163+
if (inherits(model, "lme")) {
164+
insight::check_if_installed("lme4")
165+
varcorr <- lme4::VarCorr(model)
166+
class(varcorr) <- "VarCorr.lme"
167+
} else {
168+
varcorr <- insight::get_mixed_info(model, component = component, verbose = FALSE)$vc
169+
}
170+
171+
# we have own data frame methods for VarCorr objects from lme and coxme, so
172+
# only change class attribute for other models
173+
if (!inherits(model, c("lme", "coxme"))) {
164174
class(varcorr) <- "VarCorr.merMod"
165175
}
166176

@@ -332,6 +342,47 @@ as.data.frame.VarCorr.lme <- function(x, row.names = NULL, optional = FALSE, ...
332342
}
333343

334344

345+
#' @export
346+
as.data.frame.VarCorr.coxme <- function(x, row.names = NULL, optional = FALSE, ...) {
347+
# extract variances from VarCorr object
348+
variances <- lapply(x, diag)
349+
# create data frame, similar to as.data.frame.VarCorr.merMod
350+
out <- do.call(rbind, lapply(names(variances), function(i) {
351+
# information on variances
352+
d <- data.frame(
353+
grp = i,
354+
var1 = names(variances[[i]]),
355+
var2 = NA_character_,
356+
vcov = as.numeric(variances[[i]]),
357+
sdcor = sqrt(as.numeric(variances[[i]])),
358+
stringsAsFactors = FALSE
359+
)
360+
# add correlations, if any
361+
if (nrow(x[[i]]) > 1) {
362+
d <- rbind(d, data.frame(
363+
grp = i,
364+
var1 = "(Intercept)",
365+
var2 = rownames(x[[i]])[2],
366+
vcov = NA_real_,
367+
sdcor = as.numeric(x[[i]][2, 1]),
368+
stringsAsFactors = FALSE
369+
))
370+
}
371+
d
372+
}))
373+
374+
# bind residual variance
375+
rbind(out, data.frame(
376+
grp = "Residual",
377+
var1 = NA_character_,
378+
var2 = NA_character_,
379+
vcov = NA_real_,
380+
sdcor = NA_real_,
381+
stringsAsFactors = FALSE
382+
))
383+
}
384+
385+
335386
# extract CI for random SD ------------------------
336387

337388
.random_sd_ci <- function(model,
@@ -684,188 +735,6 @@ as.data.frame.VarCorr.lme <- function(x, row.names = NULL, optional = FALSE, ...
684735
}
685736

686737

687-
# Extract Variance and Correlation Components ----
688-
689-
# store essential information about variance components...
690-
# basically, this function should return lme4::VarCorr(x)
691-
.get_variance_information <- function(model, model_component = "conditional") {
692-
# check if packages are available
693-
.check_mixedmodels_namespace(model)
694-
695-
# stanreg
696-
# ---------------------------
697-
if (inherits(model, "stanreg")) {
698-
varcorr <- lme4::VarCorr(model)
699-
700-
# GLMMapdative
701-
# ---------------------------
702-
} else if (inherits(model, "MixMod")) {
703-
vc1 <- vc2 <- NULL
704-
re_names <- insight::find_random(model)
705-
706-
vc_cond <- !startsWith(colnames(model$D), "zi_")
707-
if (any(vc_cond)) {
708-
vc1 <- model$D[vc_cond, vc_cond, drop = FALSE]
709-
attr(vc1, "stddev") <- sqrt(diag(vc1))
710-
attr(vc1, "correlation") <- stats::cov2cor(model$D[vc_cond, vc_cond, drop = FALSE])
711-
}
712-
713-
vc_zi <- startsWith(colnames(model$D), "zi_")
714-
if (any(vc_zi)) {
715-
colnames(model$D) <- gsub("^zi_(.*)", "\\1", colnames(model$D))
716-
rownames(model$D) <- colnames(model$D)
717-
vc2 <- model$D[vc_zi, vc_zi, drop = FALSE]
718-
attr(vc2, "stddev") <- sqrt(diag(vc2))
719-
attr(vc2, "correlation") <- stats::cov2cor(model$D[vc_zi, vc_zi, drop = FALSE])
720-
}
721-
722-
vc1 <- list(vc1)
723-
names(vc1) <- re_names[[1]]
724-
attr(vc1, "sc") <- sqrt(insight::get_deviance(model, verbose = FALSE) / insight::get_df(model, type = "residual", verbose = FALSE)) # nolint
725-
attr(vc1, "useSc") <- TRUE
726-
727-
if (!is.null(vc2)) {
728-
vc2 <- list(vc2)
729-
names(vc2) <- re_names[[2]]
730-
attr(vc2, "sc") <- sqrt(insight::get_deviance(model, verbose = FALSE) / insight::get_df(model, type = "residual", verbose = FALSE)) # nolint
731-
attr(vc2, "useSc") <- FALSE
732-
}
733-
734-
varcorr <- insight::compact_list(list(vc1, vc2))
735-
names(varcorr) <- c("cond", "zi")[seq_along(varcorr)]
736-
737-
# joineRML
738-
# ---------------------------
739-
} else if (inherits(model, "mjoint")) {
740-
re_names <- insight::find_random(model, flatten = TRUE)
741-
varcorr <- summary(model)$D
742-
attr(varcorr, "stddev") <- sqrt(diag(varcorr))
743-
attr(varcorr, "correlation") <- stats::cov2cor(varcorr)
744-
varcorr <- list(varcorr)
745-
names(varcorr) <- re_names[1]
746-
attr(varcorr, "sc") <- model$coef$sigma2[[1]]
747-
attr(varcorr, "useSc") <- TRUE
748-
749-
# nlme
750-
# ---------------------------
751-
} else if (inherits(model, "lme")) {
752-
varcorr <- lme4::VarCorr(model)
753-
754-
# ordinal
755-
# ---------------------------
756-
} else if (inherits(model, "clmm")) {
757-
varcorr <- ordinal::VarCorr(model)
758-
attr(varcorr, "useSc") <- FALSE
759-
760-
# glmmadmb
761-
# ---------------------------
762-
} else if (inherits(model, "glmmadmb")) {
763-
varcorr <- lme4::VarCorr(model)
764-
765-
# brms
766-
# ---------------------------
767-
} else if (inherits(model, "brmsfit")) {
768-
varcorr <- lapply(names(lme4::VarCorr(model)), function(i) {
769-
element <- lme4::VarCorr(model)[[i]]
770-
if (i != "residual__") {
771-
if (is.null(element$cov)) {
772-
out <- as.matrix(drop(element$sd[, 1])^2)
773-
colnames(out) <- rownames(out) <- gsub("Intercept", "(Intercept)", rownames(element$sd), fixed = TRUE)
774-
} else {
775-
out <- as.matrix(drop(element$cov[, 1, ]))
776-
colnames(out) <- rownames(out) <- gsub("Intercept", "(Intercept)", rownames(element$cov), fixed = TRUE)
777-
}
778-
attr(out, "sttdev") <- element$sd[, 1]
779-
} else {
780-
out <- NULL
781-
}
782-
out
783-
})
784-
varcorr <- insight::compact_list(varcorr)
785-
names(varcorr) <- setdiff(names(lme4::VarCorr(model)), "residual__")
786-
attr(varcorr, "sc") <- lme4::VarCorr(model)$residual__$sd[1, 1]
787-
788-
# cpglmm
789-
# ---------------------------
790-
} else if (inherits(model, "cpglmm")) {
791-
varcorr <- cplm::VarCorr(model)
792-
attr(varcorr, "useSc") <- FALSE
793-
794-
# lme4 / glmmTMB
795-
# ---------------------------
796-
} else {
797-
varcorr <- lme4::VarCorr(model)
798-
}
799-
800-
801-
# for glmmTMB, tell user that dispersion model is ignored
802-
803-
if (inherits(model, c("glmmTMB", "MixMod"))) {
804-
if (is.null(model_component) || model_component == "conditional") {
805-
varcorr <- .collapse_cond(varcorr)
806-
} else {
807-
varcorr <- .collapse_zi(varcorr)
808-
}
809-
}
810-
811-
varcorr
812-
}
813-
814-
815-
.check_mixedmodels_namespace <- function(model) {
816-
# reason to be installed
817-
reason <- "to compute random effect variances for mixed models"
818-
819-
# installed?
820-
insight::check_if_installed("lme4", reason = reason)
821-
822-
if (inherits(model, "lme")) {
823-
insight::check_if_installed("nlme", reason = reason)
824-
}
825-
826-
if (inherits(model, "glmmTMB")) {
827-
insight::check_if_installed("glmmTMB", reason = reason)
828-
}
829-
830-
if (inherits(model, "clmm")) {
831-
insight::check_if_installed("ordinal", reason = reason)
832-
}
833-
834-
if (inherits(model, "brmsfit")) {
835-
insight::check_if_installed("brms", reason = reason)
836-
}
837-
838-
if (inherits(model, "cpglmm")) {
839-
insight::check_if_installed("cplm", reason = reason)
840-
}
841-
842-
if (inherits(model, "rstanarm")) {
843-
insight::check_if_installed("rstanarm", reason = reason)
844-
}
845-
}
846-
847-
848-
# glmmTMB returns a list of model information, one for conditional
849-
# and one for zero-inflation part, so here we "unlist" it, returning
850-
# only the conditional part.
851-
.collapse_cond <- function(x) {
852-
if (is.list(x) && "cond" %in% names(x)) {
853-
x[["cond"]]
854-
} else {
855-
x
856-
}
857-
}
858-
859-
860-
.collapse_zi <- function(x) {
861-
if (is.list(x) && "zi" %in% names(x)) {
862-
x[["zi"]]
863-
} else {
864-
x
865-
}
866-
}
867-
868-
869738
# this is used to only temporarily load merDeriv and to point registered
870739
# methods from merDeriv to lme4-methods. if merDeriv was loaded before,
871740
# nothing will be changed. If merDeriv was not loaded, vcov-methods registered

0 commit comments

Comments
 (0)