From 647b08b8e716ec578045bfa4bb5b930a38527024 Mon Sep 17 00:00:00 2001 From: Balasubramanian Narasimhan Date: Tue, 7 Apr 2026 17:48:31 -0700 Subject: [PATCH 1/8] Fix issues #2, #7, #4/#1; subsumes PR #8 and PR #5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Issue #2: Grouped jackknife Imat construction was using sapply(seq_len_m, sample.int, ...) which passed iteration values as the first positional arg of sample.int, overriding the keyword n= and enabling replacement. Fix: matrix(sample.int(n, n-r), nrow=m) restores the correct without-replacement partition. Regenerated bcajack fixture since old fixture captured buggy acceleration values. Issue #7: Matrix subsetting x[-i, ] on single-column matrices drops the dimension, returning a vector instead of a matrix. This causes type inconsistency between jackknife (vector) and bootstrap (matrix) calls to func. Fix: drop=FALSE on all 5 matrix subsetting sites that pass data to func (jackknife_accel, bootstrap_resample, bca_nonpar). Issue #4/#1 (+ PR #5 idea): regression_accel silently produced NAs when ncol(Y) > length(nearby_idx), making lm() underdetermined. Fix: explicit check with actionable error message suggesting to increase B, increase kl_fraction, or use accel="jackknife". PR #8 (Bettina Gruen): Missing return() in K=0 path was already fixed in the tidy rewrite — bca_nonpar/bca_par use explicit return(new_bcaboot(...)). --- R/bca_core.R | 21 ++++++++++++++++----- R/bca_nonpar.R | 2 +- inst/tinytest/fixtures/ref_bcajack.rds | Bin 3378 -> 3503 bytes inst/tinytest/test_regression.R | 4 ++-- 4 files changed, 19 insertions(+), 8 deletions(-) diff --git a/R/bca_core.R b/R/bca_core.R index 3076355..f02e5eb 100644 --- a/R/bca_core.R +++ b/R/bca_core.R @@ -34,6 +34,17 @@ regression_accel <- function(Y, tt, t0, pct) { kl_cutoff <- stats::quantile(kl_dist, pct) nearby_idx <- seq_len(B)[kl_dist <= kl_cutoff] + if (length(nearby_idx) < m) { + stop(sprintf( + paste0("Regression for acceleration estimation is underdetermined: ", + "%d nearby bootstrap samples but %d columns in the count matrix. ", + "Possible fixes: (1) increase B so that B * kl_fraction > %d, ", + "(2) increase kl_fraction (currently %.3f), or ", + "(3) use accel = \"jackknife\" which avoids this regression entirely."), + length(nearby_idx), m, m, pct + ), call. = FALSE) + } + ## Influence via local regression on nearby count vectors reg_infl <- as.vector(m * stats::lm(tt[nearby_idx] ~ Y[nearby_idx, ] - 1)$coef) reg_infl <- reg_infl - mean(reg_infl) @@ -65,7 +76,7 @@ jackknife_accel <- function(x, func, ..., m, mr) { if (m == n) { for (i in seq_len(n)) { - u[i] <- func(x[-i, ], ...) + u[i] <- func(x[-i, , drop = FALSE], ...) } jack_infl <- (mean(u) - u) * (m - 1) a <- (1 / 6) * sum(jack_infl^3) / (sum(jack_infl^2))^1.5 @@ -75,12 +86,12 @@ jackknife_accel <- function(x, func, ..., m, mr) { r <- n %% m seq_len_m <- seq_len(m) for (k in seq_len(mr)) { - Imat <- sapply(seq_len_m, sample.int, n = n, size = n - r) + Imat <- matrix(sample.int(n, size = n - r), nrow = m) Iout <- setdiff(seq_len(n), Imat) for (j in seq_len_m) { Ij <- setdiff(seq_len_m, j) ij <- c(c(Imat[Ij, ], Iout)) - u[j] <- func(x[ij, ]) + u[j] <- func(x[ij, , drop = FALSE]) } jack_infl <- (mean(u) - u) * (m - 1) aa[k] <- (1/6) * sum(jack_infl^3)/(sum(jack_infl^2))^1.5 @@ -123,7 +134,7 @@ bootstrap_resample <- function(x, B, func, ..., m, verbose, boot_data = NULL) { if (verbose) pb <- utils::txtProgressBar(min = 0, max = B, style = 3) for (k in seq_len(B)) { ik <- ii[k, ] - tt[k] <- func(x[ik, ], ...) + tt[k] <- func(x[ik, , drop = FALSE], ...) Y[k, ] <- table(c(ik, 1:n)) - 1 if (verbose) utils::setTxtProgressBar(pb, k) } @@ -140,7 +151,7 @@ bootstrap_resample <- function(x, B, func, ..., m, verbose, boot_data = NULL) { ik <- ii[k, ] Ik <- c(t(Imat[ik, ])) Ik <- c(Ik, Iout) - tt[k] <- func(x[Ik, ], ...) + tt[k] <- func(x[Ik, , drop = FALSE], ...) Y[k, ] <- table(c(ik, 1:m)) - 1 if (verbose) utils::setTxtProgressBar(pb, k) } diff --git a/R/bca_nonpar.R b/R/bca_nonpar.R index 3710180..062b85f 100644 --- a/R/bca_nonpar.R +++ b/R/bca_nonpar.R @@ -114,7 +114,7 @@ bca_nonpar <- function(x, B, func, ..., for (j in seq_len(B)) { ij <- sample(x = n, size = n, replace = TRUE) Yj <- table(c(ij, 1:n)) - 1 - tt[j] <- func(x[ij, ], ...) + tt[j] <- func(x[ij, , drop = FALSE], ...) boot_wt_sum <- boot_wt_sum + tt[j] * Yj boot_ct_sum <- boot_ct_sum + Yj if (verbose) utils::setTxtProgressBar(pb, j) diff --git a/inst/tinytest/fixtures/ref_bcajack.rds b/inst/tinytest/fixtures/ref_bcajack.rds index c96c9b61372112aeb564cc40fbba91eebce11437..f39d061ee0150079e20f1aa129eed4b9cc2a5f0d 100644 GIT binary patch literal 3503 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 literal 3378 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 IUE>Y_00PsqN&o-= diff --git a/inst/tinytest/test_regression.R b/inst/tinytest/test_regression.R index 9942090..de1e69e 100644 --- a/inst/tinytest/test_regression.R +++ b/inst/tinytest/test_regression.R @@ -29,9 +29,9 @@ cur <- bca_nonpar(x = Xy, B = 1000, func = rfun, verbose = FALSE) ## BCa and std limits must match exactly -expect_equal(cur$limits[, "bca"], ref$lims[, "bca"], tolerance = tol, +expect_equal(cur$limits[, "bca"], ref$limits[, "bca"], tolerance = tol, info = "bcajack equiv: bca limits match") -expect_equal(cur$limits[, "std"], ref$lims[, "std"], tolerance = tol, +expect_equal(cur$limits[, "std"], ref$limits[, "std"], tolerance = tol, info = "bcajack equiv: std limits match") ## Stats estimate row must match expect_equal(unname(cur$stats["est", ]), unname(ref$stats["est", ]), From 31789441f801f6d7d0d83e292c8c34054a854b94 Mon Sep 17 00:00:00 2001 From: Balasubramanian Narasimhan Date: Tue, 7 Apr 2026 17:57:51 -0700 Subject: [PATCH 2/8] Migrate all user-facing messages to cli Replace stop/warning/cat with cli::cli_abort/cli_warn/cli_text/cli_h3 across all R files for consistent, formatted output: - Error messages with bullet points and styled arg/code/pkg references - Print method with cli headers and styled labels - Warnings with cli formatting - Add cli to Imports in DESCRIPTION --- DESCRIPTION | 3 ++- R/abcpar.R | 2 +- R/autoplot.R | 7 ++++--- R/bca_core.R | 19 +++++++++---------- R/bca_nonpar.R | 3 +-- R/deprecated.R | 9 +++++---- R/utils.R | 26 +++++++++++++------------- 7 files changed, 35 insertions(+), 34 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0e7494a..e755614 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -21,7 +21,8 @@ Suggests: rmarkdown, knitr, tinytest -Imports: graphics, +Imports: cli, + graphics, generics, lifecycle, stats, diff --git a/R/abcpar.R b/R/abcpar.R index 9d9fabc..6b8903b 100644 --- a/R/abcpar.R +++ b/R/abcpar.R @@ -29,7 +29,7 @@ abcpar <- function (func,bb,lambda = 0.001,alpha = c(0.025, 0.05, 0.1, 0.16), .. ## Derivatives are computed along eigenvectors (principal directions). eig <- eigen(S) if (any(eig$values <= 0)) - stop("Covariance matrix of bb is singular or near-singular; ABC method requires full rank") + cli::cli_abort("Covariance matrix of {.arg bb} is singular or near-singular; ABC method requires full rank.") evals <- (eig$values)^0.5 evecs <- as.matrix(eig$vectors) ## Numerical first (b.) and second (b..) derivatives of func along each diff --git a/R/autoplot.R b/R/autoplot.R index ef4de89..54db1bc 100644 --- a/R/autoplot.R +++ b/R/autoplot.R @@ -24,9 +24,10 @@ #' @export autoplot.bcaboot <- function(object, ...) { if (!requireNamespace("ggplot2", quietly = TRUE)) { - stop("Package 'ggplot2' is required for autoplot(). ", - "Install it with install.packages('ggplot2').", - call. = FALSE) + cli::cli_abort(c( + "Package {.pkg ggplot2} is required for {.fun autoplot}.", + "i" = "Install it with {.code install.packages(\"ggplot2\")}." + )) } df <- tidy.bcaboot(object) diff --git a/R/bca_core.R b/R/bca_core.R index f02e5eb..0e81516 100644 --- a/R/bca_core.R +++ b/R/bca_core.R @@ -35,14 +35,13 @@ regression_accel <- function(Y, tt, t0, pct) { nearby_idx <- seq_len(B)[kl_dist <= kl_cutoff] if (length(nearby_idx) < m) { - stop(sprintf( - paste0("Regression for acceleration estimation is underdetermined: ", - "%d nearby bootstrap samples but %d columns in the count matrix. ", - "Possible fixes: (1) increase B so that B * kl_fraction > %d, ", - "(2) increase kl_fraction (currently %.3f), or ", - "(3) use accel = \"jackknife\" which avoids this regression entirely."), - length(nearby_idx), m, m, pct - ), call. = FALSE) + cli::cli_abort(c( + "Regression for acceleration estimation is underdetermined.", + "x" = "{length(nearby_idx)} nearby bootstrap samples but {m} columns in the count matrix.", + "i" = "Increase {.arg B} so that {.code B * kl_fraction > {m}}.", + "i" = "Increase {.arg kl_fraction} (currently {round(pct, 3)}).", + "i" = "Use {.code accel = \"jackknife\"} which avoids this regression." + )) } ## Influence via local regression on nearby count vectors @@ -102,7 +101,7 @@ jackknife_accel <- function(x, func, ..., m, mr) { ## Use the last partition's influence values (consistent with original bcajack) jack_infl <- (mean(u) - u) * (m - 1) } else { - stop("m must be <= n") + cli::cli_abort("{.arg n_groups} ({m}) must be <= number of observations ({n}).") } list(a = a, sdjack = sdjack, jack_infl = jack_infl) @@ -157,7 +156,7 @@ bootstrap_resample <- function(x, B, func, ..., m, verbose, boot_data = NULL) { } if (verbose) close(pb) } else { - stop("m must be <= n") + cli::cli_abort("{.arg n_groups} ({m}) must be <= number of observations ({n}).") } list(Y = Y, tt = tt, t0 = t0, B = B) diff --git a/R/bca_nonpar.R b/R/bca_nonpar.R index 062b85f..909fcb6 100644 --- a/R/bca_nonpar.R +++ b/R/bca_nonpar.R @@ -154,8 +154,7 @@ bca_nonpar <- function(x, B, func, ..., diagnostic <- tryCatch( gbca_diagnostic(Y, tt, t0, a, sdjack, local_dir, alpha), error = function(e) { - warning("gbca diagnostic failed: ", conditionMessage(e), - call. = FALSE) + cli::cli_warn("gbca diagnostic failed: {conditionMessage(e)}") NULL } ) diff --git a/R/deprecated.R b/R/deprecated.R index 4ce4254..9e5f03b 100644 --- a/R/deprecated.R +++ b/R/deprecated.R @@ -53,7 +53,7 @@ bcajack2 <- function(x, B, func, ..., m = NULL, mr, pct = 0.333, K = 2, J = 12, alpha = c(0.025, 0.05, 0.1, 0.16), verbose = TRUE) { lifecycle::deprecate_warn("1.0", "bcajack2()", "bca_nonpar()") if (!missing(mr)) - warning("'mr' argument is unused in bcajack2/bca_nonpar with regression acceleration") + cli::cli_warn("{.arg mr} is unused with regression acceleration.") ## Handle B as Blist boot_data <- if (is.list(B)) B else NULL if (is.null(m) && !is.list(B)) m <- nrow(x) @@ -115,9 +115,10 @@ bcaplot <- function(vl, ...) { if (requireNamespace("ggplot2", quietly = TRUE)) { print(ggplot2::autoplot(vl, ...)) } else { - warning("ggplot2 is required for plotting. ", - "Install it with install.packages('ggplot2').", - call. = FALSE) + cli::cli_warn(c( + "Package {.pkg ggplot2} is required for plotting.", + "i" = "Install it with {.code install.packages(\"ggplot2\")}." + )) } invisible(vl) } diff --git a/R/utils.R b/R/utils.R index 009dbbd..40b6d01 100644 --- a/R/utils.R +++ b/R/utils.R @@ -58,12 +58,12 @@ print.bcaboot <- function(x, digits = getOption("digits"), ...) { ## --- Header --- method_str <- x$method accel_str <- x$accel - cat("BCa Bootstrap Confidence Intervals\n") + cli::cli_h3("BCa Bootstrap Confidence Intervals") if (!is.null(method_str)) { if (!is.null(accel_str) && !is.na(accel_str)) - cat(sprintf(" Method: %s (%s acceleration)\n", method_str, accel_str)) + cli::cli_text("Method: {method_str} ({accel_str} acceleration)") else - cat(sprintf(" Method: %s\n", method_str)) + cli::cli_text("Method: {method_str}") } ## Extract theta and sdboot from stats (vector or matrix) @@ -75,9 +75,8 @@ print.bcaboot <- function(x, digits = getOption("digits"), ...) { sdboot <- stats[2] } B_mean <- x$B_mean %||% x$B.mean - cat(sprintf(" B = %g, theta = %s, sdboot = %s\n\n", - B_mean[1], format(theta, digits = digits), - format(sdboot, digits = digits))) + cli::cli_text("B = {B_mean[1]}, theta = {format(theta, digits = digits)}, sdboot = {format(sdboot, digits = digits)}") + cli::cli_text("") ## --- Confidence limits table --- ## Read alpha from rownames @@ -100,15 +99,16 @@ print.bcaboot <- function(x, digits = getOption("digits"), ...) { tbl$std.hi <- lims[rev(hi_idx), std_col] } - cat("Confidence limits:\n") + cli::cli_text("{.strong Confidence limits:}") print(tbl, digits = digits, row.names = FALSE) } else { - cat("Limits:\n") + cli::cli_text("{.strong Limits:}") print(lims, digits = digits) } ## --- Diagnostics line --- - cat("\nDiagnostics:\n") + cli::cli_text("") + cli::cli_text("{.strong Diagnostics:}") if (is.matrix(stats)) { row <- if ("est" %in% rownames(stats)) "est" else rownames(stats)[1] z0 <- stats[row, "z0"] @@ -120,12 +120,12 @@ print.bcaboot <- function(x, digits = getOption("digits"), ...) { sdjack <- stats["sdjack"] } diag_parts <- c( - sprintf("z0 = %s", format(z0, digits = digits)), - sprintf("a = %s", format(a, digits = digits)) + paste0("z0 = ", format(z0, digits = digits)), + paste0("a = ", format(a, digits = digits)) ) if (!is.na(sdjack)) - diag_parts <- c(diag_parts, sprintf("sdjack = %s", format(sdjack, digits = digits))) - cat(sprintf(" %s\n", paste(diag_parts, collapse = ", "))) + diag_parts <- c(diag_parts, paste0("sdjack = ", format(sdjack, digits = digits))) + cli::cli_text(" {paste(diag_parts, collapse = ', ')}") invisible(x) } From ba5ade418586fc1029b9ebcb81ab6d221e6ff016 Mon Sep 17 00:00:00 2001 From: Balasubramanian Narasimhan Date: Tue, 7 Apr 2026 18:00:58 -0700 Subject: [PATCH 3/8] Update NEWS.md with bug fixes, regenerate pkgdown site --- NEWS.md | 35 +++++++++++++++-- docs/articles/bcaboot.html | 32 ++++------------ docs/articles/bcaboot.md | 24 ++---------- docs/news/index.html | 10 ++++- docs/news/index.md | 37 +++++++++++++++++- docs/pkgdown.yml | 2 +- docs/reference/bcajack.html | 25 +++++++------ docs/reference/bcajack.md | 17 +++++---- docs/reference/bcajack2.html | 17 +++++---- docs/reference/bcajack2.md | 9 +++-- docs/reference/bcapar.html | 17 +++++---- docs/reference/bcapar.md | 9 +++-- docs/reference/figures/make_logo.R | 60 ++++++++++++++++++++++++++++++ docs/search.json | 2 +- 14 files changed, 201 insertions(+), 95 deletions(-) create mode 100644 docs/reference/figures/make_logo.R diff --git a/NEWS.md b/NEWS.md index 25f8d1a..4661b75 100644 --- a/NEWS.md +++ b/NEWS.md @@ -24,12 +24,39 @@ ## Deprecations * `bcajack()`, `bcajack2()`, `bcapar()`, and `bcaplot()` are - deprecated with `lifecycle` warnings (once per session). They will + deprecated with `lifecycle` warnings (once per session). They will continue to work through thin wrappers that translate to the new API. +## Bug fixes + +* **Issue #2**: Fixed grouped jackknife (`n_groups < n`) using + `sapply(seq_len_m, sample.int, ...)` which accidentally passed + iteration values as the first positional arg, overriding the + `n=` keyword and enabling replacement. Replaced with + `matrix(sample.int(n, n-r), nrow=m)` for correct without-replacement + partition. (Reported by admash) + +* **Issue #7**: Fixed dimension drop when `x` is a single-column + matrix (vector input). `x[-i, ]` on an `n x 1` matrix returned a + vector instead of a matrix, causing type inconsistency between + jackknife and bootstrap code paths. Added `drop = FALSE` to all + matrix subsetting passed to `func`. (Reported by R180) + +* **Issue #4 / #1**: `regression_accel` now checks for underdetermined + regression (`nearby samples < ncol(Y)`) and errors with an + actionable message instead of silently returning NAs. Suggests + increasing `B`, increasing `kl_fraction`, or switching to + `accel = "jackknife"`. (Reported by Tim Pollington, Thomas Covert) + +* **PR #8**: Fixed missing `return()` in `K = 0` early-exit path of + `bcajack()`, `bcajack2()`, and `bcapar()`, which caused execution + to fall through to the `K > 0` code. In the rewrite, all early + exits use explicit `return(new_bcaboot(...))`. (Reported by Bettina + Gruen) + ## New features - + * `tidy()` method: returns a tibble with one row per (confidence level, method) pair, following broom conventions (`conf.level`, `method`, `estimate`, `conf.low`, `conf.high`). @@ -47,9 +74,11 @@ * `bca_nonpar()` always computes the gbca diagnostic when using `accel = "regression"` (with `tryCatch` for robustness). +* All user-facing messages use `cli` for consistent formatting. + ## Dependencies -* Added `tibble`, `generics`, `lifecycle` to Imports. +* Added `cli`, `tibble`, `generics`, `lifecycle` to Imports. * Added `ggplot2` to Suggests (for `autoplot()`). ## Internal diff --git a/docs/articles/bcaboot.html b/docs/articles/bcaboot.html index c5d4f93..c083212 100644 --- a/docs/articles/bcaboot.html +++ b/docs/articles/bcaboot.html @@ -137,19 +137,11 @@

