levenc / posologyr

Model-Informed Precision Dosing (MIPD) with R
https://levenc.github.io/posologyr/
GNU Affero General Public License v3.0
12 stars 1 forks source link

poso_simu_pop() and time varying coviariets #53

Closed mat194 closed 1 month ago

mat194 commented 2 months ago

Hi Cyril,

Here the code and the results I get using the Goti model

mod_vancomycin_Goti2018 <- list(
  ppk_model   = rxode2::rxode({
    centr(0) = 0;
    TVCl  = THETA_Cl*(CLCREAT/120)^0.8*(0.7^DIAL);
    TVVc  = THETA_Vc*(WT/70)          *(0.5^DIAL);
    TVVp  = THETA_Vp;
    TVQ   = THETA_Q;
    Cl    = TVCl*exp(ETA_Cl);
    Vc    = TVVc*exp(ETA_Vc);
    Vp    = TVVp*exp(ETA_Vp);
    Q     = TVQ;
    ke    = Cl/Vc;
    k12   = Q/Vc;
    k21   = Q/Vp;
    Cc    = centr/Vc;
    d/dt(centr)  = - ke*centr - k12*centr + k21*periph;
    d/dt(periph) =            + k12*centr - k21*periph;
    d/dt(AUC)    =   Cc;
  }),
  error_model = function(f,sigma){
    g <- sigma[1] + sigma[2]*f
    return(g)
  },
  theta = c(THETA_Cl=4.5, THETA_Vc=58.4, THETA_Vp=38.4,THETA_Q=6.5),
  omega = lotri::lotri({ETA_Cl + ETA_Vc + ETA_Vp + ETA_Q ~
      c(0.147,
        0     ,   0.510,
        0     ,       0,   0.282,
        0     ,       0,       0,    0)}),
  covariates  = c("CLCREAT","WT","DIAL"),
  sigma       = c(additive_a = 3.4, proportional_b = 0.227))

df_patient <- data.frame(ID=1,TIME=c(0.0,13.0,24.2,48),
                          DV=c(NA,NA,NA,NA),
                          AMT=c(0, 0 , 0, 0),
                          EVID=c(0,0,0,0),
                          CLCREAT=c(65,30,20,10),WT=70,DIAL=0)

results <- poso_simu_pop(dat=df_patient, prior_model=mod_vancomycin_Goti2018, n_simul = 0)

The values of CLCREAT in the result$model are as expected

── Solved rxode2 object ──
── Parameters ($params): ──
THETA_Cl     DIAL THETA_Vc       WT THETA_Vp  THETA_Q   ETA_Cl   ETA_Vc   ETA_Vp 
     4.5      0.0     58.4     70.0     38.4      6.5      0.0      0.0      0.0 
── Initial Conditions ($inits): ──
 centr periph    AUC 
     0      0      0 
── First part of data (object): ──
# A tibble: 4 × 20
   evid  time  TVCl  TVVc  TVVp   TVQ    Cl    Vc    Vp     Q     ke   k12   k21    Cc centr periph   AUC CLCREAT
  <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl> <dbl>   <dbl>
1     2   0   2.76   58.4  38.4   6.5 2.76   58.4  38.4   6.5 0.0472 0.111 0.169     0     0      0     0      65
2     2  13   1.48   58.4  38.4   6.5 1.48   58.4  38.4   6.5 0.0254 0.111 0.169     0     0      0     0      30
3     2  24.2 1.07   58.4  38.4   6.5 1.07   58.4  38.4   6.5 0.0184 0.111 0.169     0     0      0     0      20
4     2  48   0.616  58.4  38.4   6.5 0.616  58.4  38.4   6.5 0.0106 0.111 0.169     0     0      0     0      10
# ℹ 2 more variables: WT <dbl>, DIAL <dbl>

With an appropriate number of estimated parameters is

uniqueCl <- unique(results$model$Cl)

uniqueCl
[1] 2.7554860 1.4844464 1.0732268 0.6164069 

But when the time sequence is expanded the first only the first values are carried forward

results$model$time <- seq(0, 48, by = 0.1)
uniqueCl <- unique(results$model$Cl)

As the result is

uniqueCL
[1] 2.755486
levenc commented 2 months ago

