From 3fe300a9c8012949b7888935db533ca9d93e24ba Mon Sep 17 00:00:00 2001 From: "alex.hill@gmail.com" Date: Thu, 17 Oct 2024 16:41:47 +0100 Subject: [PATCH] rename fn, add tests, fix trajectories --- NAMESPACE | 2 +- R/biokinetics.R | 17 ++++++++++------- R/cpp11.R | 4 ++-- R/utils.R | 10 +++++----- man/biokinetics.Rd | 6 ++++++ ...inverse.Rd => convert_log2_scale_inverse.Rd} | 12 ++++++------ src/convert_log_scale_inverse.cpp | 2 +- src/cpp11.cpp | 10 +++++----- tests/testthat/test-convert-log-scale.R | 8 ++++---- tests/testthat/test-data.R | 2 +- .../test-simulate-individual-trajectories.R | 16 ++++++++++++++++ .../test-simulate-population-trajectories.R | 16 ++++++++++++++++ 12 files changed, 73 insertions(+), 32 deletions(-) rename man/{convert_log_scale_inverse.Rd => convert_log2_scale_inverse.Rd} (57%) diff --git a/NAMESPACE b/NAMESPACE index d7dad8b..d5b46ca 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,7 +3,7 @@ export(add_exposure_data) export(biokinetics) export(biokinetics_priors) -export(convert_log_scale_inverse) +export(convert_log2_scale_inverse) importFrom(R6,R6Class) importFrom(data.table,":=") importFrom(data.table,.BY) diff --git a/R/biokinetics.R b/R/biokinetics.R index 16fdaa8..82295db 100644 --- a/R/biokinetics.R +++ b/R/biokinetics.R @@ -212,6 +212,7 @@ biokinetics <- R6::R6Class( #' @param priors Object of type \link[epikinetics]{biokinetics_priors}. Default biokinetics_priors(). #' @param covariate_formula Formula specifying linear regression model. Note all variables in the formula #' will be treated as categorical variables. Default ~0. + #' @param preds_sd Standard deviation of predictor coefficients. Default 0.25. #' @param scale One of "log" or "natural". Default "natural". Is provided data on a log or a natural scale? If on a natural scale it #' will be converted to a log scale for model fitting. initialize = function(priors = biokinetics_priors(), @@ -246,7 +247,7 @@ biokinetics <- R6::R6Class( validate_formula_vars(private$all_formula_vars, private$data) logger::log_info("Preparing data for stan") if (scale == "natural") { - private$data <- convert_log_scale(private$data, "value") + private$data <- convert_log2_scale(private$data, "value") } private$data[, `:=`(obs_id = seq_len(.N), t_since_last_exp = as.integer(day - last_exp_day, units = "days"))] @@ -269,6 +270,8 @@ biokinetics <- R6::R6Class( get_stan_data = function() { private$stan_input_data }, + #' @description View the mapping of human readable covariate names to the model variable p. + #' @return A data.table mapping the model variable p to human readable covariates. get_covariate_lookup_table = function() { private$covariate_lookup_table }, @@ -390,10 +393,10 @@ biokinetics <- R6::R6Class( } if (summarise) { - dt_out <- convert_log_scale_inverse( + dt_out <- convert_log2_scale_inverse( dt_out, vars_to_transform = c("me", "lo", "hi")) } else { - dt_out <- convert_log_scale_inverse( + dt_out <- convert_log2_scale_inverse( dt_out, vars_to_transform = "mu") } dt_out @@ -430,7 +433,7 @@ biokinetics <- R6::R6Class( dt_peak_switch <- private$recover_covariate_names(dt_peak_switch) if (private$scale == "natural") { - dt_peak_switch <- convert_log_scale_inverse( + dt_peak_switch <- convert_log2_scale_inverse( dt_peak_switch, vars_to_transform = c("mu_0", "mu_p", "mu_s")) } @@ -491,11 +494,11 @@ biokinetics <- R6::R6Class( # Running the C++ code to simulate trajectories for each parameter sample # for each individual logger::log_info("Simulating individual trajectories") - dt_params_ind_traj <- data.table::setDT(biokinetics_simulate_trajectories(dt_params_ind)) + dt_params_ind_traj <- biokinetics_simulate_trajectories(dt_params_ind) if (private$scale == "natural") { - dt_params_ind_traj <- convert_log_scale_inverse_cpp( - dt_params_ind_traj, vars_to_transform = "mu") + dt_params_ind_traj <- data.table::setDT(convert_log2_scale_inverse_cpp( + dt_params_ind_traj, vars_to_transform = "mu")) } # convert numeric pid to original pid diff --git a/R/cpp11.R b/R/cpp11.R index 2317c1c..5141cfe 100644 --- a/R/cpp11.R +++ b/R/cpp11.R @@ -1,7 +1,7 @@ # Generated by cpp11: do not edit by hand -convert_log_scale_inverse_cpp <- function(dt, vars_to_transform) { - .Call(`_epikinetics_convert_log_scale_inverse_cpp`, dt, vars_to_transform) +convert_log2_scale_inverse_cpp <- function(dt, vars_to_transform) { + .Call(`_epikinetics_convert_log2_scale_inverse_cpp`, dt, vars_to_transform) } simulate_trajectories_cpp <- function(person_params) { diff --git a/R/utils.R b/R/utils.R index 576cfc2..1ee1a2e 100644 --- a/R/utils.R +++ b/R/utils.R @@ -1,4 +1,4 @@ -convert_log_scale <- function( +convert_log2_scale <- function( dt_in, vars_to_transform = "titre", simplify_limits = TRUE) { @@ -14,14 +14,14 @@ convert_log_scale <- function( #' @title Invert base 2 log scale conversion #' -#' @description User provided data is converted to a base 2 log scale before model fitting. This -#' function reverses that transformation. This function does not modify the provided data.table in-place, -#' but returns a transformed copy. +#' @description Natural scale data is converted to a base 2 log scale before model fitting. This +#' function reverses that transformation and may be useful if working directly with fitted parameters. +#' This function does not modify the provided data.table in-place, but returns a transformed copy. #' @return A data.table, identical to the input data but with specified columns transformed. #' @param dt_in data.table containing data to be transformed from base 2 log to natural scale. #' @param vars_to_transform Names of columns to apply the transformation to. #' @export -convert_log_scale_inverse <- function(dt_in, vars_to_transform) { +convert_log2_scale_inverse <- function(dt_in, vars_to_transform) { dt_out <- data.table::copy(dt_in) for (var in vars_to_transform) { # Reverse the log2 transformation and multiplication by 5. diff --git a/man/biokinetics.Rd b/man/biokinetics.Rd index 1de7305..5481497 100644 --- a/man/biokinetics.Rd +++ b/man/biokinetics.Rd @@ -50,6 +50,8 @@ for required columns: \code{vignette("data", package = "epikinetics")}.} \item{\code{covariate_formula}}{Formula specifying linear regression model. Note all variables in the formula will be treated as categorical variables. Default ~0.} +\item{\code{preds_sd}}{Standard deviation of predictor coefficients. Default 0.25.} + \item{\code{scale}}{One of "log" or "natural". Default "natural". Is provided data on a log or a natural scale? If on a natural scale it will be converted to a log scale for model fitting.} } @@ -76,10 +78,14 @@ A list of arguments that will be passed to the stan model. \if{html}{\out{}} \if{latex}{\out{\hypertarget{method-biokinetics-get_covariate_lookup_table}{}}} \subsection{Method \code{get_covariate_lookup_table()}}{ +View the mapping of human readable covariate names to the model variable p. \subsection{Usage}{ \if{html}{\out{
}}\preformatted{biokinetics$get_covariate_lookup_table()}\if{html}{\out{
}} } +\subsection{Returns}{ +A data.table mapping the model variable p to human readable covariates. +} } \if{html}{\out{
}} \if{html}{\out{}} diff --git a/man/convert_log_scale_inverse.Rd b/man/convert_log2_scale_inverse.Rd similarity index 57% rename from man/convert_log_scale_inverse.Rd rename to man/convert_log2_scale_inverse.Rd index d84c93a..f5409a2 100644 --- a/man/convert_log_scale_inverse.Rd +++ b/man/convert_log2_scale_inverse.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/utils.R -\name{convert_log_scale_inverse} -\alias{convert_log_scale_inverse} +\name{convert_log2_scale_inverse} +\alias{convert_log2_scale_inverse} \title{Invert base 2 log scale conversion} \usage{ -convert_log_scale_inverse(dt_in, vars_to_transform) +convert_log2_scale_inverse(dt_in, vars_to_transform) } \arguments{ \item{dt_in}{data.table containing data to be transformed from base 2 log to natural scale.} @@ -15,7 +15,7 @@ convert_log_scale_inverse(dt_in, vars_to_transform) A data.table, identical to the input data but with specified columns transformed. } \description{ -User provided data is converted to a base 2 log scale before model fitting. This -function reverses that transformation. This function does not modify the provided data.table in-place, -but returns a transformed copy. +Natural scale data is converted to a base 2 log scale before model fitting. This +function reverses that transformation and may be useful if working directly with fitted parameters. +This function does not modify the provided data.table in-place, but returns a transformed copy. } diff --git a/src/convert_log_scale_inverse.cpp b/src/convert_log_scale_inverse.cpp index 2abe7ba..77524e6 100644 --- a/src/convert_log_scale_inverse.cpp +++ b/src/convert_log_scale_inverse.cpp @@ -3,7 +3,7 @@ using namespace cpp11; [[cpp11::register]] -cpp11::data_frame convert_log_scale_inverse_cpp(cpp11::writable::list dt, +cpp11::data_frame convert_log2_scale_inverse_cpp(cpp11::writable::list dt, cpp11::strings vars_to_transform) { for (int i = 0; i < vars_to_transform.size(); i++) { diff --git a/src/cpp11.cpp b/src/cpp11.cpp index caad6d2..18c707b 100644 --- a/src/cpp11.cpp +++ b/src/cpp11.cpp @@ -6,10 +6,10 @@ #include // convert_log_scale_inverse.cpp -cpp11::data_frame convert_log_scale_inverse_cpp(cpp11::writable::list dt, cpp11::strings vars_to_transform); -extern "C" SEXP _epikinetics_convert_log_scale_inverse_cpp(SEXP dt, SEXP vars_to_transform) { +cpp11::data_frame convert_log2_scale_inverse_cpp(cpp11::writable::list dt, cpp11::strings vars_to_transform); +extern "C" SEXP _epikinetics_convert_log2_scale_inverse_cpp(SEXP dt, SEXP vars_to_transform) { BEGIN_CPP11 - return cpp11::as_sexp(convert_log_scale_inverse_cpp(cpp11::as_cpp>(dt), cpp11::as_cpp>(vars_to_transform))); + return cpp11::as_sexp(convert_log2_scale_inverse_cpp(cpp11::as_cpp>(dt), cpp11::as_cpp>(vars_to_transform))); END_CPP11 } // simulate_trajectories.cpp @@ -22,8 +22,8 @@ extern "C" SEXP _epikinetics_simulate_trajectories_cpp(SEXP person_params) { extern "C" { static const R_CallMethodDef CallEntries[] = { - {"_epikinetics_convert_log_scale_inverse_cpp", (DL_FUNC) &_epikinetics_convert_log_scale_inverse_cpp, 2}, - {"_epikinetics_simulate_trajectories_cpp", (DL_FUNC) &_epikinetics_simulate_trajectories_cpp, 1}, + {"_epikinetics_convert_log2_scale_inverse_cpp", (DL_FUNC) &_epikinetics_convert_log2_scale_inverse_cpp, 2}, + {"_epikinetics_simulate_trajectories_cpp", (DL_FUNC) &_epikinetics_simulate_trajectories_cpp, 1}, {NULL, NULL, 0} }; } diff --git a/tests/testthat/test-convert-log-scale.R b/tests/testthat/test-convert-log-scale.R index b6c378b..26715fb 100644 --- a/tests/testthat/test-convert-log-scale.R +++ b/tests/testthat/test-convert-log-scale.R @@ -1,14 +1,14 @@ test_that("Can convert to and from log scale in R", { inputs <- data.table::fread(test_path("testdata", "testdata.csv")) - log_inputs <- convert_log_scale(inputs, "me", simplify_limits = FALSE) - unlog_inputs <- convert_log_scale_inverse(log_inputs, "me") + log_inputs <- convert_log2_scale(inputs, "me", simplify_limits = FALSE) + unlog_inputs <- convert_log2_scale_inverse(log_inputs, "me") expect_equal(inputs, unlog_inputs) }) test_that("Can convert from log scale in R", { inputs <- data.table::fread(test_path("testdata", "testdata.csv")) - res <- convert_log_scale_inverse( + res <- convert_log2_scale_inverse( inputs, vars_to_transform = c("me", "lo")) expect_equal(res$me, 5 * 2^inputs$me) @@ -19,7 +19,7 @@ test_that("Can convert from log scale in R", { test_that("Can convert from log scale in Cpp", { inputs <- data.table::fread(test_path("testdata", "testdata.csv")) - rescpp <- convert_log_scale_inverse_cpp( + rescpp <- convert_log2_scale_inverse_cpp( inputs, vars_to_transform = c("me", "lo")) expect_equal(rescpp$me, 5 * 2^(inputs$me)) diff --git a/tests/testthat/test-data.R b/tests/testthat/test-data.R index 233f1cb..a9aa1a1 100644 --- a/tests/testthat/test-data.R +++ b/tests/testthat/test-data.R @@ -52,7 +52,7 @@ test_that("Natural scale data is converted to log scale for stan", { dat <- data.table::fread(system.file("delta_full.rds", package = "epikinetics")) mod <- biokinetics$new(data = dat) stan_data <- mod$get_stan_data() - expect_equivalent(stan_data$value, convert_log_scale(dat, "value")$value) + expect_equivalent(stan_data$value, convert_log2_scale(dat, "value")$value) }) test_that("Log scale data is passed directly to stan", { diff --git a/tests/testthat/test-simulate-individual-trajectories.R b/tests/testthat/test-simulate-individual-trajectories.R index dec0046..507b877 100644 --- a/tests/testthat/test-simulate-individual-trajectories.R +++ b/tests/testthat/test-simulate-individual-trajectories.R @@ -81,3 +81,19 @@ test_that("Exposure dates are brought forward by time_shift days", { expect_equal(trajectories$mu, trajectories_shifted$mu) expect_true(all(as.numeric(difftime(trajectories$exposure_date, trajectories_shifted$exposure_date, units = "days")) == 75)) }) + +test_that("Natural scale data is returned on natural scale", { + mod <- biokinetics$new(file_path = system.file("delta_full.rds", package = "epikinetics"), + covariate_formula = ~0 + infection_history, scale = "natural") + mod$fit() + trajectories <- mod$simulate_individual_trajectories(summarise = TRUE, n_draws = 10) + expect_false(all(trajectories$me < 10)) +}) + +test_that("Log scale data is returned on log scale", { + mod <- biokinetics$new(file_path = system.file("delta_full.rds", package = "epikinetics"), + covariate_formula = ~0 + infection_history, scale = "log") + mod$fit() + trajectories <- mod$simulate_individual_trajectories(summarise = TRUE, n_draws = 10) + expect_true(all(trajectories$me < 10)) +}) diff --git a/tests/testthat/test-simulate-population-trajectories.R b/tests/testthat/test-simulate-population-trajectories.R index 246d392..73b7092 100644 --- a/tests/testthat/test-simulate-population-trajectories.R +++ b/tests/testthat/test-simulate-population-trajectories.R @@ -45,3 +45,19 @@ test_that("Only times up to t_max are returned", { trajectories <- mod$simulate_population_trajectories(summarise = TRUE, t_max = 10) expect_true(all(trajectories$t <= 10)) }) + +test_that("Natural scale data is returned on natural scale", { + mod <- biokinetics$new(file_path = system.file("delta_full.rds", package = "epikinetics"), + covariate_formula = ~0 + infection_history, scale = "natural") + mod$fit() + trajectories <- mod$simulate_population_trajectories(summarise = TRUE, t_max = 10) + expect_false(all(trajectories$me < 10)) +}) + +test_that("Log scale data is returned on log scale", { + mod <- biokinetics$new(file_path = system.file("delta_full.rds", package = "epikinetics"), + covariate_formula = ~0 + infection_history, scale = "log") + mod$fit() + trajectories <- mod$simulate_population_trajectories(summarise = TRUE, t_max = 10) + expect_true(all(trajectories$me < 10)) +})