NCA Functions

The NCA functions can perform calculations on both NCAPopulation and NCASubject. When the input is a NCAPopulation, then the output is a DataFrame, and when the input is a NCASubject, the output is a number.

Tip

All functions are accessed from within the NCA submodule. For example, to use auc on NCAPopulation, one would use NCA.auc(pop).

The following keyword arguments apply to all NCA functions:

  • interval takes a tuple of two numbers like (1, 10) which will compute the quantity in the time interval and assumed to be the time after dose, or nothing which will compute the quantity in the entire time span. Default is nothing.
  • normalize (normalize with respect to dosage) takes true or false. Default is false.
  • auctype (types of AUC) takes :inf or :last. Default is :inf.
  • method takes :linear, :linuplogdown, or :linlog. Default is :linear
  • pred (predicted) takes true or false. Default is false.
  • threshold sets the number for the maximum number of points that can be used for lambdaz calculation.

NCA Function List

We will use this example dataset to illustrate the use of the functions below.

df = DataFrame(;
    id = [1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2],
    time = [0, 1, 2, 3, 4, 6, 0, 1, 2, 3, 4, 6, 8],
    amt = [10, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0],
    sss = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
    iii = [4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0],
    conc = [missing, 8, 6, 4, 2, 0.1, missing, 2, 6, 3, 2, 0.5, 0.1],
    isblq = [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1],
    route = ["iv", "iv", "iv", "iv", "iv", "iv", "ev", "ev", "ev", "ev", "ev", "ev", "ev"],
)
13×8 DataFrame
Rowidtimeamtsssiiiconcisblqroute
Int64Int64Int64Int64Int64Float64?Int64String
1101014missing0iv
2110008.00iv
3120006.00iv
4130004.00iv
5140002.00iv
6160000.11iv
7202014missing0ev
8210002.00ev
9220006.00ev
10230003.00ev
11240002.00ev
12260000.50ev
13280000.11ev

To illustrate the use of different functions in varying scenarios, we will use four versions of read_nca.

The first is a standard format where we don't use additional options.

df_r1 = read_nca(df; observations = :conc)
NCAPopulation (2 subjects):
  Number of missing observations: 2
  Number of blq observations: 0

In the second format, we let Pumas NCA package know that there is a blq column. This results in some observations being counted as blq and some as missing.

df_r2 = read_nca(df; observations = :conc, blq = :isblq)
NCAPopulation (2 subjects):
  Number of missing observations: 2
  Number of blq observations: 2

In the third format, we specify that the dose is steady-state, sss, and the frequency to be iii.

df_r3 = read_nca(df; observations = :conc, ii = :iii, ss = :sss)
NCAPopulation (2 subjects):
  Number of missing observations: 2
  Number of blq observations: 0

Lastly, in addition to specifying the steady-state nature and frequency, we also map the isblq column.

df_r4 = read_nca(df; observations = :conc, ii = :iii, ss = :sss, blq = :isblq)
NCAPopulation (2 subjects):
  Number of missing observations: 2
  Number of blq observations: 2

n_samples(subj)

The number of measurements that are above the lower limit of quantification.

When isblq is not mapped, below is the result:

NCA.n_samples(df_r1)
2×2 DataFrame
Rowidn_samples
StringInt64
115
226

When isblq is mapped, below is the result where you see the number of blq points.

NCA.n_samples(df_r2)
2×2 DataFrame
Rowidn_samples
StringInt64
114
225

When called on only the first subject by indexing into the NCAPopulation, df_r1[1] we get a number

NCA.n_samples(df_r1[1])
5

dosetype(subj)

Provides the route of administration.

NCA.dosetype(df_r1)
2×2 DataFrame
Rowiddosetype
StringString
11IVBolus
22EV

tau(subj)

Provides the dosing interval provided by ii

