From 94eb84e31f41847c115c54693ffc3658c8806290 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 9 Oct 2024 16:15:13 +0100 Subject: [PATCH] find and replace naming --- .github/workflows/check-cmdstan.yaml | 2 +- CITATION.cff | 15 +- DESCRIPTION | 13 +- LICENSE | 2 +- LICENSE.md | 2 +- NAMESPACE | 18 +- NEWS.md | 35 +-- R/check.R | 4 +- ...imarycensoreddist.R => dprimarycensored.R} | 22 +- R/fitdistdoublecens.R | 20 +- R/pcd-stan-tools.R | 16 +- R/pcd_cmdstan_model.R | 20 +- ...imarycensoreddist.R => pprimarycensored.R} | 28 +-- ...mary_censored_dist.R => primarycensored.R} | 58 ++--- ...imarycensoreddist.R => rprimarycensored.R} | 22 +- README.Rmd | 50 ++-- README.md | 66 ++--- _pkgdown.yml | 10 +- codemeta.json | 18 +- inst/WORDLIST | 4 +- inst/make_hexsticker.R | 6 +- ...ensored_dist.stan => primarycensored.stan} | 236 +++++------------- ...an => primarycensored_analytical_cdf.stan} | 46 ++-- inst/stan/functions/primarycensored_ode.stan | 111 ++++++++ inst/stan/pcens_model.stan | 23 +- man/dot-dpcens.Rd | 4 +- man/dot-ppcens.Rd | 4 +- ...arycensoreddist.Rd => dprimarycensored.Rd} | 22 +- man/figures/logo.png | Bin 23539 -> 31117 bytes man/fitdistdoublecens.Rd | 4 +- ...ensored_dist.Rd => new_primarycensored.Rd} | 22 +- man/pcd_as_stan_data.Rd | 10 +- man/pcd_cmdstan_model.Rd | 6 +- man/pcd_load_stan_functions.Rd | 4 +- man/pcd_stan_files.Rd | 7 +- man/pcd_stan_functions.Rd | 4 +- ...arycensoreddist.Rd => pprimarycensored.Rd} | 26 +- man/primary_censored_cdf.Rd | 36 --- ...primary_censored_cdf.pcens_pgamma_dunif.Rd | 33 --- ...primary_censored_cdf.pcens_plnorm_dunif.Rd | 33 --- ...imary_censored_cdf.pcens_pweibull_dunif.Rd | 33 --- man/primarycensored_cdf.Rd | 36 +++ ...ault.Rd => primarycensored_cdf.default.Rd} | 28 +-- man/primarycensored_cdf.pcens_pgamma_dunif.Rd | 33 +++ man/primarycensored_cdf.pcens_plnorm_dunif.Rd | 33 +++ ...rimarycensored_cdf.pcens_pweibull_dunif.Rd | 33 +++ man/roxygen/meta.R | 4 +- ...arycensoreddist.Rd => rprimarycensored.Rd} | 18 +- pkgdown/favicon/apple-touch-icon.png | Bin 18162 -> 8097 bytes pkgdown/favicon/favicon-48x48.png | Bin 0 -> 1567 bytes pkgdown/favicon/favicon.ico | Bin 15086 -> 15086 bytes pkgdown/favicon/favicon.svg | 3 + pkgdown/favicon/site.webmanifest | 21 ++ pkgdown/favicon/web-app-manifest-192x192.png | Bin 0 -> 8804 bytes pkgdown/favicon/web-app-manifest-512x512.png | Bin 0 -> 32336 bytes tests/testthat.R | 4 +- ...censoreddist.R => test-dprimarycensored.R} | 12 +- tests/testthat/test-fitdistdoublecens.R | 4 +- tests/testthat/test-pcd-stan-tools.R | 20 +- ...cmdstan_data.R => test-pcd_as_stan_data.R} | 12 +- tests/testthat/test-pcd_cmdstan_model.R | 16 +- ...censoreddist.R => test-pprimarycensored.R} | 12 +- ...censored_dist.R => test-primarycensored.R} | 76 +++--- ...soreddist.R => test-rpd-primarycensored.R} | 12 +- ...censoreddist.R => test-rprimarycensored.R} | 8 +- ...est-stan-primarycensored_analytical_cdf.R} | 24 +- ..._ode.R => test-stan-primarycensored_ode.R} | 42 ++-- ...dist.R => test-stan-rpd-primarycensored.R} | 106 ++++---- touchstone/script.R | 52 ++-- vignettes/analytic-solutions.Rmd | 2 +- .../_readme-install-primarycensoreddist.Rmd | 8 +- vignettes/fitting-dists-with-fitdistrplus.Rmd | 32 +-- vignettes/fitting-dists-with-stan.Rmd | 48 ++-- vignettes/primarycensoreddist.Rmd | 46 ++-- vignettes/using-stan-tools.Rmd | 28 +-- vignettes/why-it-works.Rmd | 22 +- 76 files changed, 955 insertions(+), 935 deletions(-) rename R/{dprimarycensoreddist.R => dprimarycensored.R} (91%) rename R/{pprimarycensoreddist.R => pprimarycensored.R} (86%) rename R/{primary_censored_dist.R => primarycensored.R} (86%) rename R/{rprimarycensoreddist.R => rprimarycensored.R} (87%) rename inst/stan/functions/{primary_censored_dist.stan => primarycensored.stan} (50%) rename inst/stan/functions/{primary_censored_dist_analytical_cdf.stan => primarycensored_analytical_cdf.stan} (80%) create mode 100644 inst/stan/functions/primarycensored_ode.stan rename man/{dprimarycensoreddist.Rd => dprimarycensored.Rd} (90%) rename man/{new_primary_censored_dist.Rd => new_primarycensored.Rd} (80%) rename man/{pprimarycensoreddist.Rd => pprimarycensored.Rd} (85%) delete mode 100644 man/primary_censored_cdf.Rd delete mode 100644 man/primary_censored_cdf.pcens_pgamma_dunif.Rd delete mode 100644 man/primary_censored_cdf.pcens_plnorm_dunif.Rd delete mode 100644 man/primary_censored_cdf.pcens_pweibull_dunif.Rd create mode 100644 man/primarycensored_cdf.Rd rename man/{primary_censored_cdf.default.Rd => primarycensored_cdf.default.Rd} (57%) create mode 100644 man/primarycensored_cdf.pcens_pgamma_dunif.Rd create mode 100644 man/primarycensored_cdf.pcens_plnorm_dunif.Rd create mode 100644 man/primarycensored_cdf.pcens_pweibull_dunif.Rd rename man/{rprimarycensoreddist.Rd => rprimarycensored.Rd} (90%) create mode 100644 pkgdown/favicon/favicon-48x48.png create mode 100644 pkgdown/favicon/favicon.svg create mode 100644 pkgdown/favicon/site.webmanifest create mode 100644 pkgdown/favicon/web-app-manifest-192x192.png create mode 100644 pkgdown/favicon/web-app-manifest-512x512.png rename tests/testthat/{test-dprimarycensoreddist.R => test-dprimarycensored.R} (79%) rename tests/testthat/{test-pcd_as_cmdstan_data.R => test-pcd_as_stan_data.R} (95%) rename tests/testthat/{test-pprimarycensoreddist.R => test-pprimarycensored.R} (74%) rename tests/testthat/{test-primary_censored_dist.R => test-primarycensored.R} (75%) rename tests/testthat/{test-rpd-primarycensoreddist.R => test-rpd-primarycensored.R} (93%) rename tests/testthat/{test-rprimarycensoreddist.R => test-rprimarycensored.R} (77%) rename tests/testthat/{test-stan-primary_censored_dist_analytical_cdf.R => test-stan-primarycensored_analytical_cdf.R} (82%) rename tests/testthat/{test-stan-primary_censored_ode.R => test-stan-primarycensored_ode.R} (68%) rename tests/testthat/{test-stan-rpd-primarycensoreddist.R => test-stan-rpd-primarycensored.R} (66%) diff --git a/.github/workflows/check-cmdstan.yaml b/.github/workflows/check-cmdstan.yaml index e75b719..033b7cb 100644 --- a/.github/workflows/check-cmdstan.yaml +++ b/.github/workflows/check-cmdstan.yaml @@ -51,7 +51,7 @@ jobs: - name: Compile model and check syntax run: | stan_file <- file.path(tempdir(), "pcd_functions.stan") - primarycensoreddist::pcd_load_stan_functions( + primarycensored::pcd_load_stan_functions( wrap_in_block = TRUE, write_to_file = TRUE, output_file = stan_file diff --git a/CITATION.cff b/CITATION.cff index 7130f71..cb68354 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,17 +2,17 @@ # CITATION file created with {cffr} R package # See also: https://docs.ropensci.org/cffr/ # -------------------------------------------- - + cff-version: 1.2.0 -message: 'To cite package "primarycensoreddist" in publications use:' +message: 'To cite package "primarycensored" in publications use:' type: software license: MIT -title: 'primarycensoreddist: Primary Event Censored Distributions in R and Stan' +title: 'primarycensored: Primary Event Censored Distributions in R and Stan' version: 0.1.0.1000 doi: 10.5281/zenodo.13632839 identifiers: - type: url - value: https://github.com/epinowcast/primarycensoreddist/ + value: https://github.com/epinowcast/primarycensored/ abstract: This package provides both R functions for working with primary event censored distributions and Stan implementations for use in Bayesian modeling. Primary event censored distributions are useful for modeling delayed reporting scenarios in epidemiology @@ -25,15 +25,15 @@ authors: orcid: https://orcid.org/0000-0001-8057-8037 preferred-citation: type: manual - title: 'primarycensoreddist: Primary Event Censored Distributions in R and Stan' + title: 'primarycensored: Primary Event Censored Distributions in R and Stan' authors: - name: Sam Abbott email: contact@samabbott.co.uk orcid: https://orcid.org/0000-0001-8057-8037 year: '2024' doi: 10.5281/zenodo.13632839 -repository-code: https://github.com/epinowcast/primarycensoreddist/issues/ -url: https://primarycensoreddist.epinowcast.org +repository-code: https://github.com/epinowcast/primarycensored/issues/ +url: https://primarycensored.epinowcast.org contact: - name: Sam Abbott email: contact@samabbott.co.uk @@ -195,4 +195,3 @@ references: orcid: https://orcid.org/0000-0002-7840-692X year: '2024' doi: 10.32614/CRAN.package.usethis - diff --git a/DESCRIPTION b/DESCRIPTION index 16eb8a2..8fd428b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,4 +1,4 @@ -Package: primarycensoreddist +Package: primarycensored Title: Primary Event Censored Distributions in R and Stan Version: 0.5.0.1000 Authors@R: @@ -22,17 +22,18 @@ Authors@R: role = c("ctb"), email = "sebastian.funk@lshtm.ac.uk", comment = c(ORCID = "0000-0002-2842-3406"))) -Description: This package provides both R functions for working with primary +Description: This package provides R functions for working with primary event censored distributions and Stan implementations for use in Bayesian modeling. Primary event censored distributions are useful for modeling delayed reporting scenarios in epidemiology and other fields. It provides support for arbitrary delay distributions, a range of common primary distributions, and allows for truncation and secondary event censoring - to be accounted for. + to be accounted for. In addition, it provides both frequentist and Bayesian + methods for fitting primary event censored distributions to data. License: MIT + file LICENSE -URL: https://primarycensoreddist.epinowcast.org, - https://github.com/epinowcast/primarycensoreddist/ -BugReports: https://github.com/epinowcast/primarycensoreddist/issues/ +URL: https://primarycensored.epinowcast.org, + https://github.com/epinowcast/primarycensored/ +BugReports: https://github.com/epinowcast/primarycensored/issues/ Depends: R (>= 4.0.0) Imports: diff --git a/LICENSE b/LICENSE index 2c010e2..c4ee1cd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,2 +1,2 @@ YEAR: 2024 -COPYRIGHT HOLDER: primarycensoreddist authors +COPYRIGHT HOLDER: primarycensored authors diff --git a/LICENSE.md b/LICENSE.md index 1ccebc0..a730714 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ # MIT License -Copyright (c) 2024 primarycensoreddist authors +Copyright (c) 2024 primarycensored authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/NAMESPACE b/NAMESPACE index 96309f2..7257cc4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,17 +1,17 @@ # Generated by roxygen2: do not edit by hand -S3method(primary_censored_cdf,default) -S3method(primary_censored_cdf,pcens_pgamma_dunif) -S3method(primary_censored_cdf,pcens_plnorm_dunif) -S3method(primary_censored_cdf,pcens_pweibull_dunif) +S3method(primarycensored_cdf,default) +S3method(primarycensored_cdf,pcens_pgamma_dunif) +S3method(primarycensored_cdf,pcens_plnorm_dunif) +S3method(primarycensored_cdf,pcens_pweibull_dunif) export(check_dprimary) export(check_pdist) export(check_truncation) export(dexpgrowth) export(dpcens) -export(dprimarycensoreddist) +export(dprimarycensored) export(fitdistdoublecens) -export(new_primary_censored_dist) +export(new_primarycensored) export(pcd_as_stan_data) export(pcd_cmdstan_model) export(pcd_load_stan_functions) @@ -20,11 +20,11 @@ export(pcd_stan_functions) export(pcd_stan_path) export(pexpgrowth) export(ppcens) -export(pprimarycensoreddist) -export(primary_censored_cdf) +export(pprimarycensored) +export(primarycensored_cdf) export(rexpgrowth) export(rpcens) -export(rprimarycensoreddist) +export(rprimarycensored) importFrom(stats,dunif) importFrom(stats,runif) importFrom(stats,setNames) diff --git a/NEWS.md b/NEWS.md index 08e6fe0..e2749a4 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,6 @@ -# primarycensoreddist 0.5.0.1000 +# primarycensored 0.5.0.1000 -This is the development version of `primarycensoreddist` and is not yet ready for release. +This is the development version of `primarycensored` and is not yet ready for release. ## Package @@ -9,6 +9,7 @@ This is the development version of `primarycensoreddist` and is not yet ready fo * `pcd_as_cmdstan_data()` has been renamed to `pcd_as_stan_data()` to better reflect that it is used for `Stan` models in general rather than just the `CmdStan` models. * The stan code has been refactored into a folder of functions within the current `stan` folder and the `stan` model has been moved into the `stan` folder. All paths to the stan code have been updated to reflect this. * Added R and stan implementations of the primary censored cdf for the weibull distribution with uniform primary censoring. +* The package has been renamed to `primarycensored` as have all functions that use "dist" in their name. ## Documentation @@ -18,7 +19,7 @@ This is the development version of `primarycensoreddist` and is not yet ready fo * Fixed error in "Analytic solutions" vignette where the Weibull density was not being treated as zero for negative delays. * Split "Why it works" vignette into two separate vignettes, "Why it works" and "Analytic solutions for censored delay distributions". -# primarycensoreddist 0.5.0 +# primarycensored 0.5.0 This release adds a new `{touchstone}` based benchmark suite to the package. It also adds a new "How it works" vignette which aims to give the reader more details into how the primary censored distributions work. @@ -28,41 +29,41 @@ As part of the "How it works" we (@SamuelBrand1) found analytical solutions for * Add `{touchstone}` based benchmarks for benchmarking R utility functions, and fitting the `stan` and `fitdistplus` models. * Added a "How it works" vignette. -* Added R infrastructure for analytical solutions via the `primary_censored_dist` S3 class. +* Added R infrastructure for analytical solutions via the `primarycensored` S3 class. * Added Weibull analytical solution to "How it works" vignette. * Added analytical solutions for the gamma and lognormal distributions with uniform primary censoring to both the `R` and `stan` code. * Added numerical protection to ensure that CDFs for delays greater than the maximum truncation are exactly 1. -# primarycensoreddist 0.4.0 +# primarycensored 0.4.0 -In this release, we have added a new package `stan` model for fitting distributions using the `cmdstanr` package. We have also added a new function `fitdistdoublecens()` to allow for fitting of double censored and truncated data using the `fitdistrplus` package. As well as these functionality improvements this release focuses on improving the stability of the `stan` model and improving the speed of the `primary_censored_ode` function. +In this release, we have added a new package `stan` model for fitting distributions using the `cmdstanr` package. We have also added a new function `fitdistdoublecens()` to allow for fitting of double censored and truncated data using the `fitdistrplus` package. As well as these functionality improvements this release focuses on improving the stability of the `stan` model and improving the speed of the `primarycensored_ode` function. ## Package * Added a new function `fitdistdoublecens()` to allow for fitting of double censored and truncated data using the `fitdistrplus` package. -* Added low level tests for the Stan `primary_censored_ode` function. +* Added low level tests for the Stan `primarycensored_ode` function. * Rephrased the stan code to use a ODE solver rather than a numerical integration method. This allows for much faster and more stable computation of the likelihood * Added a `CmdStan` model for fitting distributions using the `cmdstanr` package. * Added helpers functions for working with the new `CmdStan` model and added an example to the vignette. -* Added parameter recovery tests for the new `CmdStan` model which tests the `primary_censored_dist_lpmf` function when used with NUTS based fitting. +* Added parameter recovery tests for the new `CmdStan` model which tests the `primarycensored_lpmf` function when used with NUTS based fitting. -# primarycensoreddist 0.3.0 +# primarycensored 0.3.0 -This release fixes and improves truncation handling across the code base. It also adds a new vignette showcasing how to use the `primarycensoreddist` and `fitdistrplus` packages together to fit distributions. +This release fixes and improves truncation handling across the code base. It also adds a new vignette showcasing how to use the `primarycensored` and `fitdistrplus` packages together to fit distributions. ## Package * Updated the approach to truncation to be outside the primary censored distribution integral. * Improved tests that compare random sampling and probability mass/density functions between R and Stan. * Improved cross-testing between R and Stan implementations of the primary censored distributions. -* Worked on improving the stability of the `primary_censored_dist_lpmf` when used for NUTS based fitting (i.e. in Stan). +* Worked on improving the stability of the `primarycensored_lpmf` when used for NUTS based fitting (i.e. in Stan). ## Documentation * @athowes improved the getting started vignette by catching a few grammar errors and simplifying language. -* Added a new vignette showcasing how to use the `primarycensoreddist` and `fitdistrplus` packages together to fit distributions. +* Added a new vignette showcasing how to use the `primarycensored` and `fitdistrplus` packages together to fit distributions. -# primarycensoreddist 0.2.0 +# primarycensored 0.2.0 This release puts in place initial documentation and vignettes. It also includes a new primary censored distribution interface to allow for non-secondary event censored distributions. Development of this release as identified some numerical issues in the gradient evaluations for the primary censored distributions which may lead to breaking @@ -70,8 +71,8 @@ interface changes in `0.3.0` for the Stan code. ## Package -* Added support for `swindow = 0` to `rprimarycensoreddist` to allow for non-secondary event censored distributions. -* Adapted `rprimarycensoreddist` so that truncation is based on the primary censored distribution before secondary events are censored. This better matches the generative process. +* Added support for `swindow = 0` to `rprimarycensored` to allow for non-secondary event censored distributions. +* Adapted `rprimarycensored` so that truncation is based on the primary censored distribution before secondary events are censored. This better matches the generative process. * Added a new Stan interface tool to enable finding which files functions are implemented in the Stan code. ## Documentation @@ -80,9 +81,9 @@ interface changes in `0.3.0` for the Stan code. * Added a vignette showcasing how to use the package Stan code with `cmdstanr`. * Added a vignette showcasing how to fit distributions using the `cmdstanr` package. -# primarycensoreddist 0.1.0 +# primarycensored 0.1.0 -This is the initial `primarycensoreddist` release and includes R and stan tools for dealing with potentially truncated primary event censored delay distributions. We expect all current features to work but the UI may change as the package matures over the next few versions. +This is the initial `primarycensored` release and includes R and stan tools for dealing with potentially truncated primary event censored delay distributions. We expect all current features to work but the UI may change as the package matures over the next few versions. ## Package diff --git a/R/check.R b/R/check.R index 7ee5b69..0be55d2 100644 --- a/R/check.R +++ b/R/check.R @@ -3,7 +3,7 @@ #' This function tests whether a given function behaves like a valid CDF by #' checking if it's monotonically increasing and bounded between 0 and 1. #' -#' @inheritParams pprimarycensoreddist +#' @inheritParams pprimarycensored #' @return NULL. The function will stop execution with an error message if #' pdist is not a valid CDF. #' @export @@ -40,7 +40,7 @@ check_pdist <- function(pdist, D, ...) { #' checking if it integrates to approximately 1 over the specified range #' and if it takes the arguments min and max. #' -#' @inheritParams pprimarycensoreddist +#' @inheritParams pprimarycensored #' @param tolerance The tolerance for the integral to be considered close to 1 #' #' @return NULL. The function will stop execution with an error message if diff --git a/R/dprimarycensoreddist.R b/R/dprimarycensored.R similarity index 91% rename from R/dprimarycensoreddist.R rename to R/dprimarycensored.R index 71a2bb4..9f32778 100644 --- a/R/dprimarycensoreddist.R +++ b/R/dprimarycensored.R @@ -7,7 +7,7 @@ #' truncation at a maximum delay (D). The function allows for custom primary #' event distributions and delay distributions. #' -#' @inheritParams pprimarycensoreddist +#' @inheritParams pprimarycensored #' #' @param x Vector of quantiles #' @@ -37,7 +37,7 @@ #' where \eqn{F_{\text{cens}}} is the primary event censored CDF. #' #' The function first computes the CDFs for all unique points (including both -#' \eqn{d} and \eqn{d + \text{swindow}}) using [pprimarycensoreddist()]. It then +#' \eqn{d} and \eqn{d + \text{swindow}}) using [pprimarycensored()]. It then #' creates a lookup table for these CDFs to efficiently calculate the PMF for #' each input value. For non-positive delays, the function returns 0. #' @@ -51,23 +51,23 @@ #' where \eqn{f_{\text{cens,norm}}(d)} is the normalized PMF and #' \eqn{f_{\text{cens}}(d)} is the unnormalized PMF. For the explanation and #' mathematical details of the CDF, refer to the documentation of -#' [pprimarycensoreddist()]. +#' [pprimarycensored()]. #' -#' @family primarycensoreddist +#' @family rpd_primarycensored #' #' @importFrom stats setNames #' #' @examples #' # Example: Weibull distribution with uniform primary events -#' dprimarycensoreddist(c(0.1, 0.5, 1), pweibull, shape = 1.5, scale = 2.0) +#' dprimarycensored(c(0.1, 0.5, 1), pweibull, shape = 1.5, scale = 2.0) #' #' # Example: Weibull distribution with exponential growth primary events -#' dprimarycensoreddist( +#' dprimarycensored( #' c(0.1, 0.5, 1), pweibull, #' dprimary = dexpgrowth, #' dprimary_args = list(r = 0.2), shape = 1.5, scale = 2.0 #' ) -dprimarycensoreddist <- function( +dprimarycensored <- function( x, pdist, pwindow = 1, swindow = 1, D = Inf, dprimary = stats::dunif, dprimary_args = list(), log = FALSE, @@ -97,7 +97,7 @@ dprimarycensoreddist <- function( return(rep(0, length(x))) } - cdfs <- pprimarycensoreddist( + cdfs <- pprimarycensored( unique_points, pdist, pwindow, Inf, dprimary, dprimary_args, pdist_name = pdist_name, dprimary_name = dprimary_name, ... ) @@ -123,7 +123,7 @@ dprimarycensoreddist <- function( if (max(unique_points) == D) { cdf_D <- max(cdfs) } else { - cdf_D <- pprimarycensoreddist( + cdf_D <- pprimarycensored( D, pdist, pwindow, Inf, dprimary, dprimary_args, ... ) } @@ -137,6 +137,6 @@ dprimarycensoreddist <- function( } } -#' @rdname dprimarycensoreddist +#' @rdname dprimarycensored #' @export -dpcens <- dprimarycensoreddist +dpcens <- dprimarycensored diff --git a/R/fitdistdoublecens.R b/R/fitdistdoublecens.R index b1fa01e..8fc78ef 100644 --- a/R/fitdistdoublecens.R +++ b/R/fitdistdoublecens.R @@ -1,7 +1,7 @@ #' Fit a distribution to doubly censored data #' #' This function wraps the custom approach for fitting distributions to doubly -#' censored data using fitdistrplus and primarycensoreddist. +#' censored data using fitdistrplus and primarycensored. #' #' @details #' This function temporarily assigns and then removes functions from the global @@ -16,7 +16,7 @@ #' #' @param distr A character string naming the distribution to be fitted. #' -#' @inheritParams pprimarycensoreddist +#' @inheritParams pprimarycensored #' #' @param ... Additional arguments to be passed to [fitdistrplus::fitdist()]. #' @@ -37,7 +37,7 @@ #' pwindow <- 2 #' swindow <- 2 #' D <- 10 -#' samples <- rprimarycensoreddist( +#' samples <- rprimarycensored( #' n, rnorm, #' mean = true_mean, sd = true_sd, #' pwindow = pwindow, swindow = swindow, D = D @@ -143,8 +143,8 @@ fitdistdoublecens <- function(censdata, distr, return(fit) } -#' Define a fitdistrplus compatible wrapper around dprimarycensoreddist -#' @inheritParams dprimarycensoreddist +#' Define a fitdistrplus compatible wrapper around dprimarycensored +#' @inheritParams dprimarycensored #' #' @param swindows A numeric vector of secondary window sizes corresponding to #' each element in x @@ -154,7 +154,7 @@ fitdistdoublecens <- function(censdata, distr, tryCatch( { if (length(unique(swindows)) == 1) { - dprimarycensoreddist( + dprimarycensored( x, pdist, pwindow = pwindow, swindow = swindows[1], D = D, dprimary = dprimary, dprimary_args = dprimary_args, pdist_name = pdist_name, @@ -167,7 +167,7 @@ fitdistdoublecens <- function(censdata, distr, for (sw in unique_swindows) { mask <- swindows == sw - result[mask] <- dprimarycensoreddist( + result[mask] <- dprimarycensored( x[mask], pdist, pwindow = pwindow, swindow = sw, D = D, dprimary = dprimary, dprimary_args = dprimary_args, @@ -184,14 +184,14 @@ fitdistdoublecens <- function(censdata, distr, ) } -#' Define a fitdistrplus compatible wrapper around pprimarycensoreddist -#' @inheritParams pprimarycensoreddist +#' Define a fitdistrplus compatible wrapper around pprimarycensored +#' @inheritParams pprimarycensored #' @keywords internal .ppcens <- function(q, pdist, pwindow, D, dprimary, dprimary_args, pdist_name, dprimary_name, ...) { tryCatch( { - pprimarycensoreddist( + pprimarycensored( q, pdist, pwindow = pwindow, D = D, dprimary = dprimary, dprimary_args = dprimary_args, diff --git a/R/pcd-stan-tools.R b/R/pcd-stan-tools.R index 97b39c2..dac46b2 100644 --- a/R/pcd-stan-tools.R +++ b/R/pcd-stan-tools.R @@ -6,7 +6,7 @@ #' #' @export pcd_stan_path <- function() { - system.file("stan", "functions", package = "primarycensoreddist") + system.file("stan", "functions", package = "primarycensored") } #' Count the number of unmatched braces in a line @@ -84,7 +84,7 @@ pcd_stan_path <- function() { #' the names of all functions defined in those files. #' #' @param stan_path Character string specifying the path to the directory -#' containing Stan files. Defaults to the Stan path of the primarycensoreddist +#' containing Stan files. Defaults to the Stan path of the primarycensored #' package. #' #' @return A character vector containing unique names of all functions found in @@ -94,7 +94,7 @@ pcd_stan_path <- function() { #' #' @family stantools pcd_stan_functions <- function( - stan_path = primarycensoreddist::pcd_stan_path()) { + stan_path = primarycensored::pcd_stan_path()) { stan_files <- list.files( stan_path, pattern = "\\.stan$", full.names = TRUE, @@ -126,7 +126,7 @@ pcd_stan_functions <- function( #' @family stantools pcd_stan_files <- function( functions = NULL, - stan_path = primarycensoreddist::pcd_stan_path()) { + stan_path = primarycensored::pcd_stan_path()) { # List all Stan files in the directory all_files <- list.files( stan_path, @@ -164,7 +164,7 @@ pcd_stan_files <- function( #' functions. #' #' @param stan_path Character string, the path to the Stan code. Defaults to the -#' path to the Stan code in the primarycensoreddist package. +#' path to the Stan code in the primarycensored package. #' #' @param wrap_in_block Logical, whether to wrap the functions in a #' `functions{}` block. Default is FALSE. @@ -181,7 +181,7 @@ pcd_stan_files <- function( #' #' @export pcd_load_stan_functions <- function( - functions = NULL, stan_path = primarycensoreddist::pcd_stan_path(), + functions = NULL, stan_path = primarycensored::pcd_stan_path(), wrap_in_block = FALSE, write_to_file = FALSE, output_file = "pcd_functions.stan") { stan_files <- list.files( @@ -209,8 +209,8 @@ pcd_load_stan_functions <- function( # Add version comment version_comment <- paste( - "// Stan functions from primarycensoreddist version", - utils::packageVersion("primarycensoreddist") + "// Stan functions from primarycensored version", + utils::packageVersion("primarycensored") ) all_content <- c(version_comment, all_content) diff --git a/R/pcd_cmdstan_model.R b/R/pcd_cmdstan_model.R index 524f93f..040ddf1 100644 --- a/R/pcd_cmdstan_model.R +++ b/R/pcd_cmdstan_model.R @@ -1,7 +1,7 @@ -#' Create a CmdStanModel with primarycensoreddist Stan functions +#' Create a CmdStanModel with primarycensored Stan functions #' #' This function creates a CmdStanModel object using the Stan model and -#' functions from primarycensoreddist and optionally includes additional +#' functions from primarycensored and optionally includes additional #' user-specified Stan files. #' #' @param include_paths Character vector of paths to include for Stan @@ -30,7 +30,7 @@ #' fit <- model$sample(data = stan_data) #' } pcd_cmdstan_model <- function( - include_paths = primarycensoreddist::pcd_stan_path(), + include_paths = primarycensored::pcd_stan_path(), ...) { if (!requireNamespace("cmdstanr", quietly = TRUE)) { stop("Package 'cmdstanr' is required but not installed for this function.") @@ -38,7 +38,7 @@ pcd_cmdstan_model <- function( pcd_stan_model <- system.file( "stan", "pcens_model.stan", - package = "primarycensoreddist" + package = "primarycensored" ) cmdstanr::cmdstan_model( @@ -48,10 +48,10 @@ pcd_cmdstan_model <- function( ) } -#' Prepare data for primarycensoreddist Stan model +#' Prepare data for primarycensored Stan model #' #' This function takes in delay data and prepares it for use with the -#' primarycensoreddist Stan model. +#' primarycensored Stan model. #' #' @param data A data frame containing the delay data. #' @@ -74,7 +74,7 @@ pcd_cmdstan_model <- function( #' 13 = Chi-square, 14 = Dirichlet, 15 = Gumbel, 16 = Inverse Gamma, #' 17 = Logistic #' -#' @param primary_dist_id Integer identifying the primary distribution: +#' @param primray_id Integer identifying the primary distribution: #' 1 = Uniform, 2 = Exponential growth #' #' @param param_bounds A list with elements `lower` and `upper`, each a numeric @@ -116,7 +116,7 @@ pcd_cmdstan_model <- function( #' stan_data <- pcd_as_stan_data( #' data, #' dist_id = 1, -#' primary_dist_id = 1, +#' primray_id = 1, #' param_bounds = list(lower = c(0, 0), upper = c(10, 10)), #' primary_param_bounds = list(lower = numeric(0), upper = numeric(0)), #' priors = list(location = c(1, 1), scale = c(1, 1)), @@ -127,7 +127,7 @@ pcd_as_stan_data <- function( data, delay = "delay", delay_upper = "delay_upper", n = "n", pwindow = "pwindow", relative_obs_time = "relative_obs_time", - dist_id, primary_dist_id, + dist_id, primray_id, param_bounds, primary_param_bounds, priors, primary_priors, compute_log_lik = FALSE, @@ -168,7 +168,7 @@ pcd_as_stan_data <- function( pwindow = data[[pwindow]], D = data[[relative_obs_time]], dist_id = dist_id, - primary_dist_id = primary_dist_id, + primray_id = primray_id, n_params = length(param_bounds$lower), n_primary_params = length(primary_param_bounds$lower), compute_log_lik = as.integer(compute_log_lik), diff --git a/R/pprimarycensoreddist.R b/R/pprimarycensored.R similarity index 86% rename from R/pprimarycensoreddist.R rename to R/pprimarycensored.R index 3ed45a9..c0f78c8 100644 --- a/R/pprimarycensoreddist.R +++ b/R/pprimarycensored.R @@ -73,9 +73,9 @@ #' } #' where \eqn{F_{\text{cens,norm}}(q)} is the normalized CDF. #' -#' This function creates a `primary_censored_dist` object using -#' [new_primary_censored_dist()] and then computes the primary event -#' censored CDF using [primary_censored_cdf()]. This abstraction allows +#' This function creates a `primarycensored` object using +#' [new_primarycensored()] and then computes the primary event +#' censored CDF using [primarycensored_cdf()]. This abstraction allows #' for automatic use of analytical solutions when available, while #' seamlessly falling back to numerical integration when necessary. #' @@ -84,20 +84,20 @@ #' `pdist_name` and `dprimary_name` must be used to override the default #' extraction of the function name. #' -#' @family primarycensoreddist -#' @seealso [new_primary_censored_dist()] and [primary_censored_cdf()] +#' @family rpd_primarycensored +#' @seealso [new_primarycensored()] and [primarycensored_cdf()] #' #' @examples #' # Example: Lognormal distribution with uniform primary events -#' pprimarycensoreddist(c(0.1, 0.5, 1), plnorm, meanlog = 0, sdlog = 1) +#' pprimarycensored(c(0.1, 0.5, 1), plnorm, meanlog = 0, sdlog = 1) #' #' # Example: Lognormal distribution with exponential growth primary events -#' pprimarycensoreddist( +#' pprimarycensored( #' c(0.1, 0.5, 1), plnorm, #' dprimary = dexpgrowth, #' dprimary_args = list(r = 0.2), meanlog = 0, sdlog = 1 #' ) -pprimarycensoreddist <- function( +pprimarycensored <- function( q, pdist, pwindow = 1, D = Inf, dprimary = stats::dunif, dprimary_args = list(), pdist_name = NULL, dprimary_name = NULL, ...) { check_pdist(pdist, D, ...) @@ -110,8 +110,8 @@ pprimarycensoreddist <- function( dprimary_name <- .extract_function_name(substitute(dprimary)) } - # Create a new primary_censored_dist object - pcens_obj <- new_primary_censored_dist( + # Create a new primarycensored object + pcens_obj <- new_primarycensored( pdist, dprimary, dprimary_args, @@ -121,14 +121,14 @@ pprimarycensoreddist <- function( ) # Compute the CDF using the S3 method - result <- primary_censored_cdf(pcens_obj, q, pwindow) + result <- primarycensored_cdf(pcens_obj, q, pwindow) if (!is.infinite(D)) { # Compute normalization factor for finite D normalizer <- if (max(q) == D) { result[length(result)] } else { - pprimarycensoreddist(D, pdist, pwindow, Inf, dprimary, dprimary_args, ...) + pprimarycensored(D, pdist, pwindow, Inf, dprimary, dprimary_args, ...) } result <- result / normalizer @@ -138,6 +138,6 @@ pprimarycensoreddist <- function( return(result) } -#' @rdname pprimarycensoreddist +#' @rdname pprimarycensored #' @export -ppcens <- pprimarycensoreddist +ppcens <- pprimarycensored diff --git a/R/primary_censored_dist.R b/R/primarycensored.R similarity index 86% rename from R/primary_censored_dist.R rename to R/primarycensored.R index b088895..afaf0e9 100644 --- a/R/primary_censored_dist.R +++ b/R/primarycensored.R @@ -1,13 +1,13 @@ #' S3 class for primary event censored distribution computation #' -#' @inheritParams pprimarycensoreddist +#' @inheritParams pprimarycensored #' -#' @return An object of class primary_censored_cdf +#' @return An object of class primarycensored_cdf #' -#' @family primary_censored_dist +#' @family primarycensored #' #' @export -new_primary_censored_dist <- function( +new_primarycensored <- function( pdist, dprimary, dprimary_args, pdist_name = NULL, dprimary_name = NULL, ...) { @@ -37,10 +37,10 @@ new_primary_censored_dist <- function( #' Compute primary event censored CDF #' -#' @inheritParams pprimarycensoreddist +#' @inheritParams pprimarycensored #' -#' @param object A `primary_censored_dist` object as created by -#' [new_primary_censored_dist()]. +#' @param object A `primarycensored` object as created by +#' [new_primarycensored()]. #' #' @param use_numeric Logical, if TRUE forces use of numeric integration #' even for distributions with analytical solutions. This is primarily @@ -49,12 +49,12 @@ new_primary_censored_dist <- function( #' #' @return Vector of primary event censored CDFs #' -#' @family primary_censored_dist +#' @family primarycensored #' #' @export -primary_censored_cdf <- function( +primarycensored_cdf <- function( object, q, pwindow, use_numeric = FALSE) { - UseMethod("primary_censored_cdf") + UseMethod("primarycensored_cdf") } #' Default method for computing primary event censored CDF @@ -63,22 +63,22 @@ primary_censored_cdf <- function( #' event distributions that don't have specific implementations. It uses #' the numeric integration method. #' -#' @inheritParams primary_censored_cdf -#' @inheritParams pprimarycensoreddist +#' @inheritParams primarycensored_cdf +#' @inheritParams pprimarycensored #' #' @details #' This method implements the numerical integration approach for computing #' the primary event censored CDF. It uses the same mathematical formulation -#' as described in the details section of [pprimarycensoreddist()], but +#' as described in the details section of [pprimarycensored()], but #' applies numerical integration instead of analytical solutions. #' -#' @seealso [pprimarycensoreddist()] for the mathematical details of the +#' @seealso [pprimarycensored()] for the mathematical details of the #' primary event censored CDF computation. #' -#' @family primary_censored_dist +#' @family primarycensored #' #' @export -primary_censored_cdf.default <- function( +primarycensored_cdf.default <- function( object, q, pwindow, use_numeric = FALSE) { result <- vapply(q, function(d) { if (d <= 0) { @@ -105,16 +105,16 @@ primary_censored_cdf.default <- function( #' Method for Gamma delay with uniform primary #' -#' @inheritParams primary_censored_cdf +#' @inheritParams primarycensored_cdf #' -#' @family primary_censored_dist +#' @family primarycensored #' #' @export -primary_censored_cdf.pcens_pgamma_dunif <- function( +primarycensored_cdf.pcens_pgamma_dunif <- function( object, q, pwindow, use_numeric = FALSE) { if (isTRUE(use_numeric)) { return( - primary_censored_cdf.default(object, q, pwindow, use_numeric) + primarycensored_cdf.default(object, q, pwindow, use_numeric) ) } # Extract Gamma distribution parameters @@ -178,16 +178,16 @@ primary_censored_cdf.pcens_pgamma_dunif <- function( #' Method for Log-Normal delay with uniform primary #' -#' @inheritParams primary_censored_cdf +#' @inheritParams primarycensored_cdf #' -#' @family primary_censored_dist +#' @family primarycensored #' #' @export -primary_censored_cdf.pcens_plnorm_dunif <- function( +primarycensored_cdf.pcens_plnorm_dunif <- function( object, q, pwindow, use_numeric = FALSE) { if (isTRUE(use_numeric)) { return( - primary_censored_cdf.default(object, q, pwindow, use_numeric) + primarycensored_cdf.default(object, q, pwindow, use_numeric) ) } @@ -250,22 +250,22 @@ primary_censored_cdf.pcens_plnorm_dunif <- function( #' Method for Weibull delay with uniform primary #' -#' @inheritParams primary_censored_cdf +#' @inheritParams primarycensored_cdf #' -#' @family primary_censored_dist +#' @family primarycensored #' #' @export -primary_censored_cdf.pcens_pweibull_dunif <- function( +primarycensored_cdf.pcens_pweibull_dunif <- function( object, q, pwindow, use_numeric = FALSE) { if (isTRUE(use_numeric)) { return( - primary_censored_cdf.default(object, q, pwindow, use_numeric) + primarycensored_cdf.default(object, q, pwindow, use_numeric) ) } if (pwindow > 3) { return( - primary_censored_cdf.default(object, q, pwindow, use_numeric) + primarycensored_cdf.default(object, q, pwindow, use_numeric) ) } diff --git a/R/rprimarycensoreddist.R b/R/rprimarycensored.R similarity index 87% rename from R/rprimarycensoreddist.R rename to R/rprimarycensored.R index 89f962a..7a94c8d 100644 --- a/R/rprimarycensoreddist.R +++ b/R/rprimarycensored.R @@ -6,7 +6,7 @@ #' function allows for custom primary event distributions and delay #' distributions. #' -#' @inheritParams dprimarycensoreddist +#' @inheritParams dprimarycensored #' #' @param rdist Function to generate random samples from the delay distribution #' for example [stats::rlnorm()] for lognormal distribution. @@ -63,22 +63,22 @@ #' The function oversamples to account for potential truncation and generates #' additional samples if needed to reach the desired number of valid samples. #' -#' @family primarycensoreddist +#' @family rpd_primarycensored #' #' @examples #' # Example: Lognormal distribution with uniform primary events -#' rprimarycensoreddist(10, rlnorm, meanlog = 0, sdlog = 1) +#' rprimarycensored(10, rlnorm, meanlog = 0, sdlog = 1) #' #' # Example: Lognormal distribution with exponential growth primary events -#' rprimarycensoreddist( +#' rprimarycensored( #' 10, rlnorm, #' rprimary = rexpgrowth, rprimary_args = list(r = 0.2), #' meanlog = 0, sdlog = 1 #' ) -rprimarycensoreddist <- function(n, rdist, pwindow = 1, swindow = 1, - D = Inf, rprimary = stats::runif, - rprimary_args = list(), - oversampling_factor = 1.2, ...) { +rprimarycensored <- function(n, rdist, pwindow = 1, swindow = 1, + D = Inf, rprimary = stats::runif, + rprimary_args = list(), + oversampling_factor = 1.2, ...) { # Generate more samples than needed to account for truncation n_generate <- ceiling(n * oversampling_factor) @@ -98,7 +98,7 @@ rprimarycensoreddist <- function(n, rdist, pwindow = 1, swindow = 1, # If we don't have enough samples, generate more while (length(valid_samples) < n) { - additional_samples <- rprimarycensoreddist( + additional_samples <- rprimarycensored( n - length(valid_samples), rdist, pwindow, swindow, D, rprimary, rprimary_args, ... ) @@ -118,6 +118,6 @@ rprimarycensoreddist <- function(n, rdist, pwindow = 1, swindow = 1, return(rounded_samples) } -#' @rdname rprimarycensoreddist +#' @rdname rprimarycensored #' @export -rpcens <- rprimarycensoreddist +rpcens <- rprimarycensored diff --git a/README.Rmd b/README.Rmd index 303f3ab..944b6c6 100644 --- a/README.Rmd +++ b/README.Rmd @@ -13,17 +13,17 @@ knitr::opts_chunk$set( message = FALSE, warning = FALSE ) ``` -# Primary Event Censored Distributions in R and Stan primarycensoreddist website +# Primary event censored distributions in R and Stan primarycensored website [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) -[![R-CMD-check](https://github.com/epinowcast/primarycensoreddist/workflows/R-CMD-check/badge.svg)](https://github.com/epinowcast/primarycensoreddist/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/epinowcast/primarycensoreddist/branch/main/graph/badge.svg)](https://app.codecov.io/gh/epinowcast/primarycensoreddist) +[![R-CMD-check](https://github.com/epinowcast/primarycensored/workflows/R-CMD-check/badge.svg)](https://github.com/epinowcast/primarycensored/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/epinowcast/primarycensored/branch/main/graph/badge.svg)](https://app.codecov.io/gh/epinowcast/primarycensored) -[![Universe](https://epinowcast.r-universe.dev/badges/primarycensoreddist)](https://epinowcast.r-universe.dev/primarycensoreddist) +[![Universe](https://epinowcast.r-universe.dev/badges/primarycensored)](https://epinowcast.r-universe.dev/primarycensored) [![MIT -license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/epinowcast/primarycensoreddist/blob/master/LICENSE.md/) -[![GitHub contributors](https://img.shields.io/github/contributors/epinowcast/primarycensoreddist)](https://github.com/epinowcast/primarycensoreddist/graphs/contributors) +license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/epinowcast/primarycensored/blob/master/LICENSE.md/) +[![GitHub contributors](https://img.shields.io/github/contributors/epinowcast/primarycensored)](https://github.com/epinowcast/primarycensored/graphs/contributors) [![DOI](https://zenodo.org/badge/845633278.svg)](https://zenodo.org/doi/10.5281/zenodo.13632838) @@ -31,14 +31,14 @@ license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/ ## Summary ```{r, results = "asis", echo=FALSE} -cat(gsub("\n[ ]+", " ", packageDescription("primarycensoreddist")$Description)) +cat(gsub("\n[ ]+", " ", packageDescription("primarycensored")$Description)) ``` ## Installation
Installing the package -```{r, child="vignettes/chunks/_readme-install-primarycensoreddist.Rmd"} +```{r, child="vignettes/chunks/_readme-install-primarycensored.Rmd"} ```
@@ -49,7 +49,7 @@ If you wish to use the Stan functions, you will need to install [CmdStan](https: `cmdstanr`_](https://mc-stan.org/cmdstanr/articles/cmdstanr.html) vignette, with other details and support at the [package site](https://mc-stan.org/cmdstanr/) along with some key instructions available in the [Stan resources package vignette](https://package.epinowcast.org/articles/stan-help.html#toolchain), but the brief version is: ```{r, eval = FALSE} -# if you not yet installed `primarycensoreddist`, or you installed it without +# if you not yet installed `primarycensored`, or you installed it without # `Suggests` dependencies install.packages( "cmdstanr", @@ -69,13 +69,13 @@ We provide a range of other documentation, case studies, and community spaces to
Package Website -The [`primarycensoreddist` website](https://primarycensoreddist.epinowcast.org/) includes a function reference, model outline, and case studies using the package. The site mainly concerns the release version, but you can also find documentation for [the latest development version](https://primarycensoreddist.epinowcast.org/dev/). +The [`primarycensored` website](https://primarycensored.epinowcast.org/) includes a function reference, model outline, and case studies using the package. The site mainly concerns the release version, but you can also find documentation for [the latest development version](https://primarycensored.epinowcast.org/dev/).
Vignettes -We have created [package vignettes](https://primarycensoreddist.epinowcast.org/articles) to help you get started with primarycensoreddist and to highlight other features with case studies. +We have created [package vignettes](https://primarycensored.epinowcast.org/articles) to help you get started with primarycensored and to highlight other features with case studies.
@@ -87,27 +87,27 @@ Our [organisation website](https://www.epinowcast.org/) includes links to other
Community Forum -Our [community forum](https://community.epinowcast.org/) has areas for [question and answer](https://community.epinowcast.org/c/interface/15) and [considering new methods and tools](https://community.epinowcast.org/c/projects/11), among others. If you are generally interested in real-time analysis of infectious disease, you may find this useful even if you do not use `primarycensoreddist`. +Our [community forum](https://community.epinowcast.org/) has areas for [question and answer](https://community.epinowcast.org/c/interface/15) and [considering new methods and tools](https://community.epinowcast.org/c/projects/11), among others. If you are generally interested in real-time analysis of infectious disease, you may find this useful even if you do not use `primarycensored`.
## Contributing -We welcome contributions and new contributors! We particularly appreciate help on [identifying and identified issues](https://github.com/epinowcast/primarycensoreddist/issues). Please check and add to the issues, and/or add a [pull request](https://github.com/epinowcast/primarycensoreddist/pulls) and see our [contributing guide](https://github.com/epinowcast/.github/blob/main/CONTRIBUTING.md) for more information. +We welcome contributions and new contributors! We particularly appreciate help on [identifying and identified issues](https://github.com/epinowcast/primarycensored/issues). Please check and add to the issues, and/or add a [pull request](https://github.com/epinowcast/primarycensored/pulls) and see our [contributing guide](https://github.com/epinowcast/.github/blob/main/CONTRIBUTING.md) for more information. -If you need a different underlying model for your work: `primarycensoreddist` provides a flexible framework for censored distributions in both R and Stan. If you implement new distributions or censoring mechanisms that expand the overall flexibility or improve the defaults, please let us know either here or on the [community forum](https://community.epinowcast.org/). We always like to hear about new use-cases and extensions to the package. +If you need a different underlying model for your work: `primarycensored` provides a flexible framework for censored distributions in both R and Stan. If you implement new distributions or censoring mechanisms that expand the overall flexibility or improve the defaults, please let us know either here or on the [community forum](https://community.epinowcast.org/). We always like to hear about new use-cases and extensions to the package. ### How to make a bug report or feature request -Please briefly describe your problem and what output you expect in an [issue](https://github.com/epinowcast/primarycensoreddist/issues). If you have a question, please don't open an issue. Instead, ask on our [Q and A page](https://github.com/epinowcast/primarycensoreddist/discussions/categories/q-a). See our [contributing guide](https://github.com/epinowcast/.github/blob/main/CONTRIBUTING.md) for more information. +Please briefly describe your problem and what output you expect in an [issue](https://github.com/epinowcast/primarycensored/issues). If you have a question, please don't open an issue. Instead, ask on our [Q and A page](https://github.com/epinowcast/primarycensored/discussions/categories/q-a). See our [contributing guide](https://github.com/epinowcast/.github/blob/main/CONTRIBUTING.md) for more information. ### Code of Conduct -Please note that the `primarycensoreddist` project is released with a [Contributor Code of Conduct](https://github.com/epinowcast/.github/blob/main/CODE_OF_CONDUCT.md). By contributing to this project, you agree to abide by its terms. +Please note that the `primarycensored` project is released with a [Contributor Code of Conduct](https://github.com/epinowcast/.github/blob/main/CODE_OF_CONDUCT.md). By contributing to this project, you agree to abide by its terms. ## Citation -If making use of our methodology or the methodology on which ours is based, please cite the relevant papers from our [methods outline](https://primarycensoreddist.epinowcast.org/articles/primarycensoreddist.html). If you use `primarycensoreddist` in your work, please consider citing it with `citation("primarycensoreddist")`. +If making use of our methodology or the methodology on which ours is based, please cite the relevant papers from our [methods outline](https://primarycensored.epinowcast.org/articles/primarycensored.html). If you use `primarycensored` in your work, please consider citing it with `citation("primarycensored")`. ## Contributors @@ -130,31 +130,27 @@ All contributions to this project are gratefully acknowledged using the [`allcon ### Code -seabbs, -SamuelBrand1, -athowes, -sbfnk +seabbs, +SamuelBrand1, +athowes, +sbfnk ### Issue Authors -zsusswein, -jcblemai +zsusswein, +jcblemai ### Issue Contributors -parksw3 +parksw3 - - - - diff --git a/README.md b/README.md index e244ed8..e1ed199 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,21 @@ -# Primary Event Censored Distributions in R and Stan primarycensoreddist website +# Primary Event Censored Distributions in R and Stan primarycensored website [![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) -[![R-CMD-check](https://github.com/epinowcast/primarycensoreddist/workflows/R-CMD-check/badge.svg)](https://github.com/epinowcast/primarycensoreddist/actions/workflows/R-CMD-check.yaml) +[![R-CMD-check](https://github.com/epinowcast/primarycensored/workflows/R-CMD-check/badge.svg)](https://github.com/epinowcast/primarycensored/actions/workflows/R-CMD-check.yaml) [![Codecov test -coverage](https://codecov.io/gh/epinowcast/primarycensoreddist/branch/main/graph/badge.svg)](https://app.codecov.io/gh/epinowcast/primarycensoreddist) +coverage](https://codecov.io/gh/epinowcast/primarycensored/branch/main/graph/badge.svg)](https://app.codecov.io/gh/epinowcast/primarycensored) -[![Universe](https://epinowcast.r-universe.dev/badges/primarycensoreddist)](https://epinowcast.r-universe.dev/primarycensoreddist) +[![Universe](https://epinowcast.r-universe.dev/badges/primarycensored)](https://epinowcast.r-universe.dev/primarycensored) [![MIT -license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/epinowcast/primarycensoreddist/blob/master/LICENSE.md/) +license](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/epinowcast/primarycensored/blob/master/LICENSE.md/) [![GitHub -contributors](https://img.shields.io/github/contributors/epinowcast/primarycensoreddist)](https://github.com/epinowcast/primarycensoreddist/graphs/contributors) +contributors](https://img.shields.io/github/contributors/epinowcast/primarycensored)](https://github.com/epinowcast/primarycensored/graphs/contributors) [![DOI](https://zenodo.org/badge/845633278.svg)](https://zenodo.org/doi/10.5281/zenodo.13632838) @@ -42,7 +42,7 @@ function, though you need to point to `r-universe` instead of CRAN: ``` r install.packages( - "primarycensoreddist", + "primarycensored", repos = "https://epinowcast.r-universe.dev" ) ``` @@ -54,18 +54,18 @@ bugs): ``` r remotes::install_github( - "epinowcast/primarycensoreddist", + "epinowcast/primarycensored", dependencies = TRUE ) ``` Similarly, you can install historical versions by specifying the release tag (e.g. this installs -[`0.2.0`](https://github.com/epinowcast/primarycensoreddist/releases/tag/v0.2.0)): +[`0.2.0`](https://github.com/epinowcast/primarycensored/releases/tag/v0.2.0)): ``` r remotes::install_github( - "epinowcast/primarycensoreddist", + "epinowcast/primarycensored", dependencies = TRUE, ref = "v0.2.0" ) ``` @@ -93,7 +93,7 @@ vignette](https://package.epinowcast.org/articles/stan-help.html#toolchain), but the brief version is: ``` r -# if you not yet installed `primarycensoreddist`, or you installed it without +# if you not yet installed `primarycensored`, or you installed it without # `Suggests` dependencies install.packages( "cmdstanr", @@ -120,12 +120,12 @@ spaces to ask (and answer!) questions: Package Website -The [`primarycensoreddist` -website](https://primarycensoreddist.epinowcast.org/) includes a +The [`primarycensored` +website](https://primarycensored.epinowcast.org/) includes a function reference, model outline, and case studies using the package. The site mainly concerns the release version, but you can also find documentation for [the latest development -version](https://primarycensoreddist.epinowcast.org/dev/). +version](https://primarycensored.epinowcast.org/dev/).
@@ -134,8 +134,8 @@ Vignettes We have created [package -vignettes](https://primarycensoreddist.epinowcast.org/articles) to help -you get started with primarycensoreddist and to highlight other features +vignettes](https://primarycensored.epinowcast.org/articles) to help +you get started with primarycensored and to highlight other features with case studies.
@@ -161,7 +161,7 @@ and [considering new methods and tools](https://community.epinowcast.org/c/projects/11), among others. If you are generally interested in real-time analysis of infectious disease, you may find this useful even if you do not use -`primarycensoreddist`. +`primarycensored`. @@ -169,15 +169,15 @@ disease, you may find this useful even if you do not use We welcome contributions and new contributors! We particularly appreciate help on [identifying and identified -issues](https://github.com/epinowcast/primarycensoreddist/issues). +issues](https://github.com/epinowcast/primarycensored/issues). Please check and add to the issues, and/or add a [pull -request](https://github.com/epinowcast/primarycensoreddist/pulls) and +request](https://github.com/epinowcast/primarycensored/pulls) and see our [contributing guide](https://github.com/epinowcast/.github/blob/main/CONTRIBUTING.md) for more information. If you need a different underlying model for your work: -`primarycensoreddist` provides a flexible framework for censored +`primarycensored` provides a flexible framework for censored distributions in both R and Stan. If you implement new distributions or censoring mechanisms that expand the overall flexibility or improve the defaults, please let us know either here or on the [community @@ -187,17 +187,17 @@ new use-cases and extensions to the package. ### How to make a bug report or feature request Please briefly describe your problem and what output you expect in an -[issue](https://github.com/epinowcast/primarycensoreddist/issues). If +[issue](https://github.com/epinowcast/primarycensored/issues). If you have a question, please don’t open an issue. Instead, ask on our [Q and A -page](https://github.com/epinowcast/primarycensoreddist/discussions/categories/q-a). +page](https://github.com/epinowcast/primarycensored/discussions/categories/q-a). See our [contributing guide](https://github.com/epinowcast/.github/blob/main/CONTRIBUTING.md) for more information. ### Code of Conduct -Please note that the `primarycensoreddist` project is released with a +Please note that the `primarycensored` project is released with a [Contributor Code of Conduct](https://github.com/epinowcast/.github/blob/main/CODE_OF_CONDUCT.md). By contributing to this project, you agree to abide by its terms. @@ -206,9 +206,9 @@ By contributing to this project, you agree to abide by its terms. If making use of our methodology or the methodology on which ours is based, please cite the relevant papers from our [methods -outline](https://primarycensoreddist.epinowcast.org/articles/primarycensoreddist.html). -If you use `primarycensoreddist` in your work, please consider citing it -with `citation("primarycensoreddist")`. +outline](https://primarycensored.epinowcast.org/articles/primarycensored.html). +If you use `primarycensored` in your work, please consider citing it +with `citation("primarycensored")`. ## Contributors @@ -224,19 +224,19 @@ Contributions of any kind are welcome! ### Code -seabbs, -SamuelBrand1, -athowes, -sbfnk +seabbs, +SamuelBrand1, +athowes, +sbfnk ### Issue Authors -zsusswein, -jcblemai +zsusswein, +jcblemai ### Issue Contributors -parksw3 +parksw3 diff --git a/_pkgdown.yml b/_pkgdown.yml index 3ff9c86..bfc0e58 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -1,4 +1,4 @@ -url: https://primarycensoreddist.epinowcast.org/ +url: https://primarycensored.epinowcast.org/ template: package: enwtheme math-rendering: mathjax @@ -17,11 +17,11 @@ navbar: - text: R reference href: reference/index.html - text: Stan reference - href: https://primarycensoreddist.epinowcast.org/stan/ + href: https://primarycensored.epinowcast.org/stan/ articles: text: Articles menu: - - text: How to use primarycensoreddist with Stan + - text: How to use primarycensored with Stan href: articles/using-stan-tools.html - text: Fitting distributions using primarycensorseddist and cmdstan href: articles/fitting-dists-with-stan.html @@ -40,7 +40,7 @@ reference: - title: Primary event censored distribution functions desc: Functions for generating, evaluating density, and computing cumulative probabilities of primary event censored distributions contents: - - has_concept("primarycensoreddist") + - has_concept("primarycensored") - title: Primary event distributions desc: Probability density and random generation functions for primary event distributions contents: @@ -48,7 +48,7 @@ reference: - title: Primary censored distribution class and methods desc: S3 class and methods for computing primary event censored distributions, focusing on the internal machinery used by the package. Unlike the primary event distributions section which deals with specific distribution functions, this section covers the general framework for handling censored distributions. contents: - - has_concept("primary_censored_dist") + - has_concept("primarycensored") - title: Distribution checking functions desc: Functions to validate cumulative distribution functions (CDFs) and probability density functions (PDFs) contents: diff --git a/codemeta.json b/codemeta.json index c1ecb13..de4e87a 100644 --- a/codemeta.json +++ b/codemeta.json @@ -1,12 +1,12 @@ { "@context": "https://doi.org/10.5063/schema/codemeta-2.0", "@type": "SoftwareSourceCode", - "identifier": "primarycensoreddist", + "identifier": "primarycensored", "description": "This package provides both R functions for working with primary event censored distributions and Stan implementations for use in Bayesian modeling. Primary event censored distributions are useful for modeling delayed reporting scenarios in epidemiology and other fields. It provides support for arbitrary delay distributions, a range of common primary distributions, and allows for truncation and secondary event censoring to be accounted for.", - "name": "primarycensoreddist: Primary Event Censored Distributions in R and Stan", - "relatedLink": "https://primarycensoreddist.epinowcast.org", - "codeRepository": "https://github.com/epinowcast/primarycensoreddist/", - "issueTracker": "https://github.com/epinowcast/primarycensoreddist/issues/", + "name": "primarycensored: Primary Event Censored Distributions in R and Stan", + "relatedLink": "https://primarycensored.epinowcast.org", + "codeRepository": "https://github.com/epinowcast/primarycensored/", + "issueTracker": "https://github.com/epinowcast/primarycensored/issues/", "license": "https://spdx.org/licenses/MIT", "version": "0.5.0.1000", "programmingLanguage": { @@ -226,15 +226,15 @@ "@id": "https://orcid.org/0000-0003-0645-5367" } ], - "name": "primarycensoreddist: Primary Event Censored Distributions in R and Stan", + "name": "primarycensored: Primary Event Censored Distributions in R and Stan", "identifier": "10.5281/zenodo.13632839", "@id": "https://doi.org/10.5281/zenodo.13632839", "sameAs": "https://doi.org/10.5281/zenodo.13632839" } ], - "releaseNotes": "https://github.com/epinowcast/primarycensoreddist/blob/master/NEWS.md", - "readme": "https://github.com/epinowcast/primarycensoreddist/blob/main/README.md", - "contIntegration": ["https://github.com/epinowcast/primarycensoreddist/actions/workflows/R-CMD-check.yaml", "https://app.codecov.io/gh/epinowcast/primarycensoreddist"], + "releaseNotes": "https://github.com/epinowcast/primarycensored/blob/master/NEWS.md", + "readme": "https://github.com/epinowcast/primarycensored/blob/main/README.md", + "contIntegration": ["https://github.com/epinowcast/primarycensored/actions/workflows/R-CMD-check.yaml", "https://app.codecov.io/gh/epinowcast/primarycensored"], "developmentStatus": "https://www.tidyverse.org/lifecycle/#experimental", "keywords": ["censoring", "distributions", "truncation"] } diff --git a/inst/WORDLIST b/inst/WORDLIST index 7e8240c..d712560 100644 --- a/inst/WORDLIST +++ b/inst/WORDLIST @@ -27,7 +27,7 @@ discretising dist dp dprimary -dprimarycensoreddist +dprimarycensored dx dy dz @@ -58,7 +58,7 @@ parksw partexp pcd pdist -pprimarycensoreddist +pprimarycensored primarycensorseddist primaryeventdistributions propto diff --git a/inst/make_hexsticker.R b/inst/make_hexsticker.R index 1227b92..1384438 100644 --- a/inst/make_hexsticker.R +++ b/inst/make_hexsticker.R @@ -37,8 +37,8 @@ data <- data[data$id != 5, ] # Create the plot plot <- ggplot(data, aes(y = id)) + # Add linking bar between primary and secondary events (behind other elements) - geom_segment(data = subset(data, !crosses_observation), aes(x = primary, xend = secondary, yend = id), size = 0.6, color = "#708090") + - geom_segment(data = subset(data, crosses_observation), aes(x = primary, xend = secondary, yend = id), size = 0.6, color = "#708090", linetype = "dotted") + + geom_segment(data = subset(data, !crosses_observation), aes(x = primary, xend = secondary, yend = id), linewidth = 0.6, color = "#708090") + + geom_segment(data = subset(data, crosses_observation), aes(x = primary, xend = secondary, yend = id), linewidth = 0.6, color = "#708090", linetype = "dotted") + # Add primary events geom_point(aes(x = primary), color = "#4682B4", size = 1) + # Add secondary events @@ -62,7 +62,7 @@ plot <- ggplot(data, aes(y = id)) + # make and save hexsticker sticker( plot, - package = "primarycensoreddist", + package = "primarycensored", p_size = 12, p_color = "#4682B4", s_x = 1, diff --git a/inst/stan/functions/primary_censored_dist.stan b/inst/stan/functions/primarycensored.stan similarity index 50% rename from inst/stan/functions/primary_censored_dist.stan rename to inst/stan/functions/primarycensored.stan index bb7d3e0..32cb2d0 100644 --- a/inst/stan/functions/primary_censored_dist.stan +++ b/inst/stan/functions/primarycensored.stan @@ -2,118 +2,6 @@ * Primary event censored distribution functions */ -/** - * Compute the log CDF of the delay distribution - * - * @param delay Time delay - * @param params Distribution parameters - * @param dist_id Distribution identifier - * 1: Lognormal, 2: Gamma, 3: Normal, 4: Exponential, 5: Weibull, - * 6: Beta, 7: Cauchy, 8: Chi-square, 9: Inverse Chi-square, - * 10: Double Exponential, 11: Inverse Gamma, 12: Logistic, - * 13: Pareto, 14: Scaled Inverse Chi-square, 15: Student's t, - * 16: Uniform, 17: von Mises - * - * @return Log CDF of the delay distribution - * - * @code - * // Example: Lognormal distribution - * real delay = 5.0; - * array[2] real params = {0.0, 1.0}; // mean and standard deviation on log scale - * int dist_id = 1; // Lognormal - * real log_cdf = dist_lcdf(delay, params, dist_id); - * @endcode - */ -real dist_lcdf(real delay, array[] real params, int dist_id) { - if (delay <= 0) return negative_infinity(); - - // Use if-else statements to handle different distribution types - if (dist_id == 1) return lognormal_lcdf(delay | params[1], params[2]); - else if (dist_id == 2) return gamma_lcdf(delay | params[1], params[2]); - else if (dist_id == 3) return normal_lcdf(delay | params[1], params[2]); - else if (dist_id == 4) return exponential_lcdf(delay | params[1]); - else if (dist_id == 5) return weibull_lcdf(delay | params[1], params[2]); - else if (dist_id == 6) return beta_lcdf(delay | params[1], params[2]); - else if (dist_id == 7) return cauchy_lcdf(delay | params[1], params[2]); - else if (dist_id == 8) return chi_square_lcdf(delay | params[1]); - else if (dist_id == 9) return inv_chi_square_lcdf(delay | params[1]); - else if (dist_id == 10) return double_exponential_lcdf(delay | params[1], params[2]); - else if (dist_id == 11) return inv_gamma_lcdf(delay | params[1], params[2]); - else if (dist_id == 12) return logistic_lcdf(delay | params[1], params[2]); - else if (dist_id == 13) return pareto_lcdf(delay | params[1], params[2]); - else if (dist_id == 14) return scaled_inv_chi_square_lcdf(delay | params[1], params[2]); - else if (dist_id == 15) return student_t_lcdf(delay | params[1], params[2], params[3]); - else if (dist_id == 16) return uniform_lcdf(delay | params[1], params[2]); - else if (dist_id == 17) return von_mises_lcdf(delay | params[1], params[2]); - else reject("Invalid distribution identifier"); -} - -/** - * Compute the log PDF of the primary distribution - * - * @param x Value - * @param primary_dist_id Primary distribution identifier - * @param params Distribution parameters - * @param min Minimum value - * @param max Maximum value - * - * @return Log PDF of the primary distribution - * - * @code - * // Example: Uniform distribution - * real x = 0.5; - * int primary_dist_id = 1; // Uniform - * array[0] real params = {}; // No additional parameters for uniform - * real min = 0; - * real max = 1; - * real log_pdf = primary_dist_lpdf(x, primary_dist_id, params, min, max); - * @endcode - */ -real primary_dist_lpdf(real x, int primary_dist_id, array[] real params, real min, real max) { - // Implement switch for different primary distributions - if (primary_dist_id == 1) return uniform_lpdf(x | min, max); - if (primary_dist_id == 2) return expgrowth_lpdf(x | min, max, params[1]); - // Add more primary distributions as needed - reject("Invalid primary distribution identifier"); -} - -/** - * ODE system for the primary censored distribution - * - * @param t Time - * @param y State variables - * @param theta Parameters - * @param x_r Real data - * @param x_i Integer data - * - * @return Derivatives of the state variables - */ -vector primary_censored_ode(real t, vector y, array[] real theta, - array[] real x_r, array[] int x_i) { - real d = x_r[1]; - int dist_id = x_i[1]; - int primary_dist_id = x_i[2]; - real pwindow = x_r[2]; - int dist_params_len = x_i[3]; - int primary_params_len = x_i[4]; - - // Extract distribution parameters - array[dist_params_len] real params; - if (dist_params_len) { - params = theta[1:dist_params_len]; - } - array[primary_params_len] real primary_params; - if (primary_params_len) { - int primary_loc = size(theta); - primary_params = theta[primary_loc - primary_params_len + 1:primary_loc]; - } - - real log_cdf = dist_lcdf(t | params, dist_id); - real log_primary_pdf = primary_dist_lpdf(d - t | primary_dist_id, primary_params, 0, pwindow); - - return rep_vector(exp(log_cdf + log_primary_pdf), 1); -} - /** * Compute the primary event censored CDF for a single delay * @@ -122,14 +10,14 @@ vector primary_censored_ode(real t, vector y, array[] real theta, * @param params Array of distribution parameters * @param pwindow Primary event window * @param D Maximum delay (truncation point) - * @param primary_dist_id Primary distribution identifier + * @param primray_id Primary distribution identifier * @param primary_params Primary distribution parameters * * @return Primary event censored CDF, normalized by D if finite (truncation adjustment) */ -real primary_censored_dist_cdf(data real d, int dist_id, array[] real params, +real primarycensored_cdf(data real d, int dist_id, array[] real params, data real pwindow, data real D, - int primary_dist_id, + int primray_id, array[] real primary_params) { real result; if (d <= 0) { @@ -141,23 +29,23 @@ real primary_censored_dist_cdf(data real d, int dist_id, array[] real params, } // Check if an analytical solution exists - if (check_for_analytical(dist_id, primary_dist_id)) { + if (check_for_analytical(dist_id, primray_id)) { // Use analytical solution - result = primary_censored_dist_analytical_cdf( - d | dist_id, params, pwindow, D, primary_dist_id, primary_params + result = primarycensored_analytical_cdf( + d | dist_id, params, pwindow, D, primray_id, primary_params ); } else { // Use numerical integration for other cases real lower_bound = max({d - pwindow, 1e-6}); array[size(params) + size(primary_params)] real theta = append_array(params, primary_params); - array[4] int ids = {dist_id, primary_dist_id, size(params), size(primary_params)}; + array[4] int ids = {dist_id, primray_id, size(params), size(primary_params)}; vector[1] y0 = rep_vector(0.0, 1); - result = ode_rk45(primary_censored_ode, y0, lower_bound, {d}, theta, {d, pwindow}, ids)[1, 1]; + result = ode_rk45(primarycensored_ode, y0, lower_bound, {d}, theta, {d, pwindow}, ids)[1, 1]; if (!is_inf(D)) { - real log_cdf_D = primary_censored_dist_lcdf( - D | dist_id, params, pwindow, positive_infinity(), primary_dist_id,primary_params + real log_cdf_D = primarycensored_lcdf( + D | dist_id, params, pwindow, positive_infinity(), primray_id,primary_params ); result = exp(log(result) - log_cdf_D); } @@ -174,7 +62,7 @@ real primary_censored_dist_cdf(data real d, int dist_id, array[] real params, * @param params Array of distribution parameters * @param pwindow Primary event window * @param D Maximum delay (truncation point) - * @param primary_dist_id Primary distribution identifier + * @param primray_id Primary distribution identifier * @param primary_params Primary distribution parameters * * @return Primary event censored log CDF, normalized by D if finite (truncation adjustment) @@ -186,16 +74,16 @@ real primary_censored_dist_cdf(data real d, int dist_id, array[] real params, * array[2] real params = {2.0, 1.5}; // shape and scale * real pwindow = 1.0; * real D = positive_infinity(); - * int primary_dist_id = 1; // Uniform + * int primray_id = 1; // Uniform * array[0] real primary_params = {}; - * real log_cdf = primary_censored_dist_lcdf( - * d, dist_id, params, pwindow, D, primary_dist_id, primary_params + * real log_cdf = primarycensored_lcdf( + * d, dist_id, params, pwindow, D, primray_id, primary_params * ); * @endcode */ -real primary_censored_dist_lcdf(data real d, int dist_id, array[] real params, +real primarycensored_lcdf(data real d, int dist_id, array[] real params, data real pwindow, data real D, - int primary_dist_id, + int primray_id, array[] real primary_params) { real result; @@ -208,21 +96,21 @@ real primary_censored_dist_lcdf(data real d, int dist_id, array[] real params, } // Check if an analytical solution exists - if (check_for_analytical(dist_id, primary_dist_id)) { - result = primary_censored_dist_analytical_lcdf( - d | dist_id, params, pwindow, positive_infinity(), primary_dist_id, primary_params + if (check_for_analytical(dist_id, primray_id)) { + result = primarycensored_analytical_lcdf( + d | dist_id, params, pwindow, positive_infinity(), primray_id, primary_params ); } else { // Use numerical integration - result = log(primary_censored_dist_cdf( - d | dist_id, params, pwindow, positive_infinity(), primary_dist_id, primary_params + result = log(primarycensored_cdf( + d | dist_id, params, pwindow, positive_infinity(), primray_id, primary_params )); } // Handle truncation if (!is_inf(D)) { - real log_cdf_D = primary_censored_dist_lcdf( - D | dist_id, params, pwindow, positive_infinity(), primary_dist_id, primary_params + real log_cdf_D = primarycensored_lcdf( + D | dist_id, params, pwindow, positive_infinity(), primray_id, primary_params ); result = result - log_cdf_D; } @@ -239,7 +127,7 @@ real primary_censored_dist_lcdf(data real d, int dist_id, array[] real params, * @param pwindow Primary event window * @param d_upper Upper bound for the delay interval * @param D Maximum delay (truncation point) - * @param primary_dist_id Primary distribution identifier + * @param primray_id Primary distribution identifier * @param primary_params Primary distribution parameters * * @return Primary event censored log PMF, normalized by D if finite (truncation adjustment) @@ -252,16 +140,16 @@ real primary_censored_dist_lcdf(data real d, int dist_id, array[] real params, * real pwindow = 1.0; * real d_upper = 4.0; * real D = positive_infinity(); - * int primary_dist_id = 1; // Uniform + * int primray_id = 1; // Uniform * array[0] real primary_params = {}; - * real log_pmf = primary_censored_dist_lpmf( - * d, dist_id, params, pwindow, d_upper, D, primary_dist_id, primary_params + * real log_pmf = primarycensored_lpmf( + * d, dist_id, params, pwindow, d_upper, D, primray_id, primary_params * ); * @endcode */ -real primary_censored_dist_lpmf(data int d, int dist_id, array[] real params, +real primarycensored_lpmf(data int d, int dist_id, array[] real params, data real pwindow, data real d_upper, - data real D, int primary_dist_id, + data real D, int primray_id, array[] real primary_params) { if (d_upper > D) { reject("Upper truncation point is greater than D. It is ", d_upper, @@ -271,11 +159,11 @@ real primary_censored_dist_lpmf(data int d, int dist_id, array[] real params, reject("Upper truncation point is less than or equal to d. It is ", d_upper, " and d is ", d, ". Resolve this by increasing d to be less than d_upper."); } - real log_cdf_upper = primary_censored_dist_lcdf( - d_upper | dist_id, params, pwindow, positive_infinity(), primary_dist_id, primary_params + real log_cdf_upper = primarycensored_lcdf( + d_upper | dist_id, params, pwindow, positive_infinity(), primray_id, primary_params ); - real log_cdf_lower = primary_censored_dist_lcdf( - d | dist_id, params, pwindow, positive_infinity(), primary_dist_id, primary_params + real log_cdf_lower = primarycensored_lcdf( + d | dist_id, params, pwindow, positive_infinity(), primray_id, primary_params ); if (!is_inf(D)) { real log_cdf_D; @@ -283,8 +171,8 @@ real primary_censored_dist_lpmf(data int d, int dist_id, array[] real params, if (d_upper == D) { log_cdf_D = log_cdf_upper; } else { - log_cdf_D = primary_censored_dist_lcdf( - D | dist_id, params, pwindow, positive_infinity(), primary_dist_id, primary_params + log_cdf_D = primarycensored_lcdf( + D | dist_id, params, pwindow, positive_infinity(), primray_id, primary_params ); } return log_diff_exp(log_cdf_upper, log_cdf_lower) - log_cdf_D; @@ -302,7 +190,7 @@ real primary_censored_dist_lpmf(data int d, int dist_id, array[] real params, * @param pwindow Primary event window * @param d_upper Upper bound for the delay interval * @param D Maximum delay (truncation point) - * @param primary_dist_id Primary distribution identifier + * @param primray_id Primary distribution identifier * @param primary_params Primary distribution parameters * * @return Primary event censored PMF, normalized by D if finite (truncation adjustment) @@ -316,18 +204,18 @@ real primary_censored_dist_lpmf(data int d, int dist_id, array[] real params, * real pwindow = 1.0; * real swindow = 0.1; * real D = positive_infinity(); - * int primary_dist_id = 1; // Uniform + * int primray_id = 1; // Uniform * array[0] real primary_params = {}; - * real pmf = primary_censored_dist_pmf(d, dist_id, params, pwindow, swindow, D, primary_dist_id, primary_params); + * real pmf = primarycensored_pmf(d, dist_id, params, pwindow, swindow, D, primray_id, primary_params); * @endcode */ -real primary_censored_dist_pmf(data int d, int dist_id, array[] real params, +real primarycensored_pmf(data int d, int dist_id, array[] real params, data real pwindow, data real d_upper, - data real D, int primary_dist_id, + data real D, int primray_id, array[] real primary_params) { return exp( - primary_censored_dist_lpmf( - d | dist_id, params, pwindow, d_upper, D, primary_dist_id, primary_params + primarycensored_lpmf( + d | dist_id, params, pwindow, d_upper, D, primray_id, primary_params ) ); } @@ -340,13 +228,13 @@ real primary_censored_dist_pmf(data int d, int dist_id, array[] real params, * @param dist_id Distribution identifier * @param params Array of distribution parameters * @param pwindow Primary event window - * @param primary_dist_id Primary distribution identifier + * @param primray_id Primary distribution identifier * @param primary_params Primary distribution parameters * * @return Vector of primary event censored log PMFs for delays \[0, 1\] to * \[max_delay, max_delay + 1\]. * - * This function differs from primary_censored_dist_lpmf in that it: + * This function differs from primarycensored_lpmf in that it: * 1. Computes PMFs for all integer delays from \[0, 1\] to \[max_delay, * max_delay + 1\] in one call. * 2. Assumes integer delays (swindow = 1) @@ -360,20 +248,20 @@ real primary_censored_dist_pmf(data int d, int dist_id, array[] real params, * int dist_id = 5; // Weibull * array[2] real params = {2.0, 1.5}; // shape and scale * real pwindow = 7.0; - * int primary_dist_id = 1; // Uniform + * int primray_id = 1; // Uniform * array[0] real primary_params = {}; * vector[max_delay] log_pmf = - * primary_censored_sone_lpmf_vectorized( - * max_delay, D, dist_id, params, pwindow, primary_dist_id, + * primarycensored_sone_lpmf_vectorized( + * max_delay, D, dist_id, params, pwindow, primray_id, * primary_params * ); * @endcode */ -vector primary_censored_sone_lpmf_vectorized( +vector primarycensored_sone_lpmf_vectorized( int max_delay, data real D, int dist_id, array[] real params, data real pwindow, - int primary_dist_id, array[] real primary_params + int primray_id, array[] real primary_params ) { int upper_interval = max_delay + 1; @@ -388,8 +276,8 @@ vector primary_censored_sone_lpmf_vectorized( // Compute log CDFs for (d in 1:upper_interval) { - log_cdfs[d] = primary_censored_dist_lcdf( - d | dist_id, params, pwindow, positive_infinity(), primary_dist_id, + log_cdfs[d] = primarycensored_lcdf( + d | dist_id, params, pwindow, positive_infinity(), primray_id, primary_params ); } @@ -399,9 +287,9 @@ vector primary_censored_sone_lpmf_vectorized( if (is_inf(D)) { log_normalizer = 0; // No normalization needed for infinite D } else { - log_normalizer = primary_censored_dist_lcdf( + log_normalizer = primarycensored_lcdf( D | dist_id, params, pwindow, positive_infinity(), - primary_dist_id, primary_params + primray_id, primary_params ); } } else { @@ -425,13 +313,13 @@ vector primary_censored_sone_lpmf_vectorized( * @param dist_id Distribution identifier * @param params Array of distribution parameters * @param pwindow Primary event window - * @param primary_dist_id Primary distribution identifier + * @param primray_id Primary distribution identifier * @param primary_params Primary distribution parameters * * @return Vector of primary event censored PMFs for integer delays 1 to * max_delay * - * This function differs from primary_censored_dist_pmf in that it: + * This function differs from primarycensored_pmf in that it: * 1. Computes PMFs for all integer delays from \[0, 1\] to \[max_delay, * max_delay + 1\] in one call. * 2. Assumes integer delays (swindow = 1) @@ -444,23 +332,23 @@ vector primary_censored_sone_lpmf_vectorized( * int dist_id = 5; // Weibull * array[2] real params = {2.0, 1.5}; // shape and scale * real pwindow = 7.0; - * int primary_dist_id = 1; // Uniform + * int primray_id = 1; // Uniform * array[0] real primary_params = {}; * vector[max_delay] pmf = - * primary_censored_sone_pmf_vectorized( - * max_delay, D, dist_id, params, pwindow, primary_dist_id, primary_params + * primarycensored_sone_lpmf_vectorized( + * max_delay, D, dist_id, params, pwindow, primray_id, primary_params * ); * @endcode */ -vector primary_censored_sone_pmf_vectorized( +vector primarycensored_sone_pmf_vectorized( int max_delay, data real D, int dist_id, array[] real params, data real pwindow, - int primary_dist_id, + int primray_id, array[] real primary_params ) { return exp( - primary_censored_sone_lpmf_vectorized( - max_delay, D, dist_id, params, pwindow, primary_dist_id, primary_params + primarycensored_sone_lpmf_vectorized( + max_delay, D, dist_id, params, pwindow, primray_id, primary_params ) ); } diff --git a/inst/stan/functions/primary_censored_dist_analytical_cdf.stan b/inst/stan/functions/primarycensored_analytical_cdf.stan similarity index 80% rename from inst/stan/functions/primary_censored_dist_analytical_cdf.stan rename to inst/stan/functions/primarycensored_analytical_cdf.stan index ae96298..83672b0 100644 --- a/inst/stan/functions/primary_censored_dist_analytical_cdf.stan +++ b/inst/stan/functions/primarycensored_analytical_cdf.stan @@ -3,14 +3,14 @@ * Check if an analytical solution exists for the given distribution combination * * @param dist_id Distribution identifier for the delay distribution - * @param primary_dist_id Distribution identifier for the primary distribution + * @param primray_id Distribution identifier for the primary distribution * * @return 1 if an analytical solution exists, 0 otherwise */ -int check_for_analytical(int dist_id, int primary_dist_id) { - if (dist_id == 2 && primary_dist_id == 1) return 1; // Gamma delay with Uniform primary - if (dist_id == 1 && primary_dist_id == 1) return 1; // Lognormal delay with Uniform primary - if (dist_id == 3 && primary_dist_id == 1) return 1; // Weibull delay with Uniform primary +int check_for_analytical(int dist_id, int primray_id) { + if (dist_id == 2 && primray_id == 1) return 1; // Gamma delay with Uniform primary + if (dist_id == 1 && primray_id == 1) return 1; // Lognormal delay with Uniform primary + if (dist_id == 3 && primray_id == 1) return 1; // Weibull delay with Uniform primary return 0; // No analytical solution for other combinations } @@ -25,7 +25,7 @@ int check_for_analytical(int dist_id, int primary_dist_id) { * @return Log of the primary event censored CDF for Gamma delay with Uniform * primary */ -real primary_censored_gamma_uniform_lcdf(data real d, real q, array[] real params, data real pwindow) { +real primarycensored_gamma_uniform_lcdf(data real d, real q, array[] real params, data real pwindow) { real shape = params[1]; real rate = params[2]; real shape_1 = shape + 1; @@ -80,7 +80,7 @@ real primary_censored_gamma_uniform_lcdf(data real d, real q, array[] real param * @return Log of the primary event censored CDF for Lognormal delay with * Uniform primary */ -real primary_censored_lognormal_uniform_lcdf(data real d, real q, array[] real params, data real pwindow) { +real primarycensored_lognormal_uniform_lcdf(data real d, real q, array[] real params, data real pwindow) { real mu = params[1]; real sigma = params[2]; real mu_sigma2 = mu + square(sigma); @@ -156,7 +156,7 @@ real log_weibull_g(real t, real shape, real scale) { * @return Log of the primary event censored CDF for Weibull delay with * Uniform primary */ -real primary_censored_weibull_uniform_lcdf(data real d, real q, array[] real params, data real pwindow) { +real primarycensored_weibull_uniform_lcdf(data real d, real q, array[] real params, data real pwindow) { real shape = params[1]; real scale = params[2]; real log_window = log(pwindow); @@ -207,15 +207,15 @@ real primary_censored_weibull_uniform_lcdf(data real d, real q, array[] real par * @param params Array of distribution parameters * @param pwindow Primary event window * @param D Maximum delay (truncation point) - * @param primary_dist_id Primary distribution identifier + * @param primray_id Primary distribution identifier * @param primary_params Primary distribution parameters * * @return Primary event censored log CDF, normalized by D if finite (truncation adjustment) */ -real primary_censored_dist_analytical_lcdf(data real d, int dist_id, +real primarycensored_analytical_lcdf(data real d, int dist_id, array[] real params, data real pwindow, data real D, - int primary_dist_id, + int primray_id, array[] real primary_params) { real result; real log_cdf_D; @@ -225,24 +225,24 @@ real primary_censored_dist_analytical_lcdf(data real d, int dist_id, real q = max({d - pwindow, 0}); - if (dist_id == 2 && primary_dist_id == 1) { + if (dist_id == 2 && primray_id == 1) { // Gamma delay with Uniform primary - result = primary_censored_gamma_uniform_lcdf(d | q, params, pwindow); - } else if (dist_id == 1 && primary_dist_id == 1) { + result = primarycensored_gamma_uniform_lcdf(d | q, params, pwindow); + } else if (dist_id == 1 && primray_id == 1) { // Lognormal delay with Uniform primary - result = primary_censored_lognormal_uniform_lcdf(d | q, params, pwindow); - } else if (dist_id == 3 && primary_dist_id == 1) { + result = primarycensored_lognormal_uniform_lcdf(d | q, params, pwindow); + } else if (dist_id == 3 && primray_id == 1) { // Weibull delay with Uniform primary - result = primary_censored_weibull_uniform_lcdf(d | q, params, pwindow); + result = primarycensored_weibull_uniform_lcdf(d | q, params, pwindow); } else { // No analytical solution available return negative_infinity(); } if (!is_inf(D)) { - log_cdf_D = primary_censored_dist_lcdf( + log_cdf_D = primarycensored_lcdf( D | dist_id, params, pwindow, positive_infinity(), - primary_dist_id, primary_params + primray_id, primary_params ); result = result - log_cdf_D; } @@ -258,15 +258,15 @@ real primary_censored_dist_analytical_lcdf(data real d, int dist_id, * @param params Array of distribution parameters * @param pwindow Primary event window * @param D Maximum delay (truncation point) - * @param primary_dist_id Primary distribution identifier + * @param primray_id Primary distribution identifier * @param primary_params Primary distribution parameters * * @return Primary event censored CDF, normalized by D if finite (truncation adjustment) */ -real primary_censored_dist_analytical_cdf(data real d, int dist_id, +real primarycensored_analytical_cdf(data real d, int dist_id, array[] real params, data real pwindow, data real D, - int primary_dist_id, + int primray_id, array[] real primary_params) { - return exp(primary_censored_dist_analytical_lcdf(d | dist_id, params, pwindow, D, primary_dist_id, primary_params)); + return exp(primarycensored_analytical_lcdf(d | dist_id, params, pwindow, D, primray_id, primary_params)); } diff --git a/inst/stan/functions/primarycensored_ode.stan b/inst/stan/functions/primarycensored_ode.stan new file mode 100644 index 0000000..b40ddba --- /dev/null +++ b/inst/stan/functions/primarycensored_ode.stan @@ -0,0 +1,111 @@ +/** + * Compute the log CDF of the delay distribution + * + * @param delay Time delay + * @param params Distribution parameters + * @param dist_id Distribution identifier + * 1: Lognormal, 2: Gamma, 3: Normal, 4: Exponential, 5: Weibull, + * 6: Beta, 7: Cauchy, 8: Chi-square, 9: Inverse Chi-square, + * 10: Double Exponential, 11: Inverse Gamma, 12: Logistic, + * 13: Pareto, 14: Scaled Inverse Chi-square, 15: Student's t, + * 16: Uniform, 17: von Mises + * + * @return Log CDF of the delay distribution + * + * @code + * // Example: Lognormal distribution + * real delay = 5.0; + * array[2] real params = {0.0, 1.0}; // mean and standard deviation on log scale + * int dist_id = 1; // Lognormal + * real log_cdf = dist_lcdf(delay, params, dist_id); + * @endcode + */ +real dist_lcdf(real delay, array[] real params, int dist_id) { + if (delay <= 0) return negative_infinity(); + + // Use if-else statements to handle different distribution types + if (dist_id == 1) return lognormal_lcdf(delay | params[1], params[2]); + else if (dist_id == 2) return gamma_lcdf(delay | params[1], params[2]); + else if (dist_id == 3) return normal_lcdf(delay | params[1], params[2]); + else if (dist_id == 4) return exponential_lcdf(delay | params[1]); + else if (dist_id == 5) return weibull_lcdf(delay | params[1], params[2]); + else if (dist_id == 6) return beta_lcdf(delay | params[1], params[2]); + else if (dist_id == 7) return cauchy_lcdf(delay | params[1], params[2]); + else if (dist_id == 8) return chi_square_lcdf(delay | params[1]); + else if (dist_id == 9) return inv_chi_square_lcdf(delay | params[1]); + else if (dist_id == 10) return double_exponential_lcdf(delay | params[1], params[2]); + else if (dist_id == 11) return inv_gamma_lcdf(delay | params[1], params[2]); + else if (dist_id == 12) return logistic_lcdf(delay | params[1], params[2]); + else if (dist_id == 13) return pareto_lcdf(delay | params[1], params[2]); + else if (dist_id == 14) return scaled_inv_chi_square_lcdf(delay | params[1], params[2]); + else if (dist_id == 15) return student_t_lcdf(delay | params[1], params[2], params[3]); + else if (dist_id == 16) return uniform_lcdf(delay | params[1], params[2]); + else if (dist_id == 17) return von_mises_lcdf(delay | params[1], params[2]); + else reject("Invalid distribution identifier"); +} + +/** + * Compute the log PDF of the primary distribution + * + * @param x Value + * @param primray_id Primary distribution identifier + * @param params Distribution parameters + * @param min Minimum value + * @param max Maximum value + * + * @return Log PDF of the primary distribution + * + * @code + * // Example: Uniform distribution + * real x = 0.5; + * int primray_id = 1; // Uniform + * array[0] real params = {}; // No additional parameters for uniform + * real min = 0; + * real max = 1; + * real log_pdf = primray_lpdf(x, primray_id, params, min, max); + * @endcode + */ +real primray_lpdf(real x, int primray_id, array[] real params, real min, real max) { + // Implement switch for different primary distributions + if (primray_id == 1) return uniform_lpdf(x | min, max); + if (primray_id == 2) return expgrowth_lpdf(x | min, max, params[1]); + // Add more primary distributions as needed + reject("Invalid primary distribution identifier"); +} + +/** + * ODE system for the primary censored distribution + * + * @param t Time + * @param y State variables + * @param theta Parameters + * @param x_r Real data + * @param x_i Integer data + * + * @return Derivatives of the state variables + */ +vector primarycensored_ode(real t, vector y, array[] real theta, + array[] real x_r, array[] int x_i) { + real d = x_r[1]; + int dist_id = x_i[1]; + int primray_id = x_i[2]; + real pwindow = x_r[2]; + int dist_params_len = x_i[3]; + int primary_params_len = x_i[4]; + + // Extract distribution parameters + array[dist_params_len] real params; + if (dist_params_len) { + params = theta[1:dist_params_len]; + } + array[primary_params_len] real primary_params; + if (primary_params_len) { + int primary_loc = size(theta); + primary_params = theta[primary_loc - primary_params_len + 1:primary_loc]; + } + + real log_cdf = dist_lcdf(t | params, dist_id); + real log_primary_pdf = primray_lpdf(d - t | primray_id, primary_params, 0, pwindow); + + return rep_vector(exp(log_cdf + log_primary_pdf), 1); +} diff --git a/inst/stan/pcens_model.stan b/inst/stan/pcens_model.stan index 8083098..0884932 100644 --- a/inst/stan/pcens_model.stan +++ b/inst/stan/pcens_model.stan @@ -1,19 +1,20 @@ functions { - #include primary_censored_dist.stan - #include primary_censored_dist_analytical_cdf.stan + #include primarycensored.stan + #include primarycensored_ode.stan + #include primarycensored_analytical_cdf.stan #include expgrowth.stan real partial_sum(array[] int dummy, int start, int end, array[] int d, array[] int d_upper, array[] int n, array[] int pwindow, array[] int D, int dist_id, array[] real params, - int primary_dist_id, array[] real primary_params) { + int primray_id, array[] real primary_params) { real partial_target = 0; for (i in start:end) { - partial_target += n[i] * primary_censored_dist_lpmf( + partial_target += n[i] * primarycensored_lpmf( d[i] | dist_id, params, pwindow[i], d_upper[i], D[i], - primary_dist_id, primary_params + primray_id, primary_params ); } return partial_target; @@ -28,7 +29,7 @@ data { array[N] int pwindow; // primary censoring window array[N] int D; // maximum delay int dist_id; // distribution identifier - int primary_dist_id; // primary distribution identifier + int primray_id; // primary distribution identifier int n_params; // number of distribution parameters int n_primary_params; // number of primary distribution parameters int compute_log_lik; // whether to compute log likelihood @@ -71,13 +72,13 @@ model { if (use_reduce_sum) { target += reduce_sum(partial_sum, indexes, 1, d, d_upper, n, pwindow, D, dist_id, to_array_1d(params), - primary_dist_id, to_array_1d(primary_params)); + primray_id, to_array_1d(primary_params)); } else { for (i in 1:N) { - target += n[i] * primary_censored_dist_lpmf( + target += n[i] * primarycensored_lpmf( d[i] | dist_id, to_array_1d(params), pwindow[i], d_upper[i], D[i], - primary_dist_id, to_array_1d(primary_params) + primray_id, to_array_1d(primary_params) ); } } @@ -87,10 +88,10 @@ generated quantities { vector[compute_log_lik ? N : 0] log_lik; if (compute_log_lik) { for (i in 1:N) { - log_lik[i] = primary_censored_dist_lpmf( + log_lik[i] = primarycensored_lpmf( d[i] | dist_id, to_array_1d(params), pwindow[i], d_upper[i], D[i], - primary_dist_id, to_array_1d(primary_params) + primray_id, to_array_1d(primary_params) ); } } diff --git a/man/dot-dpcens.Rd b/man/dot-dpcens.Rd index 135baaf..6d7ec6a 100644 --- a/man/dot-dpcens.Rd +++ b/man/dot-dpcens.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/fitdistdoublecens.R \name{.dpcens} \alias{.dpcens} -\title{Define a fitdistrplus compatible wrapper around dprimarycensoreddist} +\title{Define a fitdistrplus compatible wrapper around dprimarycensored} \usage{ .dpcens( x, @@ -58,6 +58,6 @@ passed a pre-assigned variable rather than a function name.} \item{...}{Additional arguments to be passed to the distribution function} } \description{ -Define a fitdistrplus compatible wrapper around dprimarycensoreddist +Define a fitdistrplus compatible wrapper around dprimarycensored } \keyword{internal} diff --git a/man/dot-ppcens.Rd b/man/dot-ppcens.Rd index 65f5428..61aad72 100644 --- a/man/dot-ppcens.Rd +++ b/man/dot-ppcens.Rd @@ -2,7 +2,7 @@ % Please edit documentation in R/fitdistdoublecens.R \name{.ppcens} \alias{.ppcens} -\title{Define a fitdistrplus compatible wrapper around pprimarycensoreddist} +\title{Define a fitdistrplus compatible wrapper around pprimarycensored} \usage{ .ppcens( q, @@ -54,6 +54,6 @@ passed a pre-assigned variable rather than a function name.} \item{...}{Additional arguments to be passed to pdist} } \description{ -Define a fitdistrplus compatible wrapper around pprimarycensoreddist +Define a fitdistrplus compatible wrapper around pprimarycensored } \keyword{internal} diff --git a/man/dprimarycensoreddist.Rd b/man/dprimarycensored.Rd similarity index 90% rename from man/dprimarycensoreddist.Rd rename to man/dprimarycensored.Rd index 0608eb2..ac2c8db 100644 --- a/man/dprimarycensoreddist.Rd +++ b/man/dprimarycensored.Rd @@ -1,11 +1,11 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/dprimarycensoreddist.R -\name{dprimarycensoreddist} -\alias{dprimarycensoreddist} +% Please edit documentation in R/dprimarycensored.R +\name{dprimarycensored} +\alias{dprimarycensored} \alias{dpcens} \title{Compute the primary event censored PMF for delays} \usage{ -dprimarycensoreddist( +dprimarycensored( x, pdist, pwindow = 1, @@ -96,7 +96,7 @@ f_{\text{cens}}(d) = F_{\text{cens}}(d + \text{swindow}) - F_{\text{cens}}(d) where \eqn{F_{\text{cens}}} is the primary event censored CDF. The function first computes the CDFs for all unique points (including both -\eqn{d} and \eqn{d + \text{swindow}}) using \code{\link[=pprimarycensoreddist]{pprimarycensoreddist()}}. It then +\eqn{d} and \eqn{d + \text{swindow}}) using \code{\link[=pprimarycensored]{pprimarycensored()}}. It then creates a lookup table for these CDFs to efficiently calculate the PMF for each input value. For non-positive delays, the function returns 0. @@ -110,14 +110,14 @@ f_{\text{cens,norm}}(d) = \frac{f_{\text{cens}}(d)}{\sum_{i=0}^{D-1} where \eqn{f_{\text{cens,norm}}(d)} is the normalized PMF and \eqn{f_{\text{cens}}(d)} is the unnormalized PMF. For the explanation and mathematical details of the CDF, refer to the documentation of -\code{\link[=pprimarycensoreddist]{pprimarycensoreddist()}}. +\code{\link[=pprimarycensored]{pprimarycensored()}}. } \examples{ # Example: Weibull distribution with uniform primary events -dprimarycensoreddist(c(0.1, 0.5, 1), pweibull, shape = 1.5, scale = 2.0) +dprimarycensored(c(0.1, 0.5, 1), pweibull, shape = 1.5, scale = 2.0) # Example: Weibull distribution with exponential growth primary events -dprimarycensoreddist( +dprimarycensored( c(0.1, 0.5, 1), pweibull, dprimary = dexpgrowth, dprimary_args = list(r = 0.2), shape = 1.5, scale = 2.0 @@ -125,7 +125,7 @@ dprimarycensoreddist( } \seealso{ Primary event censored distribution functions -\code{\link{pprimarycensoreddist}()}, -\code{\link{rprimarycensoreddist}()} +\code{\link{pprimarycensored}()}, +\code{\link{rprimarycensored}()} } -\concept{primarycensoreddist} +\concept{rpd_primarycensored} diff --git a/man/figures/logo.png b/man/figures/logo.png index 2e233d4750c140f06f5d9d2f658f141fc2f40501..7999c8ef00589d29b4804750c30a2ff952ab4af9 100644 GIT binary patch literal 31117 zcmb?@Ra9L|wB^Cw-JRg>?k)-L?i$?PAy|N5!QI`0J3)iHTW|}(p*Q!oydM2Vzy9cq zLHK|=d)Jb=)|{(~sE5jmgDH^YdD_Q zE7sB$hg;~;G_z1aeti~`t?M;Hmx;t6?Hz(vfQ5qt>r1602tlA0>46mUNNo3RnfF=u z4hXP zu)zXZ=zUU=szd3>_$gtz8wL4PZ(pF*mzaQ)CeN~IA!^8P$mGRpuxqLvpO-UH#prZ- z$**}B5F>|5%e1xqiKL$8!0&pz2zy0daC9me+Su52KM|vO;bKEem`-GHW`t@dIVOlz zLZNa(%GW}8DwXs*!eg5)Ad3p2#ge}Nl1$&X2fnjz&hQ)G6-pkt*QU;TyCeRwIf&g5(YL+gWY`sMWOS29-M#z zx+L13U#6uzXEOMj&fj+;DiP8HY>4Bl%D~x@Yf8crh}5`H^NO6lz}m7=I|i83(;heENIW%R)jYRi(*A%4m2cI;I#`Rj{jQ>bm;Z;l;YFE!0 z+a4d$&GRHUD1jIBs=X(2)PrvK1n)6ZPd7H$XytdKW{UYfcY>VTQGgVGNYqn}Uu2K2 zpfWoA<4du01#^3wzBvn8S$|*P2n1Evjd%G(@iJ*(SUFoKht66?Lb&eEtnr-9y7z?U z?YvfMW^7+Gwt58^XgD;vn{LD7k9wtc!FeMj8Z8hOBO#O)=1?nNcDThxuo8Y%jCKk* zqiYn8i-b;>Y}Pc%MonNzdKPb?W-!VfvOcQ7R?-+GKQxVgB*!NNY?e(*FQ2)^=#Zi! z1Wu};Zs#q6dM=+6rY-8{uH`P@F!-e#i&#p-i_A`CTM$v~dCa0^!?0`baram($}0EW zH?^TenFsRiYp_P&%EwVdaQqY5h++zM@~~{)#t~L^F0~xEx+-fl@K%v&iA65MV3_0T)z;j!90Of%Bj*<<3(Gv3-Kp>}V&uC3M$~+h z^kc&flG4A7o65hu%9sw1=2x(xlp|8*&zc&GaOf3cUadBKlc!yP3&K^ei|PVv{oqGE znT?u9SHF2YuJ6hBhT2R_ufZFVPXs=NZ{)|~IF00XY!X$<41|BU=+44~rHpZa3ix5^ z=%2+CC(9WQh8WpUr2;eZ0a5;$wMe(Nr-XI28n$LQu~&z_tK#nSwll7nQ6qBaAt3f) z2fLH7z%rjHNF9RmySZagwXl;S`M;Ks%Ul$ZtI)!2b&fg_q~5N0r%Vn;o;UHUuLJfaoz)KTjt!*2?-8WcDVao+w^CX3-x*#p{{JgG1f^ zLr4yNDGw{7JqDX`LK9m~VjXZ>8D&xAW|tRh=o%=BQoU|^qpS6UJv9?-46$24qq;tk zC)*q7RbRWBNv(&xXsPt9`-{bce z8XL{U4Q)9!STfC2hEdXHri^L1Vf1w$buZ(xgZXndZM zm=nLAmyXWC2N9U4Ud|5CUM`~O^Y7J3f#0_t;4U1V57$k)yY+&H^~JTv z;4rUFUcpGj0T1`1w|R0W2|4Y6>G1>ks_jqa;6|wfnR-!SXR+rrY}T@!GUNMP@SBFu zUT&2?z$oINx_(d|r2_EU1h8C|7QNY$F$pyuEK(hd;Nm16Sj!qTEu(zW5?-PBU!<0P|1^P>jR5n6D7X z_Ci45z_#I#2mOxlWNuMZuw)&M8+ERHWszUizaliO9Hq3Z*!@-C*JNf^!;{-P(!%|9 zPg*3i*7YFAzqbogQ5%%P_p7A?QWQCUn`9Q2e`7iB!!^oQNI85;orQ?NHWgC*9+UP= zIa}I@s*|CVsli&@;s=Dx12|Z{V!GMV`JG|7BqtX=x{k(zc3|rbC3V=gF7=6v<&B*# zSb>JAr-Ik$3zeU|b%IA`Yguwx>D`Cvd-2QOh$#J$fsNxx4Q;2E&7yIIuK*k8qP_ZC zXY$PZhMK`}e8Bp($6;wOdZyx=Uo}JWNdNot!(jb(S1QJ)HxzIbu(Vt{4;>>|jS@>E zsoZ)Qe3L3{gltB>;Kk8vV^!`>Hc&_Ttn~%G*5|636Ksj>z%Z~_S|{Dj){j;LQZfpf zDR{@`1JQGMTBv9UNF}VqDRL{ibz~mGLRA%Nc&lWEzH=t%C-iLWDeuwxwKcVXmCSuO zGYY@`j1{y9M5`@FE3Xiia9j>zj?)UVSLH)vH9CRwT|uG#5lm{Uh~E1Q@GF0nJs2@?HbzUJk8A z@B|;Mj##9ld`XK$crEE$&_iD0GX!a>jY5_u-36(;+PCsAO`==Y?X}NccH6PBbRkh{ zJN}6RDt&(!E#=C*(0icD+5FtR*S_&UEe=zQPq~lmg+ol-YNaN{iccWXnVyjCIm+=3 z3MZtO{b__uga=-`fbwD-zDZ|aJn%jMluXzO6G{CG{V*1I9aFr}cn#Rty_i@e6_mg22TeH5{>!*7U0R!jl zDmlj(Qs*!qReTkIC1Imvxf4--EBhxUX_>HuWo062lxpa{$-u#F&92B*PEAN@N6a{d zQlVdG&q${0@^rExQ2|Lw*|+KQb8%_^mY!@t@Sm&{)xy*$njD}c5mrx>5brZ-*=?!^ z7tnQuJktm}%L4>UQ4TV}o$MusLbXRO{v=RCl zSlyq$u@+WxgR*^QI{5LPa}$~NwR(081_9fQ&K^M)s3FO+Ykwc_PCu-Wb>nUKkA zc|q%8EN5#HGg>aBT+8Ob619s^Fb##g6jkTt+9MKaTKPN4o6uTeCVSAW9zJsqJQte5 ze-S~d@u4~y9R*foS@Nyzy~oQAq7*h~r@-(^X39i*qx(5{L&eKd285@o%$j}&CA+3M zO)Su$mc9Y)Er0ztk!U0ky858f^;LiQTsn_1Lo@+(EW*4#(NbCfDnI>LvbkJOd7`J> z$}LKYo}XJ%y%9cl>syh(?8?<6N4x$_AtaMV%2Czqc5^6gD;X0MbhCJ|la-+=5%Jj1 zp~P=eDRoU^BQVkYgEw{;3NJv_TSMT4bZlQdkWRFH;@eO}pJr_N5yHw1bIwtYMmE$o72TeQ$Q<%GnJxZsi1I+eD zM^LSddU2{$j4t}jXL(=zj;2w3I`mjC!v)RVx-~n@=BCmwg4AKHHLlw;n!KHCw}791 zP+j=)_L#6fOIg*TqmO2w>S43O0evOu*RzBu=mBaft`wY|^uB(pPpcab2uP>&(SSN2 z1-Qw90TFg&bY=+8DF*Zk`@h)Np!7*1z3#TL9UZgU$_8Ff%uIe~_n#G8dLTG{ z5A+#*mtV_vtJXK62wW~_6Gt?-mmGCvh>Jet7?Kdrt*ycX`kE_s@z{C=xJI}j>8-_A z%2ob&$2Hq{)u`15ld}X{vO#C{biP<HmvzIAAywPITx&*1Zk1E4q1s=gxk3WDG=YUstciZ~e0pVFwuBTrbD7kV~XyEAI zYSgPJS7Kh6IQa66i}vKcfEf#|3~E(VFK*F@$e~miQoN$QoUu|LM4yITUQ~+!04QO0 zrjV^!O77ug#!uRCAcGUWU1(Ycc>B@8XMTDK+i$jz16iPCqE4WZGnPDd4fUpNFG!3O z2vn4_<2MKvJ|vL{SuSMru8}g5oX#JAD?=>W9_}+r{$aGpr0-@5mhZEw3Fn_(b*^cf}>qb=M*}LW7e6=V@nGsjt3Qt z6t);B&)9IDfaw*{&E`&TMO<0mfAZz)a08%|!@y5NGwil)na06U9ZwgTRwI?c;1Rk+ zlUNWiI=HoN%VTEfkw3@-f~9oy#orSVXySsb91Q`$=ZMxmb2n4R+^GqRn6~-E9iIpi zYOt=&w=+HU5XDV>5j8)`cwVEeMH_-FUG0GGXGU2JckxPa`M91=T{O z6ePS#hVe;Q2Gnj7XDoj1Pk3UdnXa3Qc747Rmd?-f+S5?Wa*4{St-ayR>=lo>qxnv> zviVuTD`wadw#EQR;rLA5j>Me0x7!9Za*buS7l>Pa?L;0a`YB!#<}^rz2qB5oMKVjx zUS*iqtRSaylU?6uC*?Hgk=4$9wyPpGNh`#1>>31m)gBl}4qLOTs_LdOKuZpF)-`@4ANj*mc@ zbbeo*&}c57rf%z_1^a+O^N0u;XlsIg)v#v`%ld9LB*FG%&z5pT8ve8N@TW{h28JVMs;P&ri2LuBB!NalDZN4wG;=`43!c^eBaD zqu#dZE}5~TSw^2G3mGBA7`^2s7OIaaJ5Z#&d|ajgMWbk9U~rrDnKa63fat_-7{d$Z zGUxc6Yk}f@WNU;En6&dLKEn*(bjEX>JuG$)o&nX0`Ac4=vo%Zz)Bw+|fBQBN0$6pVUw1dQdBs zrh3ME;L6FPBE$7627@hQ;2*5KHK7oy)xFn)wQy;$y8ULM5}YXl z@ygjg;D6CdA z7qJg;Qa3V%KUI(qh$d5DE5FuiEsG0-6-Z|Z(}Y2XVS;_Ad);ZYMp`={HZiQ~^7lr^ z?Nah~kq|K$6SbRof(LFImlFOGUyG7nNyq3W7?qK4IUb{X+cw}RFNi=zyMjdXP7S|2F}o#UaZ2_m}2xt5STY8G^A* zfc#O|bMeA=^=Errp<#5ZE>4<^TXE%4CmB0KSTr(I-P|Uu2hb0kbZ0OjkCDnvq9Ub; z{x@#b>!m_*L8tG!uH=?yN`MeD8@YKK{HghIjgj;mCigV|O&g=nNm&Q_>vQtN7X=B9 z<8(W&4z-Mk0BjP;)QMuYM}63dbwHjzC`BX!gquH70JpNgnHL*D0`2_U$8}ocrIFzO zd7qq@Cu(>$_X6+gk?WhzO`%~ zs4-JjBO`t@a`Ki_$Lir9-|YxBmH@rT4Sa{2O8=VI%z{pkkrlg^g-7xqE&c2jEO{?nO^5>@xwXZNSKt_Xm&LXer{@`iQ z(*8}ub&e2nU!|QYz6A$5Drf7D6#@ixXr|b-0q(R&nYwp+mEBH4GEewUuX?<5m;}6e z*c%khFRj=e;&r1gIm=)A20;12Z z*yvOA9g&|~N3#d9N0_LIhDPq6Z`~=V-Jzid>gQOAbz13cMB7p%??L8@59l5il4!3g zpJ6Ak!)|cle4#QeB!8;QcdOv=@qNkZ`&Ua3pe*u)d}mib0d@sQbaRbeV;`Ebhx!3s*DEHN?jBks6;69Lh$lAFx1K~ani@6G6?1}&AEK3>JtHC z)O7Y%4AAkd)azvZ!CJW)A#SKcE21gM^Nm#;&--Q{A}#^RDCI2cEMeyYI(EOsE5@Nz z6^H+NNWnOv)$In)J=Twca#AA&FPD33van2xi#7^qpChH(3K(OxGLvFYsf&iMsAVeC zKhEEeXRkUmlp+$-7Q9;F0A&Z#?m9lORb9PU3f63ha{KRI8LY{>vsuN#J-f>Zf%%I_ z)9Bwx5o8W$^Phbu8>)DG=u-B^2^}sasq-s8RK%X8K%-2LQ%G?AKyu0aMNtDnRD$?l z869ZtC}F69YE4xuUeQ!T_ruvMyZ)Ck*CRXxt>p9Hi5RjyXc@|~=-8{jgM!w00L8Mw z?4kwCh6=DOKu0ItsA5pXp)*2DP>xc%`uNGBc*YtpqQ8_6n)=MsFJryhO9dRKV)W0# z(dBID&sJ8Zl=poPm7Tt8=s$<1|K(VbPci)a2nA)GH>EU)`7L(oM!#14nZr6DqF2Zu zdcKx=J`0%hiFGa^r$<_KG{{E*UG(MQvv{fI@`m<*U|A7y(m2pKrz=JD_GO*=@Xug@ z^&5li5dTW((BY-;4r9$Gg6PRj1@CY^kdw!MT$m0=@_M@3d>?rI09Bs+Io8qiTb?+h3sJvn z-8G$As5HxjCLpcT`iEzcP#115+;e!^y1iqm72$n-7k>P{ML}(qq;_#~X(04n3X|U7 zj1t9~Q`Rui>&aAzO0E}(z*c#4{yljL>h0<|yvX;&CdVhuOkPqmh_r-9DUh`HIh^8j zpNP(PZt3KHUkSS!g&cAuQPS$%L-+tR10>(V#Pn|`K}7!Yud|MfI*<4 zAu+;jb-xd~xCyYb;D^!RF~y?QfePS25OoF9uY)ULzjOXQLDjSmmqn*g3kjf#_w7+VXO{oRAR< z&ZDVboMRBrk5NMIqJr-FeJCw5Qeb5J0J;)T0$weE?35zAGSUk(Zzh?n>Fe_%+6OKs=-_2kp_T zQ0tSDlOJ~jx~M>+2k1gGr2+w~7IY1lwQw!d8=RS}I99r+Mex5^m?SX>h}L^2F+P9P z9C+qf4Rr=IwCTv@RjnAew&f9PYZW|Q#-fgH*Uf31I*WxQT2kEw)%&U{9P*4`_#PmV+uNbHsGIhwJv)J zK=p*}3GJ=yDJzBw-|G6Koi+*d))E{;P1+3iK38ZRDj7AN-sV>s3l5z%m*poguIu@m zVWMlzl`)|`NeO8}2~a=K|2HWe2wtbc!cMIC!?WQJO(}Gy{9pn$*c&JD01KPW!TI5b z8Ml6Akw0XiixO1v)al*HV#RT9?2GS@nS<;|^j?^z?{BsBu@(+33R*kW&Fy$0&l$B_ z8OGL+Qj~RZ3+>mB9cNr$@Kq|ojd1_a`JX&(14A!2==@G#=!K4k7Hb2{y|nkk?ro=y z8+8~)ovAK`VV|OBB**Tu&no9X9%l-98$D0n|+mMh&nz-2!MeDdOEQ6;{V9R z9G-)c6W-=zRvq7|*!n6;8w27_OQ#UZF9E16pYjE28OQhG&349bK#QU#CwCTqr(y@E z{pUkEDRqqC*JL?NS93t$=Q8zyDDD^QYD0dLiU1v{qe_!&6{;3-=9g9U5>kp%~DZk0<_8bHXyw_*OHLtj!16xDsk)B7N%0>q5udrT#NL4D0iFkwFy^R(ds#JkBmAbbDC>|`UFpJXGJz=7u|7hPkPiU-! z(jpQPdth;H&AU+@5ckS%gMsXi`nb4FI?pshM8LQ-B=y^Saf=j~0Brs~A%0#s@b7Lm z4C_q+TCGGr2;+AyAh9=Xr?OLJItYy5Ni!IfZg6i$tNKU*e z0J2|49DKF>#;bBXPO|%=pebU&$;tCS(TF(y)(jw5;sW01pLsVL+9z({yn%$oTx=K^ z8XB4#AU6IVKT_nLp%=;C7Lg@dX=ea|Vx>qqdI+M>f+#vPBqVy`9HT#}%z{@;AXiEC zEXv0Z-h5@5-sG97;GzZw)1aP$%zRKp~cP4 zzO64A>LGxUdgqfYynGe{^_Vp5hMcPSrs#%^hPyXr0-YS3Z~-v}cn2$^JNTqryqJox z1yRRYI?D8lHFRNU&U9de10TV*s1PWSV*mD@v>mZ+7=L=sA$Y=K*gkctN0OZy4CD}S zAkNX~);6vfh1f?S%u_OD{&u&h+^+8YAIr=a2Mnu}<-uaTFZ zhm=K4{{@<{FT{_P!hyUxjVK-!2O}9%gL8M&NB(nR2eG0(wG{{W>jpHX6r_og3hse& zY>*Pgnzv)diXxAiPJI5ch1y(GesvRu;u&??IM-59XuK#&0EysEKSUxiM({&Y!Ite@ zdkPZbX=jUUR6th%th<-VIc%2H*60P*&d|;5x&agD`C`QBc{e0vy*oy!q8DOpa$s%= z&abO_%x*B~E8i(orKqeTd&cU-2Y(olfj49DO9v{}P6RD0S(<<~IC|eYL8Q+5T#(~+ zMQu$f_H9*`)$?BuFb`iKxVp2go;psiH4{ z{ujy7ivB5&v`U(48!0Ds_Khi~LBGZCL9|Iqgd=>48Umdz!d*XWob@7+ShV>*EzU&M z+?3bu$AGVSOx&%lcvbsvt8(04@i)7tPp_B{U4^^*`v2iE*l>f4nNsNEw4~x+?>ib5 z*?77sk=*5dSPxavXdhVgQh#AW6q*u@Q=0X-s7W=5X-rEn2-vV`*nROUT6&fgTH#~t zd{yvC-5&sBsDbz2@D_NBk$=*QFLuW;c;$h~A+8Q|z4Fx`i<&x@@e(w6ql8JFj2pgo zQ8niH?ti!2;n!qr639(=ym~p%+(}bo?`E(;yE?jRh;4GqX-P`Hbk{fr9;gp9b~RI7 zus$1-GiuS&YuA)lwjR2+8+$+X$_5LHU-u?95r0vB@_DvhMDxwk+`oP}E=E@4cH6R& zgfMvHz_Bcr+WHn#?202=!k*?ASaBGBd?PRqM33jotRA7Gm52AFNkc)unWUR4407uH z9epZvd@6dltdk-;vF|=Ys$>v-J3{$Y&sa`g`Se5lfopjtU+wFeAd?3y$cV$2TRLcA z!g1rg-1cnX8#DancstDZ=-}*kr+mHTq&wD`p0OT+U=BFY!)*Ns)}4QFH}_Za5JKV_J3EiIkW*Vhl;k;H0j>|Um5c1Ov1il* zm!pTCR|OQE*ihA>Ft>gp6Z{*I?XM4pzB6N@g&!*PO%BgZD!pFh8(sgt=^E+F!_>#v zI=#L59n{nOF3ayiLg{)8(;f4^xdDH^U58+b3Er^c*lCm;?w#SQ^xjS~hymB^nylzs zp9%;LDRL`$GsbyrK;fDH?3-;-K}67U(Z8#E_t!SFWV@dox_|Gd{S#riYn2o!tpKX% zt5l<#&PTQNQ&-X|`}2YDDxa$olJC0z3pGLSY4tel5%RO9Y}o1bB(#xMYG$7j9mQLPg^X382ndB z)X<(3Omigew22-aKdmj-fq;%7A?nqSvAk;&0=(@yRY$yKFH+l$<}W4a8|%Gvitfgk z1%%g`P*uPT09hT6vP=$LpPjq?13WgLysQ(IBzL;<@g>42TbsUzP&KjW+9AhJ$j7=q zGyrgQe@1V}(|Oku6;-v<4~}|&mR|N(iGAed&rV&ap$ z#q4{*kmnAg=B1B1tH){sSn8anX9td#5d>4`noi!OmBdfc&V;WT`C`6kcsnrd>*vn8 z);8SRMe%Vvhnkb8oZQ{@aN2KUZr*yJG)(Nkj{y{+Qn>|V(WbXm>m|)4!-}Dzi%**e zfX@VeJP>HA2n`jL4F)Rzzs_R}ZzFQgZjvE;&bKN)v(`J1ij|m~v}K)Md(Ed3mFQ z4^i#j))S&f=Kh|u;TQc0eCdxiF;~F1i^~j({Y@-%unp)nr$NMVS>y}j2sder8CA}^Z_F7WK%&4ZsT(^CE>Cv35*-wA(^8V>buJ(R>z+Ye7 z{jM`76+0+ZSm^k2DLdFk&nw&~toJ$ zVwAfV{E}H68DQv7oxTL;Y`Rx-f9)bdB9$m=PuJ8K(vm>TFCLpRRDqpgocF^|Fw?&t z*TJZOUw8ET6j*dv*m}zk(SyUAvi_DsOcbo!Ej(|G7z$YShJ>&$0*0_9Wh$Pye*#C* z{XUa_zTGD}tBY(Z2S1izO4;M#L7lUiqP?|wN*?x1A6wvM(!V6%$UukE|8uWdNrU#h zBGLV#v^1fRZp=hn$>wrAQcE_Bu;blu*B?s~5aQ zZAF9y$94YPh7t~zj%1c`hc-`mf8U=RB}B;v6}`di=$}`y*uNXxY;>WQP)nb;9#opG zc@2IM@_-dL-@Ze?Dv&ScC3J-h9CuMkBtCoOYZWc&4H-M+#f0jKx;tndE8&l&t(A`g ztl?K3QS&Lj%^Jv@FbUnA9mZeB81{o6Fg; zVt%69D)%TqKRbHqQ9=}rAR%aAlj8=$-H}FyfT6m4e812;J~K}pW!?_5Q*WPBHRv1c zz@uFEb^^8ZrdWz!U}I@v|Jp8`e{CHGarn@ywsG=kCVV=#KV&f{Z2xA~o3Y2VAAD;w z9;}zc^L?svI>5q^Hhah2cT+4u(K!sN2TEVPvJ2?cTBTH&ZA$U%j;|aQ|99-kl>Eu< z148j|tMBtDJG##cbTJw@j-m&#q`u0!Ky8Za_D*`8Qv9tz>5u;PJAVV4(Ee^6@^){`RF9m4rmdeT9`@u&td4dGYY!;1EhFe%}2Q}0 z%?w2%K4&Y@6_DeN?z{$fohC{hx;0tCRQUPBJ}$M50R@xH=~$~LR6$94St;dCtV27L z1*yB~Nv5~n6-D% z+Rw0n_!F65{5i6l)0X?;+>@+|9Hts)L2WsndhFTn-}OBgqb;K@>s0SSqf_F;eK|Wrd4F}ef)IjnYq!yu^6Uh z5I;sL21`|9ufOj6wD;G~p}XtQ-k$LV*X_J$(PYN@01*Uo?7V3_%Vx@ z<#*4za^m!Uxqn@^7VVD!HtOW|@kBl_-qiTlbiCt`APgi@394|xY}sJ8Ue*T3v2s9k zlW>b&cCt$)m~uxaPAo(#B@60{-^F2(GxqXO8Iqko>(145^7R)ONRCB>HShXfw*ctt3P@#E5;|Rzqs!_DV*cE zcLv`GhmvKl%KCONZXnuE^kHI&% zORhgB4kY*ngr-}0h=Rz;FL<7NXPnNr8zQNKm<41&JG;Bh-G$$r5BmLfLBigP!YbG$ zdw#U56zS-#XPJ%7UAIX4-ABo6E@yT9ab_VNqK)57cyjcQ<&WdnA67$@0^T=mog3Ch zspt`-$5(T4eyuk(B-=^-SWE%PpcTcqi)oQpCSUK4VqpyCTT8G0E3dx-6{uVM&V~lR z|9B4dh~F|m`gX2ulG#XG{o0mceo`Z%rhk1_;u_Z&_8Ad>{?clqBw#TRK`t-?BNc+J zQ9;C*Wx>&yd71h4WcP85XyihenGrox_|3r~;%m7DN3$ERzJc{cJ?UNA?(G)4>J$Fd z^qa2Ux-m&lRfFHrl=hdrjjo+O@Y;h%uaOG7zJR|eJ7XPmsgho*5pc@g4J4e3S?3e| zzoz6>0xOiKUcS6`D8IinA;RAN5UBz_F9V{$+(*Av0%mt229GpfC?3|H)1YzoEO$NV zPHc?Z4TmZDnw5?@*oZz6@FiWfm>8cm=a!Pvp>2*9)cmQS>@EZEw5rW4#v|KH{eXM# zBSMRU7!;J>4(MDOg;LODoNTb~z}huh(FZ`To|1CMQG>v^Oxkab+SWs#P3!YFJtRSt z;x2L5sLwn?%2`UgEUK24!j7GV+k^6O#HC$Lvqy8fRy{oz{3P*R<4#?vk8x1!DSD=w zj9d&oM{!>S!zNpb&)xyVSpgQ@&s$gv@=t5{>aT@;XPb@J>kC#;jWBS;g&@Qr=0UaK zYW3t`XFGFp2r^)J2WZjSN(PSHg8btyKKaR8c0<>%oTA?f!msv_@x2HU=2%XhHI}V& zNE|lE0B4nmN9O%{l0MvSaK?XE=j2t&!6)8xSy?5Mo>?RILq`5K$4fX+#l`;@i+AIi zvn911O-b&YwIP4r;=<)Ci3e3U?iJ$n}PA~B;!7bk0-G&6Y2 z#Q7XT9`k-vm6Bq|SG_;nAB}m0rhcOiJ8=LZM7J#R)3lD|Qsm;yz!bK7F^vf)5A*2A>`v~#ezz}-t}D31NQ znHJ@*r0UZ0<2PN}!A|q9YzXP6YsDa!4^o=xbU+i3er&S-#J@|01l-3VJ^*5$=G4j1 z$=4G!1I??u?zy{ZYSHodjVWHpEgco@H;3ZYM(~CEq#!dzp#=2p%J5bp`&WwZOj*9Z zt>DAQ>u(3gqv>UdO6ZysESird^e_`&*=6Q4R+W*N9_(*M|Um)qQScNC=j;n#4WOEJ%6kEX$D zwB^RS@Ig$7G75>yvD%SM9~Dr~Zc2E##l5%tDN6K*Tu2C=Z8&|;lS|3k)m;Erl%g7b z=O3)&0ib=ij@yG@aae&O9st#&#wG7gl(w?|P)*^Vy%gGp{OkCf+>F6DQ7C~GCl>); z9^JFt7ph2wV>xnyJ|6(0o(u5OLG=kGL6pZX#Vh{B?5tB)i)}4Gf^(lE)zkap(nb3j zn+Bj`UN{1|&D7B% zkgD^qw3Q7v*-3){KzCo1rfwH@cYi#TDG3q@tr>=YXg+h(PE+T0Gg{YezxruBJSGUx z5{8Qc+H`mT6*gR7%}SJF+lC38E^cymUQTBW4s8-u`nUpD1+BLPp88I3RX#07le|1Y z3`9Io3{1g>7$77qhp#Lssm(S25SSn$IF8TJXLVUxbYo~WTA@5$(?4a2@bO87vTZq) zitI+4CiB1iJJZWV#rLDK)bUJWf;8ixG=UH=Gzx43c~j;NYaw@Rr?p=J7Ugq0e}4CL zU`mX|Hj1O~{^eOs)ogI8JJH}6$ssA%I0;#o)Nfb(Mi1>@E9Ro+egq6${NJ*(cIw3q zYin@)>po)>``r*ktN~(~K$jcy9~~q>GeP^k3kAG!!+bcvx9A8~+EGCGWVM&$p=eoU z7(7naMq@ZoEV}ldSN;94SM22EOE`eSqgCZT*W|XXrC%}oC9)?9593hTNk|bHBr0Uk zx*LtB_4b!IV$nZQHHz7p4vhdi@RwsubH+ENN9hepzmv>!P7MQ5y%5^`2FRt#L_Z=JP-c3g4M8c%zjGTE@Edjbv zZTSU1>8VP7WighjskFP%8Nj+DY~PUV^_6C2+rQ$XMfy}{&mLlKxqR)fJ1*z-bY3+m z`Pn>bqw`T?_Nl8Uch+csXE`nD)c>Za7dLK-oq`OrqBnYo>hzGZ=cFC&KfnSNk_KqA>UCjZ2 zU)XSFV#;8J~f@)k4Nr(B*}`Pw-~-#wpqoBMSVGr<5iP;3wlZ=1(RLVU#9upXvV!lk??=T^-6^S)mn+4Aiw*E?XxF%DbfAKoZtGaEjVhU`)k2iH1j983|v7fGMT@Wgz}}hjT#G}IA?Kjk1Qgz z_y^Ec3-RN@MB6&mHWC*#FMzBx?R6-xKunL9tWKq{NGtY%&A8&u3#_99Wy@$ zfNx9sTCpKix229Jr^NkezfF4}g(->$l?U#=_9dcDck?2sIYfv*cx7X5+4m4>j`yH2 zX9U+=(*YtE92F;{mDQP8`%mzgdE`zFh!BGi5t15=RF7a^R2<^K!bnoeP#8qn04zSy z0mm`Ev(h+;O^_Y$VLfmhzHWDs`DJQ*PFKzhmH45Tm!egQ1OMf06XtR{==was(8ryv zn%~0;CLe8j>Bid&j9Bp4fZraNm|TB+*MJ}5FxJP<4)4Lq^Iw4@F@$#Z-PiUh=a zA@`SoLI$D|??gCKVD3n*)SJ_(?5N_~z6PGSJJV>p8KCW=#CDtoQHm*p)7QD*Vcragzf5F@320VAc$i_W4* z_^Ys%RAi(oeVX6X`s_IgfSuvoa{Nj^Fgz?0eV;(9mvi zfQ#j5bGqEZTLYL4+=d)bmW7BHjja+KT>z4hi~1X|{N`{_UYFmU^_tC3)SZq5V5-qA zgA5XN&sM@)fdzY9R>VZOWQd9!p8*?~75!W@(_?W%KTY^0LOSATwOTy$nYlaT}L|~Hj1F*&aIgaXk zzf%AI_eYvY+Xw&SEWrQUhyO@403*-ezPz=3lhn0E@b&d$b$pei=;V_ijseDku?Vq9 zZdNv>5jg=pGBxmt(z`{F|IE_U;JxJ?%zcB!J#(R=QICG$48G$FtqyyEIkgnq5_F+LOrrun;>aS2L)~Tgh zGsFC8!u^lD;>E+;jNY8_348lv7rY6Lo7_}UQw(6d`Cl7=!$bj_0AHsa(2%9P)dezs zI*7-XVGwDY09KEMcn_m+^^%{_Sw3=?%hRT%0H*j0a4~=YVRL975I26=rZ9UPd~aJ< zC69*&PQU2JVHvMpp%YraItT^J3i-D(n4mjE%S$F~ums!6m|_7}{PDSl30AM}?vV5A zs>Y7m>NBuA^k#$a{-zk+&Rdc)F^C-1Q{?INA^H)>=L+qASZz$?w$vF=WG z=9qYxp0QM6#o`EE9wsHa*2O2&Z zVK)ED%9HDegr6}mp(ZW^)T`c^A7I@ZCUquLui}8TD6brpnz|UsjzTmM^7E7@<+G~< z4|$(B%y(Wbz`2`$SZ`TB1pqmi&ssKS*7w&#*wvFkh|A(*SZ}p*mx!NfQokwAq_C|7 zV3m2Eq5mW;fw}W^8YSfSA&f|3io2(=E%VQ#b4PQ^LlPWcf!ny#X?uq!F@~F7?K9)6 z4s8b3#EBmO=#(HnTBH5M6QFa^fYt1t2C!+nmYa>uIw(b?+shl2p5Tfa)w*cn?Jo%* zpLvYyXLptNNd~5|C(T8GL1SQKxT1;~Dm;Cr(vyp&a$Mh3&G_ex#zQrvD!-}RrKIHg zCt>9hG;nwB1$GK6@>hWqJx|*#kA2|&6H&B!fm2`zN|e=-o{t6q_S{GYogA{)S2NoO z2p>!b`?_L+>y+priNEu~^m;G4FyR7!X5ec=cz2Fd*I0g_6-%om+H@@%a5AYO$?51+ z>y)-s^piP;PCLWp9TzDt9jql~SpRTLI~Ayvnh#A=VqhnrNquQiF9O;H=eY^G5C(EP zxRyqN1y|Y|%lgZXAt@RXKOjirD8%$a`F$ZH?a{61f4}QZ8GM29-U$6y*w{fI6gd=j zIbwDH_|U()YFVfmHd62ospf#*I=Jt$bQWiq$6sqV<0sn`n94+{g--)?T*d$0Y*^So=l&)e8E zZ6kw@($Hj3Io-x+iT!N!S=5d{fA-G~`f6(h6k|>P)GycVwL!{xU#{N{@OqAZ5m+C! zLS9vq6M0Hwt?$480WUFn@ILL$n%o~R-=29m#fOphv8m4t9wq_(A<>eufi6PqpVf2> zpKflI61EDFqk;(TVZ{fQoss4L@rS(t<<(Cq!^}I^EmqyDlf<8RPdm~c+3eob)50rr zFeRn-uSnGs2xyL|ANGV!D@xjNQy-j(F{HXXd6d2OB=c$bA!4J1DVaw^`Ko=Qj7^M! zC@`@HZzjCJkACg2pzhV6m)^c?E(V{e zMr~m8QE9$E8)p){ROIi5A|LKm*yS>3{!FZwQ$}_M|v2YUJ8=a|be>sx}y3 zT4qj6sWB8m4<2I+GIW?2Mfu)wghDJsJ4Jc8P+Hf^tvu@M;tUnZqv-0U^($KoKlq6eEIfxZ1=~wle%y zo}%{4hjedmix)TT@3uBCKAqC(t{5qCfNxvruhezUdqMR6m8@xlD&vKD07ob@XU<~i z^7S?A2_0|yOSZcemE}0ifep86r3gLmkBet}@JpTPDLzGx`$YmjI4}f%JUsoy7og{X zB4w6;(`xwE+4nG(xfZoXqh%e@4hadX(b#A>CfM?MC`{9N-IF8gJMnD&bnb#nb!OVF z2!Hw}Xaf&JUJ=gzmHCab5w88GRNbNiN5prm^2*AZUIwqkmDqv0%Dg6lyRJLi3fAWP z_3!<63_2E#M5^cU2YQm0iSG|#ZXlF)z1oSy0XQ0QwG@0uY5&7f9``3WMW$jO5Ld!a z`ZV0T|Dz`I!)+%IR$Sk&w{bs?rCs&if>2VN4)EmO)Q_knbXzx@UPb82SS!^z%u4=d zykZrikwTRm^mh`!x@U7I@9psvPZ|oN2VJL?42i6>5oF~Ps>PT?G&Wv$t3!%%EUw4# zd&xXatc5&rvN#f5aL43Ib5F)>ca`dUaK85xp1qXk{L~RJ@QiEHE;s5X2nY9NZ`Ru+ z`wlC<0&E5$c1qu;IjdVXV-Q)qB_kjZ69Hr6Hjz^|ef4=nLon~4QR2UMhX3%JAk&nA zh@EiW>LScVe0=!12cz#E_&ItVBv1cR8NtSbgC)R_yu11E>=xfHm59<~ zz*FPs$iRo2@q#uEnt=1;Q70Dtr?A`X62+){-Uu@pJHpM?C}sLCKCu}l9@l_}uX?Ma z2dI~kah{O{tcd*G9|?gH; zQceu`O;kV2!N}npG$}uyJVNcgb<|t`_cfTKUETrRt3*9PXD2p~Bi~BQnN_p*hfWVb zla>kdV}CO%us&QLKOw&wHx)!2Ry-;^d+w1j;Ypl}7Y9v=s&v@A>uBTr%DixIz(AixjBVn!lekq|P4Bo28_PYikc zM_5X~r4@Y*DLMiJl0Em(Yt8Ue=2ti{kh;q>px!|nm}kI8Ow ztyy%=YskgU)%93kH}JrXh7z4MG4Y;#*n=Xmt%ib(KuO7T5h?SV{Jh%>uLOpl+I_T;&U|y+O|KgW| zG6Am7ID1Sa+;dxsD0jR91U|gohkhIVr`l6vAnGX|%(=xta2?bO&zm0!C2eK;_b`x7&+@Jof`-nN>rwo2{}=b4@PS(KQ;=F66K zL#Fy+d3iii8*|l7mg~^`5h2mJCH}Q}@9))T@S!cD+#GvO7$srK=Rd}ryt@mhrTfb# zH3|nw^cRrw(VgJ+Ks|$CcNsk&*}?kX(URD8|0LmmOkac(f5z0kqAK&z+6gfE!TVT+ zkIL7d-W~sb@GpZ6WM<3+h`m?NY47|fbTY8X73=xO%?qN%yunm()$!@5_huWRv)`R-*~oDQ}<~;!8@#Tv3ksnGe%2d7iLXY<2NGN zvP?E^WXT7BJhGyM8aEo1G_&sefQ)4h^C|8?>Sj->o4%eT=vD2HmAmbkPq15VXJBE% z?GjrjKHJg^Hr%e+JfLM>+K(L&F#~9+Je^Dg+w5YJ06G!gJ@tY3a4dl$ppeuL=l4jgOUUNLs%MvJbEZ zPXmoOd$GD@9MfGbZ^+Zn72$twp(0D2f%r2+NSL4maxOB$oW(+kUWAuLl5FhnN14?r z$?L^eWy-BJ{e2CzXNac_je_^@@nX$zo38v5tkVBaWY#edGANgufA__}M=Y=-1)N%J zmf-YDt`NdKn7+5(Luq~`T+fvBt`OpoiotStR7^;eG)ePzI0Sw_Fd#iYb;B84PEya* z?;gk&>FXG0)CW@)tNWF=**%t7RMj^GJ@RyW>NoNAn+foxH1w2qRdUJE5#F&93n!>@Tzq# zk7JMM1#)xnZAlg%<}I27FQgf%DgSr4LV=LZG_LID>*4!JatV_fj;iVfEnPL)I@fV~ z1}(b4ipnZJpL>V@>CyAA|GnLEAj8>mPk)m-$ouM^xXUz%teHYMp%|KLK=iNMN(K@V z*<{ucSnboj>cgf~O8M>B$J+IpEn+R$3@-kCF05>LH15-%KDVF!2znN$v4v$3#Np3u z0YWTr|3|q0!?OL8V9qMgsD<#^7{Ws+!#q_Wx~le8oFj^>I%jLjXJ==h;4`EDimN4h zmKr!WdFC;%hHV?XO)SQ?k)^rclf`aR)$(VT?=EM}i%ThZ^wL*$2gLP`y+GBU@nfL7 zr4i|KRb{y+76;$u2CctzBC2Z5P{v!pgQ*%-`$+!2u)Ml7TBPV)cKuL%B`-0o)nL7^ zM0R$#kS)scz^ZS3-q<7*eun@Ed<6fvJPv&5*Vsf`H+?MhaEDGgd}k&>k40 z;ul+A1dNV85E&^y)X{I7om0|h=FyIyBU9GgtE4WyJ2#nV2P~d^cf~J=)}lug<=2dX zE8wZBJf+E7@ZV^Ff5D@B=eR?umzYMJ;}i7i_vC%cv2E>2ZThP$cPr z&rk@Ah%NsmJ)9VVtefKuEgsXG(uPK+^N}L>`uO4;eakchb+)o~B%)*U;nK|+xMv?a zA5y={k-8N~!2hkJqs{Vir|D6y8VSm>#)#}EsuMmV^Q_U2xRNq1jZ`jNU$L-o0cR3N zB}b)Bizbvd73SJ&fOF@NV}jGwK7rak({BN=tp0c?+oyhn3HkF)B4j!=*vxgD@Y~UO zzJsd8y}j(*FnT&a%#UHLn9|w&IB~1Gqh|v1u}K9u$RrH`$v~mg+kt$q<>dfX6qAM) zE*!wj9ZvLKAr2Kq z?C`dmbpkyPnsNOhX-B`O!OqTMiA|1We@>j7bk zQp?KLi)39{tRMg4oG6eIcsOFj$rjg7lE2SYZ$#puAMvzp{`OT{`)KS|ndbV8rKOZ| z4)xlVxL~)^Ikkb!NO->`{efS|bA(2Y{nZ%F*>Z|KuMsX*cR9gT@i8Kp`7j6?E$yAy zL+NVZr{VQ!HZM7PFyWB;|A*gFf0M!>_Gp*^Rqe_*)LNfx8)fTe?n^Qb15=_lk@KUZ z|HgqqwwQainL;pbu}QZF8mmmVx-V}BOm1hR2yU)<40$5#zko&&4xyiZZ9B6Ct^ymm z1gF!6EBSarXJ$vF0W5r|OX7?5Haj?ui};uF3Qa`6B&bnJ`zDsUy{~M zr&-(lQa2@{!c4r+0MIEt;Cr*W9+bDDIs(=61WpFBf#w7TbT0`$$gx;^8{qS!%0+s6 z0354ZJoCJlp8d|@+g7Y=e0aU5qM-aHxXSZ@TRc;WocQ*Ma)xh+hp$D!-%>MTTbKuu zHIdQZJ#P#_F?a~UR6Kwwz=L_5{jQ%01!eJ*iS(8C6dXQIM&9hMy+6)0#jjO2{|~Pv zu_cQi4M*vRdmGox8hK&GEniC9EPBZ*faV>dT*cy20)o5OSR%m(S~jH+;(p<(pc>n} z;NBZv^7!9>1E4 zz{wGUyvwq?sxhnkOsadMFh#ZGPzgW2eL+VPhhI49s86N_>LKn-AtE%6?$_;gtB0@e zZ!~__`XCsQJftM!c57*+w- zf4jjnu!N&f%ii!y>Vqci8b4@<(@cGXW9?zLanRn*in%|Ve)*&@ic!HcZ%BtAUNe); zO8b!U!Bm311UlGn!2a>^SL&>GR?^WPzD>4nk)q6kQ(p6i4VL?K=H19Yk-@_wnO#4| z`V1i7d(D2Z0dN5mv=*<8Q4JZ`CIE~JRsYz_(t4Z$5dL!hCJNc=?WobnwuOa9!AEX` zNqw2S%XM`d&TUU+q;M@Jv|Xi>sd~WnQ+k*~&oHu&a^XqlRP3?=EgVPxL4AS+%_jr8#rKC4nIvTNBk=B@^hly z2Q5=1NaIM3gn|~-eA3XVY%ygVTx2i5ydd+%jt}5~(RN=I>SvYyJiF0*@n*~*W1X8_hf17X zt1tEBO%Ve0)TcF9h(|G_k@uG(lpMZe{qVB`zVAugy?s12YZ_p`wwm0v(L!ittgyGz zRfT)~DUtMh5xf+v$&Kj1H%XG)!w4420$E!u`@%Rqt(Aig;}#gpti`%8!HT^Eky~ zpEiKCjymxh|UNTz;!Gcxu*m|I?_`0k#u4OZV z3u(!T^QqG`k_G=O}=I>PnYxi)tg>! zIPh;02f9^3?xF)dTVW1XG^h8rmIlf#e~k?y)80t0l}={i?@hI{H2@mO=}Yx}gMS{+ z%v9mhu{ZlY$!*#{zgIQ24K1{!VoLi+fn-A&c3))DlJb$?aHQ?S2K!istCdb(usB!8 ze-Vh6=s b*^l);nu3acc!kol;{=Z<&2VC#Xe~Q)Wg=)7v7|y)6+E@f~M2L%Yt*J zX^Nl4)oP2P&mfJu(L(8-6d!q~XNca$n2?2yGOfGZmZf=cgs@fe-v^#5X^l~vsOdI( zI|<&pPo;lbeZ?8!%~XLGtw%nom0Z5zep{+xV7ZA+{hjBRlwz0JcDP*QsX?p^!O(m1 z`g_&MZVNf-VW2>P?0L3-wF&!v&>p+$*ZH2(pma4EMkjpf_}g3L%~V7sI8}rYpnAa* zZc@6pPP`HYNptpQg?x!q`LswiNwoK~m=WYeW=K@pEP#LTxTJy#^UWv}e5jFQo?U>N z&4#_(rPV=pT5j1UKAs6Q;OXCU z2+)B-jn*N{p(YFm5#~(Qs03+m)Ye?rQbHsD94pHJ>JxeVa*`XhYVj zhF^i}jY(2V8Nj^RNRe7FQxnx^1(ba?dcVd{ojp1tJ=;7+suC#kVIy(}UOICQE2Yo5 z587h4R`SoBRZ6&6S5JgO5)(+Sp_v?~T7a4wHk4dy;#|e$oT| z(c`?{Mwf?P=X@Qe`juX^@6m3YDb7?hYGDS(?`VFi|0+YFE$JwGapq z`zsFa?T`ECkslG zQf_k5MCkOw1IPiTz>#*gE1=QzeXTd-3`&~lYoz`n9Sjt}M$QN@BLp~bvfTQ0W^*jC zousv7;!90EOOr!O!6&4Na^#|nbRG^ruV_wyGQ_eGu4c;H(+PF9Mi&UTe)?ve2qH19 zqJ@tE4FLrOk?C5ZP1o+ zMoUJjDEd@~>$0Ohxn$_U;U?~i zI=a&((L+_+>-VqM6!*p^m<7-E|`~G2bC~ z??MuSxSo4`KeMg8iIcDq^FZ}Q77-aPc*SeO{z`NyI~t3)TUu+{!t>A)lnkokxl@Qg zbF6bMiRd)*8N~EkV7>O~@`4t)pt^PbBisH~3J=HnG4hj=2JUt=NF=;!P%3vvqJ*>y ztA3E>PP}5N1ii+y04C~6fD6~Ye-}|3G=IeSMBn*|1@H8;Ux*HszmgIQW>r(KmeWWP z>2n^U-(0LYOG$6bo44<>nzYnE(tlTO!ADuBKj@DiBCFS7m4lAKsfc%H%lGP zG$nRW!igr?ok-ylAKk` z?)ko%Aj9@c_uF@9$j`ZtE-c1qC`27-KK?j7YsT=?+8}OLbR5o*L?$)I>h=BR`lY8~ z?{j7}fB9n}kuu zzY2dCj6GHIgMsbPI(;>FU6Ex41PNpwUw!pdb*Hkbn*@k=I5x6=PVa`hMVF@IklzB~ zZ!z%EqP~Yb8bm%WIB2!H>6@Ut-+##X%tsfaLkMY}r+-xbVOZ~g`q7RGEKC&Wu1#@Bgcl){G!*7{JIk*GD{qVujZw^VrW+jlxt^90dc zFF*bq2Z+!29c~%Vs`W7CkZ^WKTSxz*EK;(fiQ=fWFcl`wfk_pLyzkmcr{*8VoJ(wRR9*jORbt%_jx zp*vN#AbW=?fb4lkFv`@R6A>u_w9UT5C|i_QYUw_p(azsdqq34{8UJzI@D)7uaO z#;Iuh=pUM`-e7JP0G*c3eS9AM#KoWvu2=l8+)3~YWORt{K^ zU`;b2LrPM|0}X&|cI#c@BM~1SSko^@Yaqx^O-+s}LYSJlzW%`95ZXcz@_v-jjQDOs zdbU8y8vQ#UFCv-MZwWVNS3{zPjm8RpejfCtWL*;jAy=Ky_uACL;PbEBe)s#emtB&D zK-ye;#1s-N^v&w=IK#Jhv{9Ve%(O;2e6H?yoB4d`IWeBI zTF!i?*bbj%4*d><67iN=I0C83`mS7#m6;y)ld&>5UQHz2Z;1S4%yAy_-Le zPD9)eZ-3Hmp!Oy=5T_&nSEi(LHXca94Gs6~j17StV2|SX;be7l0wv3FI4w~wl5kWu zRa(g0NwN?q61RY>PWgsVwrr9xFgdyjI{pxDBDAFA-VrwHMfYuHwmVUZ~fG93- z$7DK56`-%t3VWVI*2N{A*rk`={YzUsb;Cx(!W%F#RUo>GM;_+Rc`{|#`v+tG34=Qe z#HN^hy`SmF-k;5^o!w&avKOM|RCj6xkVM;l)9|7&Buth>jpG-05D<1x;35yhzGr!w z=*&QC64583XLGT2jD!jS9DKPT6fh@WH8EFne1RV(8l=PU!03bb1i|6s@4Q|SCp;t% zi;?|@h<&T4BCbFREMCL-J;9`q=%4gBd$` z+*zhEr^h7fNFFBabgB!q>QS(?(=XFih>#rgTZjcPaY2m#NW;>lvPUPwo8C+M>ck9d zdw`2$vpdwW-N8RpQ+`1MQl=Sg<|~QGGKRhaXEk(2YV?0-7%0MkJtv@*9mSlPv%YlD z20jZcl*DwnmTp~K5H752TEZ?c2yorw4*=IifM;G9gC!a`vwng1G=XssE`&#g^=(b% z_zUh)`Zm0+6y&H{K*@esfGb6}ngRvLNqRO&yDrC$y$IYLB{hR7M2TGSW9ZM zoQ#wU;Yn1ir>pLq;NU|TrC;*k%w@2V7e5}}wmLKiJ$%m86+(NR5J8gk3E)-LB=~A_*VSWbM@<_kTZ1SKc+-o6^ zAN}Sf2u0L5#N84J-m}_X|8OvllJ4Q*);M1e8tc(sD@4!hQ|B&u@ik${2R$3jqoaYv z%}G8U<{0qUiaN%In8M^gS3%u@-MXFO01Kt9`3+B3jg}2>0)5B&i5GJ!_c&(r9fS^q zsM4w&Ey7B}03Sd*WU(W}$4NSmn)Nr&*XR{lPIvCXbAjwwz z=<|VnsIr5l$=WAoK(6J`C_@TW2ZUciMnJ!c%V@jU9>)5luO^3{Ms!brG#AA#=Wqpf z&3TlE=2P~P6Ez?n`Zh1Oy;``~NSc{#dX(V1=`oR8VHp_JJYSTIGbg^HZ}8S6E0_#^ zxIS!Ugv{*365+i;b?0p~Ux#HKK3Vb=d>9_|XXv26Fqc`uhp0A*lR|#MnOAH7IAEQw z08E6KbRPtJvxk0%M1sd0*V^3-5Vfzr<6VJXZ4B5m$KbLlcW9G~a!GD6{YR}zw`^q~ z{4eu5vWS|?fz=+|OKhy7iQ)`p#yfLRpbr{#9~3kKRdhk!*uIDJ6j(7Dkk|VC1DEKC zB~T%G=7D?5I#euZ^b%Hc#(Oet$o4rcL_MsHCI>GL{u5?LL9;XpYkby^X?_ub^9^_#$z8FaFTc!&?y4Q=(LbB2RP(S3%Ax3fze%pJ1Z#lfJ7pL{BXd~6iq?2p@TFHOGwNd{i zpoY)fhLs3x{1mey$yQ0({5>@CieW)}>dxG((0vxI(0ntb6nv}in=KgZfRNo^Tz|ko zbuc}>b-+*zKU)z89KUEQSu9h7I-~M}@dz!M1bahSiLK)?V}94r))avE07HYfYq01I zoG2r7{ON^fC=)({IRl#g{(96jz#G3~E_*_{NdJv#F*}jlVnhK}Or3!b+>uQ>GP6=O z9atsd=zK&+9rMgPFAJ1$8SskKL95LZEU%_~mBDs&B++szCaD*+#sF1B;LUikPgOe% zOhQ@I5{te%>QG%XAV4{wc`Xpk8L$Jdi902&`94m5qHiA1tyU`U^-X>6rl!l>0r1Q~ zH@qeR$uAxN z*U_1uPe7LJ28O89Vb_Ensm;CXu%h$d>1HrxLDG(8rp(Vs02jEH#Ly>~jAfASuetFI z0E0)i1@B|V*M1-gR*z})`WJ-_Ls5LH?kao@U)QOH?a%s=#CVu-(AI>8z_KCXoB&F~ zX1Vd@GKkw#=R13<`H?DS!%cek2v+}%RS^MJ6~rooh)y#y5F0%!Ytt@D0Xyr>sSPt} zs1i*mNZsnLr4afu&HmS5#sOd$kqBiZhP&IBazdUC`>%VvMHf8%}rQy9$Jxev~- z?5Th}X?J<73-rz9!EC0WP@{AqlFV?Ws@M|j%JGRfmwu)C@=jfwYC04x@$lnyeb25b zF#ANAj2zX&$(=d1ZUmD9%}p924eB3JN|ySz_^+=~^%W-2EZt`h?L7H{%ypv8+9z0k z2m6ZFcQ}R~W`O|-Q{YXTMt>IQ-zqsv59u)0FVn>0R8j7wV|z^0cQ|ra3&rpEGcI8= zdfBJ0&BF`2bbUuh3kv?N3495dAcE8~{mSMh1+Q63OW!uShsL*5tJfPgM7zIJs46}W z>tv;;ZxjSY0K#N~%i|b0u;~GkC7g+~4?Z;+k`kA;Y(K>zI+a$Fs9`W*nIm|))$Feq zOmn7QnP>hxnF6}_LJq+NRyP_f`Vqtl;CZQ=`F(fqG}s49hmvwYS^aqsG%4;zd3>m_ z&YMccp|7*F_{j4hdM}{jHK5j~hFB&{EKP4!lhPqq6&wJMyr4GwTQ&Aqy(^?%8)!0? z{m3X$N7e#5Y4;@yJLbJmXyT4^#%sJhdmN#ym5ehjxids#e&Fy3iQ>xh$(e|51LTkv zvG4N~Y64}}ntCX3i6qbThq<$Qe<55|{4V-IRnU)i7`I5Xk_~T4)j?h&bK2*V{h1sI z>R?D{6wn;)mjoMR26kUR{XwWyGCIV28P}Y-^z(IS>)*+lbBQ+}6WcjW=W2mHv0+WA zLLNaRJr$W`3heU3N|webaQ?Qkup!U+H7gOh+9x}@R)$Z}^H`-~4I);P(cQYae@;}M zi*!_h=`;yohc-N;eo|0>vL3x#L?7U{KWAnh;v$=YtqG=8fB#G%s48j0VmnLF=NeGXQBUepQTTP(rqK1*6> z+wy;6RLmYq9n$xcQe6079G?yPGXXinS3sRxgbk%iz%%*oJ7+6FJe|75Na;6m2k=-% zBU+#I@pdobs^MQWYQP^`1;|m4M2FhVqP?&d@1v~vYyWYTAwijemm%Pr-?8}II({3a zKyIFxN7VF3jTeA=iJQ7IXH}N#RTLP2aTwAcB57c?q_!^OIFELq5A=^QQ1{e_B0-)3 z5a_Nx9L&?{+_-l%{|1bfxf7HB07E0_&1&+{V;E(!U8*kA?)T^6eHf6#N~_dsR2aJe zCPfs-JS_XBYJ4J!MK#gFH`myv`sp1>n-5zv)7+--8XO1{M8GaD**-hu6smn< z+*hZOIDo8f{8{x|PucPnJ;)rk0;O}&mo8g*UvLolZ+;mW2>TA{fU$b3;5^0nt@7fz zRmaoGI@Gm1GedotHniXn~RMTC}`yUw9gEv{=fC~nj zcP86c?s}r`0R8NxH~7uWb}Q2*>StS${vE$Bzp}szS-}~wEn$i@Vj*FEhJ{Q6_8NfmKNKf4pD9iR8g^?_}-(<|q?fchG2 znmfPUtBZlYWHI5&O|02DC#Xe{q(w^-fn$d3y0Il4biX03R5G!ZvwmHW>MvO_YWJ-M zcBN-i10T*l@jwf%snZ1lyy?2mlgC3!!wCamMwxt`OUKeEVQWo8;AMx)^j@j%ASOCt z0sARODq}q;b5uZA?K(X37ZZ^#S=~xV-rmOYn|QW(Qp_O3yuHN8n3V{Md~hKZe+`}b z&q`4DoJw^KEXq~g!`=qxaLNw7es&h<)wR435-tyQ-c28mNH%(&=X#X`GRBMEe<>AxsjanpK;^tkT|h@M+4h~^Da+Gfsso;^6sf_?$v3dCRbPqAT*Tv1 z=TBM`qY4yF?Jg7CEOH5>AO9_w`wMuwJ<57dmlIXf+C`uRMi-6D-C) zyYFeJ=3o%z-UZ%485?k|b<;_nKLs&BMLH)~vU+xF=9TNTrxU#P2A~3GkdD|qvfj`E z=m6z1Le9j1FL>P2yHMUs82C1Ka>r@9yn}Bwm^_6fRc@YE;z7xh=ra9^r>rtr6-|qpJwbec8OtWlu%0c4=Lb%PE zQS-0gyE*AH2YAWRY#vyup&}1DRI6?NdgU!UEJTKx)D`lVQZfETLmrP++SLu^KqZb- zKRZP#2w90+j*pezmc9W;M4h?C!@>5xe(qfeC(7)`Oq4kpWrAv*5fh^jSgwWH9lJa} zL3sEAHd_!r*w1>dsVqAE?5Bn5SSDQJbk_N-D|YqF(}u4a8P8(q2#ubKjT#klrbVZL zGlA5Y{6}V3`%yO($t|cMuXW;3x?jycTS%9}vow=(Q;zv~oIYz1fTrOHXP}QHRQuW* zcUuYuqbdRjI4=6S!wHUCA=V_j0*1wap$9xRe8^Tbn}o~5zO5Z0nNO=To_q-! zxHq(v22Z$Qfv0m=pW(nx$$z9cpK_C;2lc_qF~3Gus*N8%e=S>$N+=L(_ZkvZ{$Z1z z6O^JYNk`@6t-%?2dz4|}Y?PM)Zuw{i&;tY7TVXFF$Ij`jI?>GN1qkv=87=c^)leEH zw~wz{R2k|kLF@vMIoo~(9FaPLsJxix2}_%Ye4!lp>K{&+WbFU5jJgK@8vE_sb_5+)$UKC6n_v#j(CwfIVicicEm@|p&OWI zu%wdiiDA=Kf9p0;Mh`I%Bni@yBx0hInlKK6h7tlf~if~I_}ti`MU1&CAorvLx| literal 23539 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

s>HG)iPmDAhh@kWqZ+u0PT^~mL|=$SkO zJL>9HWu2l-vIH7-nbs8XNIO@r|E566I#DKr$}k^JftcZ-b?19q7>;&y!S7PyRArq4 zC6As@$iHdL3y(LHsR}arOd00GO(*|aRKWJ-)0zyPj*_8KcsNH*)=8#BS+`M$iu$(J zJn%Xb^}1gQGD#Du#os2!LR5Pf5A)4@HIZ>I!##~naH)!{Q>3I&j0(HX&(F_y4eP(eoW5p|!bt=Pf0+j|tnLM+CkgsXozdmdJlmrMekp$`}Q$_#!mC_O} zQ=M^?bv~7s?d&JZWI~Y12PttcL=m2`)LQW(%G$AGAbdi#$)nuwT#o~6sT9a$J!*F|>TUD2G%(hsbHYgSHB)iu9*`EJ4BJ$sTB!At6vg z)w0poSCq4YoOLaB$x^Xr-E?2))QUvEHWu25gD;yAFqPVQn zCoM4WWPd%mOQ2GnlGqsPBCYLi`N^nlp(OiEnOvcDlX7zdJ6FHwo1#o! z6Db*168pZaG|19eriv+O#?EfIE+t;=fv?S8BLa}AIZtIZ3JqkyO=>)=Z_*Izo&J(adUGMWOBJElT2m&8Z(`@*VQeHOcnj) z!16LDS*J)PEL|MA(hz>O*6qa2gA7;FP^OAgx^kMoCOHn|j&7I8RIyrTNK-Rh8m}tr zED;T1*YPkMx8t@IgnU<&$y=7pjRkU{3xF9OOIhs+OEzFkTus*LFH3(~kPsqQn7#@db^K;Zj`H;cP-24P>>HZKTxK6 zESn~EPRue{C+;NX9EOujf?oG?-j&>3ZbC@N=>jEx5M6=`6&6#Zmgw$rAh*3Bi?q$jJn7egrPL`}EW*Tv1p zO*DjEB+A6-EK{{?N{TX7ly$yPRG=#B#GS-e%EVvm>)^(1!BPTG4WPQ4)t1q*n(895 z&I~5&^yO-u+&o~n0!sd-CR4RTqd=wm{Q$VElbh$rR0S&Swk%QGMr5-fhpp$bPHvuQ v04D`Rl1oKdw@;*DL1&sGc@-i**V_LB*NZ=iA6-au00000NkvXXu0mjf=HRLg literal 18162 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 Zh(DzG?uW91Z{aNvDhitNpJdHK{y#SIo^SvF diff --git a/pkgdown/favicon/favicon-48x48.png b/pkgdown/favicon/favicon-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..f05c68156a4d044b07d5b2634ac695905a516a93 GIT binary patch literal 1567 zcmV+)2H^RLP)RCt`#T5D)qWf;zMUJwx!1cx){tm_aval?rY=1`_%40I^l z4@KCI{SoJn4P>YY7A8*F*w`HE#s-_MTbnOUn{$$;O}jKnyWZ0-=`Bs#+?wXnrb$kd zCe3=uBj>nFSK75rlQwL{?YLV#An08#c{N?2{c3M#ARg1YV#It zRFGFGd2!)qdQGxnI5aQu+0gq(@QJQ^nr6vWijY@UoH(V`K1;FWr=4nLw8-H?IFVd5 z`TGcsCsAN`;9a$XqgiqykC3NVg8MqLoE&V99DP z<>WxdnA3{lZ8S@2zHVuRIUHLy@!9AKMRBC59uy-{i&Z2qZNzg`qav+#{gTL2taSetrPTa$hY5pY6DvlXn}vbsUHoWlN3NozL(ED)TKIL5$u`oA zr2KGW9ZaFfsuAaAp>1RoU)JlBG$Sb!%VZno6+pA3f}j3Zn=m&I_#vBOiE{o|har`& z=-mZR)Hf8zNbF&%kZx3W`sZx7a4}(&N=d*KjRiMu+i^|ThmvL&R7MZ3Xsp6^h0%+1 z$}W`O@!)EW6=kghVA}@qZ=DUzwh&I086h{g@u%7hdFueqDmrni%MZrj7TOEzY`CE6 z7OqiL@4)3-mdpr{x4H$At@bdgJAG*E4?=Gq#?5*={?Ye?ZF39T%JxCwE*3X9gnfBZ zJ>Dp)I(@=_r7Z)(O<)XeRG7SI>?cNoeOG{7rw~yXK5~- z0Z7zwl{!=F4u$hOV&OF;?u%!RtxyO%!JQRt8w(o zZLp0N__+j}LlJ!2+Jv_&8SJU4%x(dNwKljy323_fXy|r7Xg_B*3;E7jvpKs3%!F?l zYY9sx1o)#*l!9(pz`?O8Joa$n;m{Go?QY4xe4q$sl-?3HDGk@P}dtD z!QZ+@yjP{b2e(xC{!SYPhQiq;z!#asbzQ%Z3d}Y8@~Lo%%Po{!{<2yTj7?>i0B(tJ z`GRXlfXO|I+tv}NJA7~i6GGeYXhInGN5-dxvZF0Bp1lJ2%yi!`AJy zIH1>HTZJ5ZYt-lrMYC5xn==fR(JQ3Qw!r^)dN44GvpqfFuTx-mz&aXVTYX?8ntGAI zk}d%q?g$$Cf(XWw$Zs;j6;7Zvz@f%Byx_4;-^l6+=Y(i`#?qoRCpG@bW8=_&{)dH zmJSSpRuI*^0$x)UZQjI4w3#a4@)&xI0m3b!@Vl_z2HY#5mkK3xe;P{YDL<{Tho$#T zY{{pOCqAEvQI)!hkt#wn%?~*_k*m7!jA&Ul5Y9plz{Y?8wnMV8RzL9r)0V6L} zSh%>b#H3kLn>hn;rj^?JsFj*>r;hO!Y|qMCu!5N_mz>&NT`ri}sSpqI)e4szyQ)^v z1m`~|pjnCRi+X*MU$`umd~+a-Lv=Mtnw1n~#b*d&hV02zl$d@nwhLM2e8laM1iouG zU^6Q<4`&{>l^Tn%+77N}|mH8=!^oWR%Cq>yBV&#n& zDVCi7SW`Js>G4D1_TocLMS^0;>0DLe3!=3WVmZm98HtKwCGs3a$(BrA=s$k&C_XD~ R&yN5A002ovPDHLkV1n{-@yGxG literal 0 HcmV?d00001 diff --git a/pkgdown/favicon/favicon.ico b/pkgdown/favicon/favicon.ico index 85ba374dd189a5ffb9a6f05da1d04e426241c26f..486f5cd98d22352ad81e0148b515de8f397b05ad 100644 GIT binary patch literal 15086 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 literal 15086 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 F{|_^)DT4q2 diff --git a/pkgdown/favicon/favicon.svg b/pkgdown/favicon/favicon.svg new file mode 100644 index 0000000..f2587e2 --- /dev/null +++ b/pkgdown/favicon/favicon.svg @@ -0,0 +1,3 @@ + diff --git a/pkgdown/favicon/site.webmanifest b/pkgdown/favicon/site.webmanifest new file mode 100644 index 0000000..ac9612c --- /dev/null +++ b/pkgdown/favicon/site.webmanifest @@ -0,0 +1,21 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/web-app-manifest-192x192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "/web-app-manifest-512x512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/pkgdown/favicon/web-app-manifest-192x192.png b/pkgdown/favicon/web-app-manifest-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..0e8545221d0c1a04567f9f47e58a9c73c9186be8 GIT binary patch literal 8804 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 literal 0 HcmV?d00001 diff --git a/pkgdown/favicon/web-app-manifest-512x512.png b/pkgdown/favicon/web-app-manifest-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..7376fa633a94b897ec736a78547c0f8b91e562f1 GIT binary patch literal 32336 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 a@dAJIIJlLPbQg}9PDSy)LaCg2=>G!-Fil?o literal 0 HcmV?d00001 diff --git a/tests/testthat.R b/tests/testthat.R index 334138e..759de50 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,7 +1,7 @@ library(testthat) -library(primarycensoreddist) +library(primarycensored) -test_results <- test_check("primarycensoreddist") +test_results <- test_check("primarycensored") if (any(as.data.frame(test_results)$warning > 0)) { stop("tests failed with warnings") diff --git a/tests/testthat/test-dprimarycensoreddist.R b/tests/testthat/test-dprimarycensored.R similarity index 79% rename from tests/testthat/test-dprimarycensoreddist.R rename to tests/testthat/test-dprimarycensored.R index 3273aa6..1733380 100644 --- a/tests/testthat/test-dprimarycensoreddist.R +++ b/tests/testthat/test-dprimarycensored.R @@ -1,4 +1,4 @@ -test_that("dprimarycensoreddist sums to 1 for discrete values", { +test_that("dprimarycensored sums to 1 for discrete values", { pwindow <- 1 D <- 10 pmf <- dpcens( @@ -8,7 +8,7 @@ test_that("dprimarycensoreddist sums to 1 for discrete values", { expect_equal(sum(pmf), 1, tolerance = 1e-6) }) -test_that("dprimarycensoreddist handles log probabilities", { +test_that("dprimarycensored handles log probabilities", { pwindow <- 1 D <- 10 pmf <- dpcens( @@ -22,7 +22,7 @@ test_that("dprimarycensoreddist handles log probabilities", { expect_equal(exp(log_pmf), pmf, tolerance = 1e-6) }) -test_that("dprimarycensoreddist handles non-finite D", { +test_that("dprimarycensored handles non-finite D", { pwindow <- 1 D <- Inf pmf <- dpcens( @@ -33,7 +33,7 @@ test_that("dprimarycensoreddist handles non-finite D", { expect_equal(sum(pmf), 1, tolerance = 0.01) }) -test_that("dprimarycensoreddist matches difference of pprimarycensoreddist", { +test_that("dprimarycensored matches difference of pprimarycensored", { x <- c(1, 2, 3) pwindow <- 5 swindow <- 0.5 @@ -58,7 +58,7 @@ test_that("dprimarycensoreddist matches difference of pprimarycensoreddist", { }) test_that( - "dprimarycensoreddist throws an error for invalid upper truncation point", + "dprimarycensored throws an error for invalid upper truncation point", { d <- 10 pwindow <- 1 @@ -75,7 +75,7 @@ test_that( } ) -test_that("dprimarycensoreddist returns 0 for negative d", { +test_that("dprimarycensored returns 0 for negative d", { d <- -1 pwindow <- 1 swindow <- 0.5 diff --git a/tests/testthat/test-fitdistdoublecens.R b/tests/testthat/test-fitdistdoublecens.R index d2a3abb..b845fde 100644 --- a/tests/testthat/test-fitdistdoublecens.R +++ b/tests/testthat/test-fitdistdoublecens.R @@ -13,7 +13,7 @@ test_that("fitdistdoublecens works correctly", { rate <- 0.44 # Generate samples - samples <- rprimarycensoreddist( + samples <- rprimarycensored( n, rgamma, shape = shape, rate = rate, pwindow = 1, swindow = 1, D = 8 @@ -75,7 +75,7 @@ test_that("fitdistdoublecens works with different distributions", { # Test with normal distribution true_mean <- 5 true_sd <- 2 - samples <- rprimarycensoreddist( + samples <- rprimarycensored( n, rnorm, mean = true_mean, sd = true_sd, pwindow = 2, swindow = 2, D = 10 diff --git a/tests/testthat/test-pcd-stan-tools.R b/tests/testthat/test-pcd-stan-tools.R index ded5a2d..5985e96 100644 --- a/tests/testthat/test-pcd-stan-tools.R +++ b/tests/testthat/test-pcd-stan-tools.R @@ -14,8 +14,8 @@ test_that("pcd_stan_functions returns unique function names", { # Check if specific Stan functions are included expected_functions <- c( "expgrowth_pdf", "expgrowth_lpdf", "expgrowth_cdf", "expgrowth_lcdf", - "expgrowth_rng", "primary_censored_ode", "dist_lcdf", - "primary_censored_dist_lcdf", "primary_censored_dist_lpmf" + "expgrowth_rng", "primarycensored_ode", "dist_lcdf", + "primarycensored_lcdf", "primarycensored_lpmf" ) for (func in expected_functions) { expect_true( @@ -27,7 +27,7 @@ test_that("pcd_stan_functions returns unique function names", { test_that("pcd_load_stan_functions loads specific functions", { specific_functions <- c( - "expgrowth_pdf", "expgrowth_lpdf", "primary_censored_ode" + "expgrowth_pdf", "expgrowth_lpdf", "primarycensored_ode" ) stan_code <- pcd_load_stan_functions(functions = specific_functions) expect_type(stan_code, "character") @@ -75,7 +75,7 @@ test_that("pcd_load_stan_functions writes to file when specified", { file_content <- readLines(output_file) expect_gt(length(file_content), 0) expect_true( - grepl("Stan functions from primarycensoreddist version", file_content[1], + grepl("Stan functions from primarycensored version", file_content[1], fixed = TRUE ) ) @@ -100,13 +100,13 @@ test_that("pcd_load_stan_functions loads functions from specific files", { info = paste("Function", func, "not found in loaded Stan code") ) } - expect_false(grepl("primary_censored_ode", stan_code, fixed = TRUE)) + expect_false(grepl("primarycensored_ode", stan_code, fixed = TRUE)) - primary_censored_functions <- c( - "primary_censored_ode", "dist_lcdf", "primary_censored_dist_lcdf" + primarycensored_functions <- c( + "primarycensored_ode", "dist_lcdf", "primarycensored_lcdf" ) - stan_code <- pcd_load_stan_functions(functions = primary_censored_functions) - for (func in primary_censored_functions) { + stan_code <- pcd_load_stan_functions(functions = primarycensored_functions) + for (func in primarycensored_functions) { expect_true( grepl(func, stan_code), info = paste("Function", func, "not found in loaded Stan code") @@ -130,7 +130,7 @@ test_that("pcd_stan_files returns correct files", { expect_true(all(grepl("expgrowth", expgrowth_files, fixed = TRUE))) # Test with functions from different files - mixed_functions <- c("expgrowth_pdf", "primary_censored_ode") + mixed_functions <- c("expgrowth_pdf", "primarycensored_ode") mixed_files <- pcd_stan_files(functions = mixed_functions) expect_type(mixed_files, "character") expect_gt(length(mixed_files), 1) diff --git a/tests/testthat/test-pcd_as_cmdstan_data.R b/tests/testthat/test-pcd_as_stan_data.R similarity index 95% rename from tests/testthat/test-pcd_as_cmdstan_data.R rename to tests/testthat/test-pcd_as_stan_data.R index c52f603..862f4b1 100644 --- a/tests/testthat/test-pcd_as_cmdstan_data.R +++ b/tests/testthat/test-pcd_as_stan_data.R @@ -8,7 +8,7 @@ test_that("pcd_as_stan_data correctly formats data", { ) dist_id <- 1 - primary_dist_id <- 1 + primray_id <- 1 param_bounds <- list(lower = c(0, 0), upper = c(10, 10)) primary_param_bounds <- list(lower = numeric(0), upper = numeric(0)) priors <- list(location = c(1, 1), scale = c(1, 1)) @@ -18,7 +18,7 @@ test_that("pcd_as_stan_data correctly formats data", { result <- pcd_as_stan_data( # nolint data, dist_id = dist_id, - primary_dist_id = primary_dist_id, + primray_id = primray_id, param_bounds = param_bounds, primary_param_bounds = primary_param_bounds, priors = priors, @@ -34,7 +34,7 @@ test_that("pcd_as_stan_data correctly formats data", { expect_identical(result$pwindow, data$pwindow) expect_identical(result$D, data$relative_obs_time) expect_identical(result$dist_id, dist_id) - expect_identical(result$primary_dist_id, primary_dist_id) + expect_identical(result$primray_id, primray_id) expect_identical(result$n_params, length(param_bounds$lower)) expect_identical(result$n_primary_params, length(primary_param_bounds$lower)) expect_identical(result$compute_log_lik, 0L) @@ -64,7 +64,7 @@ test_that("pcd_as_stan_data handles missing columns correctly", { pcd_as_stan_data( data, dist_id = 1, - primary_dist_id = 1, + primray_id = 1, param_bounds = list(lower = c(0, 0), upper = c(10, 10)), primary_param_bounds = list(lower = numeric(0), upper = numeric(0)), priors = list(location = c(1, 1), scale = c(1, 1)), @@ -87,7 +87,7 @@ test_that("pcd_as_stan_data handles optional parameters correctly", { result <- pcd_as_stan_data( # nolint data, dist_id = 1, - primary_dist_id = 1, + primray_id = 1, param_bounds = list(lower = c(0, 0), upper = c(10, 10)), primary_param_bounds = list(lower = numeric(0), upper = numeric(0)), priors = list(location = c(1, 1), scale = c(1, 1)), @@ -119,7 +119,7 @@ test_that("pcd_as_stan_data handles custom column names correctly", { pwindow = "primary_window", relative_obs_time = "obs_time", dist_id = 1, - primary_dist_id = 1, + primray_id = 1, param_bounds = list(lower = c(0, 0), upper = c(10, 10)), primary_param_bounds = list(lower = numeric(0), upper = numeric(0)), priors = list(location = c(1, 1), scale = c(1, 1)), diff --git a/tests/testthat/test-pcd_cmdstan_model.R b/tests/testthat/test-pcd_cmdstan_model.R index 3f2b492..2212b9d 100644 --- a/tests/testthat/test-pcd_cmdstan_model.R +++ b/tests/testthat/test-pcd_cmdstan_model.R @@ -41,7 +41,7 @@ test_that("pcd_cmdstan_model recovers true values for simple lognormal data", { true_meanlog <- 1.5 true_sdlog <- 0.5 - simulated_delays <- rprimarycensoreddist( + simulated_delays <- rprimarycensored( n = n, rdist = rlnorm, meanlog = true_meanlog, @@ -67,7 +67,7 @@ test_that("pcd_cmdstan_model recovers true values for simple lognormal data", { stan_data <- pcd_as_stan_data( delay_counts, dist_id = 1, # Lognormal - primary_dist_id = 1, # Uniform + primray_id = 1, # Uniform param_bounds = list(lower = c(-Inf, 0), upper = c(Inf, Inf)), primary_param_bounds = list(lower = numeric(0), upper = numeric(0)), priors = list(location = c(0, 1), scale = c(1, 1)), @@ -113,7 +113,7 @@ test_that( true_shape <- 2 true_rate <- 0.5 - simulated_delays <- rprimarycensoreddist( + simulated_delays <- rprimarycensored( n = n, rdist = rgamma, shape = true_shape, @@ -142,7 +142,7 @@ test_that( stan_data <- pcd_as_stan_data( delay_counts, dist_id = 2, # Gamma - primary_dist_id = 2, # Exponential growth + primray_id = 2, # Exponential growth param_bounds = list(lower = c(0, 0), upper = c(Inf, Inf)), primary_param_bounds = list(lower = 0, upper = Inf), priors = list(location = c(2, 1), scale = c(0.5, 0.5)), @@ -194,7 +194,7 @@ test_that( true_meanlog <- 1.6 true_sdlog <- 0.5 - simulated_delays <- rprimarycensoreddist( + simulated_delays <- rprimarycensored( n = n, rdist = rlnorm, meanlog = true_meanlog, @@ -221,7 +221,7 @@ test_that( stan_data <- pcd_as_stan_data( delay_counts, dist_id = 1, # Lognormal - primary_dist_id = 1, # Uniform + primray_id = 1, # Uniform param_bounds = list(lower = c(-Inf, 0), upper = c(Inf, Inf)), primary_param_bounds = list(lower = numeric(0), upper = numeric(0)), priors = list(location = c(1, 0.5), scale = c(1, 1)), @@ -277,7 +277,7 @@ test_that( D <- 30 simulated_data <- data.frame( - delay = rprimarycensoreddist( + delay = rprimarycensored( n = n_obs, rdist = rlnorm, meanlog = true_meanlog, @@ -302,7 +302,7 @@ test_that( stan_data <- pcd_as_stan_data( delay_counts, dist_id = 1, # Lognormal - primary_dist_id = 1, # Uniform + primray_id = 1, # Uniform param_bounds = list(lower = c(-Inf, 0), upper = c(Inf, Inf)), primary_param_bounds = list(lower = numeric(0), upper = numeric(0)), priors = list(location = c(0, 1), scale = c(1, 1)), diff --git a/tests/testthat/test-pprimarycensoreddist.R b/tests/testthat/test-pprimarycensored.R similarity index 74% rename from tests/testthat/test-pprimarycensoreddist.R rename to tests/testthat/test-pprimarycensored.R index 3805860..b48e1da 100644 --- a/tests/testthat/test-pprimarycensoreddist.R +++ b/tests/testthat/test-pprimarycensored.R @@ -1,18 +1,18 @@ -test_that("pprimarycensoreddist returns 0 for non-positive quantiles", { +test_that("pprimarycensored returns 0 for non-positive quantiles", { pwindow <- 1 D <- 10 cdf <- ppcens(c(-1, 0), plnorm, pwindow, D = D, meanlog = 1, sdlog = 1) expect_identical(cdf, c(0, 0)) }) -test_that("pprimarycensoreddist approaches 1 for large quantiles", { +test_that("pprimarycensored approaches 1 for large quantiles", { pwindow <- 1 D <- Inf cdf <- ppcens(1000, plnorm, pwindow, D = D, meanlog = 1, sdlog = 1) expect_equal(cdf, 1, tolerance = 1e-6) }) -test_that("pprimarycensoreddist is monotonically increasing", { +test_that("pprimarycensored is monotonically increasing", { pwindow <- 1 D <- 10 q <- seq(0, D, by = 0.5) @@ -20,14 +20,14 @@ test_that("pprimarycensoreddist is monotonically increasing", { expect_true(all(diff(cdf) >= 0)) }) -test_that("pprimarycensoreddist handles finite D correctly", { +test_that("pprimarycensored handles finite D correctly", { pwindow <- 1 D <- 10 cdf <- ppcens(D, plnorm, pwindow, D = D, meanlog = 1, sdlog = 1) expect_equal(cdf, 1, tolerance = 1e-6) }) -test_that("pprimarycensoreddist handles custom primary distributions", { +test_that("pprimarycensored handles custom primary distributions", { pwindow <- 5 D <- 20 cdf_uniform <- ppcens( @@ -44,7 +44,7 @@ test_that("pprimarycensoreddist handles custom primary distributions", { expect_false(all(cdf_uniform == cdf_expgrowth)) }) -test_that("pprimarycensoreddist is consistent with dprimarycensoreddist", { +test_that("pprimarycensored is consistent with dprimarycensored", { pwindow <- 1 D <- 10 q <- 0:9 diff --git a/tests/testthat/test-primary_censored_dist.R b/tests/testthat/test-primarycensored.R similarity index 75% rename from tests/testthat/test-primary_censored_dist.R rename to tests/testthat/test-primarycensored.R index 5a0bdf7..168f2ad 100644 --- a/tests/testthat/test-primary_censored_dist.R +++ b/tests/testthat/test-primarycensored.R @@ -1,4 +1,4 @@ -test_that("new_primary_censored_dist creates object with correct structure", { +test_that("new_primarycensored creates object with correct structure", { pdist_name <- "pgamma" pdist <- pgamma dprimary_name <- "dunif" @@ -6,7 +6,7 @@ test_that("new_primary_censored_dist creates object with correct structure", { shape <- 2 rate <- 1 - obj <- new_primary_censored_dist( + obj <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, @@ -18,7 +18,7 @@ test_that("new_primary_censored_dist creates object with correct structure", { expect_identical(obj$dprimary, dunif) expect_identical(obj$args, list(shape = shape, rate = rate)) - new_obj <- new_primary_censored_dist( + new_obj <- new_primarycensored( pgamma, dunif, list(), shape = shape, rate = rate ) @@ -26,7 +26,7 @@ test_that("new_primary_censored_dist creates object with correct structure", { }) test_that( - "primary_censored_cdf methods dispatch correctly to existing + "primarycensored_cdf methods dispatch correctly to existing analytical solutions", { pdist_name <- "pgamma" @@ -34,7 +34,7 @@ test_that( dprimary_name <- "dunif" dprimary <- dunif - obj_gamma <- new_primary_censored_dist( + obj_gamma <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, shape = 2, rate = 1 @@ -45,7 +45,7 @@ test_that( dprimary_name <- "dunif" dprimary <- dunif - obj_lnorm <- new_primary_censored_dist( + obj_lnorm <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, meanlog = 0, sdlog = 1 @@ -56,7 +56,7 @@ test_that( dprimary_name <- "dunif" dprimary <- dunif - obj_weibull <- new_primary_censored_dist( + obj_weibull <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, shape = 2, scale = 1 @@ -70,19 +70,19 @@ test_that( pwindow <- 2 expect_no_error( - primary_censored_cdf(obj_gamma, q = q_values, pwindow = pwindow) + primarycensored_cdf(obj_gamma, q = q_values, pwindow = pwindow) ) expect_no_error( - primary_censored_cdf(obj_lnorm, q = q_values, pwindow = pwindow) + primarycensored_cdf(obj_lnorm, q = q_values, pwindow = pwindow) ) expect_no_error( - primary_censored_cdf(obj_weibull, q = q_values, pwindow = pwindow) + primarycensored_cdf(obj_weibull, q = q_values, pwindow = pwindow) ) } ) test_that( - "primary_censored_cdf errors as expected when the wrong distributional + "primarycensored_cdf errors as expected when the wrong distributional parameters are supplied", { pdist_name <- "pgamma" @@ -90,83 +90,83 @@ test_that( dprimary_name <- "dunif" dprimary <- dunif - obj_gamma <- new_primary_censored_dist( + obj_gamma <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, rate = 1 ) expect_error( - primary_censored_cdf(obj_gamma, q = 1, pwindow = 1), + primarycensored_cdf(obj_gamma, q = 1, pwindow = 1), "shape parameter is required for Gamma distribution" ) - obj_gamma_no_rate <- new_primary_censored_dist( + obj_gamma_no_rate <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, shape = 2 ) expect_error( - primary_censored_cdf(obj_gamma_no_rate, q = 1, pwindow = 1), + primarycensored_cdf(obj_gamma_no_rate, q = 1, pwindow = 1), "scale or rate parameter is required for Gamma distribution" ) pdist_name <- "plnorm" pdist <- plnorm - obj_lnorm_no_meanlog <- new_primary_censored_dist( + obj_lnorm_no_meanlog <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, sdlog = 1 ) expect_error( - primary_censored_cdf(obj_lnorm_no_meanlog, q = 1, pwindow = 1), + primarycensored_cdf(obj_lnorm_no_meanlog, q = 1, pwindow = 1), "meanlog parameter is required for Log-Normal distribution" ) - obj_lnorm_no_sdlog <- new_primary_censored_dist( + obj_lnorm_no_sdlog <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, meanlog = 0 ) expect_error( - primary_censored_cdf(obj_lnorm_no_sdlog, q = 1, pwindow = 1), + primarycensored_cdf(obj_lnorm_no_sdlog, q = 1, pwindow = 1), "sdlog parameter is required for Log-Normal distribution" ) pdist_name <- "pweibull" pdist <- pweibull - obj_weibull_no_shape <- new_primary_censored_dist( + obj_weibull_no_shape <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, scale = 1 ) expect_error( - primary_censored_cdf(obj_weibull_no_shape, q = 1, pwindow = 1), + primarycensored_cdf(obj_weibull_no_shape, q = 1, pwindow = 1), "shape parameter is required for Weibull distribution" ) - obj_weibull_no_scale <- new_primary_censored_dist( + obj_weibull_no_scale <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, shape = 2 ) expect_error( - primary_censored_cdf(obj_weibull_no_scale, q = 1, pwindow = 1), + primarycensored_cdf(obj_weibull_no_scale, q = 1, pwindow = 1), "scale parameter is required for Weibull distribution" ) } ) test_that( - "primary_censored_cdf.default computes the same values as - primary_censored_cdf.pcens_pgamma_dunif", + "primarycensored_cdf.default computes the same values as + primarycensored_cdf.pcens_pgamma_dunif", { pdist_name <- "pgamma" pdist <- pgamma @@ -180,7 +180,7 @@ test_that( for (shape in shapes) { for (rate in rates) { for (pwindow in pwindows) { - obj <- new_primary_censored_dist( + obj <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, @@ -188,11 +188,11 @@ test_that( ) q_values <- seq(0, 30, by = 0.1) - result_numeric <- primary_censored_cdf( + result_numeric <- primarycensored_cdf( obj, q = q_values, pwindow = pwindow, use_numeric = TRUE ) - result_analytical <- primary_censored_cdf( + result_analytical <- primarycensored_cdf( obj, q = q_values, pwindow = pwindow, use_numeric = FALSE ) @@ -220,8 +220,8 @@ test_that( ) test_that( - "primary_censored_cdf.default computes the same values as - primary_censored_cdf.pcens_plnorm_dunif", + "primarycensored_cdf.default computes the same values as + primarycensored_cdf.pcens_plnorm_dunif", { pdist_name <- "plnorm" pdist <- plnorm @@ -235,7 +235,7 @@ test_that( for (meanlog in meanlogs) { for (sdlog in sdlogs) { for (pwindow in pwindows) { - obj <- new_primary_censored_dist( + obj <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, @@ -243,11 +243,11 @@ test_that( ) q_values <- seq(0, 30, by = 0.1) - result_numeric <- primary_censored_cdf( + result_numeric <- primarycensored_cdf( obj, q = q_values, pwindow = pwindow, use_numeric = TRUE ) - result_analytical <- primary_censored_cdf( + result_analytical <- primarycensored_cdf( obj, q = q_values, pwindow = pwindow, use_numeric = FALSE ) @@ -274,8 +274,8 @@ test_that( ) test_that( - "primary_censored_cdf.default computes the same values as - primary_censored_cdf.pcens_pweibull_dunif", + "primarycensored_cdf.default computes the same values as + primarycensored_cdf.pcens_pweibull_dunif", { pdist_name <- "pweibull" pdist <- pweibull @@ -289,7 +289,7 @@ test_that( for (shape in shapes) { for (scale in scales) { for (pwindow in pwindows) { - obj <- new_primary_censored_dist( + obj <- new_primarycensored( pdist, dprimary, list(), pdist_name, dprimary_name, @@ -297,11 +297,11 @@ test_that( ) q_values <- seq(0, 30, by = 0.1) - result_numeric <- primary_censored_cdf( + result_numeric <- primarycensored_cdf( obj, q = q_values, pwindow = pwindow, use_numeric = TRUE ) - result_analytical <- primary_censored_cdf( + result_analytical <- primarycensored_cdf( obj, q = q_values, pwindow = pwindow, use_numeric = FALSE ) diff --git a/tests/testthat/test-rpd-primarycensoreddist.R b/tests/testthat/test-rpd-primarycensored.R similarity index 93% rename from tests/testthat/test-rpd-primarycensoreddist.R rename to tests/testthat/test-rpd-primarycensored.R index b85c97d..5596154 100644 --- a/tests/testthat/test-rpd-primarycensoreddist.R +++ b/tests/testthat/test-rpd-primarycensored.R @@ -1,9 +1,9 @@ -# Test the interactions between dprimarycensoreddist, pprimarycensoreddist, +# Test the interactions between dprimarycensored, pprimarycensored, # and the random number generators for primary events test_that( - "rprimarycensoreddist is consistent with dprimarycensoreddist and - pprimarycensoreddist", + "rprimarycensored is consistent with dprimarycensored and + pprimarycensored", { # nolint n <- 10000 pwindow <- 4 @@ -40,8 +40,8 @@ test_that( test_that( - "rprimarycensoreddist is consistent with dprimarycensoreddist and - pprimarycensoreddist for exponential growth primary distribution", + "rprimarycensored is consistent with dprimarycensored and + pprimarycensored for exponential growth primary distribution", { # nolint n <- 1e6 pwindow <- 3 @@ -90,7 +90,7 @@ test_that( ) test_that( - "rprimarycensoreddist with wider windows and different delay distribution + "rprimarycensored with wider windows and different delay distribution mathches p and d numerically", { n <- 1e6 diff --git a/tests/testthat/test-rprimarycensoreddist.R b/tests/testthat/test-rprimarycensored.R similarity index 77% rename from tests/testthat/test-rprimarycensoreddist.R rename to tests/testthat/test-rprimarycensored.R index d2d8039..a6a1d63 100644 --- a/tests/testthat/test-rprimarycensoreddist.R +++ b/tests/testthat/test-rprimarycensored.R @@ -1,4 +1,4 @@ -test_that("rprimarycensoreddist generates samples within the correct range", { +test_that("rprimarycensored generates samples within the correct range", { n <- 1000 pwindow <- 5 D <- 10 @@ -10,7 +10,7 @@ test_that("rprimarycensoreddist generates samples within the correct range", { expect_true(all(samples >= 0 & samples < D)) }) -test_that("rprimarycensoreddist handles different primary distributions", { +test_that("rprimarycensored handles different primary distributions", { n <- 1000 pwindow <- 5 D <- 10 @@ -24,7 +24,7 @@ test_that("rprimarycensoreddist handles different primary distributions", { expect_true(all(samples >= 0 & samples < D)) }) -test_that("rprimarycensoreddist handles very truncated distributions", { +test_that("rprimarycensored handles very truncated distributions", { n <- 1000 pwindow <- 0.1 D <- 1 @@ -38,7 +38,7 @@ test_that("rprimarycensoreddist handles very truncated distributions", { }) test_that( - "rprimarycensoreddist supports non-secondary event censored distributions", + "rprimarycensored supports non-secondary event censored distributions", { n <- 1000 pwindow <- 5 diff --git a/tests/testthat/test-stan-primary_censored_dist_analytical_cdf.R b/tests/testthat/test-stan-primarycensored_analytical_cdf.R similarity index 82% rename from tests/testthat/test-stan-primary_censored_dist_analytical_cdf.R rename to tests/testthat/test-stan-primarycensored_analytical_cdf.R index 7698746..a9212ed 100644 --- a/tests/testthat/test-stan-primary_censored_dist_analytical_cdf.R +++ b/tests/testthat/test-stan-primarycensored_analytical_cdf.R @@ -5,7 +5,7 @@ if (on_ci()) { } test_that( - "Stan primary_censored_dist_analytical_lcdf matches R implementation for + "Stan primarycensored_analytical_lcdf matches R implementation for Gamma", { shapes <- c(0.5, 1, 2, 5) @@ -15,7 +15,7 @@ test_that( for (shape in shapes) { for (rate in rates) { for (pwindow in pwindows) { - obj <- new_primary_censored_dist( + obj <- new_primarycensored( pgamma, dunif, list(), "pgamma", "dunif", @@ -23,13 +23,13 @@ test_that( ) q_values <- seq(0, 30, by = 1) - r_result <- primary_censored_cdf( + r_result <- primarycensored_cdf( obj, q = q_values, pwindow = pwindow, use_numeric = FALSE ) stan_result <- vapply(q_values, function(q) { - primary_censored_dist_analytical_cdf( + primarycensored_analytical_cdf( q, 2, c(shape, rate), pwindow, Inf, 1, numeric(0) ) }, numeric(1)) @@ -50,7 +50,7 @@ test_that( ) test_that( - "Stan primary_censored_dist_analytical_lcdf matches R implementation for + "Stan primarycensored_analytical_lcdf matches R implementation for Lognormal", { meanlogs <- c(-1, 0, 1, 2) @@ -60,7 +60,7 @@ test_that( for (meanlog in meanlogs) { for (sdlog in sdlogs) { for (pwindow in pwindows) { - obj <- new_primary_censored_dist( + obj <- new_primarycensored( plnorm, dunif, list(), "plnorm", "dunif", @@ -68,13 +68,13 @@ test_that( ) q_values <- seq(0, 30, by = 1) - r_result <- primary_censored_cdf( + r_result <- primarycensored_cdf( obj, q = q_values, pwindow = pwindow, use_numeric = FALSE ) stan_result <- vapply(q_values, function(q) { - primary_censored_dist_analytical_cdf( + primarycensored_analytical_cdf( q, 1, c(meanlog, sdlog), pwindow, Inf, 1, numeric(0) ) }, numeric(1)) @@ -95,7 +95,7 @@ test_that( ) test_that( - "Stan primary_censored_dist_analytical_lcdf matches R implementation for + "Stan primarycensored_analytical_lcdf matches R implementation for Weibull", { shapes <- c(0.5, 1, 2, 3) @@ -105,7 +105,7 @@ test_that( for (shape in shapes) { for (scale in scales) { for (pwindow in pwindows) { - obj <- new_primary_censored_dist( + obj <- new_primarycensored( pweibull, dunif, list(), "pweibull", "dunif", @@ -113,13 +113,13 @@ test_that( ) q_values <- seq(0, 30, by = 1) - r_result <- primary_censored_cdf( + r_result <- primarycensored_cdf( obj, q = q_values, pwindow = pwindow, use_numeric = FALSE ) stan_result <- vapply(q_values, function(q) { - primary_censored_dist_analytical_cdf( + primarycensored_analytical_cdf( q, 3, c(shape, scale), pwindow, Inf, 1, numeric(0) ) }, numeric(1)) diff --git a/tests/testthat/test-stan-primary_censored_ode.R b/tests/testthat/test-stan-primarycensored_ode.R similarity index 68% rename from tests/testthat/test-stan-primary_censored_ode.R rename to tests/testthat/test-stan-primarycensored_ode.R index d540839..bb41329 100644 --- a/tests/testthat/test-stan-primary_censored_ode.R +++ b/tests/testthat/test-stan-primarycensored_ode.R @@ -5,15 +5,15 @@ if (on_ci()) { } -test_that("Stan primary_censored_ode produces expected output", { +test_that("Stan primarycensored_ode produces expected output", { t <- 4.5 y <- 0.5 theta <- c(1.0, 0.5) # Example parameters for lognormal distribution x_r <- c(5.0, 1.0) # d and pwindow - # dist_id, primary_dist_id, dist_params_len, primary_params_len + # dist_id, primray_id, dist_params_len, primary_params_len x_i <- c(1, 1, 2, 0) - result <- primary_censored_ode(t, y, theta, x_r, x_i) + result <- primarycensored_ode(t, y, theta, x_r, x_i) expect_type(result, "double") expect_length(result, 1) @@ -22,36 +22,36 @@ test_that("Stan primary_censored_ode produces expected output", { expect_gt(result[1], 0) }) -test_that("Stan primary_censored_ode handles edge cases", { +test_that("Stan primarycensored_ode handles edge cases", { theta <- c(1.0, 0.5) # Example parameters for lognormal distribution x_r <- c(1.0, 1.0) # d and pwindow - # dist_id, primary_dist_id, dist_params_len, primary_params_len + # dist_id, primray_id, dist_params_len, primary_params_len x_i <- c(1, 1, 2, 0) - result_near_zero <- primary_censored_ode(1e-10, 1e-10, theta, x_r, x_i) + result_near_zero <- primarycensored_ode(1e-10, 1e-10, theta, x_r, x_i) expect_true(is.finite(result_near_zero[1])) - result_at_d <- primary_censored_ode(1, 0.0, theta, x_r, x_i) + result_at_d <- primarycensored_ode(1, 0.0, theta, x_r, x_i) expect_true(is.finite(result_at_d[1])) expect_gt(result_at_d[1], result_near_zero[1]) - result_near_d <- primary_censored_ode( + result_near_d <- primarycensored_ode( 0.99999, 0.00001, theta, x_r, x_i ) expect_true(is.finite(result_near_d[1])) expect_gt(result_at_d[1], result_near_d[1]) }) -test_that("Stan primary_censored_ode is continuous", { +test_that("Stan primarycensored_ode is continuous", { theta <- c(1.5, 0.75) # Example parameters for lognormal distribution x_r <- c(5.0, 5.0) # d and pwindow - # dist_id, primary_dist_id, dist_params_len, primary_params_len + # dist_id, primray_id, dist_params_len, primary_params_len x_i <- c(1, 1, 2, 0) t_values <- seq(0.1, 4.9, by = 0.1) results <- vapply( t_values, - function(t) primary_censored_ode(t, 0.1, theta, x_r, x_i)[1], + function(t) primarycensored_ode(t, 0.1, theta, x_r, x_i)[1], numeric(1) ) @@ -60,7 +60,7 @@ test_that("Stan primary_censored_ode is continuous", { expect_lt(max(abs(diffs)), 1e-2) }) -test_that("Stan primary_censored_ode handles different distributions", { +test_that("Stan primarycensored_ode handles different distributions", { t <- 4.5 y <- 0.5 x_r <- c(5.0, 1.0) # d and pwindow @@ -68,7 +68,7 @@ test_that("Stan primary_censored_ode handles different distributions", { # Test for lognormal distribution theta_lognormal <- c(1.0, 0.5) x_i_lognormal <- c(1, 1, 2, 0) - result_lognormal <- primary_censored_ode( + result_lognormal <- primarycensored_ode( t, y, theta_lognormal, x_r, x_i_lognormal ) expect_true(is.finite(result_lognormal[1])) @@ -76,7 +76,7 @@ test_that("Stan primary_censored_ode handles different distributions", { # Test for gamma distribution theta_gamma <- c(2.0, 1.0) x_i_gamma <- c(2, 1, 2, 0) - result_gamma <- primary_censored_ode( + result_gamma <- primarycensored_ode( t, y, theta_gamma, x_r, x_i_gamma ) expect_true(is.finite(result_gamma[1])) @@ -84,28 +84,28 @@ test_that("Stan primary_censored_ode handles different distributions", { # Test for weibull distribution theta_weibull <- c(1.5, 2.0) x_i_weibull <- c(5, 1, 2, 0) - result_weibull <- primary_censored_ode( + result_weibull <- primarycensored_ode( t, y, theta_weibull, x_r, x_i_weibull ) expect_true(is.finite(result_weibull[1])) }) -test_that("Stan primary_censored_ode handles extreme parameter values", { +test_that("Stan primarycensored_ode handles extreme parameter values", { t <- 4.5 y <- 0.5 x_r <- c(5.0, 1.0) # d and pwindow - # dist_id, primary_dist_id, dist_params_len, primary_params_len + # dist_id, primray_id, dist_params_len, primary_params_len x_i <- c(1, 1, 2, 0) # Test with very small scale parameter theta_small_scale <- c(1.0, 1e-10) - result_small_scale <- primary_censored_ode( + result_small_scale <- primarycensored_ode( t, y, theta_small_scale, x_r, x_i ) expect_true(is.finite(result_small_scale[1])) # Test with very large scale parameter theta_large_scale <- c(1.0, 1e10) - result_large_scale <- primary_censored_ode( + result_large_scale <- primarycensored_ode( t, y, theta_large_scale, x_r, x_i ) expect_true(is.finite(result_large_scale[1])) @@ -113,14 +113,14 @@ test_that("Stan primary_censored_ode handles extreme parameter values", { # Test with very small shape parameter (for distributions that use it) x_i_weibull <- c(5, 1, 2, 0) theta_small_shape <- c(1e-10, 1.0) - result_small_shape <- primary_censored_ode( + result_small_shape <- primarycensored_ode( t, y, theta_small_shape, x_r, x_i_weibull ) expect_true(is.finite(result_small_shape[1])) # Test with very large shape parameter theta_large_shape <- c(1e10, 1.0) - result_large_shape <- primary_censored_ode( + result_large_shape <- primarycensored_ode( t, y, theta_large_shape, x_r, x_i_weibull ) expect_true(is.finite(result_large_shape[1])) diff --git a/tests/testthat/test-stan-rpd-primarycensoreddist.R b/tests/testthat/test-stan-rpd-primarycensored.R similarity index 66% rename from tests/testthat/test-stan-rpd-primarycensoreddist.R rename to tests/testthat/test-stan-rpd-primarycensored.R index 600ed75..2d92e54 100644 --- a/tests/testthat/test-stan-rpd-primarycensoreddist.R +++ b/tests/testthat/test-stan-rpd-primarycensored.R @@ -5,7 +5,7 @@ if (on_ci()) { } -test_that("Stan primary_censored_dist_cdf matches R pprimarycensoreddist", { +test_that("Stan primarycensored_cdf matches R pprimarycensored", { d_values <- list( seq(0, 10, by = 0.5), seq(0, 5, by = 0.25), @@ -18,14 +18,14 @@ test_that("Stan primary_censored_dist_cdf matches R pprimarycensoreddist", { dist_id <- 1 # Lognormal params <- c(0, 1) # meanlog, sdlog pwindow <- 1 - primary_dist_id <- 1 # Uniform + primray_id <- 1 # Uniform primary_params <- array(numeric(0)) stan_cdf <- sapply( - d, primary_censored_dist_cdf, dist_id, params, pwindow, D, - primary_dist_id, primary_params + d, primarycensored_cdf, dist_id, params, pwindow, D, + primray_id, primary_params ) - r_cdf <- pprimarycensoreddist( + r_cdf <- pprimarycensored( d, plnorm, pwindow = pwindow, D = D, meanlog = params[1], sdlog = params[2] ) @@ -43,7 +43,7 @@ test_that("Stan primary_censored_dist_cdf matches R pprimarycensoreddist", { }) test_that( - "Stan primary_censored_dist_lcdf matches R pprimarycensoreddist with + "Stan primarycensored_lcdf matches R pprimarycensored with log.p = TRUE", { d <- seq(0, 10, by = 0.5) @@ -51,14 +51,14 @@ test_that( params <- c(0, 1) # meanlog, sdlog pwindow <- 1 D <- 12 - primary_dist_id <- 1 # Uniform + primray_id <- 1 # Uniform primary_params <- numeric(0) stan_lcdf <- sapply( - d, primary_censored_dist_lcdf, dist_id, params, pwindow, D, - primary_dist_id, primary_params + d, primarycensored_lcdf, dist_id, params, pwindow, D, + primray_id, primary_params ) - r_cdf <- pprimarycensoreddist( + r_cdf <- pprimarycensored( d, plnorm, pwindow = pwindow, D = D, meanlog = params[1], sdlog = params[2] @@ -71,14 +71,14 @@ test_that( params <- c(2, 1) # shape, scale pwindow <- 2 D <- 12 - primary_dist_id <- 1 # Uniform + primray_id <- 1 # Uniform primary_params <- numeric(0) stan_lcdf <- sapply( - d, primary_censored_dist_lcdf, dist_id, params, pwindow, D, - primary_dist_id, primary_params + d, primarycensored_lcdf, dist_id, params, pwindow, D, + primray_id, primary_params ) - r_cdf <- pprimarycensoreddist( + r_cdf <- pprimarycensored( d, pgamma, pwindow = pwindow, D = D, shape = params[1], scale = params[2] ) @@ -88,7 +88,7 @@ test_that( ) test_that( - "Stan primary_censored_dist_lpmf throws an error for invalid upper truncation + "Stan primarycensored_lpmf throws an error for invalid upper truncation point", { d <- 10 @@ -97,12 +97,12 @@ test_that( pwindow <- 1 d_upper <- 11 D <- 10 - primary_dist_id <- 1 # Uniform + primray_id <- 1 # Uniform primary_params <- numeric(0) expect_error( - primary_censored_dist_lpmf( - d, dist_id, params, pwindow, d_upper, D, primary_dist_id, primary_params + primarycensored_lpmf( + d, dist_id, params, pwindow, d_upper, D, primray_id, primary_params ), "Upper truncation point is greater than D" ) @@ -110,13 +110,13 @@ test_that( ) test_that( - "Stan primary_censored_dist matches R primarycensoreddist when d is the same + "Stan primarycensored matches R primarycensored when d is the same as D - 1", { dist_id <- 1 # Lognormal params <- c(0, 1) # meanlog, sdlog pwindow <- 1 - primary_dist_id <- 1 # Uniform + primray_id <- 1 # Uniform primary_params <- numeric(0) d_values <- 1:10 @@ -129,11 +129,11 @@ test_that( "with truncation" } for (d in d_values) { - stan_pmf <- primary_censored_dist_pmf( - d, dist_id, params, pwindow, d + 1, D, primary_dist_id, + stan_pmf <- primarycensored_pmf( + d, dist_id, params, pwindow, d + 1, D, primray_id, primary_params ) - r_pmf <- dprimarycensoreddist( + r_pmf <- dprimarycensored( d, plnorm, pwindow = pwindow, swindow = 1, D = D, meanlog = params[1], sdlog = params[2] @@ -153,18 +153,18 @@ test_that( } ) -test_that("Stan primary_censored_dist_pmf matches R dprimarycensoreddist", { +test_that("Stan primarycensored_pmf matches R dprimarycensored", { d <- 0:10 dist_id <- 1 # Lognormal params <- c(0, 1) # meanlog, sdlog pwindow <- 1 d_upper <- d + 1 D <- Inf - primary_dist_id <- 1 # Uniform + primray_id <- 1 # Uniform primary_params <- numeric(0) stan_pmf <- mapply( - primary_censored_dist_pmf, + primarycensored_pmf, d = d, d_upper = d + 1, MoreArgs = list( @@ -172,11 +172,11 @@ test_that("Stan primary_censored_dist_pmf matches R dprimarycensoreddist", { params = params, pwindow = pwindow, D = D, - primary_dist_id = primary_dist_id, + primray_id = primray_id, primary_params = primary_params ) ) - r_pmf <- dprimarycensoreddist( + r_pmf <- dprimarycensored( d, plnorm, pwindow = pwindow, swindow = 1, D = D, meanlog = params[1], sdlog = params[2] @@ -186,7 +186,7 @@ test_that("Stan primary_censored_dist_pmf matches R dprimarycensoreddist", { }) test_that( - "Stan primary_censored_dist_lpmf matches R dprimarycensoreddist with + "Stan primarycensored_lpmf matches R dprimarycensored with log = TRUE", { d <- 0:10 @@ -195,11 +195,11 @@ test_that( pwindow <- 1 d_upper <- d + 1 D <- Inf - primary_dist_id <- 1 # Uniform + primray_id <- 1 # Uniform primary_params <- numeric(0) stan_lpmf <- mapply( - primary_censored_dist_lpmf, + primarycensored_lpmf, d = d, d_upper = d_upper, MoreArgs = list( @@ -207,12 +207,12 @@ test_that( params = params, pwindow = pwindow, D = D, - primary_dist_id = primary_dist_id, + primray_id = primray_id, primary_params = primary_params ) ) r_lpmf <- log( - dprimarycensoreddist( + dprimarycensored( d, plnorm, pwindow = pwindow, swindow = 1, D = D, meanlog = params[1], sdlog = params[2] @@ -224,20 +224,20 @@ test_that( ) test_that( - "Stan primary_censored_sone_pmf_vectorized matches R dprimarycensoreddist", + "Stan primarycensored_sone_lpmf_vectorized matches R dprimarycensored", { max_delay <- 10 dist_id <- 1 # Lognormal params <- c(0, 1) # meanlog, sdlog pwindow <- 1 D <- Inf - primary_dist_id <- 1 # Uniform + primray_id <- 1 # Uniform primary_params <- numeric(0) - stan_pmf <- primary_censored_sone_pmf_vectorized( - max_delay, D, dist_id, params, pwindow, primary_dist_id, primary_params + stan_pmf <- primarycensored_sone_lpmf_vectorized( + max_delay, D, dist_id, params, pwindow, primray_id, primary_params ) - r_pmf <- dprimarycensoreddist( + r_pmf <- dprimarycensored( 0:max_delay, plnorm, pwindow = pwindow, swindow = 1, D = D, meanlog = params[1], sdlog = params[2] @@ -248,7 +248,7 @@ test_that( ) test_that( - "Stan primary_censored_sone_pmf_vectorized matches R dprimarycensoreddist + "Stan primarycensored_sone_lpmf_vectorized matches R dprimarycensored with finite D", { max_delay <- 10 @@ -256,13 +256,13 @@ test_that( params <- c(0, 1) # meanlog, sdlog pwindow <- 1 D <- 15 - primary_dist_id <- 1 # Uniform + primray_id <- 1 # Uniform primary_params <- numeric(0) - stan_pmf <- primary_censored_sone_pmf_vectorized( - max_delay, D, dist_id, params, pwindow, primary_dist_id, primary_params + stan_pmf <- primarycensored_sone_lpmf_vectorized( + max_delay, D, dist_id, params, pwindow, primray_id, primary_params ) - r_pmf <- dprimarycensoreddist( + r_pmf <- dprimarycensored( 0:max_delay, plnorm, pwindow = pwindow, swindow = 1, D = D, meanlog = params[1], sdlog = params[2] @@ -273,7 +273,7 @@ test_that( ) test_that( - "Stan primary_censored_sone_pmf_vectorized matches R dprimarycensoreddist + "Stan primarycensored_sone_lpmf_vectorized matches R dprimarycensored with D equal to max_delay + 1", { max_delay <- 10 @@ -281,13 +281,13 @@ test_that( params <- c(0, 1) # meanlog, sdlog pwindow <- 1 D <- max_delay + 1 - primary_dist_id <- 1 # Uniform + primray_id <- 1 # Uniform primary_params <- numeric(0) - stan_pmf <- primary_censored_sone_pmf_vectorized( - max_delay, D, dist_id, params, pwindow, primary_dist_id, primary_params + stan_pmf <- primarycensored_sone_lpmf_vectorized( + max_delay, D, dist_id, params, pwindow, primray_id, primary_params ) - r_pmf <- dprimarycensoreddist( + r_pmf <- dprimarycensored( 0:max_delay, plnorm, pwindow = pwindow, swindow = 1, D = D, meanlog = params[1], sdlog = params[2] @@ -298,7 +298,7 @@ test_that( ) test_that( - "Stan primary_censored_sone_lpmf_vectorized matches R dprimarycensoreddist + "Stan primarycensored_sone_lpmf_vectorized matches R dprimarycensored with log = TRUE", { max_delay <- 10 @@ -306,13 +306,13 @@ test_that( params <- c(0, 1) # meanlog, sdlog pwindow <- 1 D <- Inf - primary_dist_id <- 1 # Uniform + primray_id <- 1 # Uniform primary_params <- numeric(0) - stan_lpmf <- primary_censored_sone_lpmf_vectorized( - max_delay, D, dist_id, params, pwindow, primary_dist_id, primary_params + stan_lpmf <- primarycensored_sone_lpmf_vectorized( + max_delay, D, dist_id, params, pwindow, primray_id, primary_params ) - r_lpmf <- dprimarycensoreddist( + r_lpmf <- dprimarycensored( 0:max_delay, plnorm, pwindow = pwindow, swindow = 1, D = D, meanlog = params[1], sdlog = params[2], log = TRUE diff --git a/touchstone/script.R b/touchstone/script.R index 61d3d0d..d56278a 100644 --- a/touchstone/script.R +++ b/touchstone/script.R @@ -4,26 +4,26 @@ # installs branches to benchmark touchstone::branch_install() -# Benchmark for pprimarycensoreddist with lognormal distribution +# Benchmark for pprimarycensored with lognormal distribution touchstone::benchmark_run( expr_before_benchmark = { - library(primarycensoreddist) + library(primarycensored) q <- seq(0, 10, by = 0.01) }, - pprimarycensoreddist_lnorm = { - pprimarycensoreddist(q, plnorm, meanlog = 0, sdlog = 1, D = 12) + pprimarycensored_lnorm = { + pprimarycensored(q, plnorm, meanlog = 0, sdlog = 1, D = 12) }, n = 20 ) -# Benchmark for pprimarycensoreddist with exponential growth +# Benchmark for pprimarycensored with exponential growth touchstone::benchmark_run( expr_before_benchmark = { - library(primarycensoreddist) + library(primarycensored) q <- seq(0, 10, by = 0.01) }, - pprimarycensoreddist_plnrom_expgrowth = { - pprimarycensoreddist( + pprimarycensored_plnrom_expgrowth = { + pprimarycensored( q, plnorm, dprimary = dexpgrowth, dprimary_args = list(r = 0.2), @@ -33,26 +33,26 @@ touchstone::benchmark_run( n = 20 ) -# Benchmark for dprimarycensoreddist with Weibull distribution +# Benchmark for dprimarycensored with Weibull distribution touchstone::benchmark_run( expr_before_benchmark = { - library(primarycensoreddist) + library(primarycensored) x <- seq(0, 10, by = 1) }, - dprimarycensoreddist_weibull = { - dprimarycensoreddist(x, pweibull, shape = 1.5, scale = 2.0, D = 12) + dprimarycensored_weibull = { + dprimarycensored(x, pweibull, shape = 1.5, scale = 2.0, D = 12) }, n = 20 ) -# Benchmark for dprimarycensoreddist with exponential growth +# Benchmark for dprimarycensored with exponential growth touchstone::benchmark_run( expr_before_benchmark = { - library(primarycensoreddist) + library(primarycensored) x <- seq(0, 10, by = 1) }, - dprimarycensoreddist_pweibull_expgrowth = { - dprimarycensoreddist( + dprimarycensored_pweibull_expgrowth = { + dprimarycensored( x, pweibull, dprimary = dexpgrowth, dprimary_args = list(r = 0.2), @@ -65,7 +65,7 @@ touchstone::benchmark_run( # Benchmark for fitdistdoublecens with normal distribution touchstone::benchmark_run( expr_before_benchmark = { - library(primarycensoreddist) + library(primarycensored) library(fitdistrplus) set.seed(123) n <- 1000 @@ -74,7 +74,7 @@ touchstone::benchmark_run( pwindow <- 1 swindow <- 1 D <- 10 - samples <- rprimarycensoreddist( + samples <- rprimarycensored( n, rnorm, mean = true_mean, sd = true_sd, pwindow = pwindow, swindow = swindow, D = D @@ -99,7 +99,7 @@ touchstone::benchmark_run( # Benchmark for fitdistdoublecens with exponential growth touchstone::benchmark_run( expr_before_benchmark = { - library(primarycensoreddist) + library(primarycensored) library(fitdistrplus) set.seed(456) n <- 1000 @@ -108,7 +108,7 @@ touchstone::benchmark_run( pwindow <- 1 swindow <- 1 D <- 8 - samples <- rprimarycensoreddist( + samples <- rprimarycensored( n, rgamma, shape = true_shape, rate = true_rate, pwindow = pwindow, swindow = swindow, D = D, @@ -133,7 +133,7 @@ touchstone::benchmark_run( # Benchmark for fitting lognormal distribution touchstone::benchmark_run( expr_before_benchmark = { - library(primarycensoreddist) + library(primarycensored) library(dplyr) library(cmdstanr) @@ -145,7 +145,7 @@ touchstone::benchmark_run( true_meanlog1 <- 1.5 true_sdlog1 <- 0.5 - simulated_delays1 <- rprimarycensoreddist( + simulated_delays1 <- rprimarycensored( n = n1, rdist = rlnorm, meanlog = true_meanlog1, @@ -170,7 +170,7 @@ touchstone::benchmark_run( stan_data1 <- pcd_as_stan_data( delay_counts1, dist_id = 1, - primary_dist_id = 1, + primray_id = 1, param_bounds = list(lower = c(-Inf, 0), upper = c(Inf, Inf)), primary_param_bounds = list(lower = numeric(0), upper = numeric(0)), priors = list(location = c(0, 1), scale = c(1, 1)), @@ -197,7 +197,7 @@ touchstone::benchmark_run( # Benchmark for fitting gamma distribution touchstone::benchmark_run( expr_before_benchmark = { - library(primarycensoreddist) + library(primarycensored) library(dplyr) library(cmdstanr) @@ -209,7 +209,7 @@ touchstone::benchmark_run( true_shape2 <- 2 true_rate2 <- 0.5 - simulated_delays2 <- rprimarycensoreddist( + simulated_delays2 <- rprimarycensored( n = n2, rdist = rgamma, shape = true_shape2, @@ -237,7 +237,7 @@ touchstone::benchmark_run( stan_data2 <- pcd_as_stan_data( delay_counts2, dist_id = 2, - primary_dist_id = 2, + primray_id = 2, param_bounds = list(lower = c(0, 0), upper = c(Inf, Inf)), primary_param_bounds = list(lower = 0, upper = Inf), priors = list(location = c(2, 1), scale = c(0.5, 0.5)), diff --git a/vignettes/analytic-solutions.Rmd b/vignettes/analytic-solutions.Rmd index efbae01..615d57d 100644 --- a/vignettes/analytic-solutions.Rmd +++ b/vignettes/analytic-solutions.Rmd @@ -273,6 +273,6 @@ Which was also found by Cori _et al_ [@cori2013new]. # Learning more - For more mathematical background on the analytic solutions see the `vignette("why-it-works")`. -- For a more introductory explanation of the primary event censored distribution see the `vignette("primarycensoreddist")`. +- For a more introductory explanation of the primary event censored distribution see the `vignette("primarycensored")`. # References diff --git a/vignettes/chunks/_readme-install-primarycensoreddist.Rmd b/vignettes/chunks/_readme-install-primarycensoreddist.Rmd index e91684a..1abb53f 100644 --- a/vignettes/chunks/_readme-install-primarycensoreddist.Rmd +++ b/vignettes/chunks/_readme-install-primarycensoreddist.Rmd @@ -2,7 +2,7 @@ You can install the latest released version using the normal `R` function, thoug ```{r, eval = FALSE} install.packages( - "primarycensoreddist", + "primarycensored", repos = "https://epinowcast.r-universe.dev" ) ``` @@ -11,16 +11,16 @@ Alternatively, you can use the [`remotes` package](https://remotes.r-lib.org/) t ```{r, eval = FALSE} remotes::install_github( - "epinowcast/primarycensoreddist", + "epinowcast/primarycensored", dependencies = TRUE ) ``` -Similarly, you can install historical versions by specifying the release tag (e.g. this installs [`0.2.0`](https://github.com/epinowcast/primarycensoreddist/releases/tag/v0.2.0)): +Similarly, you can install historical versions by specifying the release tag (e.g. this installs [`0.2.0`](https://github.com/epinowcast/primarycensored/releases/tag/v0.2.0)): ```{r, eval = FALSE} remotes::install_github( - "epinowcast/primarycensoreddist", + "epinowcast/primarycensored", dependencies = TRUE, ref = "v0.2.0" ) ``` diff --git a/vignettes/fitting-dists-with-fitdistrplus.Rmd b/vignettes/fitting-dists-with-fitdistrplus.Rmd index 0e2ee60..c9ae29f 100644 --- a/vignettes/fitting-dists-with-fitdistrplus.Rmd +++ b/vignettes/fitting-dists-with-fitdistrplus.Rmd @@ -1,6 +1,6 @@ --- title: "Fitting distributions using primarycensorseddist and fitdistrplus" -description: "A guide on how to fit distributions using primarycensoreddist and fitdistrplus." +description: "A guide on how to fit distributions using primarycensored and fitdistrplus." author: Sam Abbott output: bookdown::html_document2: @@ -12,7 +12,7 @@ bibliography: library.bib csl: https://raw.githubusercontent.com/citation-style-language/styles/master/apa-numeric-superscript-brackets.csl link-citations: true vignette: > - %\VignetteIndexEntry{Fitting distributions using primarycensoreddist and fitdistrplus} + %\VignetteIndexEntry{Fitting distributions using primarycensored and fitdistrplus} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- @@ -21,13 +21,13 @@ vignette: > ## What are we going to do in this Vignette -In this vignette, we'll demonstrate how to use `primarycensoreddist` in conjunction with `fitdistrplus` for fitting distributions. We'll cover the following key points: +In this vignette, we'll demonstrate how to use `primarycensored` in conjunction with `fitdistrplus` for fitting distributions. We'll cover the following key points: 1. Simulating censored delay distribution data 2. Fitting a naive model using `fitdistrplus` 3. Evaluating the naive model's performance -4. Fitting an improved model using `primarycensoreddist` functionality -5. Comparing the `primarycensoreddist` model's performance to the naive model +4. Fitting an improved model using `primarycensored` functionality +5. Comparing the `primarycensored` model's performance to the naive model ## What might I need to know before starting @@ -35,10 +35,10 @@ This vignette assumes some familiarity with the `fitdistrplus` package. If you a ## Packages used in this vignette -Alongside the `primarycensoreddist` package we will use the `fitdistrplus` package for fitting distributions. We will also use the `ggplot2` package for plotting and `dplyr` for data manipulation. +Alongside the `primarycensored` package we will use the `fitdistrplus` package for fitting distributions. We will also use the `ggplot2` package for plotting and `dplyr` for data manipulation. ```{r setup, message = FALSE} -library(primarycensoreddist) +library(primarycensored) library(fitdistrplus) library(ggplot2) library(dplyr) @@ -46,7 +46,7 @@ library(dplyr) # Simulating censored and truncated delay distribution data -We'll start by simulating some censored and truncated delay distribution data. We'll use the `rprimarycensoreddist` function (actually we will use the `rpcens ` alias for brevity). +We'll start by simulating some censored and truncated delay distribution data. We'll use the `rprimarycensored` function (actually we will use the `rpcens ` alias for brevity). ```{r sample-lognormal} set.seed(123) # For reproducibility @@ -149,17 +149,17 @@ summary(fit) We see that the naive model has fit poorly due to the primary censoring and right truncation in the data. -# Fitting an improved model using `primarycensoreddist` and `fitdistrplus` +# Fitting an improved model using `primarycensored` and `fitdistrplus` -We'll now fit an improved model using the `primarycensoreddist` package. To do this we need to define the custom distribution functions using the `primarycensoreddist` package that are required by `fitdistrplus`. Rather than using `fitdistcens` we use `fitdist` because our functions are handling the censoring themselves. +We'll now fit an improved model using the `primarycensored` package. To do this we need to define the custom distribution functions using the `primarycensored` package that are required by `fitdistrplus`. Rather than using `fitdistcens` we use `fitdist` because our functions are handling the censoring themselves. ```{r fit-improved-model} -# Define custom distribution functions using primarycensoreddist +# Define custom distribution functions using primarycensored # The try catch is required by fitdistrplus dpcens_gamma <- function(x, shape, rate) { result <- tryCatch( { - dprimarycensoreddist( + dprimarycensored( x, pgamma, shape = shape, rate = rate, pwindow = 1, swindow = 1, D = 8 @@ -175,7 +175,7 @@ dpcens_gamma <- function(x, shape, rate) { ppcens_gamma <- function(q, shape, rate) { result <- tryCatch( { - pprimarycensoreddist( + pprimarycensored( q, pgamma, shape = shape, rate = rate, pwindow = 1, D = 8 @@ -200,9 +200,9 @@ summary(pcens_fit) We see very good agreement between the true and estimated parameters. -Rather than using `fitdist()` directly `primarycensoreddist` provides a wrapper function `fitdistdoublecens()` that can be used to estimate double censored and truncated data. A bonus of this approach is we can specify our data using the `fitdistcens` `left` and `right` formulation and support mixed secondary censoring intervals. +Rather than using `fitdist()` directly `primarycensored` provides a wrapper function `fitdistdoublecens()` that can be used to estimate double censored and truncated data. A bonus of this approach is we can specify our data using the `fitdistcens` `left` and `right` formulation and support mixed secondary censoring intervals. -```{r primarycensoreddist-fitdistdoublecens} +```{r primarycensored-fitdistdoublecens} fitdistdoublecens_fit <- delay_data |> dplyr::select(left = observed_delay, right = observed_delay_upper) |> fitdistdoublecens( @@ -215,4 +215,4 @@ summary(fitdistdoublecens_fit) ``` -**Note** Currently this functionality is limited to a single pwindow, and observation time for all data. If this functionality is of interest then please open an issue on the `primarycensoreddist` GitHub page. +**Note** Currently this functionality is limited to a single pwindow, and observation time for all data. If this functionality is of interest then please open an issue on the `primarycensored` GitHub page. diff --git a/vignettes/fitting-dists-with-stan.Rmd b/vignettes/fitting-dists-with-stan.Rmd index eaee40d..f3698bb 100644 --- a/vignettes/fitting-dists-with-stan.Rmd +++ b/vignettes/fitting-dists-with-stan.Rmd @@ -1,6 +1,6 @@ --- title: "Fitting distributions using primarycensorseddist and cmdstan" -description: "A guide on how to use primarycensoreddist with Stan for Bayesian inference of epidemiological delay distributions." +description: "A guide on how to use primarycensored with Stan for Bayesian inference of epidemiological delay distributions." author: Sam Abbott output: bookdown::html_document2: @@ -21,24 +21,24 @@ vignette: > ## What are we going to do in this Vignette -In this vignette, we'll demonstrate how to use `primarycensoreddist` in conjunction with Stan for Bayesian inference of epidemiological delay distributions. We'll cover the following key points: +In this vignette, we'll demonstrate how to use `primarycensored` in conjunction with Stan for Bayesian inference of epidemiological delay distributions. We'll cover the following key points: 1. Simulating censored delay distribution data 2. Fitting a naive model using cmdstan 3. Evaluating the naive model's performance -4. Fitting an improved model using `primarycensoreddist` functionality -5. Comparing the `primarycensoreddist` model's performance to the naive model +4. Fitting an improved model using `primarycensored` functionality +5. Comparing the `primarycensored` model's performance to the naive model ## What might I need to know before starting -This vignette builds on the concepts introduced in the [Getting Started with primarycensoreddist](primarycensoreddist.html) vignette and assumes familiarity with using Stan tools as covered in the [How to use primarycensoreddist with Stan](using-stan-tools.html) vignette. +This vignette builds on the concepts introduced in the [Getting Started with primarycensored](primarycensored.html) vignette and assumes familiarity with using Stan tools as covered in the [How to use primarycensored with Stan](using-stan-tools.html) vignette. ## Packages used in this vignette -Alongside the `primarycensoreddist` package we will use the `cmdstanr` package for interfacing with cmdstan. We will also use the `ggplot2` package for plotting and `dplyr` for data manipulation. +Alongside the `primarycensored` package we will use the `cmdstanr` package for interfacing with cmdstan. We will also use the `ggplot2` package for plotting and `dplyr` for data manipulation. ```{r setup, message = FALSE} -library(primarycensoreddist) +library(primarycensored) library(cmdstanr) library(ggplot2) library(dplyr) @@ -46,7 +46,7 @@ library(dplyr) # Simulating censored and truncated delay distribution data -We'll start by simulating some censored and truncated delay distribution data. We'll use the `rprimarycensoreddist` function (actually we will use the `rpcens ` alias for brevity). +We'll start by simulating some censored and truncated delay distribution data. We'll use the `rprimarycensored` function (actually we will use the `rpcens ` alias for brevity). ```{r sample-lognormal} set.seed(123) # For reproducibility @@ -147,7 +147,7 @@ We've aggregated the data to unique combinations of `pwindow`, `swindow`, and `o # Fitting a naive model using cmdstan -We'll start by fitting a naive model using cmdstan. We'll use the `cmdstanr` package to interface with cmdstan. We define the model in a string and then write it to a file as in the [How to use primarycensoreddist with Stan](using-stan-tools.html) vignette. +We'll start by fitting a naive model using cmdstan. We'll use the `cmdstanr` package to interface with cmdstan. We define the model in a string and then write it to a file as in the [How to use primarycensored with Stan](using-stan-tools.html) vignette. ```{r naive-model} writeLines( @@ -198,16 +198,18 @@ naive_fit We see that the model has converged and the diagnostics look good. However, just from the model posterior summary we see that we might not be very happy with the fit. `mu` is smaller than the target `r meanlog` and `sigma` is larger than the target `r sdlog`. -# Fitting an improved model using primarycensoreddist +# Fitting an improved model using primarycensored -We'll now fit an improved model using the `primarycensoreddist` package. The main improvement is that we will use the `primary_censored_dist_lpdf` function to fit the model. This is the Stan version of the `pcens()` function and accounts for the primary and secondary censoring windows as well as the truncation. We encode that our primary distribution is a lognormal distribution by passing 1 as the `dist_id` parameter and that our primary event distribution is uniform by passing 1 as the `primary_dist_id` parameter. See the [Stan documentation](https://primarycensoreddist.epinowcast.org/stan/primary__censored__dist_8stan.html#acc97240dee1bc19e4f02013118f3857d) for more details on the `primary_censored_dist_lpdf` function. +We'll now fit an improved model using the `primarycensored` package. The main improvement is that we will use the `primarycensored_lpdf` function to fit the model. This is the Stan version of the `pcens()` function and accounts for the primary and secondary censoring windows as well as the truncation. We encode that our primary distribution is a lognormal distribution by passing 1 as the `dist_id` parameter and that our primary event distribution is uniform by passing 1 as the `primray_id` parameter. See the [Stan documentation](https://primarycensored.epinowcast.org/stan/primary__censored__dist_8stan.html#acc97240dee1bc19e4f02013118f3857d) for more details on the `primarycensored_lpdf` function. ```{r pimarycensoreddist-model} writeLines( " functions { - #include primary_censored_dist.stan - #include primary_censored_dist_analytical_cdf.stan + #include primarycensored.stan + // These functions are required for the primarycensored_lpdf function + #include primarycensored_ode.stan + #include primarycensored_analytical_cdf.stan #include expgrowth.stan } data { @@ -232,30 +234,30 @@ writeLines( // Likelihood for (i in 1:N) { - target += n[i] * primary_censored_dist_lpmf( + target += n[i] * primarycensored_lpmf( y[i] | 1, {mu, sigma}, pwindow[i], y_upper[i], D[i], 1, primary_params ); } }", - con = file.path(tempdir(), "primarycensoreddist_lognormal.stan") + con = file.path(tempdir(), "primarycensored_lognormal.stan") ) ``` Now let's compile the model -```{r compile-primarycensoreddist-model, message = FALSE} -primarycensoreddist_model <- cmdstan_model( - file.path(tempdir(), "primarycensoreddist_lognormal.stan"), +```{r compile-primarycensored-model, message = FALSE} +primarycensored_model <- cmdstan_model( + file.path(tempdir(), "primarycensored_lognormal.stan"), include_paths = pcd_stan_path() ) ``` Now let's fit the compiled model. -```{r fit-primarycensoreddist-model, message = FALSE} -primarycensoreddist_fit <- primarycensoreddist_model$sample( +```{r fit-primarycensored-model, message = FALSE} +primarycensored_fit <- primarycensored_model$sample( data = list( y = delay_counts$observed_delay, y_upper = delay_counts$observed_delay_upper, @@ -269,7 +271,7 @@ primarycensoreddist_fit <- primarycensoreddist_model$sample( refresh = ifelse(interactive(), 50, 0), show_messages = interactive() ) -primarycensoreddist_fit +primarycensored_fit ``` We see that the model has converged and the diagnostics look good. We also see that the posterior means are very near the true parameters and the 90% credible intervals include the true parameters. @@ -277,7 +279,7 @@ We see that the model has converged and the diagnostics look good. We also see t # Using `pcd_cmdstan_model()` for a more efficient approach -While the previous approach works well, `primarycensoreddist` provides a more efficient and convenient model which we can compile using `pcd_cmdstan_model()`. This approach not only saves time in model specification but also leverages within chain parallelisation to make best use of your machine's resources. Alongside this we also supply a convenience function `pcd_as_stan_data()` to convert our data into a format that can be used to fit the model and supply priors, bounds, and other settings. +While the previous approach works well, `primarycensored` provides a more efficient and convenient model which we can compile using `pcd_cmdstan_model()`. This approach not only saves time in model specification but also leverages within chain parallelisation to make best use of your machine's resources. Alongside this we also supply a convenience function `pcd_as_stan_data()` to convert our data into a format that can be used to fit the model and supply priors, bounds, and other settings. Let's use this function to fit our data: @@ -291,7 +293,7 @@ pcd_data <- pcd_as_stan_data( delay_upper = "observed_delay_upper", relative_obs_time = "obs_time", dist_id = 1, # Lognormal distribution - primary_dist_id = 1, # Uniform primary distribution + primray_id = 1, # Uniform primary distribution param_bounds = list(lower = c(-Inf, 0), upper = c(Inf, Inf)), primary_param_bounds = list(lower = numeric(0), upper = numeric(0)), priors = list(location = c(1, 0.5), scale = c(1, 0.5)), diff --git a/vignettes/primarycensoreddist.Rmd b/vignettes/primarycensoreddist.Rmd index a2ca97a..4804ac4 100644 --- a/vignettes/primarycensoreddist.Rmd +++ b/vignettes/primarycensoreddist.Rmd @@ -1,6 +1,6 @@ --- -title: "Getting Started with primarycensoreddist" -description: "A quick start example demonstrating use of primarycensoreddist." +title: "Getting Started with primarycensored" +description: "A quick start example demonstrating use of primarycensored." author: Sam Abbott output: bookdown::html_document2: @@ -12,7 +12,7 @@ bibliography: library.bib csl: https://raw.githubusercontent.com/citation-style-language/styles/master/apa-numeric-superscript-brackets.csl link-citations: true vignette: > - %\VignetteIndexEntry{Getting Started with primarycensoreddist} + %\VignetteIndexEntry{Getting Started with primarycensored} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- @@ -29,23 +29,23 @@ The estimation of delay distributions often faces the following challenges: - **Secondary event censoring**: The secondary event (e.g., symptom onset or the end of a process) is also frequently observed with interval censoring. This additional layer of censoring further complicates the estimation of the delay distribution. -The `primarycensoreddist` package aims to address these challenges by providing tools to manipulate primary censored delay distributions. By accounting for the censoring and truncation present in the data, the package enables more accurate estimation and use of the underlying true distribution. +The `primarycensored` package aims to address these challenges by providing tools to manipulate primary censored delay distributions. By accounting for the censoring and truncation present in the data, the package enables more accurate estimation and use of the underlying true distribution. -In this vignette, we will provide a quick introduction to the main functions and concepts in the `primarycensoreddist` package. We will cover the mathematical formulation of the problem, demonstrate the usage of the key functions, and provide signposting on how to learn more. +In this vignette, we will provide a quick introduction to the main functions and concepts in the `primarycensored` package. We will cover the mathematical formulation of the problem, demonstrate the usage of the key functions, and provide signposting on how to learn more. # Packages in this getting started vignette. -As well as the `primarycensoreddist` package this vignette also uses `ggplot2`. +As well as the `primarycensored` package this vignette also uses `ggplot2`. ```{r setup, message = FALSE} # Load packages -library(primarycensoreddist) +library(primarycensored) library(ggplot2) # Set seed for reproducibility set.seed(123) ``` -# Generating random samples with `rprimarycensoreddist()` +# Generating random samples with `rprimarycensored()` This function generates random samples from a primary event censored distribution. It adjusts the distribution by accounting for the primary event distribution, potential truncation at a maximum delay ($D$), and secondary event censoring. @@ -71,7 +71,7 @@ $$t_{truncated} = \{t \mid 0 \leq t < D\}$$ $$t_{valid} = \lfloor \frac{t_{truncated}}{swindow} \rfloor \times swindow$$ -Here's an example of how to use `rprimarycensoreddist()` to sample from a log-normal distribution with and without secondary interval censoring. For simplicity we will use a daily censoring window for both events. +Here's an example of how to use `rprimarycensored()` to sample from a log-normal distribution with and without secondary interval censoring. For simplicity we will use a daily censoring window for both events. ```{r sample-lognormal} n <- 1e4 @@ -81,14 +81,14 @@ obs_time <- 10 pwindow <- 1 # Random samples without secondary censoring -samples <- rprimarycensoreddist( +samples <- rprimarycensored( n, rdist = rlnorm, rprimary = runif, pwindow = pwindow, swindow = 0, D = obs_time, meanlog = meanlog, sdlog = sdlog ) # Random samples with secondary censoring -samples_sc <- rprimarycensoreddist( +samples_sc <- rprimarycensored( n, rdist = rlnorm, rprimary = runif, pwindow = pwindow, swindow = 1, D = obs_time, @@ -148,7 +148,7 @@ ggplot() + Neither distribution matches the true distribution due to the truncation at `D` which biases both observed distributions towards shorter delays, as well as the primary and secondary event censoring. -# Compute the primary event censored cumulative distribution function (CDF) for delays with `pprimarycensoreddist()` +# Compute the primary event censored cumulative distribution function (CDF) for delays with `pprimarycensored()` This function computes the primary event censored cumulative distribution function (CDF) for a given set of quantiles. It adjusts the CDF of delay distribution by accounting for the primary event distribution and potential truncation at a maximum delay ($D$). @@ -168,11 +168,11 @@ $$ where $F_{\text{cens,norm}}(q)$ is the normalized CDF. -Let's compare the empirical CDF of our samples without secondary censoring to the theoretical CDF computed using `pprimarycensoreddist()`: +Let's compare the empirical CDF of our samples without secondary censoring to the theoretical CDF computed using `pprimarycensored()`: ```{r cdf-lognormal} empirical_cdf <- ecdf(samples) -theoretical_cdf <- pprimarycensoreddist( +theoretical_cdf <- pprimarycensored( seq(0, obs_time, length.out = 100), pdist = plnorm, dprimary = dunif, pwindow = pwindow, D = obs_time, @@ -196,7 +196,7 @@ ggplot(cdf_data, aes(x = x)) + y = "Cumulative Probability", caption = paste0( "Blue line: Empirical CDF from samples without secondary censoring\n", - "Black line: Theoretical CDF computed using pprimarycensoreddist()" + "Black line: Theoretical CDF computed using pprimarycensored()" ) ) + scale_y_continuous(expand = expansion(mult = c(0, 0.05))) + @@ -207,10 +207,10 @@ ggplot(cdf_data, aes(x = x)) + ) ``` -The theoretical CDF matches the empirical CDF very well, confirming that `pprimarycensoreddist()` is working as expected. +The theoretical CDF matches the empirical CDF very well, confirming that `pprimarycensored()` is working as expected. -# Compute the primary event censored probability mass function (PMF) with `dprimarycensoreddist()` +# Compute the primary event censored probability mass function (PMF) with `dprimarycensored()` This function computes the primary event censored probability mass function (PMF) for a given set of quantiles using the CDF. On top of accounting for the primary event distribution and truncation, it also accounts for secondary event censoring. @@ -222,11 +222,11 @@ $$ where ($F_{\text{cens}}$) is the potentially right truncated primary event censored CDF and ($\text{swindow}$) is the secondary event window. -Let's compare the empirical PMF of our samples with secondary censoring to the theoretical PMF computed using `dprimarycensoreddist()`: +Let's compare the empirical PMF of our samples with secondary censoring to the theoretical PMF computed using `dprimarycensored()`: ```{r pmf-lognormal} -# Calculate the theoretical PMF using dprimarycensoreddist -theoretical_pmf <- dprimarycensoreddist( +# Calculate the theoretical PMF using dprimarycensored +theoretical_pmf <- dprimarycensored( 0:(obs_time - 1), pdist = plnorm, dprimary = dunif, pwindow = pwindow, swindow = 1, D = obs_time, @@ -264,7 +264,7 @@ ggplot() + y = "Density", caption = paste0( "Green bars: Empirical PMF from samples with secondary censoring\n", - "Black line: Theoretical PMF computed using dprimarycensoreddist()" + "Black line: Theoretical PMF computed using dprimarycensored()" ) ) + scale_y_continuous(expand = expansion(mult = c(0, 0.05))) + @@ -275,7 +275,7 @@ ggplot() + ) ``` -Again the theoretical PMF matches the empirical PMF very well, confirming that `dprimarycensoreddist()` is also working as expected. +Again the theoretical PMF matches the empirical PMF very well, confirming that `dprimarycensored()` is also working as expected. # Other key functionality @@ -287,7 +287,7 @@ In addition to these main functions, the package also includes: # Learning more -- For more on `primarycensoreddist` see the other package vignettes and the function documentation. +- For more on `primarycensored` see the other package vignettes and the function documentation. - For a more detailed explanation of the mathematical formulation of the primary event censored distribution see the `vignette("why-it-works")`. - For more mathematical details on the analytic solutions see the `vignette("analytic-solutions")`. - For more methodological background on delay distributions see Park et al.[@Park2024]. diff --git a/vignettes/using-stan-tools.Rmd b/vignettes/using-stan-tools.Rmd index 0c1b81c..9e22040 100644 --- a/vignettes/using-stan-tools.Rmd +++ b/vignettes/using-stan-tools.Rmd @@ -1,6 +1,6 @@ --- -title: "How to use primarycensoreddist with Stan" -description: "A guide on how to use primarycensoreddist with Stan." +title: "How to use primarycensored with Stan" +description: "A guide on how to use primarycensored with Stan." author: Sam Abbott output: bookdown::html_document2: @@ -12,7 +12,7 @@ bibliography: library.bib csl: https://raw.githubusercontent.com/citation-style-language/styles/master/apa-numeric-superscript-brackets.csl link-citations: true vignette: > - %\VignetteIndexEntry{How to use primarycensoreddist with Stan} + %\VignetteIndexEntry{How to use primarycensored with Stan} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- @@ -22,16 +22,16 @@ vignette: > ## What are we going to do in this Vignette -In this vignette, we'll explore how to use the `primarycensoreddist` package in conjunction with Stan. We'll cover the following key points: +In this vignette, we'll explore how to use the `primarycensored` package in conjunction with Stan. We'll cover the following key points: 1. Introduction to Stan and its relevance for our analysis 2. Overview of the packages we'll be using -3. How to access and use Stan functions provided by `primarycensoreddist` +3. How to access and use Stan functions provided by `primarycensored` 4. Methods for integrating these Stan functions into your workflow ## What is Stan and why are we using it -Stan is a probabilistic programming language for statistical inference. It provides a flexible and efficient platform for Bayesian modeling and is widely used in various fields of data science and statistics. In this vignette, we'll use Stan in conjunction with `primarycensoreddist` to perform Bayesian inference on censored data. +Stan is a probabilistic programming language for statistical inference. It provides a flexible and efficient platform for Bayesian modeling and is widely used in various fields of data science and statistics. In this vignette, we'll use Stan in conjunction with `primarycensored` to perform Bayesian inference on censored data. For more information on Stan: @@ -41,16 +41,16 @@ For more information on Stan: ## Packages used in this vignette -Alongside the `primarycensoreddist` package we will use the `cmdstanr` package for interfacing with cmdstan. +Alongside the `primarycensored` package we will use the `cmdstanr` package for interfacing with cmdstan. ```{r setup, message = FALSE} -library(primarycensoreddist) +library(primarycensored) library(cmdstanr) ``` -# Using Stan code in primarycensoreddist +# Using Stan code in primarycensored -`primarycensoreddist` includes a set of Stan functions that mirror the R functions in `primarycensoreddist`. Documentation for these functions can be found [here](https://primarycensoreddist.epinowcast.org/stan/). We support a range of approaches to integrate this Stan code into your workflow. +`primarycensored` includes a set of Stan functions that mirror the R functions in `primarycensored`. Documentation for these functions can be found [here](https://primarycensored.epinowcast.org/stan/). We support a range of approaches to integrate this Stan code into your workflow. ## Checking available Stan functions using `pcd_stan_functions()` @@ -65,7 +65,7 @@ pcd_stan_functions() Stan functions are accessed using the `pcd_load_stan_functions()` function. This function takes the name of the function as an argument and returns the function as a string. It can additionally write the functions to a file and wrap them in a `functions{}` block. ```{r} -pcd_load_stan_functions("primary_censored_dist_lpmf") +pcd_load_stan_functions("primarycensored_lpmf") ``` ## Linking the Stan functions to your workflow @@ -95,7 +95,7 @@ Alternatively, you could use `#include expgrowth_rng.stan` in a stan file functi ### Including the functions directly via `include_paths` -Rather than writing the functions to a file it is also possible to include them directly in the stan file using the `include_paths` argument to `cmdstan_model()`. This is useful if you don't to clutter your model with the stan code from `primarycensoreddist` and want automatic updating of the functions. To demonstrate we will first write a small model has has `expgrowth.stan` in its include paths (rather than writing it to a file and then including it). The first step is find the file and path for the `expgrowth_rng` function. +Rather than writing the functions to a file it is also possible to include them directly in the stan file using the `include_paths` argument to `cmdstan_model()`. This is useful if you don't to clutter your model with the stan code from `primarycensored` and want automatic updating of the functions. To demonstrate we will first write a small model has has `expgrowth.stan` in its include paths (rather than writing it to a file and then including it). The first step is find the file and path for the `expgrowth_rng` function. ```{r} pcd_stan_files("expgrowth_rng") @@ -118,7 +118,7 @@ writeLines( ) ``` -We can now use this file to compile a model. **Note** that we need to include the path to the `primarycensoreddist` Stan functions using the `include_paths` argument to `cmdstan_model()`. +We can now use this file to compile a model. **Note** that we need to include the path to the `primarycensored` Stan functions using the `include_paths` argument to `cmdstan_model()`. ```{r} model <- cmdstan_model(expgrowth_stan_file, include_paths = pcd_stan_path()) @@ -134,7 +134,7 @@ samples ## Using Stan functions directly in R -Whilst it is possible to use Stan functions directly in R it is not recommended for most use cases (use the R functions in `primarycensoreddist` instead). However, it can be useful to understand what is going on under the hood or for exploration (indeed we use this internally in `primarycensoreddist` to check our functions against the R implementations). To do this we use the `expose_functions()` method on our already compiled model. +Whilst it is possible to use Stan functions directly in R it is not recommended for most use cases (use the R functions in `primarycensored` instead). However, it can be useful to understand what is going on under the hood or for exploration (indeed we use this internally in `primarycensored` to check our functions against the R implementations). To do this we use the `expose_functions()` method on our already compiled model. ```{r, message = FALSE} model$expose_functions(global = TRUE) diff --git a/vignettes/why-it-works.Rmd b/vignettes/why-it-works.Rmd index d50617f..5db4a4a 100644 --- a/vignettes/why-it-works.Rmd +++ b/vignettes/why-it-works.Rmd @@ -20,18 +20,18 @@ vignette: > ## What are we going to do in this Vignette -In this vignette, we'll explain the underlying statistical model that the `primarycensoreddist` package. We'll cover the following key points: +In this vignette, we'll explain the underlying statistical model that the `primarycensored` package. We'll cover the following key points: 1. Introduction to the censored data problems in time to event analysis. 2. Discuss the relevant issues from censoring in epidemiological data. -3. Introduce the statistical model used in `primarycensoreddist`. +3. Introduce the statistical model used in `primarycensored`. 4. Distributions where we can derive the censored delay distribution analytically. -If you are new to the package, we recommend that you start with the `vignette("primarycensoreddist")` vignette. +If you are new to the package, we recommend that you start with the `vignette("primarycensored")` vignette. # Censoring and right truncation problems in time to event analysis -Time to event analysis, also known as survival analysis, concerns estimating the distribution of delay times between events. A distinctive feature of the field are the methodological techniques used to deal with the missing data problems common in data sets of delay times [@leung1997censoring]. In `primarycensoreddist` we focus on two particular missing data problems: +Time to event analysis, also known as survival analysis, concerns estimating the distribution of delay times between events. A distinctive feature of the field are the methodological techniques used to deal with the missing data problems common in data sets of delay times [@leung1997censoring]. In `primarycensored` we focus on two particular missing data problems: - **Interval censoring**. The primary (start) time and/or the secondary (end) time of the delay are unobserved but both are known to be within intervals. - **Right truncation**. Truncated delay data items are only reported if their secondary events are less than a known time, for example the current data collection time. @@ -40,11 +40,11 @@ In statistical epidemiology, these missing data problems occur frequently in bot In data analysis, events in epidemiology are commonly reported as occurring on a particular day or week (*interval censoring*). In an emerging outbreak, datasets can be incompletely observed (*right truncation*) and their can be a great deal of uncertainty around the precise timing of events (*interval censoring*). -In theoretical epidemiological modelling, it is often appropriate to model the evolution of an infectious disease as occurring in discrete time, for example in the [`EpiNow2`](https://epiforecasts.io/EpiNow2/) and [EpiEstim](https://mrc-ide.github.io/EpiEstim/index.html) modelling packages. This means appropriately discretising continuous distributions, such as the generation interval distribution. In `primarycensoreddist` we treat the discretisation of intrinsically continuous distributions as an *interval censoring* problem which allows us to simultaneously provide methods for both applied and theoretical contexts. +In theoretical epidemiological modelling, it is often appropriate to model the evolution of an infectious disease as occurring in discrete time, for example in the [`EpiNow2`](https://epiforecasts.io/EpiNow2/) and [EpiEstim](https://mrc-ide.github.io/EpiEstim/index.html) modelling packages. This means appropriately discretising continuous distributions, such as the generation interval distribution. In `primarycensored` we treat the discretisation of intrinsically continuous distributions as an *interval censoring* problem which allows us to simultaneously provide methods for both applied and theoretical contexts. -# Statistical model used in `primarycensoreddist` +# Statistical model used in `primarycensored` -As described in [Getting Started with `primarycensoreddist`](primarycensoreddist.html), `primarycensoreddist` focuses on a subset of methods from time to event analysis that address data missingness problems commonly found in epidemiological datasets. We present the statistical problem as a double interval censoring problem, where both the primary event time and the secondary event times are interval censored. We can recover single interval censoring problems by reducing one of the intervals to a point. In particular, a key assumption we make is that the censoring window for events is known and independent of the event time. This is known as non-informative censoring. +As described in [Getting Started with `primarycensored`](primarycensored.html), `primarycensored` focuses on a subset of methods from time to event analysis that address data missingness problems commonly found in epidemiological datasets. We present the statistical problem as a double interval censoring problem, where both the primary event time and the secondary event times are interval censored. We can recover single interval censoring problems by reducing one of the intervals to a point. In particular, a key assumption we make is that the censoring window for events is known and independent of the event time. This is known as non-informative censoring. The target for inference is the distribution of the delay time between the primary and secondary events. We assume that the delay time is a random variable $T = S - P_{u}$ with distribution function $F_T(t) = Pr(T < t)$ and density function $f_T(t)$. In this treatment we assume that the delay time is shift-invariant, that is, the distribution of the delay time is the same regardless of the primary event time. @@ -59,7 +59,7 @@ f_{P}(p) &= 0, \qquad &\text{otherwise}. \end{aligned} $$ -In this note, we measure the *censored delay time* $T_c$ between the primary and secondary event windows from endpoint to endpoint: $T_c = t_S + w_S - (t_P + w_P)$. Note that in the [generative model for delays](primarycensoreddist.html#generating-random-samples-with-rprimarycensoreddist) from "Getting started" the truncated delay $t_{\text{valid}}$ is measured from startpoint to startpoint of event windows. +In this note, we measure the *censored delay time* $T_c$ between the primary and secondary event windows from endpoint to endpoint: $T_c = t_S + w_S - (t_P + w_P)$. Note that in the [generative model for delays](primarycensored.html#generating-random-samples-with-rprimarycensored) from "Getting started" the truncated delay $t_{\text{valid}}$ is measured from startpoint to startpoint of event windows. In our treatment below we focus on the [survival function](https://en.wikipedia.org/wiki/Survival_function) of the time after the end of the primary window to the secondary event time which we denote $S_{+}$. We then use this to derive the distribution of the censored delay time $T_c$. This is equivalent to, but differs in mathematical approach from other treatments of the censoring problems in epidemiology, such as [@Park2024], see section [Connections to other approaches](#connections-to-other-approaches) for details. @@ -102,7 +102,7 @@ Equation \@ref(eq:survivalfunc) is the key equation in this note and is used to 1. The probability that the _actual_ delay time $T$ is greater than $t + w_P$. 2. The probability that the _actual_ delay time $T$ is between $t$ and $t + w_P$, and the primary event time $P$ occurred sufficiently close to the end of the primary censor window that the secondary even occurred more than time $t$ after the end of the primary window. -Note that in ["Getting started"](primarycensoreddist.html#compute-the-primary-event-censored-cumulative-distribution-function-cdf-for-delays-with-pprimarycensoreddist) the target for numerical quadrature is the cumulative distribution function of the sum of the primary time within the primary censor window and the delay time. +Note that in ["Getting started"](primarycensored.html#compute-the-primary-event-censored-cumulative-distribution-function-cdf-for-delays-with-pprimarycensored) the target for numerical quadrature is the cumulative distribution function of the sum of the primary time within the primary censor window and the delay time. ### Probability of secondary event time within a secondary censoring window @@ -133,7 +133,7 @@ Which is a discrete probability mass function of the secondary event time relati # Connections to other approaches -In this section, we discuss how the approach taken in `primarycensoreddist` relates to some other approaches to the censored data problems in time to event analysis. +In this section, we discuss how the approach taken in `primarycensored` relates to some other approaches to the censored data problems in time to event analysis. ## Connection to Park et al 2014 @@ -193,6 +193,6 @@ In this derivation, we have used that $G_P(x|-w_P, 0)$ is the distribution funct # Learning more - For more mathematical background on the analytic solutions see the `vignette("analytic-solutions")`. -- For a more introductory explanation of the primary event censored distribution see the `vignette("primarycensoreddist")`. +- For a more introductory explanation of the primary event censored distribution see the `vignette("primarycensored")`. # References