From 7c42e6f511c863109a16d35e40666800f113a07d Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Mon, 12 Oct 2026 21:31:47 -0500 Subject: [PATCH 1/5] Print footer line for prediction intervals --- R/estimate_predicted.R | 8 +++++- R/table_footer.R | 6 +++++ tests/testthat/test-table_footer.R | 43 ++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/R/estimate_predicted.R b/R/estimate_predicted.R index 169f42b95..320b13d35 100644 --- a/R/estimate_predicted.R +++ b/R/estimate_predicted.R @@ -591,12 +591,18 @@ estimate_relation <- function( attr(out, "coef_name") <- "Predicted" attr(out, "model_info") <- insight::model_info(model, response = 1) attr(out, "predict") <- predict + # for the footer, we use the type of predictions that insight actually used, + # because insight may change "prediction" into "expectation" for some models + footer_predict <- attr(predictions, "predict", exact = TRUE) + if (is.null(footer_predict)) { + footer_predict <- predict + } attr(out, "table_footer") <- .table_footer( out, by = grid_specs$at, type = "predictions", model = model, - info = c(grid_specs, list(predict = predict), transform = !is.null(transform)) + info = c(grid_specs, list(predict = footer_predict), transform = !is.null(transform)) ) attributes(out) <- c( diff --git a/R/table_footer.R b/R/table_footer.R index e6349197f..67be341ea 100644 --- a/R/table_footer.R +++ b/R/table_footer.R @@ -162,6 +162,12 @@ } } + # tell user about prediction intervals ------------------------------------- + + if (identical(type, "predictions") && identical(info$predict, "prediction")) { + table_footer <- paste0(table_footer, "\nIntervals are prediction intervals.") + } + # Parameter labels for special hypothesis testing -------------------------- # for special hypothesis testing, like "(b1 - b2) = (b4 - b3)", we want to diff --git a/tests/testthat/test-table_footer.R b/tests/testthat/test-table_footer.R index 581f85bd4..8f3ba82d4 100644 --- a/tests/testthat/test-table_footer.R +++ b/tests/testthat/test-table_footer.R @@ -18,3 +18,46 @@ test_that("table_footer", { out <- utils::capture.output(estimate_means(m, "c172code", estimate = "average", verbose = FALSE)) expect_length(out, 10) }) + + +test_that("table_footer, prediction intervals", { + pi_line <- "Intervals are prediction intervals." + m_lm <- lm(mpg ~ wt, data = mtcars) + m_glm <- glm(vs ~ wt, data = mtcars, family = binomial()) + + out <- utils::capture.output(estimate_prediction(m_lm, data = "grid", length = 3)) + expect_identical(sum(out == pi_line), 1L) + out <- utils::capture.output(estimate_prediction(m_glm, data = "grid", length = 3)) + expect_identical(sum(out == pi_line), 1L) + out <- utils::capture.output(estimate_relation(m_lm, predict = "prediction", length = 3)) + expect_identical(sum(out == pi_line), 1L) + + # no prediction intervals + for (m in list(m_lm, m_glm)) { + out <- utils::capture.output(estimate_expectation(m, data = "grid", length = 3)) + expect_false(pi_line %in% out) + out <- utils::capture.output(estimate_relation(m, length = 3)) + expect_false(pi_line %in% out) + out <- utils::capture.output(estimate_link(m, length = 3)) + expect_false(pi_line %in% out) + } + + skip_if_not_installed("lme4") + m_lmer <- lme4::lmer(mpg ~ wt + (1 | cyl), data = mtcars) + out <- utils::capture.output(estimate_prediction(m_lmer, data = "grid", length = 3)) + expect_identical(sum(out == pi_line), 1L) +}) + + +test_that("table_footer, no prediction intervals if insight falls back", { + skip_if_not_installed("mgcv") + m <- mgcv::gam(mpg ~ s(wt), data = mtcars) + # insight returns expectations for gam-models, even if prediction is requested + predict_type <- attr( + suppressWarnings(insight::get_predicted(m, predict = "prediction", ci = 0.95)), + "predict" + ) + skip_if(identical(predict_type, "prediction")) + out <- utils::capture.output(suppressWarnings(estimate_prediction(m, data = "grid", length = 3))) + expect_false("Intervals are prediction intervals." %in% out) +}) From 77e2f567299d5a0e4f9ca74319ef845d11954ade Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Mon, 12 Oct 2026 21:31:47 -0500 Subject: [PATCH 2/5] news, version --- DESCRIPTION | 2 +- NEWS.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index e14b5c72a..fddf7b278 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: modelbased Title: Estimation of Model-Based Predictions, Contrasts and Means -Version: 0.17.0.9 +Version: 0.17.0.10 Authors@R: c(person(given = "Dominique", family = "Makowski", diff --git a/NEWS.md b/NEWS.md index e6e975aa5..4285ae4a2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,6 +31,9 @@ * `comparison = "ratio"` is a new shortcut to compute pairwise comparisons as ratios. +* The printed footer of `estimate_prediction()` and similar functions now + names prediction intervals as such (#485). + ## Bug fixes * Fixed issue in `estimate_contrasts()` with wrong assignment of estimates in From 9c4812da6b5457c5a4cf82a72ac46504b5da6368 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Mon, 12 Oct 2026 21:43:05 -0500 Subject: [PATCH 3/5] Keep requested predict type for the scale line --- R/estimate_predicted.R | 11 ++++++++--- R/table_footer.R | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/R/estimate_predicted.R b/R/estimate_predicted.R index 320b13d35..37246ee3a 100644 --- a/R/estimate_predicted.R +++ b/R/estimate_predicted.R @@ -591,8 +591,9 @@ estimate_relation <- function( attr(out, "coef_name") <- "Predicted" attr(out, "model_info") <- insight::model_info(model, response = 1) attr(out, "predict") <- predict - # for the footer, we use the type of predictions that insight actually used, - # because insight may change "prediction" into "expectation" for some models + # for the prediction-interval line in the footer, we use the type of + # predictions that insight actually used, because insight may change + # "prediction" into "expectation" for some models footer_predict <- attr(predictions, "predict", exact = TRUE) if (is.null(footer_predict)) { footer_predict <- predict @@ -602,7 +603,11 @@ estimate_relation <- function( by = grid_specs$at, type = "predictions", model = model, - info = c(grid_specs, list(predict = footer_predict), transform = !is.null(transform)) + info = c( + grid_specs, + list(predict = predict, predict_used = footer_predict), + transform = !is.null(transform) + ) ) attributes(out) <- c( diff --git a/R/table_footer.R b/R/table_footer.R index 67be341ea..36f777623 100644 --- a/R/table_footer.R +++ b/R/table_footer.R @@ -164,7 +164,7 @@ # tell user about prediction intervals ------------------------------------- - if (identical(type, "predictions") && identical(info$predict, "prediction")) { + if (identical(type, "predictions") && identical(info$predict_used, "prediction")) { table_footer <- paste0(table_footer, "\nIntervals are prediction intervals.") } From 0c89aeb286a5b3b95cfdb9af3bf76d1f3a1ad077 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Mon, 12 Oct 2026 21:53:15 -0500 Subject: [PATCH 4/5] Only print prediction-interval line when intervals are shown --- R/estimate_predicted.R | 8 +++----- R/table_footer.R | 6 +++++- tests/testthat/test-table_footer.R | 4 ++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/R/estimate_predicted.R b/R/estimate_predicted.R index 37246ee3a..3aaa48ace 100644 --- a/R/estimate_predicted.R +++ b/R/estimate_predicted.R @@ -598,16 +598,14 @@ estimate_relation <- function( if (is.null(footer_predict)) { footer_predict <- predict } + footer_info <- c(grid_specs, list(predict = predict), transform = !is.null(transform)) + footer_info$predict_used <- footer_predict attr(out, "table_footer") <- .table_footer( out, by = grid_specs$at, type = "predictions", model = model, - info = c( - grid_specs, - list(predict = predict, predict_used = footer_predict), - transform = !is.null(transform) - ) + info = footer_info ) attributes(out) <- c( diff --git a/R/table_footer.R b/R/table_footer.R index 36f777623..99bba080a 100644 --- a/R/table_footer.R +++ b/R/table_footer.R @@ -164,7 +164,11 @@ # tell user about prediction intervals ------------------------------------- - if (identical(type, "predictions") && identical(info$predict_used, "prediction")) { + if ( + identical(type, "predictions") && + identical(info$predict_used, "prediction") && + "CI_low" %in% colnames(x) + ) { table_footer <- paste0(table_footer, "\nIntervals are prediction intervals.") } diff --git a/tests/testthat/test-table_footer.R b/tests/testthat/test-table_footer.R index 8f3ba82d4..9156b6b50 100644 --- a/tests/testthat/test-table_footer.R +++ b/tests/testthat/test-table_footer.R @@ -32,6 +32,10 @@ test_that("table_footer, prediction intervals", { out <- utils::capture.output(estimate_relation(m_lm, predict = "prediction", length = 3)) expect_identical(sum(out == pi_line), 1L) + # no intervals, no line + out <- utils::capture.output(estimate_prediction(m_lm, data = "grid", length = 3, ci = NULL)) + expect_false(pi_line %in% out) + # no prediction intervals for (m in list(m_lm, m_glm)) { out <- utils::capture.output(estimate_expectation(m, data = "grid", length = 3)) From aeded72fbff7cdd6177a663fa7f149a6c9445249 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Mon, 12 Oct 2026 22:17:55 -0500 Subject: [PATCH 5/5] Update brms snapshots for the new footer line --- tests/testthat/_snaps/brms.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/testthat/_snaps/brms.md b/tests/testthat/_snaps/brms.md index bf59e23fd..e96144b43 100644 --- a/tests/testthat/_snaps/brms.md +++ b/tests/testthat/_snaps/brms.md @@ -25,6 +25,7 @@ Variable predicted: rt, response Predictions are on the response-scale. + Intervals are prediction intervals. --- @@ -53,4 +54,5 @@ Variable predicted: rt, response Predictions are on the response-scale. + Intervals are prediction intervals.