using Random
using Pumas
using PumasUtilities
using CairoMakie
using AlgebraOfGraphics
using DataFramesMeta
using CSV
using Dates
PK50 - Analysis of multiple subjects concentration and response-time profiles
1 Background
- Structural model - Two compartment Model
- Route of administration - IV Infusion
- Dosage Regimen - 566 μg
- Number of Subjects - 12
2 Learning Outcome
To analyze and interpret exposure and effect with plasma protein binding as a co-covariate of PK parameters and exposure
3 Objectives
To build a sequential PKPD model for a drug considering fraction unbound as a covariate
4 Libraries
Call the necessary libraries to get started.
5 Model
A sequential two compartment PKPD model for a drug after infusion over 5 hours
= @model begin
pk_50 @metadata begin
= "Two Compartment Model"
desc = u"hr"
timeu end
@param begin
"""
Clearance (L/hr)
"""
∈ RealDomain(lower = 0)
tvcl """
Intercompartmental Clearance (L/hr)
"""
∈ RealDomain(lower = 0)
tvcld """
Volume of Disttibution - Central (L)
"""
∈ RealDomain(lower = 0)
tvvc """
Volume of Disttibution - Peripheral (L)
"""
∈ RealDomain(lower = 0)
tvvt """
Concentration which produces 50% effect (μg/L)
"""
∈ RealDomain(lower = 0)
tvec50 """
Maximum Effect
"""
∈ RealDomain(lower = 0)
tvemax """
Sigmoidicity factor
"""
∈ RealDomain(lower = 0)
tvsigma ∈ PDiagDomain(7)
Ω """
Proportional RUV
"""
∈ RealDomain(lower = 0)
σ_prop """
Additive RUV
"""
∈ RealDomain(lower = 0)
σ_add end
@random begin
~ MvNormal(Ω)
η end
@covariates fu
@pre begin
= tvcl * (1 / fu) * exp(η[1])
Cl = tvcld * (1 / fu) * exp(η[2])
Cld = tvvc * (1 / fu) * exp(η[3])
Vc = tvvt * (1 / fu) * exp(η[4])
Vt = tvec50 * (fu) * exp(η[5])
EC50 = tvemax * exp(η[6])
Emax = tvsigma * exp(η[7])
sigma end
@dynamics begin
' = -(Cl / Vc) * Central - (Cld / Vc) * Central + (Cld / Vt) * Peripheral
Central' = (Cld / Vc) * Central - (Cld / Vt) * Peripheral
Peripheralend
@derived begin
= @. Central / Vc
cp """
Observed Concentration (μg/L)
"""
~ @. Normal(cp, sqrt(cp^2 * σ_prop))
dv_cp = @. Emax * (cp^sigma) / (EC50^sigma + cp^sigma)
ef """
Observed Response
"""
~ @. Normal(ef, σ_add)
dv_ef end
end
PumasModel
Parameters: tvcl, tvcld, tvvc, tvvt, tvec50, tvemax, tvsigma, Ω, σ_prop, σ_add
Random effects: η
Covariates: fu
Dynamical system variables: Central, Peripheral
Dynamical system type: Matrix exponential
Derived: cp, dv_cp, ef, dv_ef
Observed: cp, dv_cp, ef, dv_ef
6 Parameters
Parameters provided for simulation are as below. Note that tv
represents the typical value for parameters.
Cl
- Clearance (L/hr)Vc
- Volume of distribution in central compartment (L)Vp
- Volume of distribution in Peripheral compartment (L)Q
- Intercompartmental Clearance (L/hr)EC50
- Concentration which produces 50% effect (μg/L)Emax
- Maximum Effectsigma
- Sigmoidicity factor
= (;
param = 11.4,
tvcl = 4.35,
tvcld = 19.9,
tvvc = 30.9,
tvvt = 1.8,
tvec50 = 2.1,
tvemax = 2.1,
tvsigma = Diagonal([0.0784, 0.1521, 0.0841, 0.1225, 0.16, 0.36, 0.09]),
Ω = 0.00,
σ_prop = 0.00,
σ_add )
7 Dosage Regimen
A group of 12 subjects is administered a dose of 566 μg infused over 5 hours
## Total Plasma Concentration
= DosageRegimen(566, cmt = 1, time = 0, duration = 5) ev1
Row | time | cmt | amt | evid | ii | addl | rate | duration | ss | route |
---|---|---|---|---|---|---|---|---|---|---|
Float64 | Int64 | Float64 | Int8 | Float64 | Int64 | Float64 | Float64 | Int8 | NCA.Route | |
1 | 0.0 | 1 | 566.0 | 1 | 0.0 | 0 | 113.2 | 5.0 | 0 | NullRoute |
=
sub_total map(i -> Subject(id = i, events = ev1, covariates = (fu = 1, group = "Total")), 1:12)
Population
Subjects: 12
Covariates: fu, group
Observations:
## Unbound Plasma Concentration
= Normal(0.016, 0.0049) fu1
Random.seed!(1234)
= rand(fu1, 12) fu
12-element Vector{Float64}:
0.01543490980387186
0.016091695415129437
0.02733828291993539
0.01056668091897708
0.02365434640105106
0.019207146265285493
0.011243058890447877
0.014273355570615185
0.009510289335555006
0.004854703035472016
0.016277903707770366
0.023564516452971834
= map(
df_unbound -> DataFrame(
((i, fui),) = i,
id = 566,
amt = 0,
time = 1,
cmt = 1,
evid = 113.2,
rate = missing,
dv_cp = missing,
dv_ef = fui,
fu = "Unbound",
group
),zip(1:12, fu),
)
= vcat(DataFrame.(df_unbound)...) df1_unbound
Row | id | amt | time | cmt | evid | rate | dv_cp | dv_ef | fu | group |
---|---|---|---|---|---|---|---|---|---|---|
Int64 | Int64 | Int64 | Int64 | Int64 | Float64 | Missing | Missing | Float64 | String | |
1 | 1 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.0154349 | Unbound |
2 | 2 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.0160917 | Unbound |
3 | 3 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.0273383 | Unbound |
4 | 4 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.0105667 | Unbound |
5 | 5 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.0236543 | Unbound |
6 | 6 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.0192071 | Unbound |
7 | 7 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.0112431 | Unbound |
8 | 8 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.0142734 | Unbound |
9 | 9 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.00951029 | Unbound |
10 | 10 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.0048547 | Unbound |
11 | 11 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.0162779 | Unbound |
12 | 12 | 566 | 0 | 1 | 1 | 113.2 | missing | missing | 0.0235645 | Unbound |
=
pop12_unbound read_pumas(df1_unbound, observations = [:dv_cp, :dv_ef], covariates = [:fu, :group])
Population
Subjects: 12
Covariates: fu, group
Observations: dv_cp, dv_ef
= [sub_total; pop12_unbound] pop24_sub
8 Simulation
Simulate the data and create a DataFrame
with specific data points.
= simobs(
sim_pop24_sub
pk_50,
pop24_sub,
param,= [
obstimes 0.1,
0.25,
0.5,
0.75,
1,
2,
3,
4,
4.999,
5.03,
5.08,
5.17,
5.25,
5.5,
5.75,
6,
6.5,
7,
8,
9,
10,
12,
24,
], )
Simulated population (Vector{<:Subject})
Simulated subjects: 24
Simulated variables: cp, dv_cp, ef, dv_ef
= DataFrame(sim_pop24_sub)
df50 first(df50, 5)
Row | id | time | cp | dv_cp | ef | dv_ef | evid | amt | cmt | rate | duration | ss | ii | route | fu | group | η_1 | η_2 | η_3 | η_4 | η_5 | η_6 | η_7 | Central | Peripheral | Cl | Cld | Vc | Vt | EC50 | Emax | sigma |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
String? | Float64 | Float64? | Float64? | Float64? | Float64? | Int64? | Float64? | Symbol? | Float64? | Float64? | Int8? | Float64? | NCA.Route? | Float64? | String? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | Float64? | |
1 | 1 | 0.0 | missing | missing | missing | missing | 1 | 566.0 | Central | 113.2 | 5.0 | 0 | 0.0 | NullRoute | 1.0 | Total | 0.0460301 | -0.0549254 | 0.221501 | -0.329275 | 0.586409 | -0.541874 | 0.130833 | missing | missing | 11.937 | 4.11752 | 24.8342 | 22.2309 | 3.23554 | 1.22148 | 2.39353 |
2 | 1 | 0.1 | 0.441424 | 0.441424 | 0.0102941 | 0.0102941 | 0 | missing | missing | missing | missing | missing | missing | missing | 1.0 | Total | 0.0460301 | -0.0549254 | 0.221501 | -0.329275 | 0.586409 | -0.541874 | 0.130833 | 10.9624 | 0.0912879 | 11.937 | 4.11752 | 24.8342 | 22.2309 | 3.23554 | 1.22148 | 2.39353 |
3 | 1 | 0.25 | 1.05257 | 1.05257 | 0.0778018 | 0.0778018 | 0 | missing | missing | missing | missing | missing | missing | missing | 1.0 | Total | 0.0460301 | -0.0549254 | 0.221501 | -0.329275 | 0.586409 | -0.541874 | 0.130833 | 26.1398 | 0.547647 | 11.937 | 4.11752 | 24.8342 | 22.2309 | 3.23554 | 1.22148 | 2.39353 |
4 | 1 | 0.5 | 1.94988 | 1.94988 | 0.28011 | 0.28011 | 0 | missing | missing | missing | missing | missing | missing | missing | 1.0 | Total | 0.0460301 | -0.0549254 | 0.221501 | -0.329275 | 0.586409 | -0.541874 | 0.130833 | 48.4237 | 2.04836 | 11.937 | 4.11752 | 24.8342 | 22.2309 | 3.23554 | 1.22148 | 2.39353 |
5 | 1 | 0.75 | 2.71656 | 2.71656 | 0.484789 | 0.484789 | 0 | missing | missing | missing | missing | missing | missing | missing | 1.0 | Total | 0.0460301 | -0.0549254 | 0.221501 | -0.329275 | 0.586409 | -0.541874 | 0.130833 | 67.4637 | 4.31569 | 11.937 | 4.11752 | 24.8342 | 22.2309 | 3.23554 | 1.22148 | 2.39353 |
9 Visualization
Plot a graph of Concentrations vs Time
@chain df50 begin
dropmissing!(:cp)
data(_) *
mapping(
:time => "Time (hrs)",
:cp => "Concentration (μg/L)",
= :group => "",
color = :id,
group *
) visual(Lines, linewidth = 2)
draw(;
= (;
axis = 0:5:25,
xticks = log10,
yscale = i -> (@. string(round(i; sigdigits = 1))),
ytickformat
),= (; fontsize = 22),
figure
)end
Plot a graph of Response vs Concentrations
@chain df50 begin
dropmissing!(:cp)
data(_) *
mapping(
:cp => "Concentration (μg/L)",
:ef => "Response",
= :group => "",
color = :id,
group *
) visual(Lines, linewidth = 2)
draw(;
= (; xscale = log10, xtickformat = i -> (@. string(round(i; sigdigits = 1)))),
axis = (; fontsize = 22),
figure
)end
Question - 1 and 2
- What infusion rate do you aim at in the present patient population during the first hour to reach a plasma concentration > 10 μg/L and < 50 μg/L.
Ans: We have targeted a plasma concentration of 30 μg/L
and thus the dose required to achieve that concentration is 784 μg given as an IV-infusion over 1 hour, followed by a 7843 μg given as an IV-infusion over 23 hours
- What Infusion rate is needed to remain at the steady state plasma concentration between 1 and 24 hours?
Ans: An infusion rate of 341 μg/hr is given to achieve the steady-state plasma concentration of 30 μg/L
## Dosage Regimen - Total Plasma Concentration
= DosageRegimen([784, 7843], cmt = 1, time = [0, 1], rate = [784, 341])
ev12 = Population(
pop12 map(i -> Subject(id = i, events = ev12, covariates = (fu = 1, group = "Total")), 1:12),
)
## Simulation
Random.seed!(123)
= simobs(
sim12
pk_50,
pop12,
param,= [
obstimes 0.1,
0.25,
0.5,
0.75,
1,
2,
3,
4,
4.999,
5.03,
5.08,
5.17,
5.25,
5.5,
5.75,
6,
6.5,
7,
8,
9,
10,
12,
24,
],
)= DataFrame(sim12)
df12 dropmissing!(df12, :cp)
@chain df12 begin
data(_) *
mapping(:time => "Time (hrs)", :cp => "Concentration (μg/L)", group = :id) *
visual(Lines, linewidth = 2)
draw(;
= (;
axis = 0:5:25,
xticks = log10,
yscale = i -> (@. string(round(i; sigdigits = 1))),
ytickformat
),= (; fontsize = 22),
figure
)end
Question - 3
- What unbound plasma concentration are reached (given the range) with the infusion rates calculated for the 1+23 hrs regimen? How does the variability seen in the predicted exposure at 1 and 24 hours compare between total and unbound concentration?
## Dosage Regimen - Unbound Plasma Concentration
= map(
df_3 -> DataFrame(
((i, fui),) = i,
id = [784, 7800],
amt = [0, 1],
time = [1, 1],
cmt = [1, 1],
evid = [784, 339],
rate = missing,
dv_cp = missing,
dv_ef = fui,
fu = "Unbound",
group
),zip(1:12, fu),
)= vcat(DataFrame.(df_3)...)
df1_3 =
pop_3_unbound read_pumas(df1_3, observations = [:dv_cp, :dv_ef], covariates = [:fu, :group])
= [pop12; pop_3_unbound]
pop_3
## Simulation
Random.seed!(12345)
= simobs(
sim3
pk_50,
pop_3,
param,= [
obstimes 0.1,
0.25,
0.5,
0.75,
1.0,
2,
3,
4,
4.999,
5.03,
5.08,
5.17,
5.25,
5.5,
5.75,
6,
6.5,
7,
8,
9,
10,
12,
24,
],
)= DataFrame(sim3)
df_sim3
@chain df_sim3 begin
dropmissing!(:cp)
data(_) *
mapping(
:time => "Time (hrs)",
:cp => "Concentration (μg/L)",
= :group => "",
color = :id,
group *
) visual(Lines, linewidth = 2)
draw(;
= (;
axis = 0:5:25,
xticks = log10,
yscale = i -> (@. string(round(i; sigdigits = 1))),
ytickformat
),= (; fontsize = 22),
figure
)end
Question - 4
- What exposure is needed in the new 1 + 23 hours infusion study to establish a response greater than one(1) Response unit?
Ans: The new 1 + 23 hr infusion chosen to achieve a steady-state concentration of 30 μg/L will help to achieve a response greater than 1 unit.
@chain df_sim3 begin
dropmissing!(:cp)
data(_) *
mapping(
:cp => "Concentration (μg/L)",
:ef => "Response",
= :group => "",
color = :id,
group *
) visual(Lines, linewidth = 2)
draw(;
= (; xscale = log10, xtickformat = i -> (@. string(round(i; sigdigits = 1)))),
axis = (; fontsize = 22),
figure
)end