Subjects and Dosage Regimens

Author

Haden Bunn

using Pumas

1 Introduction

  • Pharmacometric analyses rely on dosing, sampling, and covariate data from one or more subjects. In Pumas, a collection of subjects is represented as a Population object which can be created indirectly by calling read_pumas on an appropriately formatted tabular dataset, or directly by creating individual Subjects. The former is used for estimation workflows and was discussed in Module 4), while the latter is commonly used in simulation workflows and will be discussed in detail below.

2 The Subject Constructor

We begin by reviewing the Subject constructor; you can type ?Subject in the REPL to see the relevant docstring.

Subject(;
    id = "1",
    observations = NamedTuple(),
    events = Event[],
    time = observations isa AbstractDataFrame ? observations.time : nothing,
    covariates::Union{Nothing,NamedTuple} = nothing,
    covariates_time = observations isa AbstractDataFrame ? observations.time : nothing,
    covariates_direction = :left,
)

Subject() has several keyword arguments (kwargs), all of which have a default value (e.g., id = "1"). This means that you can create a Subject by calling Subject() without providing any additional information.

Subject()

If you group one or more subjects into an vector, it creates a Population of subjects.

_mypop = [Subject(), Subject()]

As with any ordered collection, you can access individual Subjects using indexing. Calling _mypop[2] will return the second Subject in the _mypop array.

_mypop[2]

2.1 Arguments

Next, we’ll take a closer look at each kwarg.

2.1.1 id

When we defined _mypop using Subject() we created a Population where id = "1" for both Subjects. This is bad practice and should be avoided. Below, we reassign _mypop to a vector containing 2 Subjects with unique ids.

_mypop = [Subject(; id = 1001), Subject(; id = "2002")]

The id kwarg will accept a Number or String; however, all ids are converted to Strings on the backend.

typeof(_mypop[1].id)    # String; "1001"

2.1.2 observations and time

The observations kwarg is set to nothing by default, but accepts a NamedTuple that can be used to specify observed data (e.g., plasma drug concentration). The key(s) used in the NamedTuple must match the observed variable defined in the @derived block of the intended model. If specifying observations you must also provide a vector of corresponding times.

Subject(; observations = (; dv = [9.6, 15.1]), time = [2, 4])

2.1.3 events

The events kwarg accepts a vector of Events generated by the DosageRegimen function, and is set to an empty vector Event[] by default.

Dosage Regimens

DosageRegimens are a separate topic, and rather than breaking to discuss them now, we will define a simple two-dose regimen, DR, that we can use until we will review DosageRegimen() syntax in Section 3.

# two 100-unit doses administered at t=0 and t=24
DR = DosageRegimen([100, 100]; time = [0, 24])

2.1.4 covariates, covariates_time, and covariates_direction

The default value for covariates is nothing. However, you can easily define subject-level (e.g., baseline body weight, wtbl) or time-varying covariates (e.g., body weight throughout the study period, wt).

Subject(; covariates = (; wtbl = 75.0))

The covariate, wtbl, is assigned a scalar value, 75.0, which indicates that it is a subject-level covariate. Subject-level covariates are repeated if there are multiple dosing events

Subject(; events = DR, covariates = (; wtbl = 75.0)) |> DataFrame

If more than one covariate value is provided, covariates_time must also be provided.

x = Subject(; events = DR, covariates = (; wt = [75.0, 71.2]), covariates_time = [0, 24])
Subject(covariates = (; wt = [70, 75]), covariates_time = [0, 24])

If more than one covariate is provided and one is time-varying, all covariate values must be passed as vectors of the same length. In the example below, the subject-level covariate, sex is added. If we try to create this Subject, we get an error stating that ERROR: Length of covariate sex (1) does not match length of covariates_time (2).

Subject(;
    events = DR,
    covariates = (; wt = [75.0, 71.2], sex = 1), # ERROR
    covariates_time = [0, 24],
)

