Skip to content

Commit c0155fc

Browse files
author
Florence Bockting
committed
fix: correct elpd_diff sign and return a data.frame in loo_compare() for subsampled loo objects
1 parent a59519c commit c0155fc

5 files changed

Lines changed: 72 additions & 56 deletions

File tree

NEWS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# loo (development version)
22

3+
* Fix `loo_compare()` when used with subsampling: compute model comparison by comparison-model minus reference-model and change output structure from matrix
4+
to data.frame by @florence-bockting in #TODO
35
* Update user messages in `print()` by @ishaan-arora-1, @florence-bockting in
46
#328.
57

R/loo_compare.psis_loo_ss_list.R

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,12 @@ loo_compare.psis_loo_ss_list <- function(x, ...) {
2424
for(i in 2:length(ord)){
2525
elpd_diff_mat[i,] <- loo_compare_ss(ref_loo = x[ord[1]], compare_loo = x[ord[i]])
2626
}
27-
comp <- cbind(elpd_diff_mat, comp)
28-
rownames(comp) <- rnms
27+
comp <- cbind(
28+
data.frame(model = rnms, stringsAsFactors = FALSE),
29+
as.data.frame(elpd_diff_mat),
30+
as.data.frame(comp)
31+
)
32+
rownames(comp) <- NULL
2933

3034
class(comp) <- c("compare.loo_ss", "compare.loo", class(comp))
3135
return(comp)
@@ -86,13 +90,13 @@ loo_compare_ss_naive <- function(ref_loo, compare_loo){
8690
checkmate::assert_class(ref_loo[[1]], "psis_loo_ss")
8791
checkmate::assert_class(compare_loo[[1]], "psis_loo_ss")
8892

89-
elpd_loo_diff <- ref_loo[[1]]$estimates["elpd_loo","Estimate"] - compare_loo[[1]]$estimates["elpd_loo","Estimate"]
93+
elpd_loo_diff <- compare_loo[[1]]$estimates["elpd_loo", "Estimate"] - ref_loo[[1]]$estimates["elpd_loo", "Estimate"]
9094
elpd_loo_diff_se <- sqrt(
91-
(ref_loo[[1]]$estimates["elpd_loo","SE"])^2 +
92-
(compare_loo[[1]]$estimates["elpd_loo","SE"])^2)
95+
(ref_loo[[1]]$estimates["elpd_loo", "SE"])^2 +
96+
(compare_loo[[1]]$estimates["elpd_loo", "SE"])^2)
9397
elpd_loo_diff_subsampling_se <- sqrt(
94-
(ref_loo[[1]]$estimates["elpd_loo","subsampling SE"])^2 +
95-
(compare_loo[[1]]$estimates["elpd_loo","subsampling SE"])^2)
98+
(ref_loo[[1]]$estimates["elpd_loo", "subsampling SE"])^2 +
99+
(compare_loo[[1]]$estimates["elpd_loo", "subsampling SE"])^2)
96100

