PK47 - Plasma protein binding modeling

1 Background

  • Structural Model - Plasma Protein Binding Model
  • Number of Subjects - 4
  • Number of Compounds - 2

2 Learning Outcome

  • To get an understanding of the determinants of the unbound concentration, free fraction and total concentration.
  • How the modeling of plasma protein data is done
  • To understand how the binding protein concentration affects the unbound drug concentration

3 Objectives

  • To analyze In vitro plasma protein data of two compounds
  • To analyze binding data at two different binding concentration
  • To show and understand the relationship between Cu and fu
  • To understand the properties of binding sites that can be modeled

4 Libraries

Call the necessary libraries to get started

using Random
using Pumas
using PumasUtilities
using CairoMakie
using AlgebraOfGraphics
using DataFramesMeta
using CSV
using Dates

5 Model

pk_47 = @model begin
    @metadata begin
        desc = "Plasma Protein Binding Model"
    end

    @param begin
        """
        Affinity constant between drug and protein
        """
        tvka  RealDomain(lower = 0)
        """
        Number of binding sites per molecule
        """
        tvn  RealDomain(lower = 0)
        Ω  PDiagDomain(2)
        """
        Additive RUV
        """
        σ_add  RealDomain(lower = 0)
    end

    @random begin
        η ~ MvNormal(Ω)
    end

    @covariates dose Pt

    @pre begin
        Ka = tvka * exp(η[1])
        n = tvn * exp(η[2])
        _dose = dose
        _Pt = Pt
    end

    @vars begin
        fu = (1 - (1 / (1 + (_dose / (n * _Pt)) + (1 / (Ka * n * _Pt))))) * 100
    end

    @derived begin
        dv = @. Normal(fu, σ_add)
    end
end
PumasModel
  Parameters: tvka, tvn, Ω, σ_add
  Random effects: η
  Covariates: dose, Pt
  Dynamical system variables: 
  Dynamical system type: No dynamical model
  Derived: dv, fu
  Observed: dv, fu

6 Parameters

The parameters are as given below. Note that tv represents the typical value for parameters.

  • tvka - Affinity constant between drug and protein
  • tvn - Number of binding sites per molecule

6.1 Compound 1

param1 = (; tvka = 6.09, tvn = 2.833, Ω = Diagonal([0.04, 0.04, 0.04]), σ_add = 2.0398)

6.2 Compound 2

param2 = (; tvka = 10.2353, tvn = 1.937, Ω = Diagonal([0.04, 0.04, 0.04]), σ_add = 2.2774)

7 Dosage Regimen

Since there is no dosage regimen, we will create a DataFrame with no events and read the file using read_pumas, which will be used for simulation.

7.1 Compound 1

df_sub1 = DataFrame(
    id = "0.3mg Protein Conc",
    time = 0:1:9999,
    dv = missing,
    dose = 0.01:0.01:100,
    Pt = 0.3,
)
df_sub2 = DataFrame(
    id = "50mg Protein Conc",
    time = 0:1:9999,
    dv = missing,
    dose = 0.01:0.01:100,
    Pt = 50,
)
df1 = vcat(df_sub1, df_sub2)
pop1 = read_pumas(df1, observations = [:dv], covariates = [:dose, :Pt], event_data = false)
Population
  Subjects: 2
  Covariates: dose, Pt
  Observations: dv

7.2 Compound 2

df_sub3 = DataFrame(
    id = "0.3mg Protein Conc",
    time = 0:1:9999,
    dv = missing,
    dose = 0.01:0.01:100,
    Pt = 0.1,
)
df_sub4 = DataFrame(
    id = "50mg Protein Conc",
    time = 0:1:9999,
    dv = missing,
    dose = 0.01:0.01:100,
    Pt = 10,
)
df2 = vcat(df_sub3, df_sub4)
pop2 = read_pumas(df2, observations = [:dv], covariates = [:dose, :Pt], event_data = false)
Population
  Subjects: 2
  Covariates: dose, Pt
  Observations: dv

8 Simulation

We will now simulate the experimental data for Compound 1 at two different levels of protein concentration (Pt) 0.3 and 50.Since we are simulating only a single subject, we can zero out the random effects.

Random.seed!(123)

8.1 Compound 1

zfx = zero_randeffs(pk_47, pop1, param1)
sim_1 = simobs(pk_47, pop1, param1, zfx)

df47_cmp1 = reduce(
    vcat,
    map(sim_1) do subj
        subdf = DataFrame(
            id = subj.subject.id,
            fu = subj.observations.fu,
            dose = subj.covariates.dose,
        )
    end,
)
first(df47_cmp1, 5)
5×3 DataFrame
Row id fu dose
String Float64 Float64
1 0.3mg Protein Conc 17.0104 0.01
2 0.3mg Protein Conc 17.8129 0.02
3 0.3mg Protein Conc 18.6 0.03
4 0.3mg Protein Conc 19.3723 0.04
5 0.3mg Protein Conc 20.13 0.05