A Nonparametric ExampleThe result prints a clean summary:

 result
-
## BCa Bootstrap Confidence Intervals
-##   Method: nonpar (regression acceleration)
-##   B = 2000, theta = 0.5065603, sdboot = 0.03245877
-## 
-## Confidence limits:
-##  conf.level    bca.lo    bca.hi    std.lo    std.hi
+
##  conf.level    bca.lo    bca.hi    std.lo    std.hi
 ##        0.95 0.4221740 0.5613841 0.4429423 0.5701783
 ##        0.90 0.4392968 0.5507219 0.4531704 0.5599503
 ##        0.80 0.4541906 0.5387434 0.4649627 0.5481579
-##        0.68 0.4647324 0.5298461 0.4742814 0.5388392
-## 
-## Diagnostics:
-##   z0 = -0.2676094, a = -0.005846673, sdjack = 0.03232833
+## 0.68 0.4647324 0.5298461 0.4742814 0.5388392
@@ -322,19 +314,11 @@

A Logistic ModelWe can examine the results using tidy methods:

 glm_bca
-
## BCa Bootstrap Confidence Intervals
-##   Method: par
-##   B = 2000, theta = 0.9430645, sdboot = 0.1549141
-## 
-## Confidence limits:
-##  conf.level    bca.lo   bca.hi    std.lo   std.hi
+
##  conf.level    bca.lo   bca.hi    std.lo   std.hi
 ##        0.95 0.5981733 1.213676 0.6394385 1.246691
 ##        0.90 0.6549470 1.167789 0.6882535 1.197876
 ##        0.80 0.7169529 1.113660 0.7445341 1.141595
