Introduction
This tutorial requires ubiquity 2.04 or greater. Currently this is only available using the development version off of github
Sometimes we need to analyze data where the independent variable is not time. You can do this with ubiquity and this tutorial will highlight how that is done. If you have not done so already, please review the naive-pooled parameter estimation tutorial because this will build on the concepts covered there. The workshop (workshop.ubiquity.tools) provides an example of how to analyize static in vitro data. To make a copy of these scripts and other supporting files in the current working directory run the following:
library(ubiquity)
fr = workshop_fetch(section="In Vitro", overwrite=TRUE)
This should create the following files in your current working direcotry:
-
system-in_vitro.txt
- System file that can be run both in vivo and in vitro -
in_vitro_er_data.csv
- In vitro data file -
mk_data_in_vitro.R
- Script to generate in vitro data -
analysis_in_vitro.R
- Script to perform parameter estimation using the in vitro data
This example uses a system from Chakraborty and Jusko. J Pharm Sci 91(5), 1334-1342. Specifically equation 5 from that article where the effect of two drugs, A and B, is driven by their concentrations (C_A, and C_B):
E = 100 \left( 1- \frac{ \frac{I_{max,A}(C_A) ^{\gamma_A}}{(\Psi IC_{50,A})^{\gamma_A}} + \frac{I_{max,B}(\xi C_B)^{\gamma_B}}{(\Psi IC_{50,B})^{\gamma_B}} \\ + (I_{max,A} + I_{max,B} + I_{max,A} I_{max,B}) \\ \times \frac{(C_A) ^{\gamma_A}}{(\Psi IC_{50,A})^{\gamma_A}} \times \frac{(\xi C_B)^{\gamma_B}}{(\Psi IC_{50,B})^{\gamma_B}} \\ }{ \frac{(C_A) ^{\gamma_A}}{(\Psi IC_{50,A})^{\gamma_A}} + \frac{(\xi C_B)^{\gamma_B}}{(\Psi IC_{50,B})^{\gamma_B}} \\ +\frac{(C_A) ^{\gamma_A}}{(\Psi IC_{50,A})^{\gamma_A}} \times \frac{(\xi C_B)^{\gamma_B}}{(\Psi IC_{50,B})^{\gamma_B}} +1 } \right)
\xi = \frac{IC_{50,A}}{IC_{50,B}}
This has been implemented in the PKPD system file
(system-in_vitro.txt
) shown at the bottom. This
implementation is dynamic meaning the effect changes with the PK of the
drug. However the effect is considered instantaneous and typically
analyzed from in vitro data using algebraic relationships. For
this system consider the following data:
Here both the concentrations of drug A and B are altered (independent variable) and the Effect is measured (dependent variable).
The sysem file: system-in_vitro.txt
First lets discuss the way the system file is structured. There is bolus dosing for the PK specified, but the initial conditions of the effect compartments are also defined in terms of system parameters:
<I> Cp_A = C_A0
<I> Cp_B = C_B0
These system parameters (C_A0
and C_B0
)
have a default value of zero. So by default the system would run like
any in vivo PKPD system. But these initial condition
placeholders will be used when performing in vitro analyses.
This allows you to use the same system file for both in vitro
and in vivo analyses. This is useful because you do not have to
do your static in vitro analysis in one system file and then copy and
paste to your in vivo dynamic system.
The dataset: in_vitro_er_data.csv
This table contains a snapshot of the relevant columns of the dataset:
C_A0 |
C_B0 |
Effect |
treat |
samp_time |
---|---|---|---|---|
0.001 |
0.1 |
102.15401 |
A_0_001_B_0_1 |
1 |
0.001 |
0.1 |
100.78589 |
A_0_001_B_0_1 |
1 |
0.001 |
0.1 |
91.14621 |
A_0_001_B_0_1 |
1 |
0.001 |
100 |
93.38012 |
A_0_001_B_100 |
1 |
0.001 |
100 |
91.03467 |
A_0_001_B_100 |
1 |
0.001 |
100 |
86.20118 |
A_0_001_B_100 |
1 |
0.001 |
500 |
51.76744 |
A_0_001_B_500 |
1 |
0.001 |
500 |
48.95016 |
A_0_001_B_500 |
1 |
0.001 |
500 |
52.68611 |
A_0_001_B_500 |
1 |
0.001 |
1000 |
28.46122 |
A_0_001_B_1000 |
1 |
The C_A0
and C_B0
columns correspond to the
concentrations that elicit the Effect
. The
treat
column is a unique name for the combination of the
two drug concentrations. It has only letters numbers and underscores.
This is intentional so I can use this as a cohort name when I’m
constructing the estimation script below. The samp_time
column is set to 1. This is arbitrary because the mapping in the cohort
definitions requires a time column. You’ll see why it is arbitrary
below.
The analysis script: analysis_in_vitro.R
This analysis scripts has some aspects that are unique to the in vitro analysis being performed.
Output times
First is the output times. When you run simulations (which is what happens when performing a parameter estimation) more output times results in slower simulations. So it is important to only include the ncesssary output times. In this case we only have two output times (0 and 1):
cfg=system_set_option(cfg, group = "simulation",
option = "output_times",
seq(0,1,1))
The final value of 1 was chosen because that corresponds to the time
column (samp_time
) in the analysis dataset.
Making a dynamic simulation static:
Next we set the simulation
option dynamic
to FALSE
:
cfg=system_set_option(cfg, group = "simulation",
option = "dynamic",
value = FALSE)
This will fix the values of the differential equations to 0 for the purposes of the subsequent simulations. Only available in ubiquity version \ge 2.04.
Datasets
For the dataset we are reading it into a dataframe and loading it that way. This was done so the dataset could be used internally with ubiqiuty and also to construct the analysis below:
er_data = readr::read_csv("in_vitro_er_data.csv")
cfg = system_load_data(cfg, dsname = "er_data",
data_file = er_data)
Defining cohorts
In the estimation tutorial cohorts are defined individually. We could
do that here too but it would be rather tedious. See we need a cohort
for every unique combination of C_A0
and C_B0
in the dataset. To do this we are going to loop through each unique
value in the treat
column and create a cohort. The variable
tmp_treat
contains the value of the current treatment so
the first thing we do is get the records for the current treatment:
treat_recs = dplyr::filter(er_data, treat == tmp_treat)
This subset of the data is used to define the initial condition
parameters for the current cohort using the cp
field (see
the help for system_define_cohort()
for more information
about this option). Because of the way the treat
column was
constructed we can use this as the cohort name. This will allow us to
link the simulated output below to the original dataset in the post
processing section below. The inputs are set to NULL
here
and we do not have to change them because there is no dosing.
cohort = list(
name = tmp_treat,
cf = list(
treat = c(tmp_treat)),
cp = list(
C_A0 = treat_recs$C_A0[1],
C_B0 = treat_recs$C_B0[1]),
inputs = NULL,
outputs = NULL,
dataset = "er_data")
Because we set the simulation option dynamic
to
FALSE
, the initial condition is set to the values for the
current treatment, and there are no inputs, then the system is
effectively behaving like an in vitro system.
Postprocessing
You cannot rely on the normal figure generation and reporting
elements. In this example we can take the simulated output at the
estimation erp
and the original data dataset
er_data
to create meaningful diagnostic plots. This is
because we can link the two datasets using the treat
column
from the original dataset to the COHORT
column in
erp$pred
:
df_orig = er_data |>
dplyr::select(C_A0, C_B0, ave_eff, treat) |>
dplyr::distinct()
df_est = erp$pred |>
dplyr::filter(!SMOOTH) |>
dplyr::rename(treat = COHORT)
df_plot = dplyr::full_join(df_est, df_orig, by="treat") |>
dplyr::mutate(C_B0 = as.factor(C_B0))
library(ggplot2)
p = ggplot(data=df_plot) +
geom_point(aes(x=C_A0, y=OBS, group=C_B0, color=C_B0)) +
geom_line(aes(x=C_A0, y=PRED, group=C_B0, color=C_B0)) +
scale_x_log10()
Contents of system-in_vitro.txt
# Author: John Harrold <john.m.harrold@gmail.com>
#
# Analysis of an in vitro system. The system below was taken from:
#
# Chakraborty A, Jusko WJ. Pharmacodynamic interaction of recombinant human
# interleukin-10 and prednisolone using in vitro whole blood lymphocyte
# proliferation. J Pharm Sci. 2002 May;91(5):1334-42. doi: 10.1002/jps.3000.
# PMID: 11977109.
#
# #-------------#
# | Parameters |
# #-------------#
#
# System parameters
# name value lower upper units editable grouping
# bound bound
<P> IC50_A 1.0 eps Inf ----- yes Efficacy
<P> IC50_B 50.0 eps Inf ----- yes Efficacy
<P> Imax_A 1.0 eps Inf ----- yes Efficacy
<P> Imax_B 1.0 eps Inf ----- yes Efficacy
<P> PSI 10.0 eps Inf ----- yes Efficacy
<P> G_A 0.6 eps Inf ----- yes Efficacy
<P> G_B 1.4 eps Inf ----- yes Efficacy
<P> Vp_A 1.0 eps Inf ml yes PK
<P> CL_A 1.0 eps Inf ml/hr yes PK
<P> Vp_B 1.0 eps Inf ml yes PK
<P> CL_B 1.0 eps Inf ml/hr yes PK
<P> C_A0 0.0 eps Inf ----- yes IC
<P> C_B0 0.0 eps Inf ----- yes IC
# #-------------------#
# |Input Information |
# #-------------------#
#
# Bolus Events
# ------------
# times/events state values scale units
<B:times>; [ 0 ]; 1.0; hours
<B:events>; Cp_A; [1.0 ]; 1.0/Vp_A; mg
<B:events>; Cp_B; [1.0 ]; 1.0/Vp_B; mg
# By default these values are zero but can be overwritten when
# doing analysis of in vitro data.
<I> Cp_A = C_A0
<I> Cp_B = C_B0
<As> XI = IC50_A/IC50_B
<Ad> COMP_A = SIMINT_POWER[Cp_A][G_A]/(SIMINT_POWER[PSI*IC50_A][G_A])
<Ad> COMP_B = SIMINT_POWER[XI*Cp_B][G_B]/(SIMINT_POWER[PSI*IC50_A][G_B])
<Ad> COMP_Imax = Imax_A + Imax_B - Imax_A*Imax_B
<Ad> EFF_num = Imax_A*COMP_A + Imax_B*COMP_B + COMP_Imax*COMP_A*COMP_B
<Ad> EFF_den = COMP_A + COMP_B + COMP_A*COMP_B + 1.0
<Ad> EFF = 100.0*(1.0-EFF_num/EFF_den)
# #-----------------------------#
# | ODEs, and State Information |
# #-----------------------------#
<ODE:Cp_A> CL_A/Vp_A*Cp_A
<ODE:Cp_B> CL_B/Vp_B*Cp_B
# #---------#
# | Outputs |
# #---------#
<O> Cp_A_mg_ml = Cp_A
<O> Cp_B_mg_ml = Cp_B
<O> Effect = EFF
#<VP> prop_err 1.0 eps Inf ----- yes IC
#<OE:Effect> prop=prop_err
# #---------#
# | Options #
# #---------#
# specify different time scales
<TS:hours> 1.0