using Pumas
Subjects and Dosage Regimens
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 callingread_pumas
on an appropriately formatted tabular dataset, or directly by creating individualSubject
s. 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(;
= "1",
id = NamedTuple(),
observations = Event[],
events = observations isa AbstractDataFrame ? observations.time : nothing,
time ::Union{Nothing,NamedTuple} = nothing,
covariates= observations isa AbstractDataFrame ? observations.time : nothing,
covariates_time = :left,
covariates_direction )
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.
= [Subject(), Subject()] _mypop
As with any ordered collection, you can access individual Subject
s using indexing. Calling _mypop[2]
will return the second Subject
in the _mypop
array.
2] _mypop[
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 Subject
s. This is bad practice and should be avoided. Below, we reassign _mypop
to a vector containing 2 Subject
s with unique id
s.
= [Subject(; id = 1001), Subject(; id = "2002")] _mypop
The id
kwarg will accept a Number
or String
; however, all id
s are converted to String
s 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 time
s.
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.
DosageRegimen
s 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
= DosageRegimen([100, 100]; time = [0, 24]) DR
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.
= Subject(; events = DR, covariates = (; wt = [75.0, 71.2]), covariates_time = [0, 24]) x
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(;
= DR,
events = (; wt = [75.0, 71.2], sex = 1), # ERROR
covariates = [0, 24],
covariates_time )
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(;
= DosageRegimen(100; ii = 24, addl = 10),
events = (; wtbl = [75.0, 71.2], sex = [1, 1]),
covariates = [0, 96],
covariates_time )
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(
::Numeric;
amt::Numeric = 0,
time::Union{Numeric,Symbol} = 1,
cmt::Numeric = 1,
evid::Numeric = zero.(time),
ii::Numeric = 0,
addl::Numeric = zero.(amt) ./ oneunit.(time),
rate::Numeric = zero(amt) ./ oneunit.(time),
duration::Numeric = 0,
ss::NCA.Route = NCA.NullRoute,
route )
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.
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
' = -CL / Vc * Central
Centralend
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, Symbol
s, 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
' = -Ka * Depot
Depot' = Ka * Depot - CL / Vc * Central
Centralend
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 event2
: “Other” type of event3
: 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; appliesevid = 3
followed by a dose event.
While not applicable to DosageRegimen
s, evid = 0
is also valid and corresponds to an observation when specified in tabular data formats. When constructing Subject
s 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
unlessss > 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
This kwarg was included in the current discussion for completeness. Most users will not interact with NCA.Route
s 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.Route
s (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 DosageRegimen
s. 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”
= DosageRegimen(100; ii = 12, addl = 3) _dr1
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.
= DosageRegimen(100; time = [0, 12, 24, 36]) _dr2
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
= DosageRegimen(500; cmt = :Central)
_ld
# maintenance dose
= DosageRegimen(100; ii = 12, addl = 3)
_md
# 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.