To resolve the issue, the value of sex needs to be a vector whose length is the same as covariates_time (i.e., 2).

Subject(;
    events = DosageRegimen(100; ii = 24, addl = 10),
    covariates = (; wtbl = [75.0, 71.2], sex = [1, 1]),
    covariates_time = [0, 96],
)

Lastly, in cases where the number of dosing events exceeds the number of covariate values provided. Those values are extrapolated using LOCF or NOCB

3 The DosageRegimen() Constructor

Let’s start by taking a look at the DosageRegimen() constructor; you can type ?DosageRegimen in the REPL to view the docstring.

DosageRegimen(
    amt::Numeric;
    time::Numeric = 0,
    cmt::Union{Numeric,Symbol} = 1,
    evid::Numeric = 1,
    ii::Numeric = zero.(time),
    addl::Numeric = 0,
    rate::Numeric = zero.(amt) ./ oneunit.(time),
    duration::Numeric = zero(amt) ./ oneunit.(time),
    ss::Numeric = 0,
    route::NCA.Route = NCA.NullRoute,
)

DosageRegimen() has a single positional argument (amt) and several kwargs with default values (e.g., time = 0). This means that you can create a valid DosageRegimen by calling the constructor with just an amt.

DosageRegimen(100)

The result is a one-time (ii = 0 and addl = 0), non-steadystate (ss = 0), dose event (evid = 1) where amt = 100 “mass units”. It will be administered instantaneously (rate = 0 and duration = 0) at time = 0 into the first (cmt = 1) compartment defined in the @dynamics block of the relevant model. The route kwarg is only relevant for NCA (see Section 3.1.8) and is set to NCA.NullRoute by default.

3.1 Arguments

3.1.1 amt

amt is a positional (i.e., “required”) argument that accepts Numeric values. In practice, users should perform mass conversion as needed to ensure that the amt specified is consistent with any observed concentration data.

3.1.2 time

The time kwarg accepts Numeric values and is set to t = 0 by default. It represents the “time unit” for the analysis and corresponds to the timing of dose administration.

Unit Conversion

The Unitful.jl package offers an elegant solution for performing unit conversion. While the package is included in the Pumas app, its use is beyond the scope of this tutorial. Please see the linked documentation for more information. At the time of writing,units are fully supported only in NCA workflows, but not in estimation workflows.

3.1.3 cmt

The cmt kwarg accepts a Numeric or Symbol value and is set to cmt = 1 by default. As stated above, the compartment associated with cmt = 1 will depend on the definitions used in the @dynamics block of the relevant model. To further illustrate this point, suppose you want to administer a dose into the central compartment of a one-compartment model whose dynamics are defined using a closed-form solution (i.e., Central1) instead of a differential equation as shown below. In this case, the default cmt = 1 would be sufficient because Central is the first and only compartment.

# alias for closed form solution
@dynamics Central1

# differential equation corresponding to Central1
@dynamics begin
    Central' = -CL / Vc * Central
end

However, if you wanted to dose into the central compartment of a one-compartment model includes a depot for oral dosing, cmt = 1 would no longer be appropriate. In the closed-form solution for this system (Depots1Central1), cmt = 1 corresponds to the Depot compartment, so you would need to use cmt = 2 instead. Alternatively, you could specify the target compartment explicitly using a Symbol (cmt = :Central), and avoid any potential confusion. Note that, in such cases, Symbols, are case sensitive (i.e., cmt = :central would cause an error in this example).

# alias for closed form solution
@dynamic Depots1Central1

# differential equation(s) corresponding to Depots1Central1
@dynamics begin
    Depot' = -Ka * Depot
    Central' = Ka * Depot - CL / Vc * Central
end

Lastly, you can view the compartment names used in closed-form solutions like Central1 through the help menu in the REPL (e.g., ?Central1).

3.1.4 evid

