Introduce model_compare() for model comparison with support of new predictive measures - #380
florence-bockting wants to merge 88 commits into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## pred_measure #380 +/- ##
================================================
+ Coverage 91.29% 91.68% +0.39%
================================================
Files 35 38 +3
Lines 4168 5049 +881
================================================
+ Hits 3805 4629 +824
- Misses 363 420 +57 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
This is how benchmark results would change (along with a 95% confidence interval in relative change) if 0bd95d6 is merged into pred_measure:
|
…nals Re-derives the file split on top of the current branch rather than merging the earlier WIP, which git resolved into eight duplicate definitions. model_compare.R (2082 lines) is split by concern into model_compare.R (the generic, default method, ordering and diagnostics), model_compare-pred_measure.R (the multi-measure path, rank resolution, standard errors) and model_compare-print.R (print.compare.loo and its table helpers). Every moved body is byte-identical to its previous version. loo_compare.R keeps only the forwarding generic and its methods. The copies of elpd_diffs, se_elpd_diff, find_model_names, middle_idx, order_stat_heuristic, diag_elpd, diag_diff and print.compare.loo it also held were dead: alphabetical collation meant model_compare.R won. diag_diff and print.compare.loo had diverged, so the dead copies were also wrong. loo_compare_checks, loo_compare_matrix, loo_compare_order and loo_order_stat_check are removed for the same reason. Warning helpers renamed to the package convention: .warn_insample_compare, .warn_kfold_K_mismatch and .warn_omitted_compare_measures become throw_*_warning(); .inform_compare_sign_conversion loses its dot prefix. Adds loo_compare.psis_loo_ss_list so subsampling objects keep dispatching under the old name, and fixes the undefined `loo3` in the loo_compare examples. NAMESPACE and man/ still need regenerating with roxygen2 8.0.0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The refactor copied an older print.compare.loo into model_compare.R, which alphabetical collation made the live one, so the simplify argument added later at a user's request stopped existing. Because print.compare.loo takes `...`, `print(comp, simplify = FALSE)` was silently swallowed rather than erroring, and the snapshot tests that would have caught it skip unless NOT_CRAN is set. Merges the two: keeps the pred_measure dispatch, the compare_ref_model message and .print_compare_diag_message() from the model_compare.R version, and restores the flexible column selection and simplify argument from the loo_compare.R one. test_compare.R with NOT_CRAN=true: 355 pass, 0 fail (was 352 pass, 3 fail on 8897e1f and on the split commit). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Overview of changesFeaturesComparison
Standard error of differences
Checks
Print method (
User-facing functions
Internal functions
|
…_compare # Conflicts: # R/pred_measure-compute.R # R/pred_measure-helpers.R # R/pred_measure.R # man/insample_pred_measure.Rd # man/pred_measure_params.Rd
jgabry
left a comment
There was a problem hiding this comment.
Thanks @florence-bockting, this is great! Here's a first round of review comments, but I probably missed some things. I should probably do another round, but we can start with these first and then I'll review again
…remove unnecessary line break
…n documentation in model_compare
…on in model_compare
|
Thank you @jgabry for the great review. I went now through all you comments and updated the code base correspondingly. |
jgabry
left a comment
There was a problem hiding this comment.
Changes look good. I made a bunch more comments but they're mostly more cases where attributes are referred to that we should rewrite. The only other thing was about one test.
After fixing those I think we can merge this into the other branch. Maybe I'll notice more things when I review the other branch when it's ready.
| "Not all kfold objects have the same K value" | ||
| ) | ||
|
|
||
| test_that("print warns that `measures` is ignored for 'loo' comparisons", { |
There was a problem hiding this comment.
This test_that() ended up inside the "model_compare throws appropriate warnings" one (the one starting at line 1331). It still runs, but it should probably be moved out from inside the other test
| #' `r2` it is the trivariate analogue, which additionally propagates the | ||
| #' uncertainty in the baseline `MSE(y)` shared by both models. | ||
| #' * For custom measures it comes from the measure's own | ||
| #' `attr(my_fun, "measure_se_diff")` declaration, set with |
There was a problem hiding this comment.
Let's avoid mentioning the attribute here and just say it comes from what the user passes to custom_measure()
| #' * `se_diff_fun`: for built-in measures with | ||
| #' `diff_method = "measure_specific"`, the name of the built-in implementation | ||
| #' used. For custom measures, whatever the measure declared in | ||
| #' `attr(my_fun, "measure_se_diff")`; absent when it declared nothing. |
There was a problem hiding this comment.
Another attributes mention that we can rephrase to avoid mentioning the attribute itself
| #' [custom_measure()]. With `loss = TRUE` lower values are better; without it | ||
| #' they are treated as utilities (see [insample_pred_measure()]). | ||
| #' [model_compare()] requires all models to provide matching `measure_info` for | ||
| #' each shared measure; a mismatched `measure_loss` or `measure_se_diff` |
There was a problem hiding this comment.
These are the attributes right? I think we could rephrase this too
| #' pointwise contributions (`r2`, `rmse`, `bacc`), so the measure supplies | ||
| #' its own standard error of the difference. | ||
| #' * `"custom"`: the standard error comes from the measure's own | ||
| #' `attr(my_fun, "measure_se_diff")` declaration, set with |
There was a problem hiding this comment.
Another attributes mention that we can rephrase to avoid mentioning the attribute itself
| "Models disagree on `measure_info` for measure '", | ||
| bare, | ||
| "'. For a custom measure, ensure all models use the same ", | ||
| "`measure_loss` and `measure_se_diff` declarations.", |
There was a problem hiding this comment.
Another attributes mention that we can rephrase to avoid mentioning the attributes themselves
| } | ||
| if (!ok) { | ||
| warning( | ||
| "`measure_se_diff = \"", method, "\"` was declared for measure '", |
There was a problem hiding this comment.
Another attributes mention that we can rephrase to avoid mentioning the attribute itself
| !identical(attr_name, name)) { | ||
| cli::cli_warn(c( | ||
| "Custom measure named {.val {name}} in {.arg measure} also has", | ||
| "{.code attr(fun, \"measure_name\") = {.val {attr_name}}}.", |
There was a problem hiding this comment.
Another attributes mention that we can rephrase to avoid mentioning the attribute itself
Fixes #220
Summary
This PR adds
model_compare(). The function compares models on all predictive measures of the*_pred_measure()API (#363).model_compare()replacesloo_compare().loo_compare()is deprecated. It warns once per session. It still compares"loo","waic", and"kfold"objects on ELPD.loo_comparemethods in other packages (e.g.loo_compare.brmsfit) still dispatch.model_compare()accepts results fromloo_pred_measure(),kfold_pred_measure(),test_pred_measure(), andinsample_pred_measure().mse) to the utility scale. A higher difference is then always better.print()marks each flipped measure.custom_measure()defines a custom measure. It sets the name, the orientation, and the standard error of the difference.The output for
"loo","waic", and"kfold"objects does not change.The comment below lists all new features and functions.
The review decisions are in notes/design-discussions/model_compare.md.
Examples
vignettes/articles-online-only/model-comparison.Rmd