-##        0.68 0.7635391 1.067160 0.7890090 1.097120
-## 
-## Diagnostics:
-##   z0 = -0.2147016, a = -0.01940269
+## 0.68 0.7635391 1.067160 0.7890090 1.097120
 tidy(glm_bca)
## # A tibble: 8 × 7
diff --git a/docs/articles/bcaboot.md b/docs/articles/bcaboot.md
index 23d5ab8..2c9869e 100644
--- a/docs/articles/bcaboot.md
+++ b/docs/articles/bcaboot.md
@@ -65,19 +65,11 @@ The `result` prints a clean summary:
 result
 ```
 
-    ## BCa Bootstrap Confidence Intervals
-    ##   Method: nonpar (regression acceleration)
-    ##   B = 2000, theta = 0.5065603, sdboot = 0.03245877
-    ## 
-    ## Confidence limits:
     ##  conf.level    bca.lo    bca.hi    std.lo    std.hi
     ##        0.95 0.4221740 0.5613841 0.4429423 0.5701783
     ##        0.90 0.4392968 0.5507219 0.4531704 0.5599503
     ##        0.80 0.4541906 0.5387434 0.4649627 0.5481579
     ##        0.68 0.4647324 0.5298461 0.4742814 0.5388392
-    ## 
-    ## Diagnostics:
-    ##   z0 = -0.2676094, a = -0.005846673, sdjack = 0.03232833
 
 ### Tidy output
 
@@ -147,13 +139,13 @@ tidy(result_jk)
     ## # A tibble: 8 × 7
     ##   conf.level method   estimate conf.low conf.high jacksd.low jacksd.high
     ##                                      
-    ## 1       0.95 bca         0.507    0.435     0.560    0.00397     0.00195
+    ## 1       0.95 bca         0.507    0.435     0.560    0.00412     0.00155
     ## 2       0.95 standard    0.507    0.443     0.570   NA          NA      
-    ## 3       0.9  bca         0.507    0.443     0.551    0.00564     0.00279
+    ## 3       0.9  bca         0.507    0.443     0.551    0.00497     0.00224
     ## 4       0.9  standard    0.507    0.453     0.560   NA          NA      
-    ## 5       0.8  bca         0.507    0.454     0.540    0.00162     0.00205
+    ## 5       0.8  bca         0.507    0.454     0.540    0.00127     0.00158
     ## 6       0.8  standard    0.507    0.465     0.548   NA          NA      
-    ## 7       0.68 bca         0.507    0.464     0.531    0.00213     0.00204
+    ## 7       0.68 bca         0.507    0.464     0.531    0.00186     0.00177
     ## 8       0.68 standard    0.507    0.474     0.539   NA          NA
 
 ## A Parametric Example
@@ -269,19 +261,11 @@ We can examine the results using tidy methods:
 glm_bca
 ```
 
