Utilities for Scoring and Assessing Predictions
Overview
Facilitate the evaluation of forecasts in a convenient framework based on data.table. It allows user to to check their forecasts and diagnose issues, to visualise forecasts and missing data, to transform data before scoring, to handle missing forecasts, to aggregate scores, and to visualise the results of the evaluation. The package mostly focuses on the evaluation of probabilistic forecasts and allows evaluating several different forecast types and input formats. Find more information about the package in the Vignettes as well as in the accompanying paper, doi:10.48550/arXiv.2205.07090.
This release adds tools for handling missing forecasts (filter_scores(), impute_missing_scores()), a new plot_discrimination() for binary forecasts, and a much faster get_pairwise_comparisons(). It also fixes a number of bugs, several of which silently produced wrong scores. Many of these fixes mean that invalid input which was previously accepted now raises an error. Please read the "Breaking changes" section below if you are upgrading.
Breaking changes
The following changes may cause code that ran with scoringutils 2.2.0 to fail or behave differently. Most of them turn input that previously gave wrong or corrupted results, often silently, into an error.
as_forecast_<type>()functions now error when asked to rename a column onto a name that already exists in the data, for exampleas_forecast_binary(data, predicted = "prob")whendataalso has apredictedcolumn. Previously this silently created a forecast object with duplicate column names, which passed validation and was then scored on the wrong column.assert_forecast()now also rejects data with duplicate column names. To fix affected code, remove or rename the stale column before callingas_forecast_<type>()(#1199).- Forecast validation now errors when the same forecast unit has conflicting observed values. Previously, this passed validation for quantile, sample, nominal, ordinal and multivariate sample forecasts, and
score()silently returned multiple, wrong score rows for a single forecast. This usually means that a column that distinguishes forecasts (e.g. the target type) is missing from the data, or was dropped via theforecast_unitargument. Check the forecast unit withget_forecast_unit()and add the missing column (#1201). summarise_scores()now errors whenbycontains a metric column (e.g.by = c("model", "wis")). Previously, this silently returned an unsummarised table with duplicate column names. Remove the metric column fromby(#1204).summarise_scores()now errors with a clear message when there are no score columns to summarise (e.g. because every metric inscore()warned and returned nothing). Previously, it returned a data.table with duplicate column names (#1179).get_pairwise_comparisons()andadd_relative_skill()now error when the scores contain more than one distinct score per forecast unit and comparator. Previously, such conflicting rows were silently included in the comparison. Exact duplicate rows are still dropped. Remove the conflicting rows, or summarise the scores to one row per forecast unit and comparator first (#1221).logs_categorical()now errors whenpredictedhas more or fewer columns than there are factor levels. Previously, this was only checked for a single observation. With several observations, extra columns were silently scored meaninglessly and missing columns gave an uninformative "subscript out of bounds" error.score()is unaffected.rps_ordinal(), which already errored on such input, now gives a clearer error (#1200).as_forecast_sample(),as_forecast_quantile()andas_forecast_multivariate_sample()now error at validation time whenobservedorpredictedare not numeric. Previously, the failure only surfaced later insidescore(), as one warning per metric and an effectively empty scores table. Convertobservedandpredictedto numeric first (#1211).assert_forecast()for nominal and ordinal forecasts now returnsinvisible(NULL), as documented and as for all other forecast types. Previously, it visibly returned the forecast object. Code that used the return value should use the forecast object directly (#1195).get_duplicate_forecasts()has a newtypeargument beforecounts. Code that passedcountsby position must now name it, e.g.get_duplicate_forecasts(data, counts = TRUE)(#888).
Show all changes in 2.3.0Hide the rest of the changes in 2.3.0
- scoringutils now requires data.table 1.17.0 or later (previously 1.16.0) (#935). It also newly depends on lifecycle (1.0.2 or later) (#888).
Deprecations
- Calling
get_duplicate_forecasts()on a plain data.frame without specifyingtypeis deprecated and now warns. It falls back to guessing type-specific columns from their names. Passtype(e.g.type = "quantile"), or call it on a forecast object (#888).
New features
- Added
filter_scores()andimpute_missing_scores()for handling missing forecasts before summarisation.filter_scores()removes target combinations with insufficient model coverage, whileimpute_missing_scores()fills in missing scores using configurable strategies (worst, mean, NA, or reference model). Both take a strategy function:filter_to_intersection()andfilter_to_include()for filtering, andimpute_worst_score(),impute_mean_score(),impute_na_score()andimpute_model_score()for imputation. Custom strategies can be supplied too. Seevignette("handling-missing-forecasts")for details (#1122). - Added
plot_discrimination()to visualise the discrimination ability of binary forecasts by plotting the distribution of predicted probabilities, stratified by the observed outcome. The function requires aforecast_binaryobject (created withas_forecast_binary()) (#942). - Added
get_forecast_type_ids(), an S3 generic that returns the columns (beyond the forecast unit) that identify a unique row for each forecast type.get_duplicate_forecasts()now uses it, and gains atypeargument (e.g.type = "quantile") for use on plain data.frames (#888). get_pairwise_comparisons(), and thereforeadd_relative_skill(), is now substantially faster and uses much less memory. Scores are pivoted once into a forecast unit by comparator matrix instead of being merged separately for every pair of comparators. Results are unchanged (#1221, thanks to @annakrystalli for the analysis and prototype).add_relative_skill()no longer runs a statistical test for each pair of comparators by default, as it does not return the resulting p-values. This removes unnecessary computation and spurious warnings fromwilcox.test()when scores are tied. Relative skill scores are unchanged.test_typeis now an explicit argument with defaultNULL, and a test can still be requested through it (#1222, thanks to @annakrystalli).
Bug fixes
- Fixed
wis(),interval_score()andquantile_score(weigh = FALSE)returningNaNfor forecasts that include the quantile levels 0 and 1 (which form a 100% prediction interval). Scores are now finite when the observation falls inside the interval, restoring the identity between the WIS and the mean of the quantile scores. The unweighted scores areInfwhen the observation falls outside a 100% prediction interval (#1202). - Fixed
interval_coverage()erroring on quantile levels generated withseq()(e.g.seq(0.05, 0.95, 0.05)), because quantile levels were matched with an exact floating point comparison. They are now rounded to 10 decimal places before matching, consistent with the rest of the package (#1202). - Fixed
as_forecast_quantile()for sample-based forecasts producing silently wrong quantiles, or erroring, whenprobswas not symmetric around 0.5 (e.g.probs = 0.4orprobs = c(0.1, 0.2)). Quantiles are now computed at exactly the requestedprobs(deduplicated), and missing or out-of-rangeprobsproduce a clear error (#1196). - Fixed
summarise_scores()also summarising columns whose names merely contain a metric name. Metric columns are now matched by exact name rather than by a regex partial match (#1179). - Fixed
bias_quantile()returning wrong values when quantile levels were passed unsorted, because predictions and quantile levels became mispaired. Also fixed a crash whenna.rm = TRUEremoved all quantile levels on one side of the median.bias_quantile()now returnsNAin this case, consistent withna.rm = FALSE(#1198). - Fixed
bias_sample(),ae_median_sample(),se_mean_sample()andmad_sample()mishandling the documented input for a single observation (a scalarobservedwith a vector of samples aspredicted):ae_median_sample()andse_mean_sample()silently returned wrong results, whilebias_sample()andmad_sample()errored. All sample metrics now treat this input as one forecast with N samples, consistent withcrps_sample(). Also corrected the integer bias formula in thebias_sample()documentation (the code was correct) (#1197). - Fixed
rps_ordinal()andlogs_categorical()returning wrong scores when called directly with apredicted_labelthat was not in the order of the factor levels. Scores are now invariant to how the columns ofpredictedare labelled. Forecasts scored viascore()were unaffected (#1200). as_forecast_binary(),assert_forecast(),brier_score(),logs_binary()andscore()for binary forecasts now warn when the levels ofobservedarec("1", "0")orc("TRUE", "FALSE"). Predictions are interpreted as the probability of the second level, so with these levels they were silently read as the probability of "0" or "FALSE" (#763).- Fixed
get_pit_histogram()for quantile-based forecasts: it now shows its full warning message and actually falls back to the quantiles present in the forecast when requested quantiles are missing, instead of returning an empty or incorrect result. The rounding warning inas_forecast_quantile()now correctly states that quantile levels are rounded to 9 digits (#1211). - Fixed forecast and scores objects printing spuriously when modified in place with
:=, and added aprint.scores()method. Subsetting a forecast object with[now validates the result regardless of its size (previously only subsets with more than 30 rows were checked), so invalid subsets of small forecast objects now warn (#935). - Fixed the error message of
assert_forecast()for incomplete nominal and ordinal forecasts, which named the first *complete* forecast instead of the first incomplete one (#1195).
Documentation
- Added a more descriptive explanation of the use of energy and variogram scores in the vignette "Scoring multivariate forecasts", including how to pool over forecast horizons from a single origin and a multi-model comparison. Added a note explaining where these scores cannot be applied to quantile forecasts (#1193).
- Removed the deprecated vignettes
Deprecated-functionsandDeprecated-visualisations. The code for removed functions (plot_predictions(),make_NA(),plot_ranges(),plot_score_table(),merge_pred_and_obs()) can still be found in the git history (#1158).
Internal changes
- Added an internal helper
prepare_forecast_for_scoring()that consolidates the input preparation steps previously duplicated across thescore()methods: cleaning the forecast, validating the metrics and converting to a plaindata.table, plus determining the forecast unit for the methods that need it (#941). - Updated the documentation to roxygen2 8.1.0 and fixed an R CMD check NOTE on R-devel (#1230).
From NEWS.md in the release
Install
Health
- OK13 OK2026-09-30
- NOTE12 OK, 1 NOTE2026-07-11
- OK14 OK2026-03-19
- ERROR13 OK, 1 ERROR2026-03-12
- OK14 OK2026-03-10
Documentation
- Help pages
- 145 (38 internal)includes a package overview page
- Examples that run
- 98%61 run in a plain check · 1 never run
- Exported functions with examples
- 78%
- Documented parameters
- 99%
- Return-value docs
- 95%
- Help pages citing references
- 12%
Downloads
Repository
- PRs merged (all time)
- 85%
- Median days to close, 50 most recently updated closed issues
- 80
- Median days to close, 50 most recently updated closed PRs
- 54
Stars over time
Forks over time
Issues over time
PRs over time
Commits over time
Commit identities over time
15 development-tooling and community-health practices detected across 7 families in the upstream repository
Checks run against github.com/epiforecasts/scoringutils on 2026-10-18.
The R CMD check workflow runs on Linux, macOS and Windows, and on R-devel
GitHub Pages site last deployed 2026-09-27 · The repository has a site deploy workflow
pkgdown site last built 2026-04-05, before release 2.3.0 (2026-09-27)
Show all practicesHide 3 practices
This repository shows signs of AI coding tools.
- Claude Codefirst seen 2026-02-01Named in .Rbuildignore: .claudeNamed in .gitignore: .claudePull request it wrote: opened by a project memberCommits: at least 71 crediting it in the message
- Claude Opus 4.630 commits · Feb to Apr 2026
- Claude Fable 523 commits · Jul 2026
- Claude Opus 5.55 commits · Sep 2026
- Claude Sonnet 55 commits · Sep 2026
- Claude Fable 5.14 commits · Sep 2026
2 more models not listed.
Dependencies
Installing scoringutils adds 30 packages (17 with compiled code, 25 MB of source tarballs). Heaviest: ggplot2 6.0 MB, data.table 5.7 MB, Rcpp 1.8 MB.
Code & Tests
Datasets
People & History
Show 4 peopleHide 4 people
Pick two releases to compare their code metrics.
- 2.3.0Latest2026-09-27 · current release · diff ↗ ·
added 10 exports
filter_scores, filter_to_include, filter_to_intersection, get_forecast_type_ids, impute_mean_score, impute_missing_scores, impute_model_score, impute_na_score, impute_worst_score, plot_discrimination - RR 4.6.0 released · 2026-04-24
- 2.2.02026-04-05 · diff ↗ ·
added 8 exports
as_forecast_multivariate_point, as_forecast_multivariate_sample, energy_score_multivariate, get_grouping, is_forecast_multivariate_point, is_forecast_multivariate_sample, variogram_score_multivariate, variogram_score_multivariate_point - 2.1.22025-08-25 · diff ↗
- 2.1.12025-06-26 · diff ↗
- RR 4.5.0 released · 2025-04-11
- 2.1.02025-03-03 · diff ↗ ·
removed 1 export
logs_nominaladded 4 exports
as_forecast_ordinal, is_forecast_ordinal, logs_categorical, rps_ordinal - 2.0.02024-10-31 · diff ↗ ·
removed 25 exports
abs_error, add_coverage, avail_forecasts, available_metrics, bias_range, check_forecasts, correlation, find_duplicates, interval_score, make_NA, make_na, merge_pred_and_obs, pairwise_comparison, pit, pit_sample, plot_avail_forecasts, plot_correlation, plot_pairwise_comparison, plot_pit, plot_predictions, plot_ranges, plot_score_table, sample_to_quantile, set_forecast_unit, squared_erroradded 36 exports
add_relative_skill, as_forecast_binary, as_forecast_nominal, as_forecast_point, as_forecast_quantile, as_forecast_sample, assert_forecast, dispersion_quantile, dispersion_sample, get_correlations, get_coverage, get_duplicate_forecasts, get_forecast_counts, get_forecast_unit, get_metrics, get_pairwise_comparisons, get_pit_histogram, interval_coverage, is_forecast, is_forecast_binary, is_forecast_nominal, is_forecast_point, is_forecast_quantile, is_forecast_sample, logs_nominal, new_forecast, overprediction_quantile, overprediction_sample, pit_histogram_sample, plot_correlations and 6 more - RR 4.4.0 released · 2024-04-24
- 1.2.2
- RR 4.3.0 released · 2023-04-21
- 1.1.02023-01-30 · diff ↗
- 1.0.12022-08-17 · diff ↗
- 1.0.02022-05-13 · diff ↗ ·
removed 29 exports
bias, correlation_plot, crps, dss, eval_forecasts, interval_coverage, logs, mse, pit_df, pit_df_fast, quantile_bias, quantile_coverage, quantile_to_long, quantile_to_range, quantile_to_range_long, quantile_to_wide, range_long_to_quantile, range_long_to_wide, range_plot, range_to_quantile, range_wide_to_long, sample_to_range, sample_to_range_long, score_heatmap, score_table, sharpness, show_avail_forecasts, update_list, wis_componentsadded 33 exports
add_coverage, avail_forecasts, available_metrics, bias_quantile, bias_range, bias_sample, check_forecasts, correlation, crps_sample, dss_sample, find_duplicates, logs_binary, logs_sample, mad_sample, make_NA, make_na, pit_sample, plot_avail_forecasts, plot_correlation, plot_heatmap, plot_interval_coverage, plot_pit, plot_quantile_coverage, plot_ranges, plot_score_table, plot_wis, quantile_score, score, se_mean_sample, squared_error and 3 more - RR 4.2.0 released · 2022-04-22
- 0.1.7.22021-07-21 · diff ↗
Show 5 earlier eventsHide 5 earlier events
- 0.1.72021-07-14 · diff ↗ ·
removed 1 export
ae_medianadded 15 exports
abs_error, ae_median_quantile, ae_median_sample, merge_pred_and_obs, pairwise_comparison, pit_df, pit_df_fast, plot_pairwise_comparison, quantile_to_range_long, range_long_to_quantile, range_long_to_wide, range_wide_to_long, sample_to_range_long, show_avail_forecasts, update_list - RR 4.1.0 released · 2021-05-18
- 0.1.42020-11-17 · diff ↗ ·
added 16 exports
ae_median, correlation_plot, interval_coverage, plot_predictions, quantile_bias, quantile_coverage, quantile_to_long, quantile_to_range, quantile_to_wide, range_plot, range_to_quantile, sample_to_quantile, sample_to_range, score_heatmap, score_table, wis_components - 0.1.02020-06-14
- RR 4.0.0 released · 2020-04-24
Package metadata
- First published
- 2020-06-14
- Total releases
- 14 / 6 yrs
- License
- MIT + file LICENSE OSI
- Minimum R
- ≥ 4.1
- Bundled data
- 683 KB / 8 files
- Download size
- 4.4 MB
Cite
Cite this package
Run in R for the authors' entry:
citation("scoringutils")The package provides a citation file, so citation() returns the authors' own entry.
It points to 10.48550/arxiv.2205.07090, 10.18637/jss.v090.i12.
Cite the R Observatory
For a number measured here: a download total, a coverage figure, an archival date.
From data release v2026-10-20, which the citation names so these numbers can be found later. More on citing and the projects behind them.