In the first example as ii is not passed, we get a value of missing.`

NCA.tau(df_r1)
2×2 DataFrame
Rowidtau
StringMissing
11missing
22missing

And below, as we map the iii column to ii the tau is determined.

NCA.tau(df_r3)
2×2 DataFrame
Rowidtau
StringInt64
114
224

doseamt(subj)

The amount of dose given to each subject.

NCA.doseamt(df_r1)
2×2 DataFrame
Rowiddoseamt
StringInt64
1110
2220

lambdaz

NCA.lambdazFunction
lambdaz(nca::NCASubject; concthreshold=1e-10, threshold=10, idxs=nothing) -> lambdaz

Calculate terminal elimination rate constant $λ_z$ with the end point greater than concthreshold.

Terminal elimination rate constant (λz). This is core function of NCA that computes the terminal rate constant. A list of considerations for computing λz are listed below.

  1. By default, Pumas NCA package uses an iterative best fit regression method to choose the terminal slope that provides the best adjusted r-square (adjr2factor) value. The iterative algorithm converges when the change in adjr2factor is 0.0001 or less.
  2. λz computation requires at least three data points to provide a valid result.
  3. The algorithm tries its best to not use the time at tmax to compute the λz, and if it does, a warning is provided.

Regular users of Pumas NCA package don't usually have to adjust any of the settings for computing the λz, as a best decision is made automatically. However, if there is a desire to experiment given the experimental context, certain arguments are provided that are discussed below with specific examples.

NCA.lambdaz(df_r1)
2×2 DataFrame
Rowidlambdaz
StringFloat64
111.26795
220.748933

adjr2factor can be relaxed to fit a best-fit earlier. Note in the example below, a 100-fold difference in the value results in different results of terminal slope for id=1.

NCA.lambdaz(df_r1; adjr2factor = 0.1)
2×2 DataFrame
Rowidlambdaz
StringFloat64
110.871274
220.690867

threshold defines the maximum number of points that can be used to compute the λz. Currently, the default is set to 100 points, that satisfies most use cases, but if there is a need to adjust one can do so.

Info

The iterative best-fit regression is bounded to produce a result that satisfies the threshold value, so it is normal to see a lower $r^2$ value.

When the threshold is set to 2 in the example below, Pumas NCA package warns that it cannot compute the λz which requires a minimum of three points.

NCA.lambdaz(df_r1; threshold = 2)
2×2 DataFrame
Rowidlambdaz
StringMissing
11missing
22missing

idxs and slopetimes are both used to manually pick and choose the data points used for terminal slope computations. Values are passed as an array of points, either indices of the time vector for idxs or the actual times in the case of slopetimes. If a single array is passed, then the same values are used across the population on every subject.

NCA.lambdaz(df_r1; idxs = [2, 3, 4])
2×2 DataFrame
Rowidlambdaz
StringFloat64
110.549306
220.549306
NCA.lambdaz(df_r1; slopetimes = [2, 3, 4])
2×2 DataFrame
Rowidlambdaz
StringFloat64
110.549306
220.549306

If the user has a custom array that identifies specific indices or slopetimes for each individual, they can be passed in as an array of arrays.

The lambdaz function can also be used outside the context of a NCASubject or NCAPopulation

where one can pass in a simple observation and time vector. This can be useful to do a quick linear regression or to find a slope for a x-y vector.

NCA.lambdaz(collect(10:-1:1), 1:10)
0.5493061443340549

lambdazr2(subj)

Coefficient of determination () when calculating λz. Note that this quantity must be computed after calculating λz.

Using the example where the blq is not mapped, we get the result below.

NCA.lambdaz(df_r1)

NCA.lambdazr2(df_r1)
2×2 DataFrame
Rowidlambdazr2
StringFloat64
110.975932
220.998154

And when the blq is mapped to isblq, the adjusted r2 changes as the number of data points changes

NCA.lambdaz(df_r2)

NCA.lambdazr2(df_r2)
2×2 DataFrame
Rowidlambdazr2
StringFloat64
110.977654
220.986607

lambdazadjr2(subj)

Adjusted coefficient of determination (adjr²) when calculating λz. Note that this quantity must be computed after calculating λz.

NCA.lambdaz(df_r1)

NCA.lambdazadjr2(df_r1)
2×2 DataFrame
Rowidlambdazadjr2
StringFloat64
110.951865
220.996308

lambdazr(subj)

Correlation coefficient (r) when calculating λz. Note that this quantity must be computed after calculating λz.

NCA.lambdaz(df_r1)

NCA.lambdazr(df_r1)
2×2 DataFrame
Rowidlambdazr
StringFloat64
110.987893
220.999077

lambdaznpoints(subj)

Number of points that is used in the λz calculation. Note that this quantity must be computed after calculating λz.

NCA.lambdaz(df_r1)

NCA.lambdaznpoints(df_r1)
2×2 DataFrame
Rowidlambdaznpoints
StringInt64
113
223

lambdazintercept(subj)

y-intercept in the log-linear scale when calculating λz. Note that this quantity must be computed after calculating λz.

NCA.lambdaz(df_r1)

NCA.lambdazintercept(df_r1)
2×2 DataFrame
Rowidlambdazintercept
StringFloat64
115.42005
223.72607

lambdaztimefirst(subj)

The first time point that is used in the λz calculation. Note that this quantity must be computed after calculating λz.

NCA.lambdaz(df_r1)

NCA.lambdaztimefirst(df_r1)
2×2 DataFrame
Rowidlambdaztimefirst
StringInt64
113
224

lambdaztimelast(subj)

The last time point that is used in the λz calculation. Note that this quantity must be computed after calculating λz.

NCA.lambdaz(df_r1)

NCA.lambdaztimelast(df_r1)
2×2 DataFrame
Rowidlambdaztimelast
StringInt64
116
228

thalf(subj)

Half-life.

NCA.thalf(df_r1)
2×2 DataFrame
Rowidthalf
StringFloat64
110.546669
220.925513

span(subj)

(lambdaztimelast(subj; kwargs...) - lambdaztimefirst(subj) / thalf(subj). Note that this quantity must be computed after calculating λz.

NCA.lambdaz(df_r1)

NCA.span(df_r1)
2×2 DataFrame
Rowidspan
StringFloat64
115.48778
224.32193

tmax(subj; interval=nothing)

Time of maximum concentration.

In the example below, notice how for the subject with iv, i.e, id = 1, the tmax is computed to 0. This is because, C0 is back-extrapolated from the observation vector whose value now represents the cmax. Hence, tmax is 0.

NCA.tmax(df_r1)
2×2 DataFrame
Rowidtmax
StringFloat64
110.0
222.0

In the event that a user wants to compute the time of maximum observation in a specific interval, they can do so by setting the interval argument to a tuple.

NCA.tmax(df_r1, interval = (2, 4))
2×2 DataFrame
Rowidtmax
StringFloat64
110.0
222.0

The tmax function also works on vector of observations and time. For example:

NCA.tmax(collect(10:-1:1), 0:9)
0.0

cmax(subj; interval=nothing, normalize=false)

Computes the maximum concentration.

In the example below we see the difference in the cmax results across the four datasets.

  1. For df_1r and df_r2 the cmax for id = 1 is the back-extrapolated value using the regression, even though that value does not exist in the original dataset. On the other hand for id = 2, the cmax is observed maximum concentration in the dataset. Note that presence of blq in df_r2 did not impact the result.
  2. For df_r3, where we mapped ii and ss, the computed cmax is derived as follows: cmax/accumulationindex
  3. For df_r4, in addition to mapping the ii and ss, the blq values are also specified. The computation of cmax is identical to the earlier case, however, in this case, since the number of data points is different because of blq values, the accumulation index is different.
vcat(
    map(x -> NCA.cmax(x), [df_r1, df_r2, df_r3, df_r4])...;
    source = :data => "df_r" .* string.(1:4),
)
8×3 DataFrame
Rowidcmaxdata
StringFloat64String
1110.6667df_r1
226.0df_r1
3110.6667df_r2
426.0df_r2
5110.5998df_r3
625.7df_r3
719.48148df_r4
825.47902df_r4

The next example covers the computation of cmax in an interval:

NCA.cmax(df_r1; interval = (3, 6))
2×2 DataFrame
Rowidcmax3_6
StringFloat64
114.0
223.0

cmax can also be computed using an arbitrary array of observations and time series:

NCA.cmax(collect(10:-1:1), 1:10)
10.0

The last example shows the use of the normalize argument that normalizes the derived cmax by the dose value. Compare to the first example in the cmax series. The results below are essentially dose normalized cmax of the results presented above.

vcat(
    map(x -> NCA.cmax(x, normalize = true), [df_r1, df_r2, df_r3, df_r4])...;
    source = :data => "df_r" .* string.(1:4),
)
8×3 DataFrame
Rowidcmaxdata
StringFloat64String
111.06667df_r1
220.3df_r1
311.06667df_r2
420.3df_r2
511.05998df_r3
620.285df_r3
710.948148df_r4
820.273951df_r4

cmaxss(subj; normalize=false)

Steady-state maximum concentration. If the dose is a steady-state dose specified by ss=1, then the observed cmax is cmaxss.

In the example below, iv has the back-extrapolated cmax and ev has the observed cmax:

vcat(
    map(x -> NCA.cmaxss(x), [df_r1, df_r2, df_r3, df_r4])...;
    source = :data => "df_r" .* string.(1:4),
)
8×3 DataFrame
Rowidcmaxssdata
StringFloat64String
1110.6667df_r1
226.0df_r1
3110.6667df_r2
426.0df_r2
5110.6667df_r3
626.0df_r3
7110.6667df_r4
826.0df_r4

tmin(subj)

Time of minimal concentration after a dose.

In the example below, we can notice the difference in the time of minimum concentration when ss=1 value towards the end fo the dosing interval. In all other cases where ss is not specified, the time at lowest observed concentration is chosen.

vcat(
    map(x -> NCA.tmin(x), [df_r1, df_r2, df_r3, df_r4])...;
    source = :data => "df_r" .* string.(1:4),
)
8×3 DataFrame
Rowidtmindata
StringFloat64String
116.0df_r1
220.0df_r1
314.0df_r2
420.0df_r2
516.0df_r3
628.0df_r3
714.0df_r4
826.0df_r4

cmin(subj; normalize=false)

Minimum concentration in a dosing interval.

In the example below, for df_r1 and df_r2, the observed minimum concentrations are reported back. On the other hand, for df_r3 and df_r4, the observed minimum concentration are scaled by the accumulationindex as ss=1

vcat(
    map(x -> NCA.cmin(x), [df_r1, df_r2, df_r3, df_r4])...;
    source = :data => "df_r" .* string.(1:4),
)
8×3 DataFrame
Rowidcmindata
StringFloat64?String
110.1df_r1
22missingdf_r1
312.0df_r2
42missingdf_r2
510.0993729df_r3
620.095df_r3
711.77778df_r4
820.456585df_r4

We can also output the dose-normalized values as shown in the example below:

NCA.cmin(df_r1; normalize = true)
2×2 DataFrame
Rowidcmin
StringFloat64?
110.1
22missing

cminss(subj; normalize=false)

Steady-state minimum concentration.

In the example below, the first two cases are not impacted, but where ss=1, the observed minimum concentration are the cminss as the dose is a steady-state dose.

vcat(
    map(x -> NCA.cminss(x), [df_r1, df_r2, df_r3, df_r4])...;
    source = :data => "df_r" .* string.(1:4),
)
8×3 DataFrame
Rowidcminssdata
StringFloat64?String
110.1df_r1
22missingdf_r1
312.0df_r2
42missingdf_r2
510.1df_r3
620.1df_r3
712.0df_r4
820.5df_r4

ctau(nca::NCASubject; method=:linear)

Concentration at the end of dosing interval.

Notice in the examples below, that ctau is only computed when ii or ss is specified. Further, in both df_r3 and df_r4, the ii=4, and the last observed concentration is beyond 4, yet the function does the right thing in picking up the concentration at the end of the dosing interval tau.

vcat(
    map(x -> NCA.ctau(x), [df_r1, df_r2, df_r3, df_r4])...;
    source = :data => "df_r" .* string.(1:4),
)
8×3 DataFrame
Rowidctaudata
StringFloat64?String
11missingdf_r1
22missingdf_r1
31missingdf_r2
42missingdf_r2
512.0df_r3
622.0df_r3
712.0df_r4
822.0df_r4

cavgss(subj)

Average concentration over a dosing interval.

As expected, in the examples below, the cavgss is only computed in the cases where ii or ss is specified.

vcat(
    map(x -> NCA.cavgss(x), [df_r1, df_r2, df_r3, df_r4])...;
    source = :data => "df_r" .* string.(1:4),
)
8×3 DataFrame
Rowidcavgssdata
StringFloat64?String
11missingdf_r1
22missingdf_r1
31missingdf_r2
42missingdf_r2
516.08333df_r3
623.25df_r3
716.08333df_r4
823.25df_r4

c0(subj)

Estimate the concentration at dosing time for an IV bolus dose.

Note in the example below that this value is only computed for iv dose.

NCA.c0(df_r1)
2×2 DataFrame
Rowidc0
StringFloat64?
1110.6667
22missing

tlast(subj)

Time of last measurable concentration after a dose.

NCA.tlast(df_r1)
2×2 DataFrame
Rowidtlast
StringInt64
116
228

clast(subj; pred=false)

Concentration corresponding to tlast.

NCA.clast(df_r1)
2×2 DataFrame
Rowidclast
StringFloat64
110.1
220.1

tlag(subj)

Time prior to the first increase in concentration. Mostly relevant for ev dosing.

NCA.tlag(df_r2)
2×2 DataFrame
Rowidtlag
StringMissing
11missing
22missing

auc

NCA.aucFunction
auc(nca::NCASubject; auctype::Symbol, method::Symbol, interval=nothing, normalize=false)

Compute area under the curve (AUC) by linear trapezoidal rule (method = :linear) or by log-linear trapezoidal rule (method = :linuplogdown).

The area under the curve (AUC). This is one of the most important parameters. There are many key important options that can be tailored in the auc function. The defaults are listed in the function signature, but the possible options are listed below:

method = [:linear, :linuplogdown, :linlog]
auctype = [:inf, :last]
normalize = [false, true]
pred = [false, true]

In addition, the interval can be set to a tuple that specified the range in the units of time. e.g. interval=(0,2). If users had passed in timeu into read_nca, then the tuple should be multiplied by the timeu:

NCA.auc(df1_r; interval = (0, 2)) # when no time units passed to `read_nca`
NCA.auc(df1_r; interval = (0, 2) .* timeu) # when no time units passed to `read_nca`

The code below produces the AUC for all possible scenarios of the auc signature. For brevity, the output is suppressed, but user should be able to run this locally and see all the necessary output.

pops = [df_r1, df_r2, df_r3, df_r4]
methods = [:linear, :linuplogdown, :linlog]
auctypes = [:inf, :last]
normalizes = [false, true]
preds = [false, true]
itrs = Iterators.product(eachindex(pops), methods, auctypes, normalizes, preds)
res = map(itrs) do (popsi, methodsi, auctypesi, normalizesi, predsi)
    res = NCA.auc(
        pops[popsi];
        auctype = auctypesi,
        method = methodsi,
        interval = nothing,
        normalize = normalizesi,
        pred = predsi,
    )
    res[!, :data] .= "df_r" * string(popsi)
    res[!, :auctype] .= auctypesi
    res[!, :method] .= methodsi
    res[!, :normalize] .= normalizesi
    res[!, :pred] .= predsi
    res
end
aucres = sort(reduce(vcat, res), [:data, :id, :auctype, :method, :normalize, :pred])
first(aucres, 5) # for brevity
5×7 DataFrame
Rowidaucdataauctypemethodnormalizepred
StringFloat64StringSymbolSymbolBoolBool
1126.5122df_r1inflinearfalsefalse
2126.5218df_r1inflinearfalsetrue
312.65122df_r1inflineartruefalse
412.65218df_r1inflineartruetrue
5125.3869df_r1inflinlogfalsefalse

auctau

NCA.auctauFunction
auctau(subj::NCASubject; method::Symbol)

Alias for auctau(subj; auctype=:last, interval=(zero(τ), τ)).

The AUC in the dosing interval. This primarily uses the information regarding multiple doses available via ii or if it is a plain multiple dose. This is nothing but an alias for NCA.auc(subj; auctype=:last, interval=(0, NCA.tau(subj))

We can confirm by comparing the result of auctau:

NCA.auctau(df_r3)
2×2 DataFrame
Rowidauctau
StringFloat64
1124.3333
2213.0

to the result of auc(.., interval(0, NCA.tau(subj))):

NCA.auc(df_r3; auctype = :last, interval = (0, 4))
2×2 DataFrame
Rowidauc0_4
StringFloat64
1124.3333
2213.0

aumc

NCA.aumcFunction
aumc(nca::NCASubject; method::Symbol, interval=(0, Inf))

Compute area under the first moment of the concentration (AUMC) by linear trapezoidal rule (method = :linear) or by log-linear trapezoidal rule (method = :linuplogdown).

The area under the first moment of the concentration (AUMC).

aumctau

NCA.aumctauFunction
aumctau(subj::NCASubject; method::Symbol)

Alias for aumctau(subj; auctype=:last, interval=(zero(τ), τ)).

The AUMC in the dosing interval.

vz(subj; pred=false)

Volume of distribution during the terminal phase.

cl(subj; pred=false)

Total drug clearance.

vss(subj; pred=false)

Apparent volume of distribution at equilibrium for IV bolus doses.

fluctuation(nca; usetau=false)

Peak trough fluctuation over one dosing interval at steady state. It is

100*(C_{maxss} - C_{minss})/C_{avgss} for (usetau=false)

100*(C_{maxss} - C_{tau})/C_{avgss} for (usetau=true)

accumulationindex(subj)

Theoretical accumulation ratio.

auc_extrap_percent(subj; pred=false)

The percentage of AUC infinity due to extrapolation from tlast.

auc_back_extrap_percent(subj; pred=false)

The percentage of AUC infinity due to back extrapolation of c0.

aumc_extrap_percent(subj; pred=false)

The percentage of AUMC infinity due to extrapolation from tlast.

aumc_back_extrap_percent(subj; pred=false)

The percentage of AUMC infinity due to back extrapolation of c0.

mrt(subj; auctype=:inf)

Mean residence time from the time of dosing to the time of the last measurable concentration.

swing(subj; usetau=false)

Swing. swing = (C_{maxss}-C_{minss})/C_{minss} for (usetau=false)

swing = (C_{maxss}-C_{tau})/C_{tau} for (usetau=true)

NCA Function List for Urine Analysis

For urine analysis the functions are:

n_samples(subj)

The number of measurements that is above the lower limit of quantification.

dosetype(subj)

Provides the route of administration.

doseamt(subj)

The amount of dose given to each subject.

urine_volume(subj)

Collected urine volume.

lambdaz(subj)

Terminal elimination rate constant (λz).

lambdazr2(subj)

Coefficient of determination () when calculating λz. Note that this quantity must be computed after calculating λz.

lambdazadjr2(subj)

Adjusted coefficient of determination (adjr²) when calculating λz. Note that this quantity must be computed after calculating λz.

lambdazr(subj)

Correlation coefficient (r) when calculating λz. Note that this quantity must be computed after calculating λz.

lambdaznpoints(subj)

Number of points that is used in the λz calculation. Note that this quantity must be computed after calculating λz.

lambdazintercept(subj)

y-intercept in the log-linear scale when calculating λz. Note that this quantity must be computed after calculating λz.

lambdaztimefirst(subj)

The first time point that is used in the λz calculation. Note that this quantity must be computed after calculating λz.

lambdaztimelast(subj)

The last time point that is used in the λz calculation. Note that this quantity must be computed after calculating λz.

thalf(subj)

Half life.

span(subj)

(lambdaztimelast(subj; kwargs...) - lambdaztimefirst(subj) / thalf(subj). Note that this quantity must be computed after calculating λz.

tlag(subj)

Time prior to the first increase in concentration.

tmax_rate(subj)

Midpoint of collection interval associated with the maximum observed excretion rate.

max_rate(subj)

Maximum observed excretion rate.

rate_last(subj; pred=false)

Last measurable rate.

mid_time_last(subj)

Midpoint of collection interval associated with rate_last.

amount_recovered(subj)

Cumulative amount eliminated.

percent_recovered(subj)

100*amount_recovered/doseamt.

aurc

aurc(
    subj;
    auctype = :inf,
    method = :linear,
    interval = nothing,
    normalize = false,
    pred = false,
)

The area under the urinary excretion rate curve (AURC).

aurc_extrap_percent(subj; pred=false)

The percentage of AURC infinity due to extrapolation from tlast.