Hi Matteo, At this point, this is expected behavior. In result$model, the covariate values are both available in the dataset and passed as $params parameters to rxode2. However, only the values in the first row of the data set are passed as parameters by poso_simu_pop(). Two situations can be seen:

When you extend the number of observations with results$model$time <- seq(0, 48, by = 0.1), rxode2 has no CLCREAT values to use for the additional times. In this case, the CLCREAT value of $params is used, i.e. the initial value of the dataset.

To work around this problem, the interpolated values of the covariates can be calculated separately as in poso_estim_map(), using the internal function posologyr:::extrapol_cov():

  if(return_model){
    et_poso <- rxode2::as.et(object$tdm_data)
    et_poso$clearSampling()
    et_poso$add.sampling(seq(dat$TIME[1],
                             dat$TIME[length(dat$TIME)]+1,
                             by=.1))

   --- snip---

    if(!no_covariates){
      covar_mat <- sapply(object$covariates,FUN=extrapol_cov,dat=dat,
                          covar=object$covariates,
                          interpol_approx="constant",
                          f=ifelse(object$interpolation == "nocb",1,0),
                          event_table=et_poso)

      et_poso <- cbind(et_poso,covar_mat)
    }
    estim_map$model <- rxode2::rxSolve(object$ppk_model,et_poso,
                                       c(object$theta,estim_map$eta),
                                       covsInterpolation = interpolation)
    estim_map$event <- data.table::data.table(et_poso)
  }

Currently, only poso_estim_map() does this. It would be sensible to extend this behavior to the other estimation functions (including poso_estim_sir()).

levenc commented 2 months ago

Note that if the initial CLCREAT value had not been passed to rxode2 as a parameter by poso_simu_pop(), the line results$model$time <- seq(0, 48, by = 0.1) would have failed with a warning that CLCREAT was missing, since rxode2 does not interpolate covariate values between two observations in this case.

levenc commented 2 months ago

The previous commit 2de19d6 broke the workflow described in the a_priori_dosing vignette, which relies on having all covariates as $params in the model.

Edit: fixed in ba7565f Now poso_simu_pop() outputs a model with extrapolated values of all the covariates, from the first to the last event recorded in the dataset. This model can be easily plotted.

When expanding (or changing in any way) the time sequence, the models falls back to the covariate values in $params (i.e. the first recorded values).

levenc commented 1 month ago

Hi Matteo, I had to revert the changes made to poso_simu_pop() as they were causing several issues. However, the new function poso_replace_et() enables updating a model with events from a new rxode2 event table, while accounting for and interpolating any covariates or inter-occasion variability.

You can now add doses or observations without losing information about covariates:

# convert your dataset to an event table
et_vanco <- rxode2::as.et(df_patient)

# change it to your needs
new_scenario<- et_vanco$add.sampling(seq(0, 48, by = 0.1))

# update the model from poso_simu_pop() using this new scenario
poso_replace_et(target_model = results$model,
                prior_model = mod_vancomycin_Goti2018,
                event_table = new_scenario)
mat194 commented 1 month ago

Got it! Read the tread on the github. All clear!

Dott Matteo Morra Specializzando Malattie Infettive UNIVR

Il giorno gio 12 set 2024 alle ore 13:52 Cyril Leven < @.***> ha scritto:

Hi Matteo, I had to revert the changes made to poso_simu_pop() as they were causing several issues. However, the new function poso_replace_et() enables updating a model with events from a new rxode2 event table, while accounting for and interpolating any covariates or inter-occasion variability.

You can now add doses or observations without losing information about covariates:

convert your dataset to an event tableet_vanco <- rxode2::as.et(df_patient)

change it to your needsnew_scenario<- et_vanco$add.sampling(seq(0, 48, by = 0.1))

update the model from poso_simu_pop() using this new scenario

poso_replace_et(target_model = results$model, prior_model = mod_vancomycin_Goti2018, event_table = new_scenario)

— Reply to this email directly, view it on GitHub https://github.com/levenc/posologyr/issues/53#issuecomment-2346080279, or unsubscribe https://github.com/notifications/unsubscribe-auth/AVLOAKF3QPUVZDYVY2ROAFTZWF6ARAVCNFSM6AAAAABNDK6F3KVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDGNBWGA4DAMRXHE . You are receiving this because you authored the thread.Message ID: @.***>