-    ## BCa Bootstrap Confidence Intervals
-    ##   Method: par
-    ##   B = 2000, theta = 0.9430645, sdboot = 0.1549141
-    ## 
-    ## Confidence limits:
     ##  conf.level    bca.lo   bca.hi    std.lo   std.hi
     ##        0.95 0.5981733 1.213676 0.6394385 1.246691
     ##        0.90 0.6549470 1.167789 0.6882535 1.197876
     ##        0.80 0.7169529 1.113660 0.7445341 1.141595
     ##        0.68 0.7635391 1.067160 0.7890090 1.097120
-    ## 
-    ## Diagnostics:
-    ##   z0 = -0.2147016, a = -0.01940269
 
 ``` r
 
diff --git a/docs/news/index.html b/docs/news/index.html
index 6241032..53d05bf 100644
--- a/docs/news/index.html
+++ b/docs/news/index.html
@@ -48,16 +48,24 @@ 

Deprecationsbcajack(), bcajack2(), bcapar(), and bcaplot() are deprecated with lifecycle warnings (once per session). They will continue to work through thin wrappers that translate to the new API.
+

Bug fixes

+
  • Issue #2: Fixed grouped jackknife (n_groups < n) using sapply(seq_len_m, sample.int, ...) which accidentally passed iteration values as the first positional arg, overriding the n= keyword and enabling replacement. Replaced with matrix(sample.int(n, n-r), nrow=m) for correct without-replacement partition. (Reported by admash)

  • +
  • Issue #7: Fixed dimension drop when x is a single-column matrix (vector input). x[-i, ] on an n x 1 matrix returned a vector instead of a matrix, causing type inconsistency between jackknife and bootstrap code paths. Added drop = FALSE to all matrix subsetting passed to func. (Reported by R180)

  • +
  • Issue #4 / #1: regression_accel now checks for underdetermined regression (nearby samples < ncol(Y)) and errors with an actionable message instead of silently returning NAs. Suggests increasing B, increasing kl_fraction, or switching to accel = "jackknife". (Reported by Tim Pollington, Thomas Covert)

  • +
  • PR #8: Fixed missing return() in K = 0 early-exit path of bcajack(), bcajack2(), and bcapar(), which caused execution to fall through to the K > 0 code. In the rewrite, all early exits use explicit return(new_bcaboot(...)). (Reported by Bettina Gruen)

  • +
+

New features

  • tidy() method: returns a tibble with one row per (confidence level, method) pair, following broom conventions (conf.level, method, estimate, conf.low, conf.high).

  • glance() method: returns a one-row tibble summarizing the bootstrap run (method, accel, theta, sdboot, z0, a, sdjack, B, boot_mean).

  • autoplot() method: ggplot2 visualization of BCa vs standard confidence intervals. Requires ggplot2 (in Suggests).

  • Improved print(): formatted summary with confidence limits table instead of raw list dump.

  • bca_nonpar() always computes the gbca diagnostic when using accel = "regression" (with tryCatch for robustness).

  • +
  • All user-facing messages use cli for consistent formatting.

Dependencies

-
  • Added tibble, generics, lifecycle to Imports.
  • +
    • Added cli, tibble, generics, lifecycle to Imports.
    • Added ggplot2 to Suggests (for autoplot()).
diff --git a/docs/news/index.md b/docs/news/index.md index 9d05461..b3b4f28 100644 --- a/docs/news/index.md +++ b/docs/news/index.md @@ -35,6 +35,39 @@ warnings (once per session). They will continue to work through thin wrappers that translate to the new API. +### Bug fixes + +- **Issue [\#2](https://github.com/bnaras/bcaboot/issues/2)**: Fixed + grouped jackknife (`n_groups < n`) using + `sapply(seq_len_m, sample.int, ...)` which accidentally passed + iteration values as the first positional arg, overriding the `n=` + keyword and enabling replacement. Replaced with + `matrix(sample.int(n, n-r), nrow=m)` for correct without-replacement + partition. (Reported by admash) + +- **Issue [\#7](https://github.com/bnaras/bcaboot/issues/7)**: Fixed + dimension drop when `x` is a single-column matrix (vector input). + `x[-i, ]` on an `n x 1` matrix returned a vector instead of a matrix, + causing type inconsistency between jackknife and bootstrap code paths. + Added `drop = FALSE` to all matrix subsetting passed to `func`. + (Reported by R180) + +- **Issue [\#4](https://github.com/bnaras/bcaboot/issues/4) / + [\#1](https://github.com/bnaras/bcaboot/issues/1)**: + `regression_accel` now checks for underdetermined regression + (`nearby samples < ncol(Y)`) and errors with an actionable message + instead of silently returning NAs. Suggests increasing `B`, increasing + `kl_fraction`, or switching to `accel = "jackknife"`. (Reported by Tim + Pollington, Thomas Covert) + +- **PR [\#8](https://github.com/bnaras/bcaboot/issues/8)**: Fixed + missing [`return()`](https://rdrr.io/r/base/function.html) in `K = 0` + early-exit path of [`bcajack()`](../reference/bcajack.md), + [`bcajack2()`](../reference/bcajack2.md), and + [`bcapar()`](../reference/bcapar.md), which caused execution to fall + through to the `K > 0` code. In the rewrite, all early exits use + explicit `return(new_bcaboot(...))`. (Reported by Bettina Gruen) + ### New features - `tidy()` method: returns a tibble with one row per (confidence level, @@ -55,9 +88,11 @@ - `bca_nonpar()` always computes the gbca diagnostic when using `accel = "regression"` (with `tryCatch` for robustness). +- All user-facing messages use `cli` for consistent formatting. + ### Dependencies -- Added `tibble`, `generics`, `lifecycle` to Imports. +- Added `cli`, `tibble`, `generics`, `lifecycle` to Imports. - Added `ggplot2` to Suggests (for [`autoplot()`](https://ggplot2.tidyverse.org/reference/autoplot.html)). diff --git a/docs/pkgdown.yml b/docs/pkgdown.yml index 4d28cae..87c862a 100644 --- a/docs/pkgdown.yml +++ b/docs/pkgdown.yml @@ -3,4 +3,4 @@ pkgdown: 2.2.0 pkgdown_sha: ~ articles: bcaboot: bcaboot.html -last_built: 2026-04-07T21:27Z +last_built: 2026-04-08T01:00Z diff --git a/docs/reference/bcajack.html b/docs/reference/bcajack.html index be6057f..322c1ad 100644 --- a/docs/reference/bcajack.html +++ b/docs/reference/bcajack.html @@ -196,19 +196,20 @@

Examplesbcajack(x = Xy, B = 1000, func = rfun, m = 34, verbose = FALSE) #> Warning: `bcajack()` was deprecated in bcaboot 1.0. #> ℹ Please use `bca_nonpar()` instead. -#> BCa Bootstrap Confidence Intervals -#> Method: nonpar (jackknife acceleration) -#> B = 1000, theta = 0.5065603, sdboot = 0.03287545 -#> -#> Confidence limits: +#> +#> ── BCa Bootstrap Confidence Intervals +#> Method: nonpar (jackknife acceleration) +#> B = 1000, theta = 0.5065603, sdboot = 0.03284722 +#> +#> Confidence limits: #> conf.level bca.lo bca.hi std.lo std.hi -#> 0.95 0.4349782 0.5615867 0.4421256 0.5709950 -#> 0.90 0.4425891 0.5527242 0.4524850 0.5606356 -#> 0.80 0.4545379 0.5408992 0.4644287 0.5486919 -#> 0.68 0.4643673 0.5312118 0.4738671 0.5392536 -#> -#> Diagnostics: -#> z0 = -0.2455895, a = -0.001290911, sdjack = 0.01864547 +#> 0.95 0.4238753 0.5582875 0.4421809 0.5709397 +#> 0.90 0.4342607 0.5496734 0.4525314 0.5605892 +#> 0.80 0.4513913 0.5388794 0.4644649 0.5486557 +#> 0.68 0.4617252 0.5305165 0.4738951 0.5392255 +#> +#> Diagnostics: +#> z0 = -0.26112, a = -0.01143494, sdjack = 0.02985439