diff --git a/R/class-forecast-quantile.R b/R/class-forecast-quantile.R index 6d6c1bd3d..b1d1802df 100644 --- a/R/class-forecast-quantile.R +++ b/R/class-forecast-quantile.R @@ -96,10 +96,18 @@ is_forecast_quantile <- function(x) { } -#' @rdname as_forecast_point +#' @title Convert a `forecast_quantile` to a `forecast_point` +#' #' @description #' When converting a `forecast_quantile` object into a `forecast_point` object, #' the 0.5 quantile is extracted and returned as the point forecast. +#' +#' @param data A `forecast_quantile` object (as created by +#' [as_forecast_quantile()]). +#' @param ... Unused. +#' @returns A `forecast` object of class `forecast_point`. +#' @family functions to create forecast objects +#' @seealso [as_forecast_point()] #' @export #' @keywords as_forecast as_forecast_point.forecast_quantile <- function(data, ...) { diff --git a/R/class-forecast-sample.R b/R/class-forecast-sample.R index 5bf50d865..4a0f7906a 100644 --- a/R/class-forecast-sample.R +++ b/R/class-forecast-sample.R @@ -72,18 +72,26 @@ is_forecast_sample <- function(x) { } -#' @rdname as_forecast_quantile -#' @details # Converting from `forecast_sample` to `forecast_quantile` +#' @title Convert a `forecast_sample` to a `forecast_quantile` +#' +#' @description #' When creating a `forecast_quantile` object from a `forecast_sample` object, -#' the quantiles are estimated by computing empircal quantiles from the samples +#' the quantiles are estimated by computing empirical quantiles from the samples #' via [quantile()]. Note that empirical quantiles are a biased estimator for #' the true quantiles in particular in the tails of the distribution and #' when the number of available samples is low. +#' +#' @param data A `forecast_sample` object (as created by +#' [as_forecast_sample()]). #' @param probs A numeric vector of quantile levels for which #' quantiles will be computed. Corresponds to the `probs` argument in #' [quantile()]. #' @param type Type argument passed down to the quantile function. For more #' information, see [quantile()]. +#' @param ... Unused. +#' @returns A `forecast` object of class `forecast_quantile`. +#' @family functions to create forecast objects +#' @seealso [as_forecast_quantile()] #' @importFrom stats quantile #' @importFrom methods hasArg #' @importFrom checkmate assert_numeric @@ -192,11 +200,20 @@ get_metrics.forecast_sample <- function(x, select = NULL, exclude = NULL, ...) { } -#' @rdname get_pit_histogram +#' @title PIT histogram for sample-based forecasts +#' +#' @description +#' Generate a Probability Integral Transformation (PIT) histogram for +#' sample-based forecast objects. Unlike the quantile-based method +#' ([get_pit_histogram.forecast_quantile()]), this method supports additional +#' arguments for handling integer-valued forecasts (`integers` and +#' `n_replicates`). +#' +#' @inherit get_pit_histogram params return +#' @inheritParams pit_histogram_sample +#' @seealso [get_pit_histogram()], [pit_histogram_sample()] #' @importFrom data.table `:=` as.data.table dcast #' @importFrom checkmate assert_int assert_numeric -#' @inheritParams pit_histogram_sample -#' @seealso [pit_histogram_sample()] #' @export get_pit_histogram.forecast_sample <- function(forecast, num_bins = 10, breaks = NULL, by, integers = c( diff --git a/R/helper-quantile-interval-range.R b/R/helper-quantile-interval-range.R index cba7b5ff7..10cc56590 100644 --- a/R/helper-quantile-interval-range.R +++ b/R/helper-quantile-interval-range.R @@ -142,6 +142,7 @@ quantile_to_interval_numeric <- function(observed, #' based on interval ranges. #' #' @inheritParams as_forecast_quantile +#' @inheritParams as_forecast_quantile.forecast_sample #' @param keep_quantile_col keep quantile_level column, default is TRUE #' @returns A data.table in a long interval interval range format #' @importFrom data.table as.data.table diff --git a/man/as_forecast_binary.Rd b/man/as_forecast_binary.Rd index 4938c514d..692dda5e7 100644 --- a/man/as_forecast_binary.Rd +++ b/man/as_forecast_binary.Rd @@ -113,7 +113,9 @@ Other functions to create forecast objects: \code{\link{as_forecast_nominal}()}, \code{\link{as_forecast_ordinal}()}, \code{\link{as_forecast_point}()}, +\code{\link{as_forecast_point.forecast_quantile}()}, \code{\link{as_forecast_quantile}()}, +\code{\link{as_forecast_quantile.forecast_sample}()}, \code{\link{as_forecast_sample}()} } \concept{functions to create forecast objects} diff --git a/man/as_forecast_multivariate_sample.Rd b/man/as_forecast_multivariate_sample.Rd index 04e8cf690..e647d5748 100644 --- a/man/as_forecast_multivariate_sample.Rd +++ b/man/as_forecast_multivariate_sample.Rd @@ -124,7 +124,9 @@ Other functions to create forecast objects: \code{\link{as_forecast_nominal}()}, \code{\link{as_forecast_ordinal}()}, \code{\link{as_forecast_point}()}, +\code{\link{as_forecast_point.forecast_quantile}()}, \code{\link{as_forecast_quantile}()}, +\code{\link{as_forecast_quantile.forecast_sample}()}, \code{\link{as_forecast_sample}()} } \concept{functions to create forecast objects} diff --git a/man/as_forecast_nominal.Rd b/man/as_forecast_nominal.Rd index 1af22c78a..b298ad245 100644 --- a/man/as_forecast_nominal.Rd +++ b/man/as_forecast_nominal.Rd @@ -127,7 +127,9 @@ Other functions to create forecast objects: \code{\link{as_forecast_multivariate_sample}()}, \code{\link{as_forecast_ordinal}()}, \code{\link{as_forecast_point}()}, +\code{\link{as_forecast_point.forecast_quantile}()}, \code{\link{as_forecast_quantile}()}, +\code{\link{as_forecast_quantile.forecast_sample}()}, \code{\link{as_forecast_sample}()} } \concept{functions to create forecast objects} diff --git a/man/as_forecast_ordinal.Rd b/man/as_forecast_ordinal.Rd index e6748c011..645ff6db9 100644 --- a/man/as_forecast_ordinal.Rd +++ b/man/as_forecast_ordinal.Rd @@ -127,7 +127,9 @@ Other functions to create forecast objects: \code{\link{as_forecast_multivariate_sample}()}, \code{\link{as_forecast_nominal}()}, \code{\link{as_forecast_point}()}, +\code{\link{as_forecast_point.forecast_quantile}()}, \code{\link{as_forecast_quantile}()}, +\code{\link{as_forecast_quantile.forecast_sample}()}, \code{\link{as_forecast_sample}()} } \concept{functions to create forecast objects} diff --git a/man/as_forecast_point.Rd b/man/as_forecast_point.Rd index 83ba83187..9b8855f84 100644 --- a/man/as_forecast_point.Rd +++ b/man/as_forecast_point.Rd @@ -1,10 +1,8 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/class-forecast-point.R, -% R/class-forecast-quantile.R +% Please edit documentation in R/class-forecast-point.R \name{as_forecast_point} \alias{as_forecast_point} \alias{as_forecast_point.default} -\alias{as_forecast_point.forecast_quantile} \title{Create a \code{forecast} object for point forecasts} \usage{ as_forecast_point(data, ...) @@ -16,8 +14,6 @@ as_forecast_point(data, ...) predicted = NULL, ... ) - -\method{as_forecast_point}{forecast_quantile}(data, ...) } \arguments{ \item{data}{A data.frame (or similar) with predicted and observed values. @@ -43,8 +39,16 @@ predicted values. This column will be renamed to "predicted".} A \code{forecast} object of class \code{forecast_point} } \description{ -When converting a \code{forecast_quantile} object into a \code{forecast_point} object, -the 0.5 quantile is extracted and returned as the point forecast. +Process and validate a data.frame (or similar) or similar with forecasts +and observations. If the input passes all input checks, those functions will +be converted to a \code{forecast} object. A forecast object is a \code{data.table} with +a class \code{forecast} and an additional class that depends on the forecast type. + +The arguments \code{observed}, \code{predicted}, etc. make it possible to rename +existing columns of the input data to match the required columns for a +forecast object. Using the argument \code{forecast_unit}, you can specify +the columns that uniquely identify a single forecast (and thereby removing +other, unneeded columns. See section "Forecast Unit" below for details). } \section{Target format}{ The input for all further scoring needs to be a data.frame or similar with @@ -67,7 +71,9 @@ Other functions to create forecast objects: \code{\link{as_forecast_multivariate_sample}()}, \code{\link{as_forecast_nominal}()}, \code{\link{as_forecast_ordinal}()}, +\code{\link{as_forecast_point.forecast_quantile}()}, \code{\link{as_forecast_quantile}()}, +\code{\link{as_forecast_quantile.forecast_sample}()}, \code{\link{as_forecast_sample}()} } \concept{functions to create forecast objects} diff --git a/man/as_forecast_point.forecast_quantile.Rd b/man/as_forecast_point.forecast_quantile.Rd new file mode 100644 index 000000000..460fd53d8 --- /dev/null +++ b/man/as_forecast_point.forecast_quantile.Rd @@ -0,0 +1,36 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-forecast-quantile.R +\name{as_forecast_point.forecast_quantile} +\alias{as_forecast_point.forecast_quantile} +\title{Convert a \code{forecast_quantile} to a \code{forecast_point}} +\usage{ +\method{as_forecast_point}{forecast_quantile}(data, ...) +} +\arguments{ +\item{data}{A \code{forecast_quantile} object (as created by +\code{\link[=as_forecast_quantile]{as_forecast_quantile()}}).} + +\item{...}{Unused.} +} +\value{ +A \code{forecast} object of class \code{forecast_point}. +} +\description{ +When converting a \code{forecast_quantile} object into a \code{forecast_point} object, +the 0.5 quantile is extracted and returned as the point forecast. +} +\seealso{ +\code{\link[=as_forecast_point]{as_forecast_point()}} + +Other functions to create forecast objects: +\code{\link{as_forecast_binary}()}, +\code{\link{as_forecast_multivariate_sample}()}, +\code{\link{as_forecast_nominal}()}, +\code{\link{as_forecast_ordinal}()}, +\code{\link{as_forecast_point}()}, +\code{\link{as_forecast_quantile}()}, +\code{\link{as_forecast_quantile.forecast_sample}()}, +\code{\link{as_forecast_sample}()} +} +\concept{functions to create forecast objects} +\keyword{as_forecast} diff --git a/man/as_forecast_quantile.Rd b/man/as_forecast_quantile.Rd index 4cf407d0e..052125269 100644 --- a/man/as_forecast_quantile.Rd +++ b/man/as_forecast_quantile.Rd @@ -1,10 +1,8 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/class-forecast-quantile.R, -% R/class-forecast-sample.R +% Please edit documentation in R/class-forecast-quantile.R \name{as_forecast_quantile} \alias{as_forecast_quantile} \alias{as_forecast_quantile.default} -\alias{as_forecast_quantile.forecast_sample} \title{Create a \code{forecast} object for quantile-based forecasts} \usage{ as_forecast_quantile(data, ...) @@ -17,13 +15,6 @@ as_forecast_quantile(data, ...) quantile_level = NULL, ... ) - -\method{as_forecast_quantile}{forecast_sample}( - data, - probs = c(0.05, 0.25, 0.5, 0.75, 0.95), - type = 7, - ... -) } \arguments{ \item{data}{A data.frame (or similar) with predicted and observed values. @@ -48,13 +39,6 @@ predicted values. This column will be renamed to "predicted".} \item{quantile_level}{(optional) Name of the column in \code{data} that contains the quantile level of the predicted values. This column will be renamed to "quantile_level". Only applicable to quantile-based forecasts.} - -\item{probs}{A numeric vector of quantile levels for which -quantiles will be computed. Corresponds to the \code{probs} argument in -\code{\link[=quantile]{quantile()}}.} - -\item{type}{Type argument passed down to the quantile function. For more -information, see \code{\link[=quantile]{quantile()}}.} } \value{ A \code{forecast} object of class \code{forecast_quantile} @@ -90,14 +74,6 @@ strictly necessary. See the \link{example_quantile} data set for an example. } -\section{Converting from \code{forecast_sample} to \code{forecast_quantile}}{ -When creating a \code{forecast_quantile} object from a \code{forecast_sample} object, -the quantiles are estimated by computing empircal quantiles from the samples -via \code{\link[=quantile]{quantile()}}. Note that empirical quantiles are a biased estimator for -the true quantiles in particular in the tails of the distribution and -when the number of available samples is low. -} - \section{Forecast unit}{ In order to score forecasts, \code{scoringutils} needs to know which of the rows of the data belong together and jointly form a single forecast. This is @@ -142,6 +118,8 @@ Other functions to create forecast objects: \code{\link{as_forecast_nominal}()}, \code{\link{as_forecast_ordinal}()}, \code{\link{as_forecast_point}()}, +\code{\link{as_forecast_point.forecast_quantile}()}, +\code{\link{as_forecast_quantile.forecast_sample}()}, \code{\link{as_forecast_sample}()} } \concept{functions to create forecast objects} diff --git a/man/as_forecast_quantile.forecast_sample.Rd b/man/as_forecast_quantile.forecast_sample.Rd new file mode 100644 index 000000000..0a83baa5b --- /dev/null +++ b/man/as_forecast_quantile.forecast_sample.Rd @@ -0,0 +1,50 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-forecast-sample.R +\name{as_forecast_quantile.forecast_sample} +\alias{as_forecast_quantile.forecast_sample} +\title{Convert a \code{forecast_sample} to a \code{forecast_quantile}} +\usage{ +\method{as_forecast_quantile}{forecast_sample}( + data, + probs = c(0.05, 0.25, 0.5, 0.75, 0.95), + type = 7, + ... +) +} +\arguments{ +\item{data}{A \code{forecast_sample} object (as created by +\code{\link[=as_forecast_sample]{as_forecast_sample()}}).} + +\item{probs}{A numeric vector of quantile levels for which +quantiles will be computed. Corresponds to the \code{probs} argument in +\code{\link[=quantile]{quantile()}}.} + +\item{type}{Type argument passed down to the quantile function. For more +information, see \code{\link[=quantile]{quantile()}}.} + +\item{...}{Unused.} +} +\value{ +A \code{forecast} object of class \code{forecast_quantile}. +} +\description{ +When creating a \code{forecast_quantile} object from a \code{forecast_sample} object, +the quantiles are estimated by computing empirical quantiles from the samples +via \code{\link[=quantile]{quantile()}}. Note that empirical quantiles are a biased estimator for +the true quantiles in particular in the tails of the distribution and +when the number of available samples is low. +} +\seealso{ +\code{\link[=as_forecast_quantile]{as_forecast_quantile()}} + +Other functions to create forecast objects: +\code{\link{as_forecast_binary}()}, +\code{\link{as_forecast_multivariate_sample}()}, +\code{\link{as_forecast_nominal}()}, +\code{\link{as_forecast_ordinal}()}, +\code{\link{as_forecast_point}()}, +\code{\link{as_forecast_point.forecast_quantile}()}, +\code{\link{as_forecast_quantile}()}, +\code{\link{as_forecast_sample}()} +} +\concept{functions to create forecast objects} diff --git a/man/as_forecast_sample.Rd b/man/as_forecast_sample.Rd index bf179530c..534ec460c 100644 --- a/man/as_forecast_sample.Rd +++ b/man/as_forecast_sample.Rd @@ -109,7 +109,9 @@ Other functions to create forecast objects: \code{\link{as_forecast_nominal}()}, \code{\link{as_forecast_ordinal}()}, \code{\link{as_forecast_point}()}, -\code{\link{as_forecast_quantile}()} +\code{\link{as_forecast_point.forecast_quantile}()}, +\code{\link{as_forecast_quantile}()}, +\code{\link{as_forecast_quantile.forecast_sample}()} } \concept{functions to create forecast objects} \keyword{as_forecast} diff --git a/man/get_pit_histogram.Rd b/man/get_pit_histogram.Rd index aded199ba..b59847796 100644 --- a/man/get_pit_histogram.Rd +++ b/man/get_pit_histogram.Rd @@ -1,25 +1,14 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/class-forecast-quantile.R, -% R/class-forecast-sample.R, R/get-pit-histogram.R +% R/get-pit-histogram.R \name{get_pit_histogram.forecast_quantile} \alias{get_pit_histogram.forecast_quantile} -\alias{get_pit_histogram.forecast_sample} \alias{get_pit_histogram} \alias{get_pit_histogram.default} \title{Probability integral transformation histogram} \usage{ \method{get_pit_histogram}{forecast_quantile}(forecast, num_bins = NULL, breaks = NULL, by, ...) -\method{get_pit_histogram}{forecast_sample}( - forecast, - num_bins = 10, - breaks = NULL, - by, - integers = c("nonrandom", "random", "ignore"), - n_replicates = NULL, - ... -) - get_pit_histogram(forecast, num_bins, breaks, by, ...) \method{get_pit_histogram}{default}(forecast, num_bins, breaks, by, ...) @@ -51,16 +40,6 @@ every model and location, specify \code{by = c("model", "location")}.} functions via \code{...}. See the \emph{Customising metrics} section below for details on how to use \code{\link[purrr:partial]{purrr::partial()}} to pass arguments to individual metrics.} - -\item{integers}{How to handle integer forecasts (count data). This is based -on methods described Czado et al. (2007). If "nonrandom" (default) the -function will use the non-randomised PIT method. If "random", will use the -randomised PIT method. If "ignore", will treat integer forecasts as if they -were continuous.} - -\item{n_replicates}{The number of draws for the randomised PIT for discrete -predictions. Will be ignored if forecasts are continuous or \code{integers} is -not set to \code{random}.} } \value{ A data.table with density values for each bin in the PIT histogram. @@ -93,7 +72,4 @@ Rosalind M. Eggo, W. John Edmunds (2019) Assessing the performance of real-time epidemic forecasts: A case study of Ebola in the Western Area region of Sierra Leone, 2014-15, \doi{10.1371/journal.pcbi.1006785} } -\seealso{ -\code{\link[=pit_histogram_sample]{pit_histogram_sample()}} -} \keyword{scoring} diff --git a/man/get_pit_histogram.forecast_sample.Rd b/man/get_pit_histogram.forecast_sample.Rd new file mode 100644 index 000000000..372388937 --- /dev/null +++ b/man/get_pit_histogram.forecast_sample.Rd @@ -0,0 +1,67 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/class-forecast-sample.R +\name{get_pit_histogram.forecast_sample} +\alias{get_pit_histogram.forecast_sample} +\title{PIT histogram for sample-based forecasts} +\usage{ +\method{get_pit_histogram}{forecast_sample}( + forecast, + num_bins = 10, + breaks = NULL, + by, + integers = c("nonrandom", "random", "ignore"), + n_replicates = NULL, + ... +) +} +\arguments{ +\item{forecast}{A forecast object (a validated data.table with predicted and +observed values).} + +\item{num_bins}{The number of bins in the PIT histogram. For sample-based +forecasts, the default is 10 bins. For quantile-based forecasts, the +default is one bin for each available quantile. +You can control the number of bins by supplying a number. This is fine for +sample-based pit histograms, but may fail for quantile-based formats. In +this case it is preferred to supply explicit breaks points using the +\code{breaks} argument.} + +\item{breaks}{Numeric vector with the break points for the bins in the +PIT histogram. This is preferred when creating a PIT histogram based on +quantile-based data. Default is \code{NULL} and breaks will be determined by +\code{num_bins}. If \code{breaks} is used, \code{num_bins} will be ignored. +0 and 1 will always be added as left and right bounds, respectively.} + +\item{by}{Character vector with the columns according to which the +PIT values shall be grouped. If you e.g. have the columns 'model' and +'location' in the input data and want to have a PIT histogram for +every model and location, specify \code{by = c("model", "location")}.} + +\item{integers}{How to handle integer forecasts (count data). This is based +on methods described Czado et al. (2007). If "nonrandom" (default) the +function will use the non-randomised PIT method. If "random", will use the +randomised PIT method. If "ignore", will treat integer forecasts as if they +were continuous.} + +\item{n_replicates}{The number of draws for the randomised PIT for discrete +predictions. Will be ignored if forecasts are continuous or \code{integers} is +not set to \code{random}.} + +\item{...}{Currently unused. You \emph{cannot} pass additional arguments to scoring +functions via \code{...}. See the \emph{Customising metrics} section below for +details on how to use \code{\link[purrr:partial]{purrr::partial()}} to pass arguments to individual +metrics.} +} +\value{ +A data.table with density values for each bin in the PIT histogram. +} +\description{ +Generate a Probability Integral Transformation (PIT) histogram for +sample-based forecast objects. Unlike the quantile-based method +(\code{\link[=get_pit_histogram.forecast_quantile]{get_pit_histogram.forecast_quantile()}}), this method supports additional +arguments for handling integer-valued forecasts (\code{integers} and +\code{n_replicates}). +} +\seealso{ +\code{\link[=get_pit_histogram]{get_pit_histogram()}}, \code{\link[=pit_histogram_sample]{pit_histogram_sample()}} +} diff --git a/tests/testthat/test-class-forecast-point.R b/tests/testthat/test-class-forecast-point.R index fb2fd161d..666d1dc50 100644 --- a/tests/testthat/test-class-forecast-point.R +++ b/tests/testthat/test-class-forecast-point.R @@ -8,6 +8,15 @@ test_that("as_forecast_point() works", { ) }) +test_that("as_forecast_point.forecast_quantile() extracts median without extra args", { + quantile_forecast <- as_forecast_quantile(na.omit(example_quantile)) + result <- as_forecast_point(quantile_forecast) + expect_true(is_forecast_point(result)) + expect_false("quantile_level" %in% colnames(result)) + expect_false("sample_id" %in% colnames(result)) + expect_no_condition(as_forecast_point(quantile_forecast)) +}) + # ============================================================================== # is_forecast_point() # nolint: commented_code_linter diff --git a/tests/testthat/test-class-forecast-quantile.R b/tests/testthat/test-class-forecast-quantile.R index 4f46a5ed3..be8229b70 100644 --- a/tests/testthat/test-class-forecast-quantile.R +++ b/tests/testthat/test-class-forecast-quantile.R @@ -93,6 +93,28 @@ test_that("as_forecast_quantile() works with a data.frame", { expect_no_condition(as_forecast_quantile(example_quantile_df)) }) +test_that("as_forecast_quantile.forecast_sample() accepts probs and type args", { + result <- as_forecast_quantile( + example_sample_continuous, + probs = c(0.1, 0.5, 0.9), + type = 7 + ) + expect_true(is_forecast_quantile(result)) + expect_equal(sort(unique(result$quantile_level)), c(0.1, 0.5, 0.9)) + expect_no_condition( + as_forecast_quantile(na.omit(example_sample_continuous), probs = c(0.1, 0.5, 0.9)) + ) +}) + +test_that("formals differ between as_forecast_quantile methods", { + sample_formals <- names(formals(scoringutils:::as_forecast_quantile.forecast_sample)) + default_formals <- names(formals(scoringutils:::as_forecast_quantile.default)) + expect_true("probs" %in% sample_formals) + expect_false("probs" %in% default_formals) + expect_true("quantile_level" %in% default_formals) + expect_false("quantile_level" %in% sample_formals) +}) + test_that("as_forecast_quantiles works", { samples <- data.frame( date = as.Date("2020-01-01") + 1:10, @@ -427,3 +449,9 @@ test_that("get_pit_histogram.forecast_quantile() works as expected", { # check printing works expect_output(print(pit_quantile)) }) + +test_that("get_pit_histogram.forecast_quantile() does not accept integers arg", { + expect_false( + "integers" %in% names(formals(scoringutils:::get_pit_histogram.forecast_quantile)) + ) +}) diff --git a/tests/testthat/test-class-forecast-sample.R b/tests/testthat/test-class-forecast-sample.R index 995e49106..9c8ef8807 100644 --- a/tests/testthat/test-class-forecast-sample.R +++ b/tests/testthat/test-class-forecast-sample.R @@ -74,3 +74,14 @@ test_that("get_pit_histogram.forecast_sample() works as expected", { expect_s3_class(pit_continuous, c("data.table", "data.frame"), exact = TRUE) expect_s3_class(pit_integer, c("data.table", "data.frame"), exact = TRUE) }) + +test_that("get_pit_histogram.forecast_sample() accepts integers and n_replicates args", { + result <- get_pit_histogram( + example_sample_discrete, + by = "model", + integers = "random", + n_replicates = 50 + ) + expect_named(result, c("model", "density", "bin", "mid")) + expect_true(all(result$density >= 0)) +})