From d9395cdf8928959992b2eb8edb1e772b11aec35e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Mon, 9 Oct 2023 10:44:09 +0200 Subject: [PATCH 01/21] feat: add the DiseasyModel class --- NAMESPACE | 2 + R/DiseasyModel.R | 199 ++++++++++++++++++++++++++++++++++++++++++++ man/DiseasyModel.Rd | 168 +++++++++++++++++++++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 R/DiseasyModel.R create mode 100644 man/DiseasyModel.Rd diff --git a/NAMESPACE b/NAMESPACE index aa73e280..d36c8c64 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -2,6 +2,7 @@ export(DiseasyActivity) export(DiseasyBaseModule) +export(DiseasyModel) export(DiseasyObservables) export(DiseasySeason) export(diseasyoption) @@ -10,6 +11,7 @@ import(diseasystore) import(lgr) importFrom(Matrix,sparseMatrix) importFrom(digest,digest) +importFrom(diseasystore,`%.%`) importFrom(dplyr,as_label) importFrom(lubridate,today) importFrom(pracma,logseq) diff --git a/R/DiseasyModel.R b/R/DiseasyModel.R new file mode 100644 index 00000000..b4f82ce4 --- /dev/null +++ b/R/DiseasyModel.R @@ -0,0 +1,199 @@ +#' @title Diseasy's basic model module +#' +#' @description +#' The `DiseasyModel` module implements common functionality that all models have available beyond that provided by +#' `DiseasyBaseModule`. +#' Most notably, the model module facilitates: +#' * Module interfaces: +#' The module contains the functional modules via its active bindings: +#' * `$activity`: `DiseasyActivity` +#' * `$observables`: `DiseasyObservables` +#' * `$season`: `DiseasySeason` +#' Configured instances of these modules can be provided during initialisation. If not, default instances are +#' created. +#' * Model interface: +#' The module defines the functions `$get_results()`, `$get_training_data()` and the `$parameters` binding. +#' These functions define the "API" of the models and ensure the models can take part in the ensemble. +#' @examples +#' # Normally, you would not want to create this module directly, but it is possible. +#' Model_module <- DiseasyModel$new() +#' +#' rm(Model_module) +#' @return +#' A new instance of the `DiseasyModel` [R6][R6::R6Class] class. +#' @export +DiseasyModel <- R6::R6Class( # nolint: object_name_linter + classname = "DiseasyModel", + inherit = DiseasyBaseModule, + + public = list( + + #' @description + #' Creates a new instance of the `DiseasyModel` [R6][R6::R6Class] class. + #' This module is typically not constructed directly but rather through `DiseasyModel*` classes + #' @param activity,observables,season (`boolean` or `R6::R6Class instance`)\cr + #' If a boolean is given, it dictates whether to load a new instance module of this class\cr + #' If an instance of the module is provided instead, this instance is cloned to the new `DiseasyModel` instance\cr + #' Default is `FALSE`. + #' @param label (`character`)\cr + #' A human readable label for the model instance + #' @param ... + #' parameters sent to `DiseasyBaseModule` [R6][R6::R6Class] constructor + #' @details + #' The `DiseasyModel` is the main template that the individual models should inherit from since this defines the + #' set of methods the later framework expects from each model. In addition, it provides the main interface with + #' the other modules of the framework + #' @return + #' A new instance of the `DiseasyModel` [R6][R6::R6Class] class. + initialize = function(activity = FALSE, + observables = FALSE, + season = FALSE, + label = NULL, + ...) { + + coll <- checkmate::makeAssertCollection() + checkmate::assert(checkmate::check_logical(activity, null.ok = TRUE), + checkmate::check_class(activity, "DiseasyActivity", null.ok = TRUE), + add = coll) + checkmate::assert(checkmate::check_logical(observables, null.ok = TRUE), + checkmate::check_class(observables, "DiseasyObservables", null.ok = TRUE), + add = coll) + checkmate::assert(checkmate::check_logical(season, null.ok = TRUE), + checkmate::check_class(season, "DiseasySeason", null.ok = TRUE), + add = coll) + checkmate::assert_character(label, len = 1, any.missing = FALSE, null.ok = TRUE, add = coll) + checkmate::reportAssertions(coll) + + # Pass further arguments to the DiseasyBaseModule initializer + super$initialize(...) + + # Then try to set the modules + if (isTRUE(observables)) { + self$load_module(DiseasyObservables$new()) + } else if (inherits(observables, "DiseasyObservables")) { + self$load_module(observables) + } + + if (isTRUE(activity)) { + self$load_module(DiseasyActivity$new()) + } else if (inherits(activity, "DiseasyActivity")) { + self$load_module(activity) + } + + if (isTRUE(season)) { + self$load_module(DiseasySeason$new()) + } else if (inherits(season, "DiseasySeason")) { + self$load_module(season) + } + + # Set the label for the model + private$label <- label + }, + + + # Roxygen has only limited support for R6 docs currently, so we need to do some tricks for the documentation + # of get_results + #' @description `r rd_get_results_description` + #' @param observable `r rd_observable` + #' @param prediction_length `r rd_prediction_length` + #' @param quantiles `r rd_quantiles` + #' @param aggregation `r rd_aggregation` + #' @return `r rd_get_results_return` + #' @seealso `r rd_get_results_seealso` + get_results = function(observable, prediction_length, quantiles = NULL, aggregation = NULL) { + private$not_implemented_error("Each model must implement their own `get_results` methods") + }, + + + #' @description + #' A method that returns training data for the models based on the model value of `training_length` and + #' the `last_queryable_date` of the `DiseasyObservables` module. + #' @param observable `r rd_observable` + #' @param aggregation `r rd_aggregation` + #' @return The output of `DiseasyObservables$get_observation` constrained to the training period. + get_training_data = function(observable, aggregation = NULL) { + + # Input validation + coll <- checkmate::makeAssertCollection() + checkmate::assert_character(observable, add = coll) + checkmate::assert_number(self$parameters$training_length, add = coll) + checkmate::assert_date(self$observables$last_queryable_date, add = coll) + checkmate::reportAssertions(coll) + + # Get the observable at the aggregation level + start_date <- self$observables$last_queryable_date - lubridate::days(self$parameters$training_length) + end_date <- self$observables$last_queryable_date # Only within the training period + + data <- self$observables$get_observation(observable, aggregation, start_date, end_date) |> + dplyr::mutate(t = lubridate::interval(max(zoo::as.Date(date)), zoo::as.Date(date)) / lubridate::days(1)) + + return(data) + } + ), + + # Make active bindings to the private variables + active = list( + + #' @field activity (`diseasy::activity`)\cr + #' The local copy of an activity module. Read-only. + #' @seealso [diseasy::DiseasyActivity] + #' @importFrom diseasystore `%.%` + activity = purrr::partial( + .f = active_binding, # nolint: indentation_linter + name = "activity", + expr = return(private %.% .DiseasyActivity)), + + + #' @field observables (`diseasy::DiseasyObservables`)\cr + #' The local copy of an DiseasyObservables module. Read-only. + #' @seealso [diseasy::DiseasyObservables] + #' @importFrom diseasystore `%.%` + observables = purrr::partial( + .f = active_binding, # nolint: indentation_linter + name = "observables", + expr = return(private %.% .DiseasyObservables)), + + + #' @field season (`diseasy::season`)\cr + #' The local copy of an season module. Read-only. + #' @seealso [diseasy::DiseasySeason] + #' @importFrom diseasystore `%.%` + season = purrr::partial( + .f = active_binding, # nolint: indentation_linter + name = "season", + expr = return(private %.% .DiseasySeason)), + + + #' @field parameters (`list()`)\cr + #' The parameters used in the model. Read-only. + #' @importFrom diseasystore `%.%` + parameters = purrr::partial( + .f = active_binding, # nolint: indentation_linter + name = "parameters", + expr = return(private %.% .parameters)) + ), + + private = list( + + .DiseasyActivity = NULL, + .DiseasyObservables = NULL, + .DiseasySeason = NULL, + .parameters = NULL, + + # @param label (`character`)\cr + # A human readable label for the model instance + label = NULL, + + model_cannot_predict = function(observable = NULL, aggregation = NULL) { + coll <- checkmate::makeAssertCollection() + if (!is.null(observable)) { + coll$push(glue::glue("Model not configured to predict for observable: {observable}")) + } + if (!is.null(aggregation)) { + coll$push(glue::glue("Model not configured to predict at aggregation: ", + "{private$aggregation_to_string(aggregation)}")) + } + checkmate::reportAssertions(coll) + } + ) +) diff --git a/man/DiseasyModel.Rd b/man/DiseasyModel.Rd new file mode 100644 index 00000000..f74f717a --- /dev/null +++ b/man/DiseasyModel.Rd @@ -0,0 +1,168 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/DiseasyModel.R +\name{DiseasyModel} +\alias{DiseasyModel} +\title{Meta module for the models} +\description{ +TODO +} +\seealso{ +\link{DiseasyObservables} + +\link{DiseasyActivity} + +\link{DiseasyObservables} + +\link{DiseasySeason} +} +\section{Super class}{ +\code{\link[diseasy:DiseasyBaseModule]{diseasy::DiseasyBaseModule}} -> \code{DiseasyModel} +} +\section{Active bindings}{ +\if{html}{\out{
}} +\describe{ +\item{\code{activity}}{(\code{diseasy::activity})\cr +The local copy of an activity module. Read-only.} + +\item{\code{observables}}{(\code{diseasy::DiseasyObservables})\cr +The local copy of an DiseasyObservables module. Read-only.} + +\item{\code{season}}{(\code{diseasy::season})\cr +The local copy of an season module. Read-only.} + +\item{\code{parameters}}{(\code{list()})\cr +The parameters used in the model. Read-only.} +} +\if{html}{\out{
}} +} +\section{Methods}{ +\subsection{Public methods}{ +\itemize{ +\item \href{#method-DiseasyModel-new}{\code{DiseasyModel$new()}} +\item \href{#method-DiseasyModel-get_results}{\code{DiseasyModel$get_results()}} +\item \href{#method-DiseasyModel-get_training_data}{\code{DiseasyModel$get_training_data()}} +\item \href{#method-DiseasyModel-clone}{\code{DiseasyModel$clone()}} +} +} +\if{html}{\out{ +
Inherited methods + +
+}} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-DiseasyModel-new}{}}} +\subsection{Method \code{new()}}{ +Creates a new instance of the \code{DiseasyModel} \link[R6:R6Class]{R6} class. +This module is typically not constructed directly but rather through \verb{DiseasyModel*} classes +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{DiseasyModel$new( + activity = FALSE, + observables = FALSE, + season = FALSE, + label = NULL, + ... +)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{activity, observables, season}}{(\code{boolean} or \verb{R6::R6Class instance})\cr +If a boolean is given, it dictates whether to load a new instance module of this class\cr +If an instance of the module is provided instead, this instance is cloned to the new \code{DiseasyModel} instance\cr +Default is FALSE.} + +\item{\code{label}}{(\code{character})\cr +A human readable label for the model instance} + +\item{\code{...}}{parameters sent to \code{DiseasyBaseModule} \link[R6:R6Class]{R6} constructor} +} +\if{html}{\out{
}} +} +\subsection{Details}{ +The \code{DiseasyModel} is the main template that the individual models should inherit from since this defines the +set of methods the later framework expects from each model. In addition, it provides the main interface with +the other modules of the framework +} + +\subsection{Returns}{ +A new instance of the \code{DiseasyModel} \link[R6:R6Class]{R6} class. +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-DiseasyModel-get_results}{}}} +\subsection{Method \code{get_results()}}{ +The primary method used to request model results of a given observable at a given aggregation. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{DiseasyModel$get_results( + observable, + prediction_length, + quantiles = NULL, + aggregation = NULL +)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{observable}}{(\code{character})\cr The observable to provide prediction for. Must match observable in \code{DiseasyObservables} \link[R6:R6Class]{R6} class.} + +\item{\code{prediction_length}}{(\code{numeric})\cr The number of days to predict. The prediction start is defined by \code{last_queryable_date} of the \code{DiseasyObservables} \link[R6:R6Class]{R6} class.} + +\item{\code{quantiles}}{(\code{list}(\code{numeric}))\cr Default NULL. If given, results are returned at the quantiles given.} + +\item{\code{aggregation}}{(\code{list}(\code{quosures}))\cr Default NULL. If given, expressions in aggregation evaluated to give the aggregation level.} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +A \code{tibble} \link[tibble:tibble]{tibble::tibble} with predictions at the level specified by aggregation level. In addition to aggregation columns, the output has the columns:\cr date (\code{Date}) specifying the date of the prediction\cr realization_id (\code{character}) giving a unique id for each realization in the ensemble\cr model (\code{character}) the name (classname) of the model used to provide the prediction. +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-DiseasyModel-get_training_data}{}}} +\subsection{Method \code{get_training_data()}}{ +A method that returns training data for the models based on the model value of \code{training_length} and +the \code{last_queryable_date} of the \code{DiseasyObservables} module. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{DiseasyModel$get_training_data(observable, aggregation = NULL)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{observable}}{(\code{character})\cr The observable to provide prediction for. Must match observable in \code{DiseasyObservables} \link[R6:R6Class]{R6} class.} + +\item{\code{aggregation}}{(\code{list}(\code{quosures}))\cr Default NULL. If given, expressions in aggregation evaluated to give the aggregation level.} +} +\if{html}{\out{
}} +} +\subsection{Returns}{ +The output of \code{DiseasyObservables$get_observation} constrained to the training period. +} +} +\if{html}{\out{
}} +\if{html}{\out{}} +\if{latex}{\out{\hypertarget{method-DiseasyModel-clone}{}}} +\subsection{Method \code{clone()}}{ +The objects of this class are cloneable with this method. +\subsection{Usage}{ +\if{html}{\out{
}}\preformatted{DiseasyModel$clone(deep = FALSE)}\if{html}{\out{
}} +} + +\subsection{Arguments}{ +\if{html}{\out{
}} +\describe{ +\item{\code{deep}}{Whether to make a deep clone.} +} +\if{html}{\out{
}} +} +} +} From 4c271d6fefda98d21c0bdf184c77b2db7e1c38e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Mon, 9 Oct 2023 10:44:19 +0200 Subject: [PATCH 02/21] test(DiseasyModel): Add tests for DiseasyModel --- tests/testthat/test-DiseasyModel.R | 322 +++++++++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 tests/testthat/test-DiseasyModel.R diff --git a/tests/testthat/test-DiseasyModel.R b/tests/testthat/test-DiseasyModel.R new file mode 100644 index 00000000..6174af66 --- /dev/null +++ b/tests/testthat/test-DiseasyModel.R @@ -0,0 +1,322 @@ +test_that("initialize works", { + + # Creating an empty model module + m <- DiseasyModel$new() + + # Store the current hash + hash_new_instance <- m$hash + + # Check module instances can be loaded into new model instance + act <- DiseasyActivity$new() + s <- DiseasySeason$new() + obs <- DiseasyObservables$new() + + m_act_instance <- DiseasyModel$new(activity = act) + m_s_instance <- DiseasyModel$new(season = s) + m_obs_instance <- DiseasyModel$new(observables = obs) + + # Check the hash is unique for each module when created this way + modules <- list(m, m_act_instance, m_s_instance, m_obs_instance) + expect_length(unique(purrr::map(modules, ~ .x$hash)), length(modules)) + + + # Check modules can be created during model instantiation + m_act_boolean <- DiseasyModel$new(activity = TRUE) + m_s_boolean <- DiseasyModel$new(season = TRUE) + m_obs_boolean <- DiseasyModel$new(observables = TRUE) + + # Check the hash is the same when created this way + expect_identical(m_act_instance$hash, m_act_boolean$hash) + expect_identical(m_s_instance$hash, m_s_boolean$hash) + expect_identical(m_obs_instance$hash, m_obs_boolean$hash) + + # Check a label can be set + m_label <- DiseasyModel$new(label = "test") + expect_equal(m_label$hash, m$hash) # label should not change the hash + + rm(m, m_act_instance, m_s_instance, m_obs_instance, m_act_boolean, m_s_boolean, m_obs_boolean, m_label) +}) + + +test_that("$load_module() works", { + + # Check that observable is loaded into objects that can take it + # We first create and load a module that uses "DiseasyObservables" internally but we do not provide it to the module + # (In this case, "DiseasySeason"). + # After loading this module, into the model module, no observables module should be present. + m <- DiseasyModel$new(season = TRUE) + checkmate::expect_class(m %.% season, "DiseasySeason") + expect_null(m %.% season %.% observables) + + # We then create and load a observables module into the model module. + # This should propagate the observables module to the season module. + obs <- DiseasyObservables$new() + m$load_module(obs) + checkmate::expect_class(m %.% season, "DiseasySeason") + checkmate::expect_class(m %.% season %.% observables, "DiseasyObservables") + expect_identical(m %.% season %.% observables, obs) + + rm(m) + + #.. and the other way around + m <- DiseasyModel$new(observables = TRUE) + checkmate::expect_class(m %.% observables, "DiseasyObservables") + expect_null(m %.% season) + + s <- DiseasySeason$new() + m$load_module(s) + checkmate::expect_class(m %.% season, "DiseasySeason") + checkmate::expect_class(m %.% season %.% observables, "DiseasyObservables") + expect_identical(m %.% season %.% observables, m %.% observables) + + rm(m) +}) + + +test_that("$hash works", { + + # Test hash generation works as expected + + # Creating an empty model module + m <- DiseasyModel$new() + hash_new_instance <- m$hash + + # Load modules into the module + act <- DiseasyActivity$new() + act$set_activity_units(dk_activity_units) + + scenario_1 <- data.frame(date = as.Date(character(0)), opening = character(0), closing = character(0)) |> + dplyr::add_row(date = as.Date("2020-01-01"), opening = "baseline", closing = NA) |> + dplyr::add_row(date = as.Date("2020-03-12"), opening = NA, closing = "baseline") |> + dplyr::add_row(date = as.Date("2020-03-12"), opening = "lockdown_2020", closing = NA) |> + dplyr::add_row(date = as.Date("2020-04-15"), opening = "secondary_education_phase_1_2020", closing = NA) + + act$change_activity(scenario_1) + + s <- DiseasySeason$new() + obs <- DiseasyObservables$new() + + # Loading DiseasyActivity + m$load_module(act) + hash_activity_loaded <- m$hash + expect_false(hash_activity_loaded == hash_new_instance) + + # Loading DiseasySeason + m$load_module(s) + hash_activity_season_loaded <- m$hash + expect_false(hash_activity_season_loaded == hash_new_instance) + expect_false(hash_activity_season_loaded == hash_activity_loaded) + + # Loading DiseasyObservables + m$load_module(obs) + hash_activity_season_observables_loaded <- m$hash # nolint: object_length_linter + expect_false(hash_activity_season_observables_loaded == hash_new_instance) + expect_false(hash_activity_season_observables_loaded == hash_activity_loaded) + expect_false(hash_activity_season_observables_loaded == hash_activity_season_loaded) + + + # Check reloading modules works + m$load_module(act) + expect_equal(m$hash, hash_activity_season_observables_loaded) + + m$load_module(s) + expect_equal(m$hash, hash_activity_season_observables_loaded) + + m$load_module(obs) + expect_equal(m$hash, hash_activity_season_observables_loaded) + + # Check loading of altered module changes the hash + act_alt <- DiseasyActivity$new() + act_alt$set_activity_units(dk_activity_units) + act_alt$change_activity(head(scenario_1, 3)) + m$load_module(act_alt) + expect_false(m$hash == hash_activity_season_observables_loaded) + + s_alt <- DiseasySeason$new(reference_date = as.Date("2020-03-01")) + m$load_module(act) + expect_equal(m$hash, hash_activity_season_observables_loaded) + m$load_module(s_alt) + expect_false(m$hash == hash_activity_season_observables_loaded) + + obs_alt <- DiseasyObservables$new(last_queryable_date = as.Date("2020-03-01")) + m$load_module(s) + expect_equal(m$hash, hash_activity_season_observables_loaded) + m$load_module(obs_alt) + expect_false(m$hash == hash_activity_season_observables_loaded) + + rm(m, s, act, obs, s_alt, act_alt, obs_alt) + +}) + + +test_that("cloning works", { + + # Creating modules for the model module + act <- DiseasyActivity$new() + act$set_activity_units(dk_activity_units) + + scenario_1 <- data.frame(date = as.Date(character(0)), opening = character(0), closing = character(0)) |> + dplyr::add_row(date = as.Date("2020-01-01"), opening = "baseline", closing = NA) |> + dplyr::add_row(date = as.Date("2020-03-12"), opening = NA, closing = "baseline") |> + dplyr::add_row(date = as.Date("2020-03-12"), opening = "lockdown_2020", closing = NA) |> + dplyr::add_row(date = as.Date("2020-04-15"), opening = "secondary_education_phase_1_2020", closing = NA) + + act$change_activity(scenario_1) + + s <- DiseasySeason$new() + obs <- DiseasyObservables$new() + + + + # Create instance of the DiseasyModel with the modules + m <- DiseasyModel$new(activity = act, + season = s, + observables = obs) + hash_loaded <- m$hash + + + # Create a simple clone + m_c <- m$clone() + expect_equal(m_c$hash, hash_loaded) # Hash should be the same + + + + # If we load a new module into the clone, we want new hashes in the clone + act_alt <- DiseasyActivity$new() + act_alt$set_activity_units(dk_activity_units) + act_alt$change_activity(head(scenario_1, 3)) + m_c$load_module(act_alt) + expect_equal(m$hash, hash_loaded) # Hash should be the same for the original instance + expect_false(m_c$hash == hash_loaded) # Hash should be changed for the clone instance + + s_alt <- DiseasySeason$new(reference_date = as.Date("2020-03-01")) + m_c$load_module(act) + expect_equal(m_c$hash, hash_loaded) # Hash should now be reset + m_c$load_module(s_alt) + expect_equal(m$hash, hash_loaded) # Hash should be the same for the original instance + expect_false(m_c$hash == hash_loaded) # Hash should be changed for the clone instance + + obs_alt <- DiseasyObservables$new(last_queryable_date = as.Date("2020-03-01")) + m_c$load_module(s) + expect_equal(m_c$hash, hash_loaded) # Hash should now be reset + m_c$load_module(obs_alt) + expect_equal(m$hash, hash_loaded) # Hash should be the same for the original instance + expect_false(m_c$hash == hash_loaded) # Hash should be changed for the clone instance + + + + # If we change the module inside of one, it should not change in the other + m_c$load_module(obs) + expect_equal(m_c$hash, hash_loaded) # Hash should now be reset + + + # - activity + m_c$activity$reset_scenario() + expect_equal(m$hash, hash_loaded) # Hash should be the same for the original instance + expect_false(m_c$hash == hash_loaded)# Hash should be changed for the clone instance + expect_false(act$hash == m_c$activity$hash) # module hashes should also be different + + # - season + m_c$load_module(act) + expect_equal(m_c$hash, hash_loaded) # Hash should now be reset + m_c$season$set_reference_date(as.Date("2020-03-01")) + expect_equal(m$hash, hash_loaded) # Hash should be the same for the original instance + expect_false(m_c$hash == hash_loaded)# Hash should be changed for the clone instance + expect_false(s$hash == m_c$season$hash) # module hashes should also be different + + # - observables + m_c$load_module(s) + expect_equal(m_c$hash, hash_loaded) # Hash should now be reset + m_c$observables$set_last_queryable_date(as.Date("2020-03-01")) + expect_equal(m$hash, hash_loaded) # Hash "202should be the same for the original instance + expect_false(m_c$hash == hash_loaded)# Hash should be changed for the clone instance + expect_false(obs$hash == m_c$observables$hash) # module hashes should also be different + + rm(m, m_c, s, act, obs, s_alt, act_alt, obs_alt) +}) + + +test_that("$get_results() gives error", { + + # Creating an empty module + m <- DiseasyModel$new() + + # Test the get_results + expect_error(m$get_results(), + class = "simpleError", + regex = "Each model must implement their own `get_results` methods") + + rm(m) +}) + + +test_that("active binding: activity works", { + + # Creating an empty module + m <- DiseasyModel$new() + + # Retrieve the activity + expect_NULL(m %.% activity) + + # Try to set activity through the binding + # test_that cannot capture this error, so we have to hack it + expect_identical(tryCatch(m$activity <- DiseasyActivity$new(), error = \(e) e), + simpleError("`$activity` is read only")) + expect_NULL(m %.% activity) + + rm(m) +}) + + +test_that("active binding: observables works", { + + # Creating an empty module + m <- DiseasyModel$new() + + # Retrieve the observables + expect_null(m %.% observables) + + # Try to set observables through the binding + # test_that cannot capture this error, so we have to hack it + expect_identical(tryCatch(m$observables <- DiseasyObservables$new(), error = \(e) e), + simpleError("`$observables` is read only")) + expect_null(m %.% observables) + + rm(m) +}) + + +test_that("active binding: season works", { + + # Creating an empty module + m <- DiseasyModel$new() + + # Retrieve the season + expect_null(m %.% season) + + # Try to set season through the binding + # test_that cannot capture this error, so we have to hack it + expect_identical(tryCatch(m$season <- DiseasySeason$new(), error = \(e) e), + simpleError("`$season` is read only")) + expect_null(m %.% season) + + rm(m) +}) + + +test_that("active binding: parameters works", { + + # Creating an empty module + m <- DiseasyModel$new() + + # Retrieve the parameters + expect_null(m %.% parameters) + + # Try to set parameters through the binding + # test_that cannot capture this error, so we have to hack it + expect_identical(tryCatch(m$parameters <- list(test = 2), error = \(e) e), + simpleError("`$parameters` is read only")) + expect_null(m %.% parameters) + + rm(m) +}) From 4af6fef5eddce9ebfa58a0882aa2ae682cccdf22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Tue, 6 Feb 2024 09:01:00 +0100 Subject: [PATCH 03/21] docs(DiseasyModel): Update documentation --- R/DiseasyModel.R | 29 +++++++++++++++-------------- man/DiseasyModel.Rd | 45 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/R/DiseasyModel.R b/R/DiseasyModel.R index b4f82ce4..b513c531 100644 --- a/R/DiseasyModel.R +++ b/R/DiseasyModel.R @@ -22,6 +22,7 @@ #' @return #' A new instance of the `DiseasyModel` [R6][R6::R6Class] class. #' @export +#' @seealso [lgr][lgr::lgr] DiseasyModel <- R6::R6Class( # nolint: object_name_linter classname = "DiseasyModel", inherit = DiseasyBaseModule, @@ -94,13 +95,13 @@ DiseasyModel <- R6::R6Class( # Roxygen has only limited support for R6 docs currently, so we need to do some tricks for the documentation # of get_results #' @description `r rd_get_results_description` - #' @param observable `r rd_observable` - #' @param prediction_length `r rd_prediction_length` - #' @param quantiles `r rd_quantiles` - #' @param aggregation `r rd_aggregation` + #' @param observable `r rd_observable()` + #' @param prediction_length `r rd_prediction_length()` + #' @param quantiles `r rd_quantiles()` + #' @param stratification `r rd_stratification()` #' @return `r rd_get_results_return` #' @seealso `r rd_get_results_seealso` - get_results = function(observable, prediction_length, quantiles = NULL, aggregation = NULL) { + get_results = function(observable, prediction_length, quantiles = NULL, stratification = NULL) { private$not_implemented_error("Each model must implement their own `get_results` methods") }, @@ -108,10 +109,10 @@ DiseasyModel <- R6::R6Class( #' @description #' A method that returns training data for the models based on the model value of `training_length` and #' the `last_queryable_date` of the `DiseasyObservables` module. - #' @param observable `r rd_observable` - #' @param aggregation `r rd_aggregation` + #' @param observable `r rd_observable()` + #' @param stratification `r rd_stratification()` #' @return The output of `DiseasyObservables$get_observation` constrained to the training period. - get_training_data = function(observable, aggregation = NULL) { + get_training_data = function(observable, stratification = NULL) { # Input validation coll <- checkmate::makeAssertCollection() @@ -120,11 +121,11 @@ DiseasyModel <- R6::R6Class( checkmate::assert_date(self$observables$last_queryable_date, add = coll) checkmate::reportAssertions(coll) - # Get the observable at the aggregation level + # Get the observable at the stratification level start_date <- self$observables$last_queryable_date - lubridate::days(self$parameters$training_length) end_date <- self$observables$last_queryable_date # Only within the training period - data <- self$observables$get_observation(observable, aggregation, start_date, end_date) |> + data <- self$observables$get_observation(observable, stratification, start_date, end_date) |> dplyr::mutate(t = lubridate::interval(max(zoo::as.Date(date)), zoo::as.Date(date)) / lubridate::days(1)) return(data) @@ -184,14 +185,14 @@ DiseasyModel <- R6::R6Class( # A human readable label for the model instance label = NULL, - model_cannot_predict = function(observable = NULL, aggregation = NULL) { + model_cannot_predict = function(observable = NULL, stratification = NULL) { coll <- checkmate::makeAssertCollection() if (!is.null(observable)) { coll$push(glue::glue("Model not configured to predict for observable: {observable}")) } - if (!is.null(aggregation)) { - coll$push(glue::glue("Model not configured to predict at aggregation: ", - "{private$aggregation_to_string(aggregation)}")) + if (!is.null(stratification)) { + coll$push(glue::glue("Model not configured to predict at stratification: ", + "{private$stratification_to_string(stratification)}")) } checkmate::reportAssertions(coll) } diff --git a/man/DiseasyModel.Rd b/man/DiseasyModel.Rd index f74f717a..e6547e86 100644 --- a/man/DiseasyModel.Rd +++ b/man/DiseasyModel.Rd @@ -2,11 +2,38 @@ % Please edit documentation in R/DiseasyModel.R \name{DiseasyModel} \alias{DiseasyModel} -\title{Meta module for the models} +\title{Diseasy's basic model module} +\value{ +A new instance of the \code{DiseasyModel} \link[R6:R6Class]{R6} class. +} \description{ -TODO +The \code{DiseasyModel} module implements common functionality that all models have available beyond that provided by +\code{DiseasyBaseModule}. +Most notably, the model module facilitates: +\itemize{ +\item Module interfaces: +The module contains the functional modules via its active bindings: +\itemize{ +\item \verb{$activity}: \code{DiseasyActivity} +\item \verb{$observables}: \code{DiseasyObservables} +\item \verb{$season}: \code{DiseasySeason} +Configured instances of these modules can be provided during initialisation. If not, default instances are +created. +} +\item Model interface: +The module defines the functions \verb{$get_results()}, \verb{$get_training_data()} and the \verb{$parameters} binding. +These functions define the "API" of the models and ensure the models can take part in the ensemble. +} +} +\examples{ + # Normally, you would not want to create this module directly, but it is possible. + Model_module <- DiseasyModel$new() + + rm(Model_module) } \seealso{ +\link[lgr:lgr-package]{lgr} + \link{DiseasyObservables} \link{DiseasyActivity} @@ -75,7 +102,7 @@ This module is typically not constructed directly but rather through \verb{Disea \item{\code{activity, observables, season}}{(\code{boolean} or \verb{R6::R6Class instance})\cr If a boolean is given, it dictates whether to load a new instance module of this class\cr If an instance of the module is provided instead, this instance is cloned to the new \code{DiseasyModel} instance\cr -Default is FALSE.} +Default is \code{FALSE}.} \item{\code{label}}{(\code{character})\cr A human readable label for the model instance} @@ -98,13 +125,13 @@ A new instance of the \code{DiseasyModel} \link[R6:R6Class]{R6} class. \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-DiseasyModel-get_results}{}}} \subsection{Method \code{get_results()}}{ -The primary method used to request model results of a given observable at a given aggregation. +The primary method used to request model results of a given observable at a given stratification \subsection{Usage}{ \if{html}{\out{
}}\preformatted{DiseasyModel$get_results( observable, prediction_length, quantiles = NULL, - aggregation = NULL + stratification = NULL )}\if{html}{\out{
}} } @@ -117,12 +144,12 @@ The primary method used to request model results of a given observable at a give \item{\code{quantiles}}{(\code{list}(\code{numeric}))\cr Default NULL. If given, results are returned at the quantiles given.} -\item{\code{aggregation}}{(\code{list}(\code{quosures}))\cr Default NULL. If given, expressions in aggregation evaluated to give the aggregation level.} +\item{\code{stratification}}{(\code{list}(\code{quosures}))\cr Default NULL. If given, expressions in stratification evaluated to give the stratification level.} } \if{html}{\out{}} } \subsection{Returns}{ -A \code{tibble} \link[tibble:tibble]{tibble::tibble} with predictions at the level specified by aggregation level. In addition to aggregation columns, the output has the columns:\cr date (\code{Date}) specifying the date of the prediction\cr realization_id (\code{character}) giving a unique id for each realization in the ensemble\cr model (\code{character}) the name (classname) of the model used to provide the prediction. +A \code{tibble} \link[tibble:tibble]{tibble::tibble} with predictions at the level specified by stratification level. In addition to stratification columns, the output has the columns:\cr date (\code{Date}) specifying the date of the prediction\cr realization_id (\code{character}) giving a unique id for each realization in the ensemble\cr model (\code{character}) the name (classname) of the model used to provide the prediction. } } \if{html}{\out{
}} @@ -132,7 +159,7 @@ A \code{tibble} \link[tibble:tibble]{tibble::tibble} with predictions at the lev A method that returns training data for the models based on the model value of \code{training_length} and the \code{last_queryable_date} of the \code{DiseasyObservables} module. \subsection{Usage}{ -\if{html}{\out{
}}\preformatted{DiseasyModel$get_training_data(observable, aggregation = NULL)}\if{html}{\out{
}} +\if{html}{\out{
}}\preformatted{DiseasyModel$get_training_data(observable, stratification = NULL)}\if{html}{\out{
}} } \subsection{Arguments}{ @@ -140,7 +167,7 @@ the \code{last_queryable_date} of the \code{DiseasyObservables} module. \describe{ \item{\code{observable}}{(\code{character})\cr The observable to provide prediction for. Must match observable in \code{DiseasyObservables} \link[R6:R6Class]{R6} class.} -\item{\code{aggregation}}{(\code{list}(\code{quosures}))\cr Default NULL. If given, expressions in aggregation evaluated to give the aggregation level.} +\item{\code{stratification}}{(\code{list}(\code{quosures}))\cr Default NULL. If given, expressions in stratification evaluated to give the stratification level.} } \if{html}{\out{}} } From d0a3961d76a29bc46d8834f058757a61faec45a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Fri, 31 May 2024 10:58:29 +0200 Subject: [PATCH 04/21] docs(DiseasyModel): Extend vignette on creating a model --- vignettes/creating-a-model.Rmd | 148 +++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 vignettes/creating-a-model.Rmd diff --git a/vignettes/creating-a-model.Rmd b/vignettes/creating-a-model.Rmd new file mode 100644 index 00000000..a497ba4f --- /dev/null +++ b/vignettes/creating-a-model.Rmd @@ -0,0 +1,148 @@ +--- +title: "Creating a model" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Creating a model} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, include = FALSE} +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) +``` + +```{r setup, include = FALSE} +library(diseasy) +``` + +# Introduction +This vignette provides an overview of the `DiseasyModel` class and its use in creating a `diseasy` model. + +As described in the package introduction (`vignette("diseasy")`), the power of `diseasy` comes from combining different +"functional" modules with model "templates" to create a large ensemble of models easily. + +It is `DiseasyModel` that defines how such model templates should look, and provides the mechanism by witch the +functional modules are loaded in the model templates to create the individual model members of the ensemble. + +Before we dive into the details of `DiseasyModel`, let's first consider what it means to be a "model template": + +# Model Templates +Developing a model in `diseasy` involves constructing a model template that incorporates one or more functional modules +into a model design. + +For instance, let's consider the creation of a simple SIR (Susceptible, Infectious, Recovered) infection +model with a seasonal forcing `s(t)` of the overall infection rate, denoted by \beta. + +The SEIR model with seasonal forcing can be represented by the following set of differential equations: + +$$ +\frac{dS}{dt} = - \beta \cdot s(t) \cdot S \cdot I +$$ + +$$ +\frac{dI}{dt} = \beta \cdot s(t) \cdot S \cdot I - \gamma \cdot I +$$ + +$$ +\frac{dR}{dt} = \gamma \cdot I +$$ + +When developing this model traditionally, we would choose some functional form for the seasonal forcing `s(t)`, +e.g. a sinosodial relationship: +$$ +s(t) = A \cdot \cos(kt+\phi) +$$ + +The strategy of `diseasy` is to source the seasonal forcing `s(t)` directly from the corresponding functional module +(`DiseasySeason`). + +Every implementation of a seasonal forcing in `DiseasySeason` has the same function signature +(i.e. they all take only a single argument `t` once configured). + +This way, we can configure different instances of `DiseasySeason` using the whole range of (available) plausible +functional forms of seasonal forcing: + +* No seasonal forcing ($s(t) = 1$) +* Sinosodial forcing +* Temperature dependent forcing +* Other plausible seasonal forcing forms + +Since the modules of `diseasy` are "objects" in an object-oriented-programming sense, the functional forms of the +seasonal forcing can have parameters (as we see in the list above), but these are stored in the object when configuring +the module, and the actual seasonal forcing exposed to the models all have the same function signature that takes +only time as input (`s(t)`)[^1]. + +This way, we can easily swap out different seasonal forcing models in our model templates without having to change the +model equations themselves. If new seasonal forcing models are developed, they are then automatically available to the +developed model. + +This is the philosophy behind the `diseasy` package, since we can easily swap out different functional modules for the +templates and generate a large ensemble of models with functional forms for the disease dynamics. + +# The `DiseasyModel` class + +To create a model template in `diseasy` is to create a new class that inherits the `DiseasyModel` class. + +This gives the model template access to the functional modules that are available in the package, it defines +how the user sets the parameters of the model and queries the model for results. + +In addition, it provides useful utility for the implementation of the model such as logging and caching. + +## A container for functional modules +The `DiseasyModel` class contains slots for instances of functional modules: + +```{r generate_list_of_functional_modules, echo = FALSE, results = 'asis'} +# We dynamically update this list, as new functional modules are added to DiseasyModel +purrr::keep(names(DiseasyModel$private_fields), ~ startsWith(., ".Diseasy")) |> + purrr::map_chr(~ stringr::str_remove(., stringr::fixed(".Diseasy"))) |> + purrr::map_chr(~ + paste0( + "- `$", stringr::str_to_lower(.), "`", + ": `{Diseasy", ., "}`" + ) + ) |> + cat(sep = "\n") +``` + +This means, that once a new `DiseasyModel` instance is created, it will have the functional modules internally +available. Going back to the above example of the SIR model with seasonal forcing, the model template would be +access the seasonal forcing `s(t)` via the call `self$season$model_t(t)`. +```{r check_DiseasySeason_function_signature, include = FALSE} +# If the the signature of DiseasySeason is changed, please update this vignette +stopifnot("model_t" %in% names(DiseasySeason$active)) +``` + +## Training data +The `DiseasyModel` class also contains the method `$get_training_data()` that provides training data to the models. + +This method interfaces with the `DiseasyObservables` module to provide data for an observable at some stratification +level. The level of stratification is generally a flexible parameter that will be set by the user when requesting +results from the model. For example, the user my request results at the country level or at the regional level. + +The helper method `$get_training_data()` will then provide the training data for the observable at the requested level. +This data will contain one record for each time point in the training period, with columns for the date of the +observation, the values of the different (optional) stratification, and the value of the observable on the date, and +finally, an interger counter for the day relative to the end of the training period. + +## Model-user interface +Beyond providing access to the functional modules, `DiseasyModel` defines how the user will interface with the model. + +### Setting parameters +The parameters of the model should be contained within the `$parameters` slot of the model template. + +These are then exposed to the user, and logic can be implemented to allow the user to set some parameters of the model +while leaving others free to be determined by the model during model fitting. + +### Getting results +Once the parameters of the model is set (either by the user or by the model), the user can query the model for results +using the `$get_results()` function. Through this function, the user provides the parameters of the result that should +be provided (target observable, time-window and stratification level). In return, the function returns a standardised +`data.frame`-like output that can be used for further analysis in conjunction with the results from other models. + +The outputs of the model are standardised, and should follow the structure of the `training_data` provided by +`$get_training_data()` enriched by model prediction, model id and realization id. + +[^1]: The seasonal model exposed by a configured `DiseasySeason` instance is "closure" instead of a pure function. From 0e09143afe31fbfe708551086c216244128c0ced Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Fri, 31 May 2024 15:53:33 +0200 Subject: [PATCH 05/21] feat(load_module): Generalize to load any module --- R/DiseasyBaseModule.R | 62 +++++++++++++++--------------- tests/testthat/test-DiseasyModel.R | 15 ++++++-- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/R/DiseasyBaseModule.R b/R/DiseasyBaseModule.R index 42d72e83..8e5e1df5 100644 --- a/R/DiseasyBaseModule.R +++ b/R/DiseasyBaseModule.R @@ -72,11 +72,29 @@ DiseasyBaseModule <- R6::R6Class( #' @param clone (`boolean`)\cr #' Toggle whether or not the module should be cloned when loading. Default TRUE. #' @details - #' The methods allows the setting of the internal module instances after the `DiseasyBaseModule` instance is - #' created. + #' The methods allows the setting of the internal module instances after the instance is created. #' @return `NULL` load_module = function(module, clone = TRUE) { + # Within the `diseasy` framework, some modules includes instances of other modules. + # One such module instance that is used across modules is the `DiseasyObservables` since + # it is the main interface with data and many modules need data to run. + + # Here, we check this module instance for any modules it contains. For each of these nested modules, we check + # if they require the module being loaded. If they do, we load the module into the nested instance as well. + # This happens via recursive calls to `$load_module()` on the nested instance with `clone = FALSE`, but only + # if the nested module does not already contain a loaded instance. + + # This way, a copy (reference) of the module being loaded is propagated to the nested modules. Any changes to the + # parent module is then reflected in the nested instances. + + + # Check the instance has a slot for the module to be loaded + if (!paste0(".", class(module)[1]) %in% names(private)) { + stop(glue::glue("Module {class(module)[1]} not found in {class(self)[1]}")) + } + + # Check if the module should be cloned (i.e. a new instance is created, or if the module is used by reference) if (clone) { # Create a clone of the module module <- module$clone() @@ -85,38 +103,18 @@ DiseasyBaseModule <- R6::R6Class( module$set_moduleowner(class(self)[1]) } - # Within the `diseasy` framework, some modules includes instances of other modules. - # One such module instance that is used across modules is the `DiseasyObservables` since - # it is the main interface with data and many modules need data to run. - # Here, we check if this module instance has includes another instance of a module which uses - # the `DiseasyObservables`. - # If it does, we load recursively call `load_module` on the nested instance with clone = FALSE, but only - # if the nested module does not contain a loaded instance already. - # This way, this model instance has the `DiseasyObservables` instance loaded and any nested instances - # that needs the `DiseasyObservables` module uses the same as this (parent) instance. - - # First we check if the given module is the `DiseasyObservables` module - # If it is, we load it into the nested instances - modules_with_observables <- "DiseasySeason" - if (class(module)[1] == "DiseasyObservables") { - modules_with_observables |> - purrr::map_chr(~ paste0(".", .)) |> - purrr::walk(~ { - if (!is.null(purrr::pluck(private, .)) && is.null(purrr::pluck(private, ., "observables"))) { - purrr::pluck(private, .)$load_module(module, clone = FALSE) - } - }) - } - # Then we check the reverse case: - # That is, we check if the given module requires `DiseasyObservables` and - # then load the existing `DiseasyObservables` into the given module before storing it - if (class(module)[1] %in% modules_with_observables && - is.null(purrr::pluck(module, "observables")) && - !is.null(purrr::pluck(private, ".DiseasyObservables"))) { + # Determine all current diseasy modules loaded into the current instance + nested_diseasy_modules <- as.list(private, all.names = TRUE) |> + purrr::keep(~ inherits(., "DiseasyBaseModule")) - module$load_module(purrr::pluck(private, ".DiseasyObservables"), clone = FALSE) - } + + # Use already loaded instances to populate the module being loaded + purrr::walk(nested_diseasy_modules, ~ try(module$load_module(., clone = FALSE), silent = TRUE)) + + + # Load the module into currently nested modules + purrr::walk(nested_diseasy_modules, ~ try(.$load_module(module, clone = FALSE), silent = TRUE)) # Finally, store the module diff --git a/tests/testthat/test-DiseasyModel.R b/tests/testthat/test-DiseasyModel.R index 6174af66..bf0ec4f2 100644 --- a/tests/testthat/test-DiseasyModel.R +++ b/tests/testthat/test-DiseasyModel.R @@ -54,10 +54,19 @@ test_that("$load_module() works", { m$load_module(obs) checkmate::expect_class(m %.% season, "DiseasySeason") checkmate::expect_class(m %.% season %.% observables, "DiseasyObservables") - expect_identical(m %.% season %.% observables, obs) + expect_identical(m %.% season %.% observables, m %.% observables) # Observables should be propagated to Season module + + # We also check that a clone has been made, and that changes to obs after loading does not change m + expect_false(identical(m %.% observables, obs)) + expect_identical(m %.% observables %.% hash, obs %.% hash) + obs$set_slice_ts(today()) + + expect_false(identical(m %.% observables, obs)) + expect_false(identical(m %.% observables %.% hash, obs %.% hash)) rm(m) + #.. and the other way around m <- DiseasyModel$new(observables = TRUE) checkmate::expect_class(m %.% observables, "DiseasyObservables") @@ -256,13 +265,13 @@ test_that("active binding: activity works", { m <- DiseasyModel$new() # Retrieve the activity - expect_NULL(m %.% activity) + expect_null(m %.% activity) # Try to set activity through the binding # test_that cannot capture this error, so we have to hack it expect_identical(tryCatch(m$activity <- DiseasyActivity$new(), error = \(e) e), simpleError("`$activity` is read only")) - expect_NULL(m %.% activity) + expect_null(m %.% activity) rm(m) }) From fef9297bf03ad1c8dfd0f70bb3f37dedebab52fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Mon, 3 Jun 2024 09:24:05 +0200 Subject: [PATCH 06/21] feat(DiseasyObservables): Allow date slice_ts --- R/DiseasyObservables.R | 11 +++++++---- tests/testthat/test-DiseasyObservables.R | 13 ++++--------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/R/DiseasyObservables.R b/R/DiseasyObservables.R index 662f2dc9..d2577c0d 100644 --- a/R/DiseasyObservables.R +++ b/R/DiseasyObservables.R @@ -148,7 +148,10 @@ DiseasyObservables <- R6::R6Class( #' Date to slice the database on #' @seealso [SCDB::get_table] set_slice_ts = function(slice_ts) { - checkmate::assert_character(slice_ts, pattern = r"{\d{4}-\d{2}-\d{2}( Date: Mon, 3 Jun 2024 09:28:53 +0200 Subject: [PATCH 07/21] chore: Fix spelling --- R/DiseasyBaseModule.R | 2 +- R/DiseasyModel.R | 2 +- inst/WORDLIST | 7 +++++++ man/DiseasyBaseModule.Rd | 2 +- man/DiseasyModel.Rd | 2 +- vignettes/creating-a-model.Rmd | 6 +++--- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/R/DiseasyBaseModule.R b/R/DiseasyBaseModule.R index 8e5e1df5..2751249c 100644 --- a/R/DiseasyBaseModule.R +++ b/R/DiseasyBaseModule.R @@ -1,4 +1,4 @@ -#' @title Diseasy' basic module +#' @title Base module for diseasy #' #' @description #' The `DiseasyBaseModule` module implements common functionality that all modules have available. diff --git a/R/DiseasyModel.R b/R/DiseasyModel.R index b513c531..c44f0ca1 100644 --- a/R/DiseasyModel.R +++ b/R/DiseasyModel.R @@ -1,4 +1,4 @@ -#' @title Diseasy's basic model module +#' @title Base module for diseasy model templates #' #' @description #' The `DiseasyModel` module implements common functionality that all models have available beyond that provided by diff --git a/inst/WORDLIST b/inst/WORDLIST index 1abfe9b0..9882f134 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -7,6 +7,7 @@ CCW backends bitemporal +cdot classname cloneable CMD @@ -24,6 +25,7 @@ dbi DBIConnection DBIDriver dbplyr +dI diseasy diseasystore DiseasystoreBase @@ -37,7 +39,10 @@ dk DMI doi dplyr +dR ds +dS +dt dtplyr DuckDB @@ -52,6 +57,7 @@ fn fns FeatureHandler FeatureHandlers +frac github gov @@ -66,6 +72,7 @@ Jit Klepac Kirkeby +kt Lasse linter diff --git a/man/DiseasyBaseModule.Rd b/man/DiseasyBaseModule.Rd index ede59d37..1e0a4718 100644 --- a/man/DiseasyBaseModule.Rd +++ b/man/DiseasyBaseModule.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/DiseasyBaseModule.R \name{DiseasyBaseModule} \alias{DiseasyBaseModule} -\title{Diseasy' basic module} +\title{Base module for diseasy} \value{ A new instance of the \code{DiseasyBaseModule} \link[R6:R6Class]{R6} class. } diff --git a/man/DiseasyModel.Rd b/man/DiseasyModel.Rd index e6547e86..775a1c48 100644 --- a/man/DiseasyModel.Rd +++ b/man/DiseasyModel.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/DiseasyModel.R \name{DiseasyModel} \alias{DiseasyModel} -\title{Diseasy's basic model module} +\title{Base module for diseasy model templates} \value{ A new instance of the \code{DiseasyModel} \link[R6:R6Class]{R6} class. } diff --git a/vignettes/creating-a-model.Rmd b/vignettes/creating-a-model.Rmd index a497ba4f..51d76ef6 100644 --- a/vignettes/creating-a-model.Rmd +++ b/vignettes/creating-a-model.Rmd @@ -51,7 +51,7 @@ $$ $$ When developing this model traditionally, we would choose some functional form for the seasonal forcing `s(t)`, -e.g. a sinosodial relationship: +e.g. a sinusoidal relationship: $$ s(t) = A \cdot \cos(kt+\phi) $$ @@ -66,7 +66,7 @@ This way, we can configure different instances of `DiseasySeason` using the whol functional forms of seasonal forcing: * No seasonal forcing ($s(t) = 1$) -* Sinosodial forcing +* Sinusoidal forcing * Temperature dependent forcing * Other plausible seasonal forcing forms @@ -125,7 +125,7 @@ results from the model. For example, the user my request results at the country The helper method `$get_training_data()` will then provide the training data for the observable at the requested level. This data will contain one record for each time point in the training period, with columns for the date of the observation, the values of the different (optional) stratification, and the value of the observable on the date, and -finally, an interger counter for the day relative to the end of the training period. +finally, an integer counter for the day relative to the end of the training period. ## Model-user interface Beyond providing access to the functional modules, `DiseasyModel` defines how the user will interface with the model. From dba358f31cecf2eccef954a1a16f83646ff23fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Mon, 3 Jun 2024 09:30:39 +0200 Subject: [PATCH 08/21] chore: Fix lints --- R/DiseasyObservables.R | 4 +++- tests/testthat/test-DiseasyModel.R | 8 ++++---- tests/testthat/test-DiseasyObservables.R | 2 +- vignettes/creating-a-model.Rmd | 4 ++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/R/DiseasyObservables.R b/R/DiseasyObservables.R index d2577c0d..59544156 100644 --- a/R/DiseasyObservables.R +++ b/R/DiseasyObservables.R @@ -149,7 +149,9 @@ DiseasyObservables <- R6::R6Class( #' @seealso [SCDB::get_table] set_slice_ts = function(slice_ts) { checkmate::assert( - checkmate::check_character(slice_ts, pattern = r"{\d{4}-\d{2}-\d{2}( paste0( "- `$", stringr::str_to_lower(.), "`", ": `{Diseasy", ., "}`" - ) - ) |> + ) + ) |> cat(sep = "\n") ``` From 67fe6ea423c70a52dcc40efc7afac568314f1d11 Mon Sep 17 00:00:00 2001 From: RasmusSkytte Date: Mon, 3 Jun 2024 07:44:58 +0000 Subject: [PATCH 09/21] docs: update documentation --- man/DiseasyBaseModule.Rd | 3 +-- man/DiseasyObservables.Rd | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/man/DiseasyBaseModule.Rd b/man/DiseasyBaseModule.Rd index 1e0a4718..627b239c 100644 --- a/man/DiseasyBaseModule.Rd +++ b/man/DiseasyBaseModule.Rd @@ -122,8 +122,7 @@ Toggle whether or not the module should be cloned when loading. Default TRUE.} \if{html}{\out{}} } \subsection{Details}{ -The methods allows the setting of the internal module instances after the \code{DiseasyBaseModule} instance is -created. +The methods allows the setting of the internal module instances after the instance is created. } \subsection{Returns}{ diff --git a/man/DiseasyObservables.Rd b/man/DiseasyObservables.Rd index 31d1023e..e01695d6 100644 --- a/man/DiseasyObservables.Rd +++ b/man/DiseasyObservables.Rd @@ -67,8 +67,8 @@ The currently available observables in the loaded diseasystore. Read-only.} \item{\code{available_stratifications}}{(\code{character})\cr The currently available stratifications in the loaded diseasystore. Read-only.} -\item{\code{slice_ts}}{(\code{Date})\cr -The timestamp to slice database on. Read-only.} +\item{\code{slice_ts}}{(\code{Date} or \code{character})\cr +Date to slice the database on. See \code{\link[SCDB:get_table]{SCDB::get_table()}}. Read-only.} \item{\code{conn}}{(\code{DBIConnection})\cr The connection to the database on. Read-only.} From c23f071bc518385702fb9640fb2187aa6d977ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Mon, 3 Jun 2024 10:45:58 +0200 Subject: [PATCH 10/21] docs(pkgdown): Add structure to the docs site --- R/DiseasyActivity.R | 1 + R/DiseasyObservables.R | 1 + R/DiseasySeason.R | 1 + _pkgdown.yml | 29 +++++++++++++++++++++++++++++ man/DiseasyActivity.Rd | 1 + man/DiseasyObservables.Rd | 1 + man/DiseasySeason.Rd | 1 + 7 files changed, 35 insertions(+) diff --git a/R/DiseasyActivity.R b/R/DiseasyActivity.R index 4910ae0a..46a8a160 100644 --- a/R/DiseasyActivity.R +++ b/R/DiseasyActivity.R @@ -46,6 +46,7 @@ #' @return #' A new instance of the `DiseasyActivity` [R6][R6::R6Class] class. #' @importFrom Matrix sparseMatrix +#' @keywords functional-module #' @export DiseasyActivity <- R6::R6Class( # nolint: object_name_linter classname = "DiseasyActivity", diff --git a/R/DiseasyObservables.R b/R/DiseasyObservables.R index 59544156..28e2a76d 100644 --- a/R/DiseasyObservables.R +++ b/R/DiseasyObservables.R @@ -25,6 +25,7 @@ #' rm(obs) #' @return #' A new instance of the `DiseasyBaseModule` [R6][R6::R6Class] class. +#' @keywords functional-module #' @export DiseasyObservables <- R6::R6Class( # nolint: object_name_linter classname = "DiseasyObservables", diff --git a/R/DiseasySeason.R b/R/DiseasySeason.R index 9f5627fb..3cd5277b 100644 --- a/R/DiseasySeason.R +++ b/R/DiseasySeason.R @@ -25,6 +25,7 @@ #' rm(s1, s2) #' @return #' A new instance of the `DiseasySeason` [R6][R6::R6Class] class. +#' @keywords functional-module #' @export DiseasySeason <- R6::R6Class( # nolint: object_name_linter classname = "DiseasySeason", diff --git a/_pkgdown.yml b/_pkgdown.yml index 35082858..504d5dce 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -7,3 +7,32 @@ authors: href: https://www.ssi.dk/ html: >- SSI + +reference: + - title: Functional modules + desc: The building blocks of diseasy. + contents: + - has_keyword("functional-module") + + - title: Data sets + desc: Data for the functional modules + contents: + - has_keyword("data") + + - title: Developer functions + desc: Functions to create model templates. + contents: + - DiseasyBaseModule + - DiseasyModel + - diseasyoption + +articles: +- title: Functional modules + navbar: Functional modules + contents: + - starts_with("diseasy") + +- title: Developer guides + navbar: Developer guides + contents: + - creating-a-model diff --git a/man/DiseasyActivity.Rd b/man/DiseasyActivity.Rd index 6494c9f6..5d53f768 100644 --- a/man/DiseasyActivity.Rd +++ b/man/DiseasyActivity.Rd @@ -54,6 +54,7 @@ See vignette("diseasy-activity") for examples of use. \seealso{ \link{dk_activity_units} } +\keyword{functional-module} \section{Super class}{ \code{\link[diseasy:DiseasyBaseModule]{diseasy::DiseasyBaseModule}} -> \code{DiseasyActivity} } diff --git a/man/DiseasyObservables.Rd b/man/DiseasyObservables.Rd index e01695d6..2a465d51 100644 --- a/man/DiseasyObservables.Rd +++ b/man/DiseasyObservables.Rd @@ -40,6 +40,7 @@ See vignette("diseasy-observables") \link[SCDB:get_table]{SCDB::get_table} } +\keyword{functional-module} \section{Super class}{ \code{\link[diseasy:DiseasyBaseModule]{diseasy::DiseasyBaseModule}} -> \code{DiseasyObservables} } diff --git a/man/DiseasySeason.Rd b/man/DiseasySeason.Rd index dfb45d28..77d57cbb 100644 --- a/man/DiseasySeason.Rd +++ b/man/DiseasySeason.Rd @@ -34,6 +34,7 @@ See the vignette("diseasy-season") for examples of use. \seealso{ \link{DiseasyObservables} } +\keyword{functional-module} \section{Super class}{ \code{\link[diseasy:DiseasyBaseModule]{diseasy::DiseasyBaseModule}} -> \code{DiseasySeason} } From f97b07d63a62d20ae7ec19d92e53b0fc677aba85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Tue, 4 Jun 2024 10:18:47 +0200 Subject: [PATCH 11/21] test(DiseasyModel): Skip if RSQLite not installed --- tests/testthat/test-DiseasyModel.R | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/testthat/test-DiseasyModel.R b/tests/testthat/test-DiseasyModel.R index 663aa9d0..48fa2acc 100644 --- a/tests/testthat/test-DiseasyModel.R +++ b/tests/testthat/test-DiseasyModel.R @@ -1,4 +1,5 @@ test_that("initialize works", { + skip_if_not_installed("RSQLite") # Creating an empty model module m <- DiseasyModel$new() @@ -39,6 +40,7 @@ test_that("initialize works", { test_that("$load_module() works", { + skip_if_not_installed("RSQLite") # Check that observable is loaded into objects that can take it # We first create and load a module that uses "DiseasyObservables" internally but we do not provide it to the module @@ -83,6 +85,7 @@ test_that("$load_module() works", { test_that("$hash works", { + skip_if_not_installed("RSQLite") # Test hash generation works as expected @@ -159,6 +162,7 @@ test_that("$hash works", { test_that("cloning works", { + skip_if_not_installed("RSQLite") # Creating modules for the model module act <- DiseasyActivity$new() @@ -278,6 +282,7 @@ test_that("active binding: activity works", { test_that("active binding: observables works", { + skip_if_not_installed("RSQLite") # Creating an empty module m <- DiseasyModel$new() From 18b66267a12d322b2524d5d871ccee8ef32ea17d Mon Sep 17 00:00:00 2001 From: RasmusSkytte Date: Thu, 6 Jun 2024 10:39:00 +0000 Subject: [PATCH 12/21] chore: Update pak.lock --- pak.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pak.lock b/pak.lock index 82f02037..c40aa2f1 100644 --- a/pak.lock +++ b/pak.lock @@ -2898,7 +2898,7 @@ { "ref": "rlang", "package": "rlang", - "version": "1.1.3", + "version": "1.1.4", "type": "standard", "direct": false, "binary": true, @@ -2911,10 +2911,10 @@ "RemoteRef": "rlang", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "1.1.3" + "RemoteSha": "1.1.4" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/rlang_1.1.3.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/rlang_1.1.3.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/rlang_1.1.4.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/rlang_1.1.4.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, From 0b866fdf5a4543c00a30d3e1c5a620b4eb5ba12e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Thu, 6 Jun 2024 12:40:36 +0200 Subject: [PATCH 13/21] docs(NEWS): Add entry on DiseasyModel --- NEWS.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index d7998353..4364ca17 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,15 @@ # diseasy 0.0.0.9000 -* Added a `NEWS.md` file to track changes to the package. +## Features: + +* `DiseasyModel`: A base class for the model templates (#36). + * R6 class that defines the interface for the models and empower the flexible configuration of models from the + functional modules. + +## Documentation: + +* The functions are fully documented. + +* Vignettes for the use of the package is included. + - `vignette("diseasy")` + - `vignette("creating-a-model")` From f4f19defe82a4e46c7ddd2611ae11f8f772ebcd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Thu, 11 Jul 2024 14:12:27 +0200 Subject: [PATCH 14/21] chore(creating-a-model): Fix spelling Co-authored-by: Lasse Engbo Christiansen <147731649+LasseEngboChr@users.noreply.github.com> --- vignettes/creating-a-model.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/creating-a-model.Rmd b/vignettes/creating-a-model.Rmd index a0efe7ca..8cfe2432 100644 --- a/vignettes/creating-a-model.Rmd +++ b/vignettes/creating-a-model.Rmd @@ -24,7 +24,7 @@ This vignette provides an overview of the `DiseasyModel` class and its use in cr As described in the package introduction (`vignette("diseasy")`), the power of `diseasy` comes from combining different "functional" modules with model "templates" to create a large ensemble of models easily. -It is `DiseasyModel` that defines how such model templates should look, and provides the mechanism by witch the +It is `DiseasyModel` that defines how such model templates should look, and provides the mechanism by which the functional modules are loaded in the model templates to create the individual model members of the ensemble. Before we dive into the details of `DiseasyModel`, let's first consider what it means to be a "model template": From 1cda6b94eb41963e960933ab0e7f5aeea1ea05e3 Mon Sep 17 00:00:00 2001 From: RasmusSkytte Date: Thu, 11 Jul 2024 12:43:27 +0000 Subject: [PATCH 15/21] chore: Update pak.lock --- pak.lock | 176 +++++++++++++++++++++++-------------------------------- 1 file changed, 73 insertions(+), 103 deletions(-) diff --git a/pak.lock b/pak.lock index c40aa2f1..72123648 100644 --- a/pak.lock +++ b/pak.lock @@ -1,7 +1,7 @@ { "lockfile_version": 1, "os": "Ubuntu 22.04.4 LTS", - "r_version": "R version 4.4.0 (2024-04-24)", + "r_version": "R version 4.4.1 (2024-06-14)", "platform": "x86_64-pc-linux-gnu", "packages": [ { @@ -404,7 +404,7 @@ { "ref": "cli", "package": "cli", - "version": "3.6.2", + "version": "3.6.3", "type": "standard", "direct": false, "binary": true, @@ -417,10 +417,10 @@ "RemoteRef": "cli", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "3.6.2" + "RemoteSha": "3.6.3" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/cli_3.6.2.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/cli_3.6.2.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/cli_3.6.3.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/cli_3.6.3.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -559,7 +559,7 @@ { "ref": "crayon", "package": "crayon", - "version": "1.5.2", + "version": "1.5.3", "type": "standard", "direct": false, "binary": true, @@ -572,10 +572,10 @@ "RemoteRef": "crayon", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "1.5.2" + "RemoteSha": "1.5.3" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/crayon_1.5.2.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/crayon_1.5.2.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/crayon_1.5.3.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/crayon_1.5.3.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -858,7 +858,7 @@ { "ref": "digest", "package": "digest", - "version": "0.6.35", + "version": "0.6.36", "type": "standard", "direct": false, "binary": true, @@ -871,10 +871,10 @@ "RemoteRef": "digest", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "0.6.35" + "RemoteSha": "0.6.36" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/digest_0.6.35.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/digest_0.6.35.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/digest_0.6.36.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/digest_0.6.36.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -920,7 +920,7 @@ { "ref": "downlit", "package": "downlit", - "version": "0.4.3", + "version": "0.4.4", "type": "standard", "direct": false, "binary": true, @@ -933,10 +933,10 @@ "RemoteRef": "downlit", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "0.4.3" + "RemoteSha": "0.4.4" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/downlit_0.4.3.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/downlit_0.4.3.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/downlit_0.4.4.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/downlit_0.4.4.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -982,27 +982,28 @@ { "ref": "duckdb", "package": "duckdb", - "version": "0.10.2", + "version": "1.0.0-1", "type": "standard", "direct": false, - "binary": true, + "binary": false, "dependencies": ["DBI"], "vignettes": false, - "needscompilation": false, + "needscompilation": true, "metadata": { "RemoteType": "standard", "RemotePkgRef": "duckdb", "RemoteRef": "duckdb", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", - "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "0.10.2" + "RemotePkgPlatform": "source", + "RemoteSha": "1.0.0-1" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/duckdb_0.10.2.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/duckdb_0.10.2.tar.gz", - "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "rversion": "4.4", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/duckdb_1.0.0-1.tar.gz"], + "target": "src/contrib/duckdb_1.0.0-1.tar.gz", + "platform": "source", + "rversion": "*", "directpkg": false, "license": "MIT + file LICENSE", + "filesize": 4147117, "dep_types": ["Depends", "Imports", "LinkingTo"], "params": [], "install_args": "", @@ -1044,7 +1045,7 @@ { "ref": "evaluate", "package": "evaluate", - "version": "0.23", + "version": "0.24.0", "type": "standard", "direct": false, "binary": true, @@ -1057,10 +1058,10 @@ "RemoteRef": "evaluate", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "0.23" + "RemoteSha": "0.24.0" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/evaluate_0.23.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/evaluate_0.23.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/evaluate_0.24.0.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/evaluate_0.24.0.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -1540,37 +1541,6 @@ } ] }, - { - "ref": "httr", - "package": "httr", - "version": "1.4.7", - "type": "standard", - "direct": false, - "binary": true, - "dependencies": ["curl", "jsonlite", "mime", "openssl", "R6"], - "vignettes": false, - "needscompilation": false, - "metadata": { - "RemoteType": "standard", - "RemotePkgRef": "httr", - "RemoteRef": "httr", - "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", - "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "1.4.7" - }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/httr_1.4.7.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/httr_1.4.7.tar.gz", - "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "rversion": "4.4", - "directpkg": false, - "license": "MIT + file LICENSE", - "dep_types": ["Depends", "Imports", "LinkingTo"], - "params": [], - "install_args": "", - "repotype": "cranlike", - "sysreqs": "", - "sysreqs_packages": {} - }, { "ref": "httr2", "package": "httr2", @@ -1760,7 +1730,7 @@ { "ref": "knitr", "package": "knitr", - "version": "1.47", + "version": "1.48", "type": "standard", "direct": false, "binary": true, @@ -1773,10 +1743,10 @@ "RemoteRef": "knitr", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "1.47" + "RemoteSha": "1.48" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/knitr_1.47.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/knitr_1.47.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/knitr_1.48.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/knitr_1.48.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -2270,11 +2240,11 @@ { "ref": "pkgdown", "package": "pkgdown", - "version": "2.0.9", + "version": "2.1.0", "type": "standard", "direct": false, "binary": true, - "dependencies": ["bslib", "callr", "cli", "desc", "digest", "downlit", "fs", "httr", "jsonlite", "magrittr", "memoise", "purrr", "ragg", "rlang", "rmarkdown", "tibble", "whisker", "withr", "xml2", "yaml"], + "dependencies": ["bslib", "callr", "cli", "desc", "digest", "downlit", "fontawesome", "fs", "httr2", "jsonlite", "openssl", "purrr", "ragg", "rlang", "rmarkdown", "tibble", "whisker", "withr", "xml2", "yaml"], "vignettes": false, "needscompilation": false, "metadata": { @@ -2283,10 +2253,10 @@ "RemoteRef": "pkgdown", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "2.0.9" + "RemoteSha": "2.1.0" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/pkgdown_2.0.9.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/pkgdown_2.0.9.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/pkgdown_2.1.0.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/pkgdown_2.1.0.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -2308,11 +2278,11 @@ { "ref": "pkgload", "package": "pkgload", - "version": "1.3.4", + "version": "1.4.0", "type": "standard", "direct": false, "binary": true, - "dependencies": ["cli", "crayon", "desc", "fs", "glue", "pkgbuild", "rlang", "rprojroot", "withr"], + "dependencies": ["cli", "desc", "fs", "glue", "lifecycle", "pkgbuild", "processx", "rlang", "rprojroot", "withr"], "vignettes": false, "needscompilation": false, "metadata": { @@ -2321,10 +2291,10 @@ "RemoteRef": "pkgload", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "1.3.4" + "RemoteSha": "1.4.0" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/pkgload_1.3.4.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/pkgload_1.3.4.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/pkgload_1.4.0.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/pkgload_1.4.0.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -2525,7 +2495,7 @@ { "ref": "ps", "package": "ps", - "version": "1.7.6", + "version": "1.7.7", "type": "standard", "direct": false, "binary": true, @@ -2538,10 +2508,10 @@ "RemoteRef": "ps", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "1.7.6" + "RemoteSha": "1.7.7" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/ps_1.7.6.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/ps_1.7.6.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/ps_1.7.7.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/ps_1.7.7.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -2967,7 +2937,7 @@ { "ref": "roxygen2", "package": "roxygen2", - "version": "7.3.1", + "version": "7.3.2", "type": "standard", "direct": false, "binary": true, @@ -2980,10 +2950,10 @@ "RemoteRef": "roxygen2", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "7.3.1" + "RemoteSha": "7.3.2" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/roxygen2_7.3.1.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/roxygen2_7.3.1.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/roxygen2_7.3.2.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/roxygen2_7.3.2.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -3974,7 +3944,7 @@ { "ref": "xfun", "package": "xfun", - "version": "0.44", + "version": "0.45", "type": "standard", "direct": false, "binary": true, @@ -3987,10 +3957,10 @@ "RemoteRef": "xfun", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "0.44" + "RemoteSha": "0.45" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/xfun_0.44.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/xfun_0.44.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/xfun_0.45.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/xfun_0.45.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -4136,7 +4106,7 @@ { "ref": "yaml", "package": "yaml", - "version": "2.3.8", + "version": "2.3.9", "type": "standard", "direct": false, "binary": true, @@ -4149,10 +4119,10 @@ "RemoteRef": "yaml", "RemoteRepos": "https://packagemanager.posit.co/cran/__linux__/jammy/latest", "RemotePkgPlatform": "x86_64-pc-linux-gnu-ubuntu-22.04", - "RemoteSha": "2.3.8" + "RemoteSha": "2.3.9" }, - "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/yaml_2.3.8.tar.gz"], - "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/yaml_2.3.8.tar.gz", + "sources": ["https://packagemanager.posit.co/cran/__linux__/jammy/latest/src/contrib/yaml_2.3.9.tar.gz"], + "target": "src/contrib/x86_64-pc-linux-gnu-ubuntu-22.04/4.4/yaml_2.3.9.tar.gz", "platform": "x86_64-pc-linux-gnu-ubuntu-22.04", "rversion": "4.4", "directpkg": false, @@ -4227,7 +4197,7 @@ "sysreqs_packages": {} }, { - "ref": "installed::/opt/R/4.4.0/lib/R/library/codetools", + "ref": "installed::/opt/R/4.4.1/lib/R/library/codetools", "package": "codetools", "version": "0.2-20", "type": "installed", @@ -4238,15 +4208,15 @@ "needscompilation": false, "metadata": { "RemoteType": "installed", - "RemotePkgRef": "installed::/opt/R/4.4.0/lib/R/library/codetools", + "RemotePkgRef": "installed::/opt/R/4.4.1/lib/R/library/codetools", "RemotePkgPlatform": "*", "RemoteSha": "0.2-20" }, "sources": [], "target": "src/contrib/codetools_0.2-20.tar.gz", "platform": "*", - "rversion": "R 4.4.0", - "built": "R 4.4.0; ; 2024-05-25 01:49:03 UTC; unix", + "rversion": "R 4.4.1", + "built": "R 4.4.1; ; 2024-06-14 09:07:22 UTC; unix", "directpkg": false, "license": "GPL", "dep_types": ["Depends", "Imports", "LinkingTo"], @@ -4256,7 +4226,7 @@ "sysreqs_packages": {} }, { - "ref": "installed::/opt/R/4.4.0/lib/R/library/lattice", + "ref": "installed::/opt/R/4.4.1/lib/R/library/lattice", "package": "lattice", "version": "0.22-6", "type": "installed", @@ -4267,15 +4237,15 @@ "needscompilation": true, "metadata": { "RemoteType": "installed", - "RemotePkgRef": "installed::/opt/R/4.4.0/lib/R/library/lattice", + "RemotePkgRef": "installed::/opt/R/4.4.1/lib/R/library/lattice", "RemotePkgPlatform": "x86_64-pc-linux-gnu", "RemoteSha": "0.22-6" }, "sources": [], "target": "src/contrib/lattice_0.22-6.tar.gz", "platform": "x86_64-pc-linux-gnu", - "rversion": "R 4.4.0", - "built": "R 4.4.0; x86_64-pc-linux-gnu; 2024-05-25 01:45:45 UTC; unix", + "rversion": "R 4.4.1", + "built": "R 4.4.1; x86_64-pc-linux-gnu; 2024-06-14 09:03:55 UTC; unix", "directpkg": false, "license": "GPL (>= 2)", "dep_types": ["Depends", "Imports", "LinkingTo"], @@ -4285,7 +4255,7 @@ "sysreqs_packages": {} }, { - "ref": "installed::/opt/R/4.4.0/lib/R/library/Matrix", + "ref": "installed::/opt/R/4.4.1/lib/R/library/Matrix", "package": "Matrix", "version": "1.7-0", "type": "installed", @@ -4296,15 +4266,15 @@ "needscompilation": true, "metadata": { "RemoteType": "installed", - "RemotePkgRef": "installed::/opt/R/4.4.0/lib/R/library/Matrix", + "RemotePkgRef": "installed::/opt/R/4.4.1/lib/R/library/Matrix", "RemotePkgPlatform": "x86_64-pc-linux-gnu", "RemoteSha": "1.7-0" }, "sources": [], "target": "src/contrib/Matrix_1.7-0.tar.gz", "platform": "x86_64-pc-linux-gnu", - "rversion": "R 4.4.0", - "built": "R 4.4.0; x86_64-pc-linux-gnu; 2024-05-25 01:45:54 UTC; unix", + "rversion": "R 4.4.1", + "built": "R 4.4.1; x86_64-pc-linux-gnu; 2024-06-14 09:04:04 UTC; unix", "directpkg": false, "license": "GPL (>= 2) | file LICENCE", "dep_types": ["Depends", "Imports", "LinkingTo"], From 7d77d9f697e1260ac20c7648bd419b3bb8a3ff76 Mon Sep 17 00:00:00 2001 From: RasmusSkytte Date: Thu, 11 Jul 2024 12:44:06 +0000 Subject: [PATCH 16/21] docs: update documentation --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index dd91f0e6..b804c5c6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -15,7 +15,7 @@ Description: This package facilitates creation of an ensemble of models through License: GPL-3 Encoding: UTF-8 Roxygen: list(markdown = TRUE) -RoxygenNote: 7.3.1 +RoxygenNote: 7.3.2 Language: en-GB LazyData: TRUE Depends: From fd22070bb791a1d24de5f8ef11e58f1f60f0f198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Fri, 12 Jul 2024 12:09:00 +0200 Subject: [PATCH 17/21] docs(creating-a-model): Improve wording and equations Co-authored-by: Lasse Engbo Christiansen <147731649+LasseEngboChr@users.noreply.github.com> --- vignettes/creating-a-model.Rmd | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/vignettes/creating-a-model.Rmd b/vignettes/creating-a-model.Rmd index 8cfe2432..4cf80bce 100644 --- a/vignettes/creating-a-model.Rmd +++ b/vignettes/creating-a-model.Rmd @@ -34,29 +34,29 @@ Developing a model in `diseasy` involves constructing a model template that inco into a model design. For instance, let's consider the creation of a simple SIR (Susceptible, Infectious, Recovered) infection -model with a seasonal forcing `s(t)` of the overall infection rate, denoted by \beta. +model with a seasonal forcing `\sigma(t)` of the overall infection rate, denoted by \beta. The SEIR model with seasonal forcing can be represented by the following set of differential equations: $$ -\frac{dS}{dt} = - \beta \cdot s(t) \cdot S \cdot I +\frac{dS(t)}{dt} = - \beta \cdot \sigma(t) \cdot S(t) \cdot I(t) $$ $$ -\frac{dI}{dt} = \beta \cdot s(t) \cdot S \cdot I - \gamma \cdot I +\frac{dI(t)}{dt} = \beta \cdot \sigma(t) \cdot S(t) \cdot I(t) - \gamma \cdot I(t) $$ $$ -\frac{dR}{dt} = \gamma \cdot I +\frac{dR(t)}{dt} = \gamma \cdot I(t) $$ -When developing this model traditionally, we would choose some functional form for the seasonal forcing `s(t)`, +When developing this model traditionally, we would choose some functional form for the seasonal forcing `\sigma(t)`, e.g. a sinusoidal relationship: $$ -s(t) = A \cdot \cos(kt+\phi) +\sigma(t) = A \cdot \cos(k \cdot t + \phi) $$ -The strategy of `diseasy` is to source the seasonal forcing `s(t)` directly from the corresponding functional module +The strategy of `diseasy` is to source the seasonal forcing `\sigma(t)` directly from the corresponding functional module (`DiseasySeason`). Every implementation of a seasonal forcing in `DiseasySeason` has the same function signature @@ -65,7 +65,7 @@ Every implementation of a seasonal forcing in `DiseasySeason` has the same funct This way, we can configure different instances of `DiseasySeason` using the whole range of (available) plausible functional forms of seasonal forcing: -* No seasonal forcing ($s(t) = 1$) +* No seasonal forcing ($\sigma(t) = 1$) * Sinusoidal forcing * Temperature dependent forcing * Other plausible seasonal forcing forms @@ -73,7 +73,7 @@ functional forms of seasonal forcing: Since the modules of `diseasy` are "objects" in an object-oriented-programming sense, the functional forms of the seasonal forcing can have parameters (as we see in the list above), but these are stored in the object when configuring the module, and the actual seasonal forcing exposed to the models all have the same function signature that takes -only time as input (`s(t)`)[^1]. +only time as input (`\sigma(t)`)[^1]. This way, we can easily swap out different seasonal forcing models in our model templates without having to change the model equations themselves. If new seasonal forcing models are developed, they are then automatically available to the @@ -89,7 +89,7 @@ To create a model template in `diseasy` is to create a new class that inherits t This gives the model template access to the functional modules that are available in the package, it defines how the user sets the parameters of the model and queries the model for results. -In addition, it provides useful utility for the implementation of the model such as logging and caching. +In addition, it provides useful utilities for the implementation of the model such as logging and caching. ## A container for functional modules The `DiseasyModel` class contains slots for instances of functional modules: @@ -109,7 +109,7 @@ purrr::keep(names(DiseasyModel$private_fields), ~ startsWith(., ".Diseasy")) |> This means, that once a new `DiseasyModel` instance is created, it will have the functional modules internally available. Going back to the above example of the SIR model with seasonal forcing, the model template would be -access the seasonal forcing `s(t)` via the call `self$season$model_t(t)`. +access the seasonal forcing `\sigma(t)` via the call `self$season$model_t(t)`. ```{r check_DiseasySeason_function_signature, include = FALSE} # If the the signature of DiseasySeason is changed, please update this vignette stopifnot("model_t" %in% names(DiseasySeason$active)) @@ -145,4 +145,4 @@ be provided (target observable, time-window and stratification level). In return The outputs of the model are standardised, and should follow the structure of the `training_data` provided by `$get_training_data()` enriched by model prediction, model id and realization id. -[^1]: The seasonal model exposed by a configured `DiseasySeason` instance is "closure" instead of a pure function. +[^1]: The seasonal model exposed by a configured `DiseasySeason` instance is a "closure" instead of a pure function. From 72a39e87e5ebe1d5711aa58d383db6f700532aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Fri, 12 Jul 2024 12:38:46 +0200 Subject: [PATCH 18/21] fix(creating-a-model): Correctly render inline \sigma(t) --- vignettes/creating-a-model.Rmd | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/vignettes/creating-a-model.Rmd b/vignettes/creating-a-model.Rmd index 4cf80bce..4447083c 100644 --- a/vignettes/creating-a-model.Rmd +++ b/vignettes/creating-a-model.Rmd @@ -34,7 +34,7 @@ Developing a model in `diseasy` involves constructing a model template that inco into a model design. For instance, let's consider the creation of a simple SIR (Susceptible, Infectious, Recovered) infection -model with a seasonal forcing `\sigma(t)` of the overall infection rate, denoted by \beta. +model with a seasonal forcing $\sigma(t)$ of the overall infection rate, denoted by \beta. The SEIR model with seasonal forcing can be represented by the following set of differential equations: @@ -50,17 +50,17 @@ $$ \frac{dR(t)}{dt} = \gamma \cdot I(t) $$ -When developing this model traditionally, we would choose some functional form for the seasonal forcing `\sigma(t)`, +When developing this model traditionally, we would choose some functional form for the seasonal forcing $\sigma(t)$, e.g. a sinusoidal relationship: $$ \sigma(t) = A \cdot \cos(k \cdot t + \phi) $$ -The strategy of `diseasy` is to source the seasonal forcing `\sigma(t)` directly from the corresponding functional module +The strategy of `diseasy` is to source the seasonal forcing $\sigma(t)$ directly from the corresponding functional module (`DiseasySeason`). Every implementation of a seasonal forcing in `DiseasySeason` has the same function signature -(i.e. they all take only a single argument `t` once configured). +(i.e. they all take only a single argument $t$ once configured). This way, we can configure different instances of `DiseasySeason` using the whole range of (available) plausible functional forms of seasonal forcing: @@ -73,7 +73,7 @@ functional forms of seasonal forcing: Since the modules of `diseasy` are "objects" in an object-oriented-programming sense, the functional forms of the seasonal forcing can have parameters (as we see in the list above), but these are stored in the object when configuring the module, and the actual seasonal forcing exposed to the models all have the same function signature that takes -only time as input (`\sigma(t)`)[^1]. +only time as input ($\sigma(t)$)[^1]. This way, we can easily swap out different seasonal forcing models in our model templates without having to change the model equations themselves. If new seasonal forcing models are developed, they are then automatically available to the @@ -109,7 +109,7 @@ purrr::keep(names(DiseasyModel$private_fields), ~ startsWith(., ".Diseasy")) |> This means, that once a new `DiseasyModel` instance is created, it will have the functional modules internally available. Going back to the above example of the SIR model with seasonal forcing, the model template would be -access the seasonal forcing `\sigma(t)` via the call `self$season$model_t(t)`. +access the seasonal forcing $\sigma(t)$ via the call `self$season$model_t(t)`. ```{r check_DiseasySeason_function_signature, include = FALSE} # If the the signature of DiseasySeason is changed, please update this vignette stopifnot("model_t" %in% names(DiseasySeason$active)) From 26d0383f1256ffd03c2d38e8b6b1933eb66ad50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Fri, 12 Jul 2024 12:49:47 +0200 Subject: [PATCH 19/21] docs(DiseasyModel): Improve Roxygen documentation Co-authored-by: Lasse Engbo Christiansen <147731649+LasseEngboChr@users.noreply.github.com> --- R/DiseasyModel.R | 26 +++++++++++++++----------- man/DiseasyModel.Rd | 23 +++++++++++++---------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/R/DiseasyModel.R b/R/DiseasyModel.R index c44f0ca1..51f2c67b 100644 --- a/R/DiseasyModel.R +++ b/R/DiseasyModel.R @@ -9,13 +9,15 @@ #' * `$activity`: `DiseasyActivity` #' * `$observables`: `DiseasyObservables` #' * `$season`: `DiseasySeason` -#' Configured instances of these modules can be provided during initialisation. If not, default instances are -#' created. +#' +#' Configured instances of these modules can be provided during initialisation. +#' Alternatively, default instances of these modules can optionally be created. +#' #' * Model interface: #' The module defines the functions `$get_results()`, `$get_training_data()` and the `$parameters` binding. #' These functions define the "API" of the models and ensure the models can take part in the ensemble. #' @examples -#' # Normally, you would not want to create this module directly, but it is possible. +#' # Normally, one would not want to create this module directly, but it is possible. #' Model_module <- DiseasyModel$new() #' #' rm(Model_module) @@ -31,19 +33,21 @@ DiseasyModel <- R6::R6Class( #' @description #' Creates a new instance of the `DiseasyModel` [R6][R6::R6Class] class. - #' This module is typically not constructed directly but rather through `DiseasyModel*` classes + #' This module is typically not constructed directly but rather through `DiseasyModel*` classes. #' @param activity,observables,season (`boolean` or `R6::R6Class instance`)\cr - #' If a boolean is given, it dictates whether to load a new instance module of this class\cr - #' If an instance of the module is provided instead, this instance is cloned to the new `DiseasyModel` instance\cr - #' Default is `FALSE`. + #' If a boolean is given, it dictates whether to load a new instance module of this class. + #' + #' If an instance of the module is provided instead, a copy of this instance is added to the `DiseasyModel` + #' instance. This copy is a "clone" of the instance at the time it is added and any subsequent changes to the + #' instance will not reflect in the copy that is added to `DiseasyModel`. #' @param label (`character`)\cr - #' A human readable label for the model instance + #' A human readable label for the model instance. #' @param ... - #' parameters sent to `DiseasyBaseModule` [R6][R6::R6Class] constructor + #' Parameters sent to `DiseasyBaseModule` [R6][R6::R6Class] constructor #' @details #' The `DiseasyModel` is the main template that the individual models should inherit from since this defines the #' set of methods the later framework expects from each model. In addition, it provides the main interface with - #' the other modules of the framework + #' the other modules of the framework. #' @return #' A new instance of the `DiseasyModel` [R6][R6::R6Class] class. initialize = function(activity = FALSE, @@ -182,7 +186,7 @@ DiseasyModel <- R6::R6Class( .parameters = NULL, # @param label (`character`)\cr - # A human readable label for the model instance + # A human readable label for the model instance. label = NULL, model_cannot_predict = function(observable = NULL, stratification = NULL) { diff --git a/man/DiseasyModel.Rd b/man/DiseasyModel.Rd index 775a1c48..3b7c4fea 100644 --- a/man/DiseasyModel.Rd +++ b/man/DiseasyModel.Rd @@ -17,16 +17,17 @@ The module contains the functional modules via its active bindings: \item \verb{$activity}: \code{DiseasyActivity} \item \verb{$observables}: \code{DiseasyObservables} \item \verb{$season}: \code{DiseasySeason} -Configured instances of these modules can be provided during initialisation. If not, default instances are -created. } + +Configured instances of these modules can be provided during initialisation. +Alternatively, default instances of these modules can optionally be created. \item Model interface: The module defines the functions \verb{$get_results()}, \verb{$get_training_data()} and the \verb{$parameters} binding. These functions define the "API" of the models and ensure the models can take part in the ensemble. } } \examples{ - # Normally, you would not want to create this module directly, but it is possible. + # Normally, one would not want to create this module directly, but it is possible. Model_module <- DiseasyModel$new() rm(Model_module) @@ -85,7 +86,7 @@ The parameters used in the model. Read-only.} \if{latex}{\out{\hypertarget{method-DiseasyModel-new}{}}} \subsection{Method \code{new()}}{ Creates a new instance of the \code{DiseasyModel} \link[R6:R6Class]{R6} class. -This module is typically not constructed directly but rather through \verb{DiseasyModel*} classes +This module is typically not constructed directly but rather through \verb{DiseasyModel*} classes. \subsection{Usage}{ \if{html}{\out{
}}\preformatted{DiseasyModel$new( activity = FALSE, @@ -100,21 +101,23 @@ This module is typically not constructed directly but rather through \verb{Disea \if{html}{\out{
}} \describe{ \item{\code{activity, observables, season}}{(\code{boolean} or \verb{R6::R6Class instance})\cr -If a boolean is given, it dictates whether to load a new instance module of this class\cr -If an instance of the module is provided instead, this instance is cloned to the new \code{DiseasyModel} instance\cr -Default is \code{FALSE}.} +If a boolean is given, it dictates whether to load a new instance module of this class. + +If an instance of the module is provided instead, a copy of this instance is added to the \code{DiseasyModel} +instance. This copy is a "clone" of the instance at the time it is added and any subsequent changes to the +instance will not reflect in the copy that is added to \code{DiseasyModel}.} \item{\code{label}}{(\code{character})\cr -A human readable label for the model instance} +A human readable label for the model instance.} -\item{\code{...}}{parameters sent to \code{DiseasyBaseModule} \link[R6:R6Class]{R6} constructor} +\item{\code{...}}{Parameters sent to \code{DiseasyBaseModule} \link[R6:R6Class]{R6} constructor} } \if{html}{\out{
}} } \subsection{Details}{ The \code{DiseasyModel} is the main template that the individual models should inherit from since this defines the set of methods the later framework expects from each model. In addition, it provides the main interface with -the other modules of the framework +the other modules of the framework. } \subsection{Returns}{ From a9cab843a6d1c99f32173f4a378bf28be939674b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Fri, 12 Jul 2024 12:54:46 +0200 Subject: [PATCH 20/21] feat(DiseasyModel): Use `%.%` over `$` in `$get_training_data()` --- R/DiseasyModel.R | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/R/DiseasyModel.R b/R/DiseasyModel.R index 51f2c67b..df1032a2 100644 --- a/R/DiseasyModel.R +++ b/R/DiseasyModel.R @@ -121,15 +121,16 @@ DiseasyModel <- R6::R6Class( # Input validation coll <- checkmate::makeAssertCollection() checkmate::assert_character(observable, add = coll) - checkmate::assert_number(self$parameters$training_length, add = coll) - checkmate::assert_date(self$observables$last_queryable_date, add = coll) + checkmate::assert_number(self %.% parameters %.% training_length, add = coll) + checkmate::assert_date(self %.% observables %.% last_queryable_date, add = coll) checkmate::reportAssertions(coll) # Get the observable at the stratification level - start_date <- self$observables$last_queryable_date - lubridate::days(self$parameters$training_length) - end_date <- self$observables$last_queryable_date # Only within the training period + start_date <- self %.% observables %.% last_queryable_date - + lubridate::days(self %.% parameters %.% training_length) + end_date <- self %.% observables %.% last_queryable_date # Only within the training period - data <- self$observables$get_observation(observable, stratification, start_date, end_date) |> + data <- self %.% observables %.% get_observation(observable, stratification, start_date, end_date) |> dplyr::mutate(t = lubridate::interval(max(zoo::as.Date(date)), zoo::as.Date(date)) / lubridate::days(1)) return(data) From cff5a597fe74e58dba099b8ca60f35c89ab73671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rasmus=20Skytte=20Randl=C3=B8v?= Date: Fri, 12 Jul 2024 13:45:03 +0200 Subject: [PATCH 21/21] docs(DiseasyModel): Make error message for `$get_results()` more clear --- R/DiseasyBaseModule.R | 2 +- R/DiseasyModel.R | 5 ++++- tests/testthat/test-DiseasyBaseModule.R | 4 ++-- tests/testthat/test-DiseasyModel.R | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/R/DiseasyBaseModule.R b/R/DiseasyBaseModule.R index 2751249c..f96f929c 100644 --- a/R/DiseasyBaseModule.R +++ b/R/DiseasyBaseModule.R @@ -304,7 +304,7 @@ DiseasyBaseModule <- R6::R6Class( }, not_implemented_error = function(...) { - stop("Not implemented: ", glue::glue_collapse(...), call. = FALSE) + stop("Not implemented: ", glue::glue_collapse(c(...), sep = " "), call. = FALSE) }, # Common logging diff --git a/R/DiseasyModel.R b/R/DiseasyModel.R index df1032a2..12ab795c 100644 --- a/R/DiseasyModel.R +++ b/R/DiseasyModel.R @@ -106,7 +106,10 @@ DiseasyModel <- R6::R6Class( #' @return `r rd_get_results_return` #' @seealso `r rd_get_results_seealso` get_results = function(observable, prediction_length, quantiles = NULL, stratification = NULL) { - private$not_implemented_error("Each model must implement their own `get_results` methods") + private$not_implemented_error( + "`DiseasyModel` should not be used directly. Did you do so by mistake?", + "Instead, use a model that inherits`DiseasyModel` as it should implement the `$get_results()` method." + ) }, diff --git a/tests/testthat/test-DiseasyBaseModule.R b/tests/testthat/test-DiseasyBaseModule.R index 976ba3a6..48d3f2a9 100644 --- a/tests/testthat/test-DiseasyBaseModule.R +++ b/tests/testthat/test-DiseasyBaseModule.R @@ -92,9 +92,9 @@ test_that("errors work", { class = "simpleError", regex = "Not implemented: test1") - expect_error(private$not_implemented_error(c("test1", "test2")), + expect_error(private$not_implemented_error("test1", "test2"), class = "simpleError", - regex = "Not implemented: test1test2") + regex = "Not implemented: test1 test2") rm(m) }) diff --git a/tests/testthat/test-DiseasyModel.R b/tests/testthat/test-DiseasyModel.R index 48fa2acc..c88c3cbd 100644 --- a/tests/testthat/test-DiseasyModel.R +++ b/tests/testthat/test-DiseasyModel.R @@ -257,7 +257,7 @@ test_that("$get_results() gives error", { # Test the get_results expect_error(m$get_results(), class = "simpleError", - regex = "Each model must implement their own `get_results` methods") + regex = "`DiseasyModel` should not be used directly") rm(m) })