Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
9f9e9ad
use posterior::gpdfit
avehtari Oct 17, 2025
05c69e1
use posterior::qgeneralized_pareto()g
avehtari Oct 17, 2025
aae6c17
delete gpd functions
avehtari Oct 17, 2025
099398b
remove gpdfit doc and export
avehtari Oct 17, 2025
84ee41f
set up documentation structure
vinniott Mar 17, 2026
7d2c817
srs_diff_est.Rd matches .R documentation
vinniott Mar 17, 2026
a914fc6
Merge branch 'master' into export-srs-diff-est
vinniott Mar 22, 2026
816bcf8
added documentation
vinniott Mar 22, 2026
e596847
added @seealso at loo_subsample()
vinniott Mar 22, 2026
25fddcf
added reference Cochran (1977)
vinniott Mar 22, 2026
fe5a45e
removed oudated @return duplicate
vinniott Mar 22, 2026
dd72938
corrected .R formulas to render in .Rd
vinniott Mar 22, 2026
287c039
removed example placeholder
vinniott Mar 22, 2026
d4dda71
updated .Rd to match .R
vinniott Mar 22, 2026
5d476b5
Update NEWS.md
vinniott Mar 22, 2026
e0a8bcb
feat: update print.loo to support kfold pareto-k diagnostics
Mar 27, 2026
0efb8b1
tests: add unittest for updated print method and test data
Mar 27, 2026
5145c66
refactor: create new kfold.print method instead of changing print.loo
Mar 27, 2026
9bd9ff0
tests: use expect_snapshot to check table output
Mar 28, 2026
97c2f90
tests: update test data and snapshot for kfold-print tests
Mar 30, 2026
2896b90
Bump codecov/codecov-action from 5 to 6
dependabot[bot] Mar 30, 2026
23f2117
Merge pull request #344 from stan-dev/dependabot/github_actions/codec…
VisruthSK Mar 30, 2026
8502999
Merge pull request #346 from stan-dev/master
VisruthSK Apr 7, 2026
21bdd28
Deleted gpdfit tests
VisruthSK Apr 7, 2026
82a1b9e
Swapped more print tests to snaps
VisruthSK Apr 7, 2026
1ad507e
Merge pull request #347 from stan-dev/more-snaps-in-tests
jgabry Apr 7, 2026
dce7e3c
Update DESCRIPTION
VisruthSK Apr 7, 2026
60f012d
Merge pull request #342 from florence-bockting/update-loo-print
jgabry Apr 7, 2026
1b2678e
Revert "Deleted gpdfit tests"
VisruthSK Apr 8, 2026
ac74d1e
Merge branch 'use-posterior-gpdfit' of https://github.com/stan-dev/lo…
VisruthSK Apr 8, 2026
ccb9893
Exported gpdfit, removed qgpd
VisruthSK Apr 8, 2026
0dbca97
Fixed warning
VisruthSK Apr 8, 2026
4b6e608
Export package docs
VisruthSK Apr 8, 2026
d0aa8b2
Merge pull request #351 from stan-dev/roxygen-compliance
VisruthSK Apr 8, 2026
ce9689c
Barebones touchstone setup
VisruthSK Apr 8, 2026
faaca77
Generated tests
VisruthSK Apr 9, 2026
c73be4d
Fixed script calling
VisruthSK Apr 9, 2026
bb68267
Use wine data
VisruthSK Apr 11, 2026
b7c7719
Fixed typo
VisruthSK Apr 11, 2026
e61f350
Renamed file [no ci]
VisruthSK Apr 11, 2026
672ec21
wine rds in touchstone dir for testing
VisruthSK Apr 14, 2026
4b4020b
Typo
VisruthSK Apr 14, 2026
8ddd1c3
Properly load the wine loglik for each benchmark run
VisruthSK Apr 14, 2026
4e9c78e
Restored sysdata
VisruthSK Apr 14, 2026
8af5a81
Scoped token perms
VisruthSK Apr 14, 2026
a7337f8
Merge pull request #352 from stan-dev/use-touchstone
VisruthSK Apr 14, 2026
cddb7cc
Merge pull request #354 from stan-dev/master
VisruthSK Apr 14, 2026
51b81ff
Update script.R
VisruthSK Apr 16, 2026
27fdb06
Update script.R
VisruthSK Apr 16, 2026
434c86b
Trying to use sticky comment
VisruthSK Apr 16, 2026
6dc4554
Merge pull request #356 from stan-dev/more-touchstone-iterations
VisruthSK Apr 16, 2026
e7f64e9
Update script.R
VisruthSK Apr 16, 2026
57bd12a
Merge pull request #357 from stan-dev/even-more-touchstone-iters
VisruthSK Apr 16, 2026
d77578b
Bumped touchstone reqs
VisruthSK Apr 16, 2026
70a04ed
Update script.R
VisruthSK Apr 16, 2026
df56db4
Merge pull request #358 from stan-dev/testing-touchstone-comment
VisruthSK Apr 16, 2026
8d83fc2
Merge pull request #359 from stan-dev/master
VisruthSK Apr 16, 2026
85db294
Merge pull request #305 from stan-dev/use-posterior-gpdfit
jgabry Apr 17, 2026
3d589f8
added @examples placeholder
vinniott Apr 26, 2026
886d92f
added generation of wine example
vinniott Apr 26, 2026
ec6a3eb
rename example-generate_loglik_objects.R
vinniott Apr 26, 2026
7771f1f
set up documentation structure
vinniott Mar 17, 2026
1f26a6f
srs_diff_est.Rd matches .R documentation
vinniott Mar 17, 2026
1eb7ac1
added documentation
vinniott Mar 22, 2026
9a6fe92
added @seealso at loo_subsample()
vinniott Mar 22, 2026
24332a0
added reference Cochran (1977)
vinniott Mar 22, 2026
41b0ab0
removed oudated @return duplicate
vinniott Mar 22, 2026
57b0615
corrected .R formulas to render in .Rd
vinniott Mar 22, 2026
79e838f
removed example placeholder
vinniott Mar 22, 2026
71b45cc
updated .Rd to match .R
vinniott Mar 22, 2026
bb8fc8b
Update NEWS.md
vinniott Mar 22, 2026
6ce1d15
added @examples placeholder
vinniott Apr 26, 2026
19bb48d
added generation of wine example
vinniott Apr 26, 2026
607f938
rename example-generate_loglik_objects.R
vinniott Apr 26, 2026
9dc166f
Merge branch 'export-srs-diff-est' of https://github.com/vinniott/loo…
vinniott Apr 26, 2026
1cdb60f
general loglik example file with wine export for srs_diff_est example
vinniott Apr 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ vignettes/loo2-non-factorized_cache/*
^release-prep\.R$
^_pkgdown\.yml$
^pkgdown$
^touchstone$
2 changes: 1 addition & 1 deletion .github/workflows/test-coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
covr::to_cobertura(cov)
shell: Rscript {0}

- uses: codecov/codecov-action@v5
- uses: codecov/codecov-action@v6
with:
# Fail if error if not on PR, or if on PR and token is given--dependabot is treated like fork
fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }}
Expand Down
46 changes: 46 additions & 0 deletions .github/workflows/touchstone-comment.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Continuous Benchmarks (Comment)

concurrency:
group: ${{ github.workflow }}-${{ github.run_id }}
cancel-in-progress: true

on:
workflow_run:
workflows: ["Continuous Benchmarks (Receive)"]
types: [completed]

jobs:
comment:
runs-on: ubuntu-latest
permissions:
actions: read
pull-requests: write
if: ${{ github.event.workflow_run.event == 'pull_request' }}
steps:
- name: Download Touchstone artifact
id: download
uses: actions/download-artifact@v8
with:
name: pr
github-token: ${{ github.token }}
repository: ${{ github.repository }}
run-id: ${{ github.event.workflow_run.id }}

# defensive since issues could cause commenting in random places
- name: Read PR number
id: pr
shell: bash
run: |
number="$(tr -cd '0-9' < ./NR)"
test -n "$number"
echo "number=$number" >> "$GITHUB_OUTPUT"

- name: Create or update sticky PR comment
id: comment
uses: marocchino/sticky-pull-request-comment@v3
with:
GITHUB_TOKEN: ${{ github.token }}
number_force: ${{ steps.pr.outputs.number }}
header: touchstone
path: ./info.txt
skip_unchanged: true
43 changes: 43 additions & 0 deletions .github/workflows/touchstone-receive.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Continuous Benchmarks (Receive)

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref }}
cancel-in-progress: true

on:
pull_request:

jobs:
prepare:
runs-on: ubuntu-latest
outputs:
config: ${{ steps.read_touchstone_config.outputs.config }}
steps:
- name: Checkout repo
uses: actions/checkout@v6
with:
fetch-depth: 0

- id: read_touchstone_config
run: |
echo "config=$(jq -c . ./touchstone/config.json)" >> $GITHUB_OUTPUT

build:
needs: prepare
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
config:
- ${{ fromJson(needs.prepare.outputs.config) }}
steps:
- name: Checkout repo
uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: lorenzwalthert/touchstone/actions/receive@main
with:
r-version: ${{ matrix.config.r }}
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Imports:
checkmate,
matrixStats (>= 0.52),
parallel,
posterior (>= 1.5.0),
posterior (>= 1.7.0),
stats
Suggests:
bayesplot (>= 1.7.0),
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ S3method(print,compare.loo)
S3method(print,compare.loo_ss)
S3method(print,importance_sampling)
S3method(print,importance_sampling_loo)
S3method(print,kfold)
S3method(print,loo)
S3method(print,pareto_k_table)
S3method(print,pseudobma_bb_weights)
Expand Down Expand Up @@ -104,6 +105,7 @@ export(crps)
export(elpd)
export(example_loglik_array)
export(example_loglik_matrix)
export(example_wine_loglik_matrix)
export(extract_log_lik)
export(find_model_names)
export(gpdfit)
Expand Down Expand Up @@ -153,6 +155,7 @@ export(psislw)
export(relative_eff)
export(scrps)
export(sis)
export(srs_diff_est)
export(stacking_weights)
export(tis)
export(waic)
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* Added contribution section. by @VisruthSK in #286
* Update LOO uncertainty paper to use BA doi by @avehtari in #311
* Update documentation for `E_loo()` function by @avehtari in #312
* Export `srs_diff_est()` function by @vinniott and @avehtari in #340


# loo 2.8.0
Expand Down
4 changes: 4 additions & 0 deletions R/example_log_lik_array.R → R/example_log_lik_objects.R
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,7 @@ example_loglik_matrix <- function() {
return(llarray_to_matrix(ll))
}

#' @export
example_wine_loglik_matrix <- function() {
return(.example_wine_loglik_matrix)
}
73 changes: 6 additions & 67 deletions R/gpdfit.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,71 +29,10 @@
#' for the generalized Pareto distribution. *Technometrics* **51**, 316-325.
#'
gpdfit <- function(x, wip = TRUE, min_grid_pts = 30, sort_x = TRUE) {
# See section 4 of Zhang and Stephens (2009)
if (sort_x) {
x <- sort.int(x)
}
N <- length(x)
prior <- 3
M <- min_grid_pts + floor(sqrt(N))
jj <- seq_len(M)
xstar <- x[floor(N / 4 + 0.5)] # first quartile of sample
theta <- 1 / x[N] + (1 - sqrt(M / (jj - 0.5))) / prior / xstar
l_theta <- N * lx(theta, x) # profile log-lik
w_theta <- exp(l_theta - matrixStats::logSumExp(l_theta)) # normalize
theta_hat <- sum(theta * w_theta)
k <- mean.default(log1p(-theta_hat * x))
sigma <- -k / theta_hat

if (wip) {
k <- adjust_k_wip(k, n = N)
}

if (is.na(k)) {
k <- Inf
}

nlist(k, sigma)
}


# internal ----------------------------------------------------------------

lx <- function(a,x) {
a <- -a
k <- vapply(a, FUN = function(a_i) mean(log1p(a_i * x)), FUN.VALUE = numeric(1))
log(a / k) - k - 1
}

#' Adjust k based on weakly informative prior, Gaussian centered on 0.5. This
#' will stabilize estimates for very small Monte Carlo sample sizes and low neff
#' cases.
#'
#' @noRd
#' @param k Scalar khat estimate.
#' @param n Integer number of tail samples used to fit GPD.
#' @return Scalar adjusted khat estimate.
#'
adjust_k_wip <- function(k, n) {
a <- 10
n_plus_a <- n + a
k * n / n_plus_a + a * 0.5 / n_plus_a
}


#' Inverse CDF of generalized Pareto distribution
#' (assuming location parameter is 0)
#'
#' @noRd
#' @param p Vector of probabilities.
#' @param k Scalar shape parameter.
#' @param sigma Scalar scale parameter.
#' @return Vector of quantiles.
#'
qgpd <- function(p, k, sigma) {
if (is.nan(sigma) || sigma <= 0) {
return(rep(NaN, length(p)))
}

sigma * expm1(-k * log1p(-p)) / k
posterior::gpdfit(
x = x,
wip = wip,
min_grid_pts = min_grid_pts,
sort_x = sort_x
)
}
5 changes: 1 addition & 4 deletions R/loo-package.R
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
#' Efficient LOO-CV and WAIC for Bayesian models
#'
#' @docType package
#' @name loo-package
#'
#' @importFrom stats sd var quantile setNames weights rnorm qnorm
#' @importFrom matrixStats logSumExp colLogSumExps colSums2 colVars colMaxs
#'
Expand Down Expand Up @@ -89,4 +86,4 @@
#' for the generalized Pareto distribution. *Technometrics* **51**,
#' 316-325.
#'
NULL
"_PACKAGE"
90 changes: 83 additions & 7 deletions R/loo_subsample.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#' same length containing the posterior density and the approximation density
#' for the individual draws.
#'
#' @seealso [loo()], [psis()], [loo_compare()]
#' @seealso [loo()], [psis()], [loo_compare()], [srs_diff_est()]
#' @template loo-large-data-references
#'
#' @export loo_subsample loo_subsample.function
Expand Down Expand Up @@ -1166,12 +1166,88 @@ loo_subsample_estimation_diff_srs <- function(x) {
update_psis_loo_ss_estimates(x)
}

#' Difference estimation using SRS-WOR sampling (Magnusson et al., 2020)
#' @noRd
#' @param y_approx Approximated values of all observations.
#' @param y The values observed.
#' @param y_idx The index of `y` in `y_approx`.
#' @return A list with estimates.
#' Difference estimator with simple random sampling without replacement.
#'
#' The difference estimator `srs_diff()` estimates
#' the expectation \eqn{n E[y]} when we have \eqn{n} approximate values \eqn{\tilde{y}_i},
#' \eqn{i = 1, \ldots, n} and \eqn{m < n} accurate values \eqn{y_j}, \eqn{j \in \mathcal{S}},
#' where \eqn{m} is the subsample size and \eqn{\mathcal{S}} is
#' a simple random subsample without replacement. The original
#' approach is by Cochran (1977) and we follow the equations 7--9 by
#' Magnusson et al. (2020).
#'
#' @details In Magnusson et al. (2020) Eq (9) first row, the second `+` should
#' be a `-`; Supplementary Material Eq (6) has this correct.
#' As `srs_diff_est()` in the `loo` package is used for \eqn{n E[y]}, there is
#' a proportional difference of \eqn{1/n} compared to the paper.
#'
#' @param y_approx (numeric) `n` approximated values.
#' @param y (numeric) `m<n` subsampled values.
#' @param y_idx (integerish) The index of `y` in `y_approx`.
#'
#' @return A named list containing numeric values:
#' * `y_hat`: estimated mean of \eqn{y} (Eq 7),
#' * `v_y_hat`: variance of the mean estimate (Eq 8), and
#' * `hat_v_y`: estimated variance of \eqn{y} (Eq 9).
#'
#' @references
#' Magnusson, M., Riis Andersen, M., Jonasson, J. and Vehtari, A. (2020).
#' Leave-One-Out Cross-Validation for Model Comparison in Large Data.
#' In _Proceedings of the 23rd International Conference on Artificial
#' Intelligence and Statistics (AISTATS)_, PMLR 108:341-351.
#'
#' Cochran, W. G. (1977). _Sampling Techniques, 3rd Edition_. John Wiley.
#'
#' Cortez, P., Cerdeira, A.L., Almeida, F., Matos, T., & Reis, J. (2009).
#' Modeling wine preferences by data mining from physicochemical properties.
#' _Decis. Support Syst._, _47_, 547-553.
#'
#' @seealso [loo_subsample()]
#'
#' @examples
#' ### This example predicts wine quality (data from Cortez et al., 2009).
#' ## First, commented out code shows to generate a loglik_matrix.
#' ## Second, running code illustrates how to use srs_diff_est().
#' # library(dplyr)
#' # library(brms)
#' # options(brms.backend = "cmdstanr")
#' # options(mc.cores = 4)
#' # library(loo)
#' #
#' # wine <- read.delim(root("winequality-red", "winequality-red.csv"), sep = ";") |>
#' # distinct()
#' #
#' # wine_scaled <- as.data.frame(scale(wine))
#' #
#' # fitos <- brm(ordered(quality) ~ .,
#' # family = cumulative("logit"),
#' # prior = prior(R2D2(mean_R2 = 1/3, prec_R2 = 3)),
#' # data = wine_scaled,
#' # seed = 1,
#' # silent = 2,
#' # refresh = 0)
#' #
#' # wine_loglik_matrix <- log_lik(fitos)
#' wine_loglik_matrix <- example_wine_loglik_matrix() # Installed with loo to save time of fitting model shown above
#' N <- 1359 # nrow(wine_scaled), see above
#' Nsub <- 100
#' # posterior log-score
#' lpd <- elpd(wine_loglik_matrix)
#' sum(lpd$pointwise[,"elpd"])
#'
#' # Use PSIS-LOO for subsample of Nsub randomly selected observations
#' set.seed(1)
#' idx <- sample(1:N, Nsub)
#' elpd_loo_sub <- loo(wine_loglik_matrix[,idx])
#' sum(elpd_loo_sub$pointwise[,"elpd_loo"]) / Nsub * N
#'
#' # Use difference estimator to combine fast result and subsampled accurate result
#' loo:::srs_diff_est(lpd$pointwise[,"elpd"], elpd_loo_sub$pointwise[,"elpd_loo"], idx)
#'
#' # Comparison to using PSIS-LOO for all observations
#' loo(wine_loglik_matrix)
#'
#' @export
srs_diff_est <- function(y_approx, y, y_idx) {
checkmate::assert_numeric(y_approx)
checkmate::assert_numeric(y, max.len = length(y_approx))
Expand Down
22 changes: 22 additions & 0 deletions R/print.R
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,28 @@ print.importance_sampling <- function(x, digits = 1, plot_k = FALSE, ...) {
invisible(x)
}

#' @export
#' @rdname print.loo
print.kfold <- function(x, digits = 1, plot_k = FALSE, ...) {
print.loo(x, digits = digits, ...)

if ("diagnostics" %in% names(x)) {
cat("------\n")
S <- dim(x)[1]
k_threshold <- ps_khat_threshold(S)
if (length(pareto_k_ids(x, threshold = k_threshold))) {
cat("\n")
}
print(pareto_k_table(x), digits = digits)
cat(.k_help())

if (plot_k) {
graphics::plot(x, ...)
}
}
return(invisible(x))
}

# internal ----------------------------------------------------------------

#' Print dimensions of log-likelihood or log-weights matrix
Expand Down
4 changes: 2 additions & 2 deletions R/psis.R
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,12 @@ psis_smooth_tail <- function(x, cutoff) {
exp_cutoff <- exp(cutoff)

# save time not sorting since x already sorted
fit <- gpdfit(exp(x) - exp_cutoff, sort_x = FALSE)
fit <- posterior::gpdfit(exp(x) - exp_cutoff, sort_x = FALSE)
k <- fit$k
sigma <- fit$sigma
if (is.finite(k)) {
p <- (seq_len(len) - 0.5) / len
qq <- qgpd(p, k, sigma) + exp_cutoff
qq <- posterior::qgeneralized_pareto(p, 0, sigma, k) + exp_cutoff
tail <- log(qq)
} else {
tail <- x
Expand Down
Loading
Loading