aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--R/dimethenamid_2018.R11
-rw-r--r--R/nlmixr.R79
-rw-r--r--R/tffm0.R46
-rw-r--r--tests/testthat/test_nlmixr.r99
4 files changed, 223 insertions, 12 deletions
diff --git a/R/dimethenamid_2018.R b/R/dimethenamid_2018.R
index 79018c11..76b98efe 100644
--- a/R/dimethenamid_2018.R
+++ b/R/dimethenamid_2018.R
@@ -41,6 +41,13 @@
#' f_dmta_mkin_tc <- mmkin(
#' list("DFOP-SFO3+" = dfop_sfo3_plus),
#' dmta_ds, quiet = TRUE, error_model = "tc")
-#' nlmixr_model(f_dmta_mkin_tc) # incomplete
-#' # nlmixr(f_dmta_mkin_tc, est = "saem") # not supported (yet)
+#' nlmixr_model(f_dmta_mkin_tc)
+#' f_dmta_nlmixr_saem <- nlmixr(f_dmta_mkin_tc, est = "saem",
+#' control = saemControl(print = 500))
+#' summary(f_dmta_nlmixr_saem)
+#' plot(f_dmta_nlmixr_saem)
+#' f_dmta_nlmixr_focei <- nlmixr(f_dmta_mkin_tc, est = "focei",
+#' control = foceiControl(print = 500))
+#' summary(f_dmta_nlmixr_focei)
+#' plot(f_dmta_nlmixr_focei)
"dimethenamid_2018"
diff --git a/R/nlmixr.R b/R/nlmixr.R
index 6e0b5128..9c364c4f 100644
--- a/R/nlmixr.R
+++ b/R/nlmixr.R
@@ -20,6 +20,9 @@ nlmixr::nlmixr
#' @param est Estimation method passed to [nlmixr::nlmixr]
#' @param degparms_start Parameter values given as a named numeric vector will
#' be used to override the starting values obtained from the 'mmkin' object.
+#' @param eta_start Standard deviations on the transformed scale given as a
+#' named numeric vector will be used to override the starting values obtained
+#' from the 'mmkin' object.
#' @param test_log_parms If TRUE, an attempt is made to use more robust starting
#' values for population parameters fitted as log parameters in mkin (like
#' rate constants) by only considering rate constants that pass the t-test
@@ -148,6 +151,8 @@ nlmixr.mmkin <- function(object, data = NULL,
error_model = object[[1]]$err_mod,
test_log_parms = TRUE,
conf.level = 0.6,
+ degparms_start = "auto",
+ eta_start = "auto",
...,
save = NULL,
envir = parent.frame()
@@ -155,7 +160,9 @@ nlmixr.mmkin <- function(object, data = NULL,
{
m_nlmixr <- nlmixr_model(object, est = est,
error_model = error_model, add_attributes = TRUE,
- test_log_parms = test_log_parms, conf.level = conf.level)
+ test_log_parms = test_log_parms, conf.level = conf.level,
+ degparms_start = degparms_start, eta_start = eta_start
+ )
d_nlmixr <- nlmixr_data(object)
mean_dp_start <- attr(m_nlmixr, "mean_dp_start")
@@ -164,7 +171,7 @@ nlmixr.mmkin <- function(object, data = NULL,
attributes(m_nlmixr) <- NULL
fit_time <- system.time({
- f_nlmixr <- nlmixr(m_nlmixr, d_nlmixr, est = est)
+ f_nlmixr <- nlmixr(m_nlmixr, d_nlmixr, est = est, control = control)
})
if (is.null(f_nlmixr$CMT)) {
@@ -246,7 +253,8 @@ print.nlmixr.mmkin <- function(x, digits = max(3, getOption("digits") - 3), ...)
nlmixr_model <- function(object,
est = c("saem", "focei"),
degparms_start = "auto",
- test_log_parms = FALSE, conf.level = 0.6,
+ eta_start = "auto",
+ test_log_parms = TRUE, conf.level = 0.6,
error_model = object[[1]]$err_mod, add_attributes = FALSE)
{
if (nrow(object) > 1) stop("Only row objects allowed")
@@ -278,16 +286,44 @@ nlmixr_model <- function(object,
conf.level = conf.level, random = TRUE)
degparms_optim <- degparms_mmkin$fixed
+
+ degparms_optim_ilr_names <- grep("^f_.*_ilr", names(degparms_optim), value = TRUE)
+ obs_vars_ilr <- unique(gsub("f_(.*)_ilr.*$", "\\1", degparms_optim_ilr_names))
+ degparms_optim_noilr <- degparms_optim[setdiff(names(degparms_optim),
+ degparms_optim_ilr_names)]
+
degparms_optim_back <- backtransform_odeparms(degparms_optim,
object[[1]]$mkinmod,
object[[1]]$transform_rates,
object[[1]]$transform_fractions)
- degparms_optim_back_names <- names(degparms_optim_back)
- names(degparms_optim_back_names) <- names(degparms_optim)
if (degparms_start[1] == "auto") {
- degparms_start <- degparms_optim
+ degparms_start <- degparms_optim_noilr
+ for (obs_var_ilr in obs_vars_ilr) {
+ ff_names <- grep(paste0("^f_", obs_var_ilr, "_"),
+ names(degparms_optim_back), value = TRUE)
+ f_tffm0 <- tffm0(degparms_optim_back[ff_names])
+ f_tffm0_qlogis <- qlogis(f_tffm0)
+ names(f_tffm0_qlogis) <- paste0("f_", obs_var_ilr,
+ "_tffm0_", 1:length(f_tffm0), "_qlogis")
+ degparms_start <- c(degparms_start, f_tffm0_qlogis)
+ }
+ }
+
+ if (eta_start[1] == "auto") {
+ eta_start <- degparms_mmkin$eta[setdiff(names(degparms_optim),
+ degparms_optim_ilr_names)]
+ for (obs_var_ilr in obs_vars_ilr) {
+ ff_n <- length(grep(paste0("^f_", obs_var_ilr, "_"),
+ names(degparms_optim_back), value = TRUE))
+ eta_start_ff <- rep(0.3, ff_n)
+ names(eta_start_ff) <- paste0("f_", obs_var_ilr,
+ "_tffm0_", 1:ff_n, "_qlogis")
+ eta_start <- c(eta_start, eta_start_ff)
+ }
}
+
+
degparms_fixed <- object[[1]]$bparms.fixed
odeini_optim_parm_names <- grep('_0$', names(degparms_optim), value = TRUE)
@@ -315,7 +351,7 @@ nlmixr_model <- function(object,
as.character(degparms_start[parm_name]),
"\n",
"eta.", parm_name, " ~ ",
- as.character(degparms_mmkin$eta[parm_name]),
+ as.character(eta_start[parm_name]),
"\n"
)
}
@@ -394,7 +430,7 @@ nlmixr_model <- function(object,
}
# Population initial values for log rate constants
- for (parm_name in grep("^log_", names(degparms_optim), value = TRUE)) {
+ for (parm_name in grep("^log_", names(degparms_start), value = TRUE)) {
model_block <- paste0(
model_block,
gsub("^log_", "", parm_name), " = ",
@@ -402,13 +438,36 @@ nlmixr_model <- function(object,
}
# Population initial values for logit transformed parameters
- for (parm_name in grep("_qlogis$", names(degparms_optim), value = TRUE)) {
+ for (parm_name in grep("_qlogis$", names(degparms_start), value = TRUE)) {
model_block <- paste0(
model_block,
- degparms_optim_back_names[parm_name], " = ",
+ gsub("_qlogis$", "", parm_name), " = ",
"expit(", parm_name, " + eta.", parm_name, ")\n")
}
+ # Calculate formation fractions from tffm0 transformed values
+ for (obs_var_ilr in obs_vars_ilr) {
+ ff_names <- grep(paste0("^f_", obs_var_ilr, "_"),
+ names(degparms_optim_back), value = TRUE)
+ pattern <- paste0("^f_", obs_var_ilr, "_to_(.*)$")
+ target_vars <- gsub(pattern, "\\1",
+ grep(paste0("^f_", obs_var_ilr, "_to_"), names(degparms_optim_back), value = TRUE))
+ for (i in 1:length(target_vars)) {
+ ff_name <- ff_names[i]
+ ff_line <- paste0(ff_name, " = f_", obs_var_ilr, "_tffm0_", i)
+ if (i > 1) {
+ for (j in (i - 1):1) {
+ ff_line <- paste0(ff_line, " * (1 - f_", obs_var_ilr, "_tffm0_", j , ")")
+ }
+ }
+ model_block <- paste0(
+ model_block,
+ ff_line,
+ "\n"
+ )
+ }
+ }
+
# Differential equations
model_block <- paste0(
model_block,
diff --git a/R/tffm0.R b/R/tffm0.R
new file mode 100644
index 00000000..25787962
--- /dev/null
+++ b/R/tffm0.R
@@ -0,0 +1,46 @@
+#' Transform formation fractions as in the first published mkin version
+#'
+#' The transformed fractions can be restricted between 0 and 1 in model
+#' optimisations. Therefore this transformation was used originally in mkin. It
+#' was later replaced by the [ilr] transformation because the ilr transformed
+#' fractions can assumed to follow normal distribution. As the ilr
+#' transformation is not available in [RxODE] and can therefore not be used in
+#' the nlmixr modelling language, this transformation is currently used for
+#' translating mkin models with formation fractions to more than one target
+#' compartment for fitting with nlmixr in [nlmixr_model]. However,
+#' this implementation cannot be used there, as it is not accessible
+#' from RxODE.
+#'
+#' @param ff Vector of untransformed formation fractions. The sum
+#' must be smaller or equal to one
+#' @param ff_trans
+#' @return A vector of the transformed formation fractions
+#' @export
+#' @examples
+#' ff_example <- c(
+#' 0.10983681, 0.09035905, 0.08399383
+#' )
+#' ff_example_trans <- tffm0(ff_example)
+#' invtffm0(ff_example_trans)
+tffm0 <- function(ff) {
+ n <- length(ff)
+ res <- numeric(n)
+ f_remaining <- 1
+ for (i in 1:n) {
+ res[i] <- ff[i]/f_remaining
+ f_remaining <- f_remaining - ff[i]
+ }
+ return(res)
+}
+#' @rdname tffm0
+#' @return
+invtffm0 <- function(ff_trans) {
+ n <- length(ff_trans)
+ res <- numeric(n)
+ f_remaining <- 1
+ for (i in 1:n) {
+ res[i] <- ff_trans[i] * f_remaining
+ f_remaining <- f_remaining - res[i]
+ }
+ return(res)
+}
diff --git a/tests/testthat/test_nlmixr.r b/tests/testthat/test_nlmixr.r
new file mode 100644
index 00000000..e3bd3d66
--- /dev/null
+++ b/tests/testthat/test_nlmixr.r
@@ -0,0 +1,99 @@
+
+
+dmta_ds <- lapply(1:8, function(i) {
+ ds_i <- dimethenamid_2018$ds[[i]]$data
+ ds_i[ds_i$name == "DMTAP", "name"] <- "DMTA"
+ ds_i$time <- ds_i$time * dimethenamid_2018$f_time_norm[i]
+ ds_i
+})
+names(dmta_ds) <- sapply(dimethenamid_2018$ds, function(ds) ds$title)
+dmta_ds[["Borstel"]] <- rbind(dmta_ds[["Borstel 1"]], dmta_ds[["Borstel 2"]])
+dmta_ds[["Borstel 1"]] <- NULL
+dmta_ds[["Borstel 2"]] <- NULL
+dmta_ds[["Elliot"]] <- rbind(dmta_ds[["Elliot 1"]], dmta_ds[["Elliot 2"]])
+dmta_ds[["Elliot 1"]] <- NULL
+dmta_ds[["Elliot 2"]] <- NULL
+dfop_sfo3_plus <- mkinmod(
+ DMTA = mkinsub("DFOP", c("M23", "M27", "M31")),
+ M23 = mkinsub("SFO"),
+ M27 = mkinsub("SFO"),
+ M31 = mkinsub("SFO", "M27", sink = FALSE),
+ quiet = TRUE
+)
+f_dmta_mkin_tc <- mmkin(
+ list("DFOP-SFO3+" = dfop_sfo3_plus),
+ dmta_ds, quiet = TRUE, error_model = "tc")
+
+d_dmta_nlmixr <- nlmixr_data(f_dmta_mkin_tc)
+m_dmta_nlmixr <- function ()
+{
+ ini({
+ DMTA_0 = 98.7697627680706
+ eta.DMTA_0 ~ 2.35171765917765
+ log_k_M23 = -3.92162409637283
+ eta.log_k_M23 ~ 0.549278519419884
+ log_k_M27 = -4.33774620773911
+ eta.log_k_M27 ~ 0.864474956685295
+ log_k_M31 = -4.24767627688461
+ eta.log_k_M31 ~ 0.750297149164171
+ f_DMTA_tffm0_1_qlogis = -2.092409
+ eta.f_DMTA_tffm0_1_qlogis ~ 0.3
+ f_DMTA_tffm0_2_qlogis = -2.180576
+ eta.f_DMTA_tffm0_2_qlogis ~ 0.3
+ f_DMTA_tffm0_3_qlogis = -2.142672
+ eta.f_DMTA_tffm0_3_qlogis ~ 0.3
+ log_k1 = -2.2341008812259
+ eta.log_k1 ~ 0.902976221565793
+ log_k2 = -3.7762779983269
+ eta.log_k2 ~ 1.57684519529298
+ g_qlogis = 0.450175725479389
+ eta.g_qlogis ~ 3.0851335687675
+ sigma_low_DMTA = 0.697933852349996
+ rsd_high_DMTA = 0.0257724286053519
+ sigma_low_M23 = 0.697933852349996
+ rsd_high_M23 = 0.0257724286053519
+ sigma_low_M27 = 0.697933852349996
+ rsd_high_M27 = 0.0257724286053519
+ sigma_low_M31 = 0.697933852349996
+ rsd_high_M31 = 0.0257724286053519
+ })
+ model({
+ DMTA_0_model = DMTA_0 + eta.DMTA_0
+ DMTA(0) = DMTA_0_model
+ k_M23 = exp(log_k_M23 + eta.log_k_M23)
+ k_M27 = exp(log_k_M27 + eta.log_k_M27)
+ k_M31 = exp(log_k_M31 + eta.log_k_M31)
+ k1 = exp(log_k1 + eta.log_k1)
+ k2 = exp(log_k2 + eta.log_k2)
+ g = expit(g_qlogis + eta.g_qlogis)
+ f_DMTA_tffm0_1 = expit(f_DMTA_tffm0_1_qlogis + eta.f_DMTA_tffm0_1_qlogis)
+ f_DMTA_tffm0_2 = expit(f_DMTA_tffm0_2_qlogis + eta.f_DMTA_tffm0_2_qlogis)
+ f_DMTA_tffm0_3 = expit(f_DMTA_tffm0_3_qlogis + eta.f_DMTA_tffm0_3_qlogis)
+ f_DMTA_to_M23 = f_DMTA_tffm0_1
+ f_DMTA_to_M27 = (1 - f_DMTA_tffm0_1) * f_DMTA_tffm0_2
+ f_DMTA_to_M31 = (1 - f_DMTA_tffm0_1) * (1 - f_DMTA_tffm0_2) * f_DMTA_tffm0_3
+ d/dt(DMTA) = -((k1 * g * exp(-k1 * time) + k2 * (1 -
+ g) * exp(-k2 * time))/(g * exp(-k1 * time) + (1 -
+ g) * exp(-k2 * time))) * DMTA
+ d/dt(M23) = +f_DMTA_to_M23 * ((k1 * g * exp(-k1 * time) +
+ k2 * (1 - g) * exp(-k2 * time))/(g * exp(-k1 * time) +
+ (1 - g) * exp(-k2 * time))) * DMTA - k_M23 * M23
+ d/dt(M27) = +f_DMTA_to_M27 * ((k1 * g * exp(-k1 * time) +
+ k2 * (1 - g) * exp(-k2 * time))/(g * exp(-k1 * time) +
+ (1 - g) * exp(-k2 * time))) * DMTA - k_M27 * M27 +
+ k_M31 * M31
+ d/dt(M31) = +f_DMTA_to_M31 * ((k1 * g * exp(-k1 * time) +
+ k2 * (1 - g) * exp(-k2 * time))/(g * exp(-k1 * time) +
+ (1 - g) * exp(-k2 * time))) * DMTA - k_M31 * M31
+ DMTA ~ add(sigma_low_DMTA) + prop(rsd_high_DMTA)
+ M23 ~ add(sigma_low_M23) + prop(rsd_high_M23)
+ M27 ~ add(sigma_low_M27) + prop(rsd_high_M27)
+ M31 ~ add(sigma_low_M31) + prop(rsd_high_M31)
+ })
+}
+m_dmta_nlmixr_mkin <- nlmixr_model(f_dmta_mkin_tc, test_log_parms = TRUE)
+f_dmta_nlmixr_saem <- nlmixr(f_dmta_mkin_tc, est = "saem", control = saemControl(print = 250))
+f_dmta_nlmixr_focei <- nlmixr(f_dmta_mkin_tc, est = "focei", control = foceiControl(print = 250))
+plot(f_dmta_nlmixr_saem)
+plot(f_dmta_nlmixr_focei)
+

Contact - Imprint