97101
c(elpd_loo_diff, elpd_loo_diff_se, elpd_loo_diff_subsampling_se)
98102
}
@@ -112,8 +116,8 @@ loo_compare_ss_diff <- function(ref_loo, compare_loo){
112116
checkmate::assert_true(ref_loo[[1]]$loo_subsampling$loo_approximation != "none")
113117
checkmate::assert_true(compare_loo[[1]]$loo_subsampling$loo_approximation != "none")
114118

115-
diff_approx <- ref_loo[[1]]$loo_subsampling$elpd_loo_approx - compare_loo[[1]]$loo_subsampling$elpd_loo_approx
116-
diff_sample <- ref_loo[[1]]$pointwise[,"elpd_loo"] - compare_loo[[1]]$pointwise[,"elpd_loo"]
119+
diff_approx <- compare_loo[[1]]$loo_subsampling$elpd_loo_approx - ref_loo[[1]]$loo_subsampling$elpd_loo_approx
120+
diff_sample <- compare_loo[[1]]$pointwise[,"elpd_loo"] - ref_loo[[1]]$pointwise[,"elpd_loo"]
117121
est <- srs_diff_est(diff_approx, y = diff_sample, y_idx = ref_loo[[1]]$pointwise[,"idx"])
118122

119123
elpd_loo_diff <- est$y_hat
@@ -174,15 +178,16 @@ loo_compare_checks.psis_loo_ss_list <- function(loos) {
174178
#' @rdname loo_compare
175179
#' @export
176180
print.compare.loo_ss <- function(x, ..., digits = 1) {
177-
xcopy <- x
178-
if (NCOL(xcopy) >= 2) {
179-
xcopy <- xcopy[, c("elpd_diff", "se_diff", "subsampling_se_diff")]
180-
}
181-
print(.fr(xcopy, digits), quote = FALSE)
181+
cols <- c("model", "elpd_diff", "se_diff", "subsampling_se_diff")
182+
cols <- intersect(cols, colnames(x))
183+
184+
x_sub <- x[, cols, drop = FALSE]
185+
x_sub[setdiff(cols, "model")] <- .fr(x_sub[setdiff(cols, "model")], digits)
186+
print(as.data.frame(x_sub), quote = FALSE, row.names = FALSE)
187+
182188
invisible(x)
183189
}
184190

185-
186191
#' Compute comparison matrix for `psis_loo_ss` objects
187192
#' @noRd
188193
#' @keywords internal

tests/testthat/_snaps/loo_subsampling_cases.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,34 +104,34 @@
104104
Code
105105
print(comp)
106106
Output
107-
elpd_diff se_diff subsampling_se_diff
108-
model2 0.0 0.0 0.0
109-
model1 16.5 22.5 0.4
107+
model elpd_diff se_diff subsampling_se_diff
108+
model2 0.0 0.0 0.0
109+
model1 -16.5 22.5 0.4
110110

111111
---
112112

113113
Code
114114
print(comp)
115115
Output
116-
elpd_diff se_diff subsampling_se_diff
117-
model2 0.0 0.0 0.0
118-
model1 16.1 4.4 0.1
116+
model elpd_diff se_diff subsampling_se_diff
117+
model2 0.0 0.0 0.0
118+
model1 -16.1 4.4 0.1
119119

120120
---
121121

122122
Code
123123
print(comp2)
124124
Output
125-
elpd_diff se_diff subsampling_se_diff
126-
model2 0.0 0.0 0.0
127-
model1 16.3 4.4 0.1
125+
model elpd_diff se_diff subsampling_se_diff
126+
model2 0.0 0.0 0.0
127+
model1 -16.3 4.4 0.1
128128

129129
---
130130

131131
Code
132132
print(comp3)
133133
Output
134-
elpd_diff se_diff subsampling_se_diff
135-
model2 0.0 0.0 0.0
136-
model1 16.5 4.4 0.3
134+
model elpd_diff se_diff subsampling_se_diff
135+
model2 0.0 0.0 0.0
136+
model1 -16.5 4.4 0.3
137137

tests/testthat/test_loo_subsampling.R

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1047,30 +1047,39 @@ test_that("loo_compare_subsample", {
10471047
)
10481048
)
10491049

1050-
expect_equal(lcss[, 1], lcsso[, 1], tolerance = 1)
1051-
expect_equal(lcss2[, 1], lcsso[, 1], tolerance = 1)
1052-
expect_equal(lcssohh[, 1], lcsso[, 1], tolerance = 1)
1053-
expect_equal(lcssf1[, 1], lcsso[, 1], tolerance = 1)
1054-
expect_equal(lcssf2[, 1], lcsso[, 1], tolerance = 1)
1050+
expect_equal(lcss$elpd_diff, lcsso$elpd_diff, tolerance = 1)
1051+
expect_equal(lcss2$elpd_diff, lcsso$elpd_diff, tolerance = 1)
1052+
expect_equal(lcssohh$elpd_diff, lcsso$elpd_diff, tolerance = 1)
1053+
expect_equal(lcssf1$elpd_diff, lcsso$elpd_diff, tolerance = 1)
1054+
expect_equal(lcssf2$elpd_diff, lcsso$elpd_diff, tolerance = 1)
10551055

1056-
expect_gt(lcss[, 2][2], lcsso[, 2][2])
1057-
expect_gt(lcss[, 2][3], lcsso[, 2][3])
1058-
expect_gt(lcss2[, 2][2], lcsso[, 2][2])
1059-
expect_equal(lcss2[, 2][3], lcsso[, 2][3])
1060-
expect_gt(lcssohh[, 2][2], lcsso[, 2][2])
1061-
expect_equal(lcssohh[, 2][3], lcsso[, 2][3])
1056+
expect_gt(lcss$se_diff[2], lcsso$se_diff[2])
1057+
expect_gt(lcss$se_diff[3], lcsso$se_diff[3])
1058+
expect_gt(lcss2$se_diff[2], lcsso$se_diff[2])
1059+
expect_equal(lcss2$se_diff[3], lcsso$se_diff[3])
1060+
expect_gt(lcssohh$se_diff[2], lcsso$se_diff[2])
1061+
expect_equal(lcssohh$se_diff[3], lcsso$se_diff[3])
10621062

10631063
expect_silent(
10641064
lcss2m <- loo:::loo_compare.psis_loo_ss_list(x = list(lss2o1, lss3o1))
10651065
)
1066-
expect_equal(unname(lcss2m[,]), unname(lcsso[1:2, ]))
1066+
expect_equal(
1067+
lcss2m[, setdiff(colnames(lcss2m), "model")],
1068+
lcsso[1:2, setdiff(colnames(lcsso), "model")]
1069+
)
10671070

10681071
expect_snapshot(lcssapi <- loo_compare(lss1, lss2, lss3))
10691072
expect_equal(lcssapi, lcss)
10701073
expect_warning(lcssohhapi <- loo_compare(lss1, lss2hh, lss3o1))
10711074
expect_equal(lcssohhapi, lcssohh)
10721075
expect_silent(lcss2mapi <- loo_compare(lss2o1, lss3o1))
10731076
expect_equal(lcss2mapi, lcss2m)
1077+
# check that comparison is comp - ref model (i.e., elpd_diff is neg.)
1078+
for (m in list(lcss, lcss2, lcssohh)) {
1079+
expect_lt(m$elpd_diff[2], 0)
1080+
expect_lt(m$elpd_diff[3], 0)
1081+
expect_true("data.frame" %in% class(m))
1082+
}
10741083
})
10751084