The evid kwarg is an “event identifier” that accepts a limited set of Numeric values and defaults to dose events (evid = 1). The list of possible values includes:

  • 1: Dose event
  • 2: “Other” type of event
  • 3: Reset event; the amount in each compartment is reset to zero and the on/off status of each compartment is set to its initial status.
  • 4: Reset and dose event; applies evid = 3 followed by a dose event.

While not applicable to DosageRegimens, evid = 0 is also valid and corresponds to an observation when specified in tabular data formats. When constructing Subjects directly we do not need to specify evid = 0 because we use a separate keyword observations specifically for this type of input.

In Pumas, evid = 2 is mostly present for compatibility with NONMEM styled atasets. The main point of an evid = 2 event is to insert a point in time where we need the numerical integrator of the dynamical system to step to. This could be because the system has a non-differentiability due to some intervention. Rather than specifying an event with evid = 2 it is often more useful to instead have a covariate that changes value at the time points of non-differentiablities. If a covariate changes value, the solver for the dynamical system will always be forced to step to the time point.

DosageRegimen(30; time = 10, evid = 3) # INCORRECT; amt must be 0

DosageRegimen(0; time = 10, evid = 3) # reset all compartments at time 10

DosageRegimen(30; cmt = :Central, time = 10, evid = 4) # reset all compartments at time 10 and dose 30 units into `Central`

::: {.callout-warning “evid = 4 issues”}

At this time, evid = 4 use in Pumas is known to not support certain use cases as intended, specifically with regards to resetting the time of the event correctly in multiple dosage regimens. This is a known issue and will be addressed in a future release of Pumas. Until then users are recommended to use continuous time for repeated dosing where a reset is required while giving enough washout time between doses.

:::

3.1.5 ii and addl

The ii and addl kwargs represent the inter-dose interval (i.e., “frequency”) and number of additional doses, respectively. They both accept Numeric values and default to 0. These kwargs are used in tandem to specify the number of a additional doses to be administered beyond the first, and at what frequency. As a result, if either kwarg is changed from the default, the other must be updated to satisfy the following criteria:

  • If addl > 0, ii must be also be >0
DosageRegimen(500; addl = 2) # INCORRECT (ii = 0 by default)

DosageRegimen(500; ii = 12, addl = 2) # CORRECT
  • If ii > 0, addl must be >0 unless ss > 0
DosageRegimen(500; ii = 12) # INCORRECT (addl = 0 and ss = 0 by default)

DosageRegimen(500; ii = 12, addl = 2) # CORRECT

DosageRegimen(500; ii = 12, ss = 1) # CORRECT

In the latter example where ii = 12 and addl = 0, no error occurs because the dose is being administered at steady-state (ss != 0). We’ll discuss the implications of steady-state dosing in Section 3.1.7.

3.1.6 rate and duration

The rate and duration kwargs both accept Numeric values and are set to 0 by default. When necessary, users should only pass a value for one of these kwargs since the other will be calculated automatically from that value. In addition, the value passed should have the same units as amt and/or time.

In the example, we’ll assume that the dose is given in mg and that time is specified in hours (hr). Thus, 100 mg of drug X are administered into cmt = 1 at a rate of 50 mg/hr. The corresponding duration = 2 hours is calculated automatically.

DosageRegimen(100; rate = 50)

The same DosageRegimen could be constructed by passing a duration value of 2 instead.

DosageRegimen(100; duration = 2)

3.1.7 ss

The ss kwarg is used to define steady-state status; it accepts a limited set of Numeric values and is set to 0 by default. The list of possible values includes:

  • 0: not a steady state dose.
  • 1: steady state dose, compartment amounts will be set to the steady-state amounts that result from the given dose. Compartment amounts resulting from prior dose events are “zeroed out”, and infusions in progress or pending additional doses are cancelled.
  • 2: steady state dose, compartment amounts will be set to the sum of the steady-state amounts that result from a given dose plus the amounts that would be expected at that event time if a steady-state dose had not been given.

The applications of ss are varied and complex. Please refer to the documentation for more detail.

3.1.8 route

NCA Only

