diff --git a/NEWS.md b/NEWS.md index af643760..469435c1 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,6 @@ # bayesplot (development version) +* Validate user-provided `pit` values in `ppc_loo_pit_data()` and `ppc_loo_pit_qq()`, rejecting non-numeric inputs, missing values, and values outside `[0, 1]`. * Fixed `is_chain_list()` to correctly reject empty lists instead of silently returning `TRUE`. * Added unit tests for `mcmc_areas_ridges_data()`, `mcmc_parcoord_data()`, and `mcmc_trace_data()`. * Added unit tests for `ppc_error_data()` and `ppc_loo_pit_data()` covering output structure, argument handling, and edge cases. diff --git a/R/ppc-loo.R b/R/ppc-loo.R index 2f91c5b5..a3113ee6 100644 --- a/R/ppc-loo.R +++ b/R/ppc-loo.R @@ -302,7 +302,7 @@ ppc_loo_pit_data <- boundary_correction = TRUE, grid_len = 512) { if (!is.null(pit)) { - stopifnot(is.numeric(pit), is_vector_or_1Darray(pit)) + pit <- validate_pit(pit) inform("'pit' specified so ignoring 'y','yrep','lw' if specified.") } else { suggested_package("rstantools") @@ -348,7 +348,7 @@ ppc_loo_pit_qq <- function(y, compare <- match.arg(compare) if (!is.null(pit)) { - stopifnot(is.numeric(pit), is_vector_or_1Darray(pit)) + pit <- validate_pit(pit) inform("'pit' specified so ignoring 'y','yrep','lw' if specified.") } else { suggested_package("rstantools") @@ -795,14 +795,6 @@ ppc_loo_ribbon <- # Generate boundary corrected values via a linear convolution using a # 1-D Gaussian window filter. This method uses the "reflection method" # to estimate these pvalues and helps speed up the code - if (any(is.infinite(x))) { - warn(paste( - "Ignored", sum(is.infinite(x)), - "Non-finite PIT values are invalid for KDE boundary correction method" - )) - x <- x[is.finite(x)] - } - if (grid_len < 100) { grid_len <- 100 } @@ -819,6 +811,10 @@ ppc_loo_ribbon <- # 1-D Convolution bc_pvals <- .linear_convolution(x, bw, grid_counts, grid_breaks, grid_len) + if (all(is.na(bc_pvals))) { + abort("KDE boundary correction produced all NA values.") + } + # Generate vector of x-axis values for plotting based on binned relative freqs n_breaks <- length(grid_breaks) diff --git a/tests/testthat/test-ppc-loo.R b/tests/testthat/test-ppc-loo.R index a722ad46..ecc5b475 100644 --- a/tests/testthat/test-ppc-loo.R +++ b/tests/testthat/test-ppc-loo.R @@ -59,17 +59,42 @@ test_that("ppc_loo_pit_overlay works with boundary_correction=FALSE", { expect_gg(p1) }) -test_that(".kde_correction warns when PIT values are non-finite", { - set.seed(123) - pit_vals <- c(stats::runif(500), Inf) - expect_warning( - out <- .kde_correction(pit_vals, bw = "nrd0", grid_len = 128), - "Non-finite PIT values are invalid" +test_that("ppc_loo_pit_data validates user-provided pit values", { + expect_error( + ppc_loo_pit_data(pit = c(0.5, Inf)), + "between 0 and 1" + ) + expect_error( + ppc_loo_pit_data(pit = c(-1, 0.5)), + "between 0 and 1" + ) + expect_error( + ppc_loo_pit_data(pit = c(0.5, NA)), + "NAs not allowed" + ) + expect_error( + ppc_loo_pit_data(pit = "not numeric"), + "is.numeric" + ) +}) + +test_that("ppc_loo_pit_qq validates user-provided pit values", { + expect_error( + ppc_loo_pit_qq(pit = c(0.5, Inf)), + "between 0 and 1" + ) + expect_error( + ppc_loo_pit_qq(pit = c(-1, 0.5)), + "between 0 and 1" + ) + expect_error( + ppc_loo_pit_qq(pit = c(0.5, NA)), + "NAs not allowed" + ) + expect_error( + ppc_loo_pit_qq(pit = "not numeric"), + "is.numeric" ) - expect_type(out, "list") - expect_true(all(c("xs", "bc_pvals") %in% names(out))) - expect_equal(length(out$xs), 128) - expect_equal(length(out$bc_pvals), 128) }) test_that("ppc_loo_pit_qq returns ggplot object", {