8.2 Compound 2

zfx = zero_randeffs(pk_47, pop2, param2)
sim_2 = simobs(pk_47, pop2, param2, zfx)
df47_cmp2 = reduce(
    vcat,
    map(sim_2) do subj
        subdf = DataFrame(
            id = subj.subject.id,
            fu = subj.observations.fu,
            dose = subj.covariates.dose,
        )
    end,
)
first(df47_cmp2, 5)
5×3 DataFrame
Row id fu dose
String Float64 Float64
1 0.3mg Protein Conc 35.7335 0.01
2 0.3mg Protein Conc 37.7973 0.02
3 0.3mg Protein Conc 39.7326 0.03
4 0.3mg Protein Conc 41.5512 0.04
5 0.3mg Protein Conc 43.2632 0.05

9 Visualization

9.1 Compound 1

@chain df47_cmp1 begin
    @rsubset :dose  [0.01, 0.05, 0.1, 0.5, 1, 5, 10, 50, 100]
    data(_) *
    mapping(
        :dose => "Unbound concentration",
        :fu => "Free Fraction (%)",
        color = :id => "",
    ) *
    visual(ScatterLines; linewidth = 4, markersize = 12)
    draw(;
        figure = (; fontsize = 22),
        axis = (;
            yticks = 0:20:120,
            xscale = log10,
            xtickformat = i -> (@. string(round(i; digits = 1))),
            title = "Compound 1 - Free fraction vs Unbound Concentration",
        ),
        legend = (; position = :bottom),
    )
end

9.2 Compound 2

@chain df47_cmp2 begin
    @rsubset :dose  [0.01, 0.05, 0.1, 0.5, 1, 5, 10, 50, 100]
    data(_) *
    mapping(
        :dose => "Unbound concentration",
        :fu => "Free Fraction (%)",
        color = :id => "",
    ) *
    visual(ScatterLines; linewidth = 4, markersize = 12)
    draw(;
        figure = (; fontsize = 22),
        axis = (;
            yticks = 0:20:120,
            xscale = log10,
            xtickformat = i -> (@. string(round(i; digits = 1))),
            title = "Compound 2 - Free fraction vs Unbound Concentration",
        ),
        legend = (; position = :bottom),
    )
end

10 Simulating Multiple Experiments

## Compound 1
par1 = (; tvka = 6.09, tvn = 2.833, Ω = Diagonal([0.042, 0.0926, 0.0623]), σ_add = 2.0398)

time1 = [0, 1, 2, 3, 4, 5, 6, 7, 8]
dose = [0.01, 0.05, 0.1, 0.5, 1, 5, 10, 50, 100]

df1_cmp1 = map(
    i -> DataFrame(
        id = i,
        time = time1,
        dv = missing,
        dose = dose,
        Pt = 0.3,
        Compound = "A",
    ),
    1:12,
)
df2_cmp1 = map(
    i -> DataFrame(
        id = i,
        time = time1,
        dv = missing,
        dose = dose,
        Pt = 50,
        Compound = "A",
    ),
    13:24,
)
df12_cmp1 = vcat(df1_cmp1, df2_cmp1)
df_cmp1 = vcat(DataFrame.(df12_cmp1)...)
pop_cmp1 = read_pumas(
    df_cmp1,
    observations = [:dv],
    covariates = [:dose, :Pt, :Compound],
    event_data = false,
)

sim_cmp1 = simobs(pk_47, pop_cmp1, par1)
df_sim_cmp1 = DataFrame(sim_cmp1)

## Compound 2
par2 =
    (; tvka = 10.2353, tvn = 1.937, Ω = Diagonal([0.0225, 0.0378, 0.0135]), σ_add = 2.2774)

df1_cmp2 = map(
    i -> DataFrame(
        id = i,
        time = time1,
        dv = missing,
        dose = dose,
        Pt = 0.1,
        Compound = "B",
    ),
    1:12,
)
df2_cmp2 = map(
    i -> DataFrame(
        id = i,
        time = time1,
        dv = missing,
        dose = dose,
        Pt = 10,
        Compound = "B",
    ),
    13:24,
)
df12_cmp2 = vcat(df1_cmp2, df2_cmp2)
df_cmp2 = vcat(DataFrame.(df12_cmp2)...)
pop_cmp2 = read_pumas(
    df_cmp2,
    observations = [:dv],
    covariates = [:dose, :Pt, :Compound],
    event_data = false,
)

sim_cmp2 = simobs(pk_47, pop_cmp2, par2)
df_sim_cmp2 = DataFrame(sim_cmp2)


df_sim = vcat(df_sim_cmp1, df_sim_cmp2);
#CSV.write("pk_47.csv", df_sim)