This kwarg was included in the current discussion for completeness. Most users will not interact with NCA.Routes while defining DosageRegimen()s. Instead, this option is set while constructing an NCASubject or NCAPopulation using read_nca. An Introduction to NCA in Pumas is provided elsewhere.

The route kwarg is used to specify route of administration for NCA. It accepts a limited set of NCA.Routes (IVBolus, IVInfusion, EV). In the example below, the route column has a value of NullRoute because no route was provided.

DosageRegimen(100)

In the second example, the route is set of IVBolus.

DosageRegimen(100; route = NCA.IVBolus)

3.2 Examples

In this section we take the information above and use it to define a variety of DosageRegimens. We assume that each example is applied to a simple one-compartment oral dosing model. The @dynamics block is defined using Depots1Central1 where cmt = 1 = :Depot and cmt = 2 = :Central.

3.2.1 Single dose

  • 100 mg PO x1

    • PO: “by mouth”
    • x1: “times 1”; “one-time dose”
DosageRegimen(100)

3.2.2 Multiple doses

We touched on defining regimens with multiple doses while discussing the addl and ii kwargs, but let’s take a closer look at the options available.

  • 100 mg PO q12h for 48 hours

    • q12h: “every 12 hours”
_dr1 = DosageRegimen(100; ii = 12, addl = 3)

In the example above, we used the default start time = 0 and set our frequency to q12h dosing (ii = 12). The total duration of therapy is 48 hours, which equates to 4 doses total; 3 in addition (addl) to the first.

We could have also passed a vector of Numeric values corresponding to the administration time for each dose to the time kwarg.

_dr2 = DosageRegimen(100; time = [0, 12, 24, 36])

The result is the same, but using addl and ii is preferred because it is concise and provides a clear definition of dosing.

However, the latter serves to illustrate an important point: the kwargs in DosageRegimen can be vectorized to build more complex regimens. Let’s explore that further by adding another 48 hours of therapy at a higher dose.

  • 100 mg PO q12h for 48 hours followed by 200 mg PO q12h for 48 hours
DosageRegimen([100, 200]; time = [0, 48], ii = [12, 12], addl = [3, 3])

In the example above, we’ve implemented additional doses by providing an array of values to each kwarg. If you look closely, you should be able to mentally map the first element of each kwarg array to the first part of the dosing prompt, and the second set of elements to the second part. Note that the vector elements passed to ii are the same (12 and 12; as are the elements for addl). In such cases we can simply provide a scalar value, and Pumas will automatically “convert” it to an array of appropriate length. In contrast, the time kwarg must be defined explicitly, because the second dose starts at time = 48 hours.

DosageRegimen([100, 200]; time = [0, 48], ii = 12, addl = 3)

3.2.3 Multiple Compartments

The DosageRegimen() constructor can be used to combine multiple DosageRegimens, which is especially convenient if you wish to administer a dose into two different compartments.

  • 500 mg IV x1, 100 mg PO q12h x4
# loading dose
_ld = DosageRegimen(500; cmt = :Central)

# maintenance dose
_md = DosageRegimen(100; ii = 12, addl = 3)

# combined regimen
DosageRegimen(_ld, _md)

The above regimen is constructed using 2 separate regimens: a one-time, 500 mg loading dose administered into the Central compartment and a 100 mg oral maintenance dose started at the same time. While slightly less intuitive, the same combined result could be written using “array” syntax.

DosageRegimen([500, 100]; cmt = [2, 1], ii = [0, 12], addl = [0, 3])

The same separate regimens could be used to initiate PO therapy 12 hours after the IV dose is administered. The offset kwarg allows for the second regimen to start at a pre-specified time after the last dose of the first regimen is administered.

DosageRegimen(_ld, _md, offset = 12)

4 Conclusion

This tutorial provided an overview of the Subject and DosageRegimen constructors which are a fundamental part of simulation in Pumas. The foundational elements described here are used extensively throughout the remainder of this module.