10761085
test_that("Test 'tis' and 'sis'", {

vignettes/loo2-large-data.Rmd

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -481,9 +481,9 @@ print(comp)
481481
```
482482
Warning: Different subsamples in 'model2' and 'model1'. Naive diff SE is used.
483483
484-
elpd_diff se_diff subsampling_se_diff
485-
model2 0.0 0.0 0.0
486-
model1 16.5 22.5 0.4
484+
model elpd_diff se_diff subsampling_se_diff
485+
model2 0.0 0.0 0.0
486+
model1 -16.5 22.5 0.4
487487
```
488488

489489
This new object `comp` contains the estimated difference of expected
@@ -538,23 +538,23 @@ subsampled observations.
538538

539539
```{r, eval=FALSE}
540540
comp <- loo_compare(loo_ss_1, loo_ss_2)
541-
print(comp)
541+
print(comp)
542542
```
543543

544544
```
545-
elpd_diff se_diff subsampling_se_diff
546-
model2 0.0 0.0 0.0
547-
model1 16.1 4.4 0.1
545+
model elpd_diff se_diff subsampling_se_diff
546+
model2 0.0 0.0 0.0
547+
model1 -16.1 4.4 0.1
548548
```
549549

550550
First, notice that now the `se_diff` is now around 4 (as opposed to 20 when using
551-
different subsamples). The first column shows the difference in ELPD relative to
552-
the model with the largest ELPD. In this case, the difference in `elpd` and its
553-
scale relative to the approximate standard error of the difference) indicates a
554-
preference for the second model (`model2`). Since the subsampling uncertainty is
555-
so small in this case it can effectively be ignored. If we need larger
556-
subsamples we can simply add samples using the `update()` method demonstrated
557-
earlier.
551+
different subsamples). The `elpd_diff` column shows the difference in ELPD
552+
relative to the model with the largest ELPD. In this case, the difference in
553+
`elpd` and its scale relative to the approximate standard error of the
554+
difference) indicates a preference for the second model (`model2`).
555+
Since the subsampling uncertainty is so small in this case it can effectively
556+
be ignored. If we need larger subsamples we can simply add samples using the
557+
`update()` method demonstrated earlier.
558558

559559
It is also possible to compare a subsampled loo computation with a full loo object.
560560

@@ -580,9 +580,9 @@ the loo calculations for both `model1` and `model2` are included in the
580580
computations for the comparison.
581581

582582
```
583-
elpd_diff se_diff subsampling_se_diff
584-
model2 0.0 0.0 0.0
585-
model1 16.3 4.4 0.3
583+
model elpd_diff se_diff subsampling_se_diff
584+
model2 0.0 0.0 0.0
585+
model1 -16.3 4.4 0.3
586586
```
587587

588588
Here we actually see an increase in `subsampling_se_diff`, but this is due to a

0 commit comments

Comments
 (0)