Skip to content

Commit 63a2dcd

Browse files
Handle models without LOOIC in compare_performance() (#960)
Co-authored-by: Daniel <mail@danielluedecke.de>
1 parent f2baaca commit 63a2dcd

5 files changed

Lines changed: 66 additions & 11 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.8
4+
Version: 0.18.2.9
55
Authors@R:
66
c(person(given = "Daniel",
77
family = "Lüdecke",

‎NEWS.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@
99

1010
## Bug fixes
1111

12+
* `compare_performance()` no longer fails when only some of the models have
13+
LOOIC, for example a *brms* model and a `glm` (#738). It now computes
14+
stacking weights for the models that have LOOIC and gives `NA` for the
15+
others. A missing weight now prints as an empty cell, not as `(>.999)`.
16+
1217
* `check_model()` no longer fails for *brms* models that cannot be converted
1318
to a frequentist model. It now returns only the posterior predictive check
1419
and multicollinearity panels, and a message lists the panels that were

‎R/compare_performance.R‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,15 @@ compare_performance <- function(
156156
}
157157

158158
if ("LOOIC" %in% names(dfs)) {
159-
lpd_point <- do.call(cbind, lapply(attri, function(x) x$loo$pointwise[, "elpd_loo"]))
160-
dfs$LOOIC_wt <- as.numeric(loo::stacking_weights(lpd_point))
159+
# stacking weights only for models that have LOO, e.g. not for a glm
160+
# compared with a brms model. NA for the other models.
161+
has_loo <- !vapply(attri, function(x) is.null(x$loo), logical(1))
162+
dfs$LOOIC_wt <- NA_real_
163+
if (sum(has_loo) > 1) {
164+
lpd_point <- do.call(cbind, lapply(attri[has_loo], function(x) x$loo$pointwise[, "elpd_loo"]))
165+
loo_names <- vapply(m[has_loo], function(x) x$Name[1], character(1))
166+
dfs$LOOIC_wt[match(loo_names, dfs$Name)] <- as.numeric(loo::stacking_weights(lpd_point))
167+
}
161168
}
162169

163170
# check if all models were fit from same data
@@ -181,7 +188,7 @@ compare_performance <- function(
181188
}
182189
if (all(c("AIC", "AIC_wt") %in% names(dfs))) {
183190
idx1 <- grep("^AIC$", names(dfs))
184-
idx2 <- grep("AIC_wt", names(dfs), fixed = TRUE)
191+
idx2 <- grep("^AIC_wt$", names(dfs))
185192
last_part <- (idx1 + 1):ncol(dfs)
186193
dfs <- dfs[, c(1:idx1, idx2, last_part[last_part != idx2])]
187194
}

‎R/format.R‎

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,26 @@ format.compare_performance <- function(x, digits = 2, ...) {
99
# format weighted ICs
1010
weighted_ics <- endsWith(colnames(x), "_wt")
1111
if (any(weighted_ics)) {
12-
x[weighted_ics] <- lapply(
13-
x[weighted_ics],
14-
insight::format_bf,
15-
name = NULL,
16-
exact = TRUE
17-
)
12+
x[weighted_ics] <- lapply(x[weighted_ics], function(wt) {
13+
out <- insight::format_bf(wt, name = NULL, exact = TRUE)
14+
# a model without this IC has no weight, not a weight of 1
15+
out[is.na(wt)] <- NA
16+
out
17+
})
1818
}
1919

2020
if ("BF" %in% colnames(x)) {
2121
x$BF[is.na(x$BF)] <- 1
2222
# x$BF <- insight::format_bf(x$BF, exact = TRUE)
2323
}
24-
insight::format_table(x, digits = digits, ...)
24+
out <- insight::format_table(x, digits = digits, ...)
25+
26+
# drop the empty "()" that format_table() adds for a missing weight
27+
weight_columns <- endsWith(colnames(out), "(weights)")
28+
out[weight_columns] <- lapply(out[weight_columns], function(i) {
29+
trimws(gsub("()", "", i, fixed = TRUE))
30+
})
31+
out
2532
}
2633

2734

‎tests/testthat/test-compare_performance.R‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,42 @@ test_that("compare_performance, Log_loss ranks in the right direction", {
251251
})
252252

253253

254+
test_that("compare_performance, LOOIC weights when not all models have LOOIC", {
255+
skip_on_cran()
256+
skip_if_not_installed("brms")
257+
skip_if_not_installed("loo")
258+
skip_if_not_installed("curl")
259+
skip_if_offline()
260+
skip_if_not_installed("httr2")
261+
262+
m_brms <- insight::download_model("brms_1")
263+
skip_if(is.null(m_brms))
264+
m_brms2 <- m_brms
265+
m_lm <- lm(insight::find_formula(m_brms)$conditional, data = insight::get_data(m_brms))
266+
267+
# only one model has LOOIC, so there are no stacking weights (#738)
268+
out <- compare_performance(m_lm, m_brms, verbose = FALSE)
269+
expect_identical(out$Name, c("m_lm", "m_brms"))
270+
expect_true(is.na(out$LOOIC[1]))
271+
expect_false(is.na(out$LOOIC[2]))
272+
expect_identical(out$LOOIC_wt, c(NA_real_, NA_real_))
273+
# a missing weight prints as an empty cell, not as a weight of 1
274+
formatted <- format(out)
275+
expect_identical(formatted[["LOOIC (weights)"]][1], "")
276+
expect_identical(formatted[["AIC (weights)"]][2], "")
277+
expect_identical(formatted[["AIC (weights)"]][1], "156.0")
278+
279+
# stacking weights for the models that have LOOIC, NA for the others
280+
out <- compare_performance(m_lm, m_brms, m_brms2, verbose = FALSE)
281+
expect_identical(out$Name, c("m_lm", "m_brms", "m_brms2"))
282+
expect_true(is.na(out$LOOIC_wt[1]))
283+
expect_equal(sum(out$LOOIC_wt[2:3]), 1, tolerance = 1e-6)
284+
285+
out <- compare_performance(m_lm, m_brms, m_brms2, rank = TRUE, verbose = FALSE)
286+
expect_identical(nrow(out), 3L)
287+
})
288+
289+
254290
test_that("compare_performance, RMSE and Sigma still rank in the right direction", {
255291
data(mtcars)
256292
l_best <- lm(mpg ~ wt + cyl + hp, data = mtcars)

0 commit comments

Comments
 (0)