A typical workflow for fitting a Pumas model and deriving parameter precision typically involves:
Preparing the data and the model.
Checking model-data compatibility.
Obtaining initial parameter estimates.
Fitting the model via a chosen estimation method.
Interpreting the fit results.
Computing parameter uncertainty based on the asymptotic variance-covariance formulas (robust or not).
(Optionally) proceeding with more advanced techniques like bootstrapping or sampling importance re-sampling (SIR) for robust uncertainty quantification.
In previous tutorials, we already set up the data and performed a fit. We also obtained some parameter uncertainty estimates. In this tutorial, we will go more into depth with parameter uncertainty calculations using different methods. Exploratory data analysis (EDA), although extremely important, is out of scope here. Readers interested in EDA are encouraged to consult other tutorials.
2 Model and Data
2.1 Model Definition
Below is the PK model, named warfarin_pk_model, defined in Pumas. This model contains:
Differential equations describing the PK behavior in the compartments.
usingPumasusingPharmaDatasetsusingDataFramesMetausingPumasUtilitiesusingDimensionalData: DimArray, X, YusingRandom# Bootstrap and SIR draw random numbers, so we seed the random number generator to make the# results of this tutorial reproducible.Random.seed!(545)
PumasModel
Parameters: pop_CL, pop_Vc, pop_tabs, pop_lag, pk_Ω, σ_prop, σ_add
Random effects: pk_η
Covariates: FSZCL, FSZV
Dynamical system variables: Depot, Central
Dynamical system type: Closed form
Derived: conc
Observed: conc
2.2 Data Preparation
The Warfarin data used in this tutorial is pulled from PharmaDatasets for demonstration purposes. Note how the code reshapes and prepares the data in “wide” format for reading into Pumas. Only the conc column is treated as observations for the PK model.
Four concentrations in this dataset are recorded as exactly zero, 0.5 hours after the dose and hence before the absorption lag, where the model predicts a concentration of exactly zero as well. The likelihood of such an observation is unbounded as the additive standard deviation σ_add approaches zero, so we treat these values as missing: a concentration of exactly zero means that the drug was not quantifiable, not that it was absent. See the BQL case study for a more detailed discussion.
warfarin_data =dataset("pumas/warfarin_pumas")# Transform the data in a single chain of operationswarfarin_data_scales =@chain warfarin_data begin@rtransformbegin# Scaling factors:FSZV =:wtbl /70# volume scaling:FSZCL = (:wtbl /70)^0.75# clearance scaling (allometric)end# Concentrations of exactly zero are below the limit of quantification@rtransform@passmissing:conc =iszero(:conc) ? missing::concend
330×12 DataFrame
305 rows omitted
Row
id
time
evid
amt
cmt
conc
pca
wtbl
age
sex
FSZV
FSZCL
Int64
Float64
Int64
Float64?
Int64?
Float64?
Float64?
Float64
Int64
String1
Float64
Float64
1
1
0.0
1
100.0
1
missing
missing
66.7
50
M
0.952857
0.96443
2
1
0.5
0
missing
missing
missing
missing
66.7
50
M
0.952857
0.96443
3
1
1.0
0
missing
missing
1.9
missing
66.7
50
M
0.952857
0.96443
4
1
2.0
0
missing
missing
3.3
missing
66.7
50
M
0.952857
0.96443
5
1
3.0
0
missing
missing
6.6
missing
66.7
50
M
0.952857
0.96443
6
1
6.0
0
missing
missing
9.1
missing
66.7
50
M
0.952857
0.96443
7
1
9.0
0
missing
missing
10.8
missing
66.7
50
M
0.952857
0.96443
8
1
12.0
0
missing
missing
8.6
missing
66.7
50
M
0.952857
0.96443
9
1
24.0
0
missing
missing
5.6
44.0
66.7
50
M
0.952857
0.96443
10
1
36.0
0
missing
missing
4.0
27.0
66.7
50
M
0.952857
0.96443
11
1
48.0
0
missing
missing
2.7
28.0
66.7
50
M
0.952857
0.96443
12
1
72.0
0
missing
missing
0.8
31.0
66.7
50
M
0.952857
0.96443
13
1
96.0
0
missing
missing
missing
60.0
66.7
50
M
0.952857
0.96443
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
319
32
48.0
0
missing
missing
6.9
24.0
62.0
21
M
0.885714
0.912999
320
32
72.0
0
missing
missing
4.4
23.0
62.0
21
M
0.885714
0.912999
321
32
96.0
0
missing
missing
3.5
20.0
62.0
21
M
0.885714
0.912999
322
32
120.0
0
missing
missing
2.5
22.0
62.0
21
M
0.885714
0.912999
323
33
0.0
1
100.0
1
missing
missing
66.7
50
M
0.952857
0.96443
324
33
0.0
0
missing
missing
missing
100.0
66.7
50
M
0.952857
0.96443
325
33
24.0
0
missing
missing
9.2
49.0
66.7
50
M
0.952857
0.96443
326
33
36.0
0
missing
missing
8.5
32.0
66.7
50
M
0.952857
0.96443
327
33
48.0
0
missing
missing
6.4
26.0
66.7
50
M
0.952857
0.96443
328
33
72.0
0
missing
missing
4.8
22.0
66.7
50
M
0.952857
0.96443
329
33
96.0
0
missing
missing
3.1
28.0
66.7
50
M
0.952857
0.96443
330
33
120.0
0
missing
missing
2.5
33.0
66.7
50
M
0.952857
0.96443
3 Creating a Pumas Population
Below is the creation of a population object in Pumas using read_pumas. Only the conc data are treated as the observation variable:
pop_pk =read_pumas( warfarin_data_scales; id =:id, time =:time, amt =:amt, cmt =:cmt, evid =:evid, covariates = [:sex, :wtbl, :FSZV, :FSZCL], observations = [:conc],)
Population
Subjects: 32
Covariates: sex, wtbl, FSZV, FSZCL
Observations: conc
Note
The same data can contain multiple endpoints or PD observations. In this tutorial, the focus is solely on PK fitting. PKPD modeling on this warfarin dataset will be introduced later.
Also note, that parameter inference can be expensive and for that reason we simplified the model for this tutorial to decrease overall runtime.
3.1 Obtaining fit results
Following the examples in previous tutorials, we perform a fit. We need the output of the fit function call to perform inference to obtain parameter uncertainty estimates.
# A named tuple of parameter valuesparam_vals = ( pop_CL =0.134, pop_Vc =8.11, pop_tabs =0.523, pop_lag =0.1, pk_Ω =Diagonal([0.01, 0.01, 0.01]), σ_prop =0.00752, σ_add =0.0661,)foce_fit =fit(warfarin_pk_model, pop_pk, param_vals, FOCE();)
FittedPumasModel
Dynamical system type: Closed form
Number of subjects: 32
Observation records: Active Missing
conc: 247 51
Total: 247 51
Number of parameters: Constant Optimized
0 9
Likelihood approximation: FOCE
Likelihood optimizer: BFGS
Termination Reason: GradientNorm
Log-likelihood value: -350.46191
--------------------
Estimate
--------------------
pop_CL 0.13469
pop_Vc 8.048
pop_tabs 0.5449
pop_lag 0.87702
pk_Ω₁,₁ 0.070708
pk_Ω₂,₂ 0.018349
pk_Ω₃,₃ 0.92441
σ_prop 0.087933
σ_add 0.41375
--------------------
3.2 Computing Parameter Precision with infer
The infer function in Pumas estimates the uncertainty (precision) of parameter estimates. Depending on the chosen method, infer can provide standard errors, confidence intervals, and correlation matrices.
In its simplest form infer takes the output of fit and returns the asymptotic variance-covariance based inference results. The keyword arguments that this tutorial relies on are:
level: The confidence interval level (e.g., 0.95). The confidence intervals are calculated as the (1-level)/2 and (1+level)/2 quantiles of the estimated parameters.
rethrow_error: If rethrow_error is false (the default value), no error will be thrown if the variance-covariance matrix estimator fails. If it is true, an error will be thrown if the estimator fails.
sandwich_estimator: Whether to use the sandwich estimator also known as the robust variance-covariance estimator. If set to true (the default value), the sandwich estimator will be used. If set to false, the standard error will be calculated using the inverse of the Hessian matrix calculated using finite difference derivatives of the gradient calculated using automatic differentiation.
The complete interface is documented in the Inference section of the Pumas documentation.
And we see that the cond call above matches the condition number of the correlation matrix
cond(cor_from_cov)
47.213447836476526
3.2.1 Failure of the asymptotic variance-covariance matrix
It is well-known that the asymptotic variance-covariance matrix can sometimes fail to compute. This can happen for a variety of reasons including:
There are parameters very close to a bound (often 0)
The parameter vector does not represent a local minimum (optimization failed)
The parameter vector does represent a local minimum but it’s not the global solution
The first one is often easy to check. The problematic parameters can be ones than have lower or upper bounds set. Often this will be a variance of standard deviation that has moved very close to the lower boundary. Consider removing the associated random effect if the problematic parameter is a variance in its specification or error model component if a combined additive and proportional error model is used and a standard deviation is close to zero.
It is also possible that the parameters do not represent a local minimum. In other words, they come from a failed fit. In this case, it can often be hard to perform the associated calculations in a stable way, but most importantly the results would not be interpretable even if they can be calculated in this case. The formulas are only valid for parameters that represent the actual (maximum likelihood) estimates. Please try to restart the optimization at different starting points in this case.
If you have reasons to believe that the model should indeed be a combined error model or if the random effect should be present it is also possible that the model converged to a local minimum that is not the global minimum. If the optimization happened to move to a region of the parameter state space that is hard to get out of you will often have to restart the fit at different parameter values. It is not possible to verify if the minimum is global in general, but it is always advised to try out more than one set of initial parameters when fitting models.
3.2.2 Bootstrap
Sometimes it is appropriate to use a different method to calculate estimate the uncertainty of the estimated parameters. Bootstrapping is a very popular approach that is simply but can often come a quite significant computational cost. Researchers often perform a bootstrapping step if their computational budget allows it or if the asymptotic variance-covariance estimator fails. Bootstrapping is advantageous because it does not rely on any invertability of matrices and it cannot produce negative variance confidence intervals because the resampled estimator respects the bounds of the model.
To bootstrap, pass a Bootstrap specification as the second argument to infer. Bootstrap accepts the random number generator rng, the number samples of resampled datasets to produce, the covariates stratify_by to stratify the sampling by, and finally the ensemble algorithm ensemblealg to control parallelization across fits. On the JuliaHub platform this can be used together with distributed computing to perform many resampled estimations in parallel. See Bootstrap-based inference for the full interface.
and we can even get a DataFrame that includes all the estimated parameters from the sampled population fits
samples_table(bootstrap_results)
50×10 DataFrame
25 rows omitted
Row
pop_CL
pop_Vc
pop_tabs
pop_lag
pk_Ω₁,₁
pk_Ω₂,₂
pk_Ω₃,₃
σ_prop
σ_add
loglikelihood
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
Float64
1
0.138199
7.88269
0.261311
0.853437
0.0652341
0.019051
0.839352
0.07037
0.388899
-359.84
2
0.147409
8.06405
0.542726
0.898654
0.0766633
0.00943141
0.446809
0.0865969
0.40379
-356.595
3
0.130045
8.32838
0.187929
0.937123
0.0327084
0.00915136
1.85535
0.0917191
0.303618
-369.617
4
0.142971
7.99311
0.621799
0.87429
0.0821683
0.0207287
0.712888
0.089067
0.430751
-351.717
5
0.136384
8.04222
0.659984
1.38333
0.0756629
0.0219668
1.18834
0.0893547
0.505606
-444.244
6
0.135858
7.89788
0.208673
0.943898
0.0652191
0.0196336
1.92101
0.0932864
0.378394
-354.771
7
0.131619
8.19803
0.375198
0.893224
0.0458887
0.0134883
1.09276
0.0810651
0.355713
-356.315
8
0.145999
8.15935
0.663138
0.795883
0.10125
0.0164858
0.484316
0.0760988
0.387434
-358.184
9
0.148771
8.29753
0.611214
0.860173
0.0609043
0.0179712
1.36196
0.0953851
0.422294
-354.566
10
0.146446
8.13234
0.450113
0.83895
0.13755
0.0248324
0.5953
0.0733565
0.418513
-358.32
11
0.133372
7.75314
0.693663
0.898928
0.0523328
0.0169064
0.918215
0.0938977
0.457898
-353.912
12
0.143182
8.06781
0.837878
0.712793
0.0684965
0.0101765
0.389407
0.10978
0.296499
-360.457
13
0.141068
8.27269
0.773788
0.851858
0.0878996
0.0135328
0.353973
0.0778773
0.384881
-358.158
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
⋮
39
0.145104
7.72301
0.490232
0.96761
0.0826272
0.0153741
2.31155
0.1321
0.549725
-374.62
40
0.137929
8.50375
0.541095
0.849905
0.0738461
0.0175471
0.398782
0.0694119
0.314915
-369.007
41
0.138132
8.04736
0.536048
0.820793
0.105426
0.0145463
1.05958
0.080938
0.451293
-353.848
42
0.137203
8.08756
0.437777
0.873898
0.0694537
0.0216998
0.960834
0.0777146
0.41107
-352.07
43
0.151397
8.01287
0.245036
0.911006
0.107094
0.0252359
1.61701
0.0878252
0.424717
-356.191
44
0.141688
8.00417
0.202426
0.970769
0.0580152
0.0209218
2.25374
0.100388
0.419911
-375.654
45
0.125658
7.89791
0.500011
0.947889
0.0273326
0.0214628
1.48732
0.106666
0.362837
-367.292
46
0.139285
7.88893
0.379051
0.944136
0.0695366
0.0246278
1.1083
0.0927457
0.378043
-354.556
47
0.131241
8.10441
0.482215
0.862209
0.0606528
0.0163825
0.458794
0.0798909
0.334757
-357.276
48
0.135471
8.1099
0.485273
0.885359
0.0680653
0.0129145
0.915354
0.0493309
0.334917
-390.426
49
0.12667
7.85383
0.339741
0.927379
0.0271793
0.0228594
1.07212
0.104092
0.331625
-366.854
50
0.129295
8.10596
0.436212
0.872782
0.0501195
0.0127704
0.503576
0.0773306
0.221905
-382.848
This is very useful for histogram plotting of parameter distributions.
3.2.2.1 Diagnosing the bootstrap
The bootstrap estimates are meant to be distributed like the maximum likelihood estimator, and Dosne et al. (Dosne, Niebecker, et al. 2016) proposed a diagnostic for whether they are. The likelihood-ratio statistic of a bootstrap estimate, the difference in objective function value (dOFV) of the pharmacometrics literature, is twice the difference between the log-likelihood of the parameter estimates and the log-likelihood of that bootstrap estimate, both evaluated on the original dataset. It therefore measures how much worse the bootstrap estimate explains those data, and is not the objective function value that its own fit reached. Were the estimates distributed like the estimator, their statistics would asymptotically follow a χ² distribution with one degree of freedom per estimated parameter for an unconstrained fixed effects model. For a nonlinear mixed effects model the degrees of freedom can only be lower. The likelihood approximation of the estimation method and any misspecification of the model influence the statistics as well (Dosne, Niebecker, et al. 2016). likelihood_ratio_ppplot plots the empirical cumulative distribution of the statistics of the bootstrap estimates against the cumulative distribution function of the χ² reference.
likelihood_ratio_ppplot(bootstrap_results)
A curve above the identity line has smaller statistics than the reference and is spread more narrowly than it, and below the line it is spread more widely. The annotated degrees of freedom, obtained by fitting a χ² distribution to the statistics, express that comparison as a single number. Since the degrees of freedom cannot exceed the number of estimated parameters, a curve on or above the identity line is acceptable, whereas one below it describes the estimator badly, the estimates then including parameter vectors that explain the data worse than the reference allows for. Since the reference assumes that the fit converged to the maximum likelihood estimates, the diagnostic is only meaningful for a converged fit.
3.2.3 Sampling Importance Re-sampling
Pumas supports inference through SIR with the SIR specification. SIR accepts the random number generator rng, the number samples of parameter samples drawn from the proposal distribution, the number resamples of parameter samples that are then drawn from those by weighted sampling without replacement, and finally the scale proposal_scale and the shape proposal_shape of the space that a proposal distribution is fitted on. Neither samples nor resamples has a default value, so both have to be set. Dosne et al. (Dosne, Bergstrand, et al. 2016), who introduced SIR for nonlinear mixed effects models, recommend a samples/resamples ratio of 5 as a starting point so that enough samples are available to resample from; the ratio that turned out to be sufficient varied between 2 and 6 across their examples. Like the bootstrap confidence intervals, and unlike the asymptotic ones, the confidence intervals based on SIR respect the bounds of the model, so for instance the interval of a strictly positive parameter cannot contain negative values.
In each iteration, parameter samples are drawn from a multivariate normal proposal distribution whose mean is the parameter estimates of the fitted model. From the second iteration onwards, the proposal distribution is fitted to the parameter samples resampled in the previous iteration, with its covariance matrix set to their empirical covariance matrix. The proposal distribution of the initial iteration, on the other hand, is built from a covariance matrix that has to be specified. Such a matrix is expressed on the natural scale of the parameters, where a sample can violate the domain of a parameter and is then rejected, so that proposal distribution is a truncated multivariate normal. One choice is to set it to an estimate of the variance-covariance matrix of the parameters, which can be taken from existing inference results:
Since inference_results was computed with infer(foce_fit; level = 0.95) above, the initial proposal distribution here is based on the robust variance-covariance matrix, which is reused rather than recomputed.
The ensemblealg keyword argument controls the parallelization of the SIR procedure itself: it accepts EnsembleThreads() (the default value) to use multi-threading or EnsembleSerial() to use a single thread. Because the inference results are computed in a separate step, that step can use an ensemble algorithm that the SIR procedure does not support, such as EnsembleDistributed(). See Sampling importance resampling (SIR) for the full interface.
3.2.3.1 Choosing the initial proposal distribution
Because the initial proposal distribution above is based on the robust variance-covariance matrix, that SIR procedure fails whenever the matrix fails to be computed or is not positive definite. This is not inherent to SIR: any of the other inference results in this tutorial can serve as its basis instead. Passing the results of an infer call with sandwich_estimator = false bases it on the inverse of the Hessian matrix,
and passing the results of a Bootstrap, SIR, or MarginalMCMC inference bases it on the covariance matrix of their parameter samples, which requires no matrix to be inverted at all:
The covariance matrix can also be supplied directly with the proposal_covariance keyword argument. It has to be a DimArray whose rows and columns are labelled with the names of the estimated parameters, so that the matrix is unambiguous regardless of how Pumas vectorizes the parameters internally. These names are the parameter entries of the rows of coeftable for which constant is false, since constant parameters are not estimated:
samples and resamples also accept vectors, in which case the SIR procedure is repeated for as many iterations as there are entries. Since every iteration but the first builds its proposal distribution from the parameter samples resampled in the previous one, iterating lets the procedure move away from an initial proposal distribution that approximated the parameter uncertainty poorly. The schedule below is the one Dosne et al. (Dosne et al. 2017) use for the first three iterations of their automated SIR procedure: a large samples/resamples ratio improves the proposal distribution quickly while the number of likelihood evaluations stays small.
In every iteration but the last, resamples has to be greater than the number of estimated parameters, since otherwise the covariance matrix of the proposal distribution of the next iteration is singular.
3.2.3.3 The space the proposal distribution is fitted on
Whenever a proposal distribution is fitted to parameter samples rather than built from a covariance matrix, the proposal_scale and proposal_shape keyword arguments select the space it is fitted on.
proposal_scale selects the scale of the parameters. Its default value UnconstrainedScale() is the unconstrained parameterization that Pumas uses internally, on which every sample drawn from the proposal distribution corresponds to a valid parameter value, so that no sample is ever rejected. The alternative NaturalScale() is the original domain of the parameters, which is also the scale that covariance matrices are expressed on, and there a sample can violate the domain of a parameter and is then rejected.
proposal_shape selects whether the marginals of the parameter samples are reshaped before the proposal distribution is fitted to them. Its default value nothing leaves them as they are, whereas YeoJohnson() applies a Yeo-Johnson transformation with one parameter per marginal that is estimated from the samples themselves, and rejects the samples that fall outside the image of the transformation. The proposal distribution is multivariate normal on the space it is fitted on, and the unconstrained scale can already reduce the skewness that the domain of a parameter induces, for instance by taking the logarithm of a strictly positive parameter. Reshaping the marginals addresses skewness that remains on the selected scale: SIR(samples = [1000, 1000, 1000], resamples = [200, 400, 500], proposal_shape = YeoJohnson()) runs the schedule above with the proposal distributions of its second and third iteration fitted in this way.
Both keyword arguments apply to the proposal distributions of the second and all subsequent iterations, and to the one of the initial iteration if the SIR procedure is based on the parameter samples of a Bootstrap, SIR, or MarginalMCMC inference, as sir_from_bootstrap is. A proposal distribution that is built from a covariance matrix is used on the natural scale of that matrix, and neither keyword argument affects it, so neither of them has any effect on sir_results, sir_from_hessian, or sir_from_matrix.
3.2.3.4 Diagnosing the SIR procedure
How many iterations are needed cannot be known in advance. Dosne et al. (Dosne, Bergstrand, et al. 2016; Dosne et al. 2017) diagnose this with the same distribution of likelihood-ratio statistics as for the bootstrap above, here computed for the parameter samples. likelihood_ratio_ppplot shows it separately for the proposal samples of each iteration and for the samples resampled from them, which the plot labels Posterior.
likelihood_ratio_ppplot(sir_iterated)
As discussed above for the bootstrap, curves below the identity line contain parameter vectors that are less likely under the model and the data than the reference allows for. However, the initial proposal distribution should actually lie at or below the identity line, i.e., it should be at least as wide as the reference. Since the resampled parameters are a subset of the sampled ones, it is much harder for the SIR procedure to widen the spread of the samples than to narrow it, so a proposal distribution above the line is too narrow to start from and further iterations are not a reliable remedy. Dosne et al. (Dosne et al. 2017) inflate the proposal distribution in that case, which here means passing a scaled covariance matrix as proposal_covariance.
The SIR procedure is considered to have converged once the posterior curves of two consecutive iterations agree up to sampling noise (Dosne et al. 2017).
The methods for vcov and samples_table that we saw for Bootstrap also apply here
An alternative to Bootstrap and SIR is the MarginalMCMC sampler which is a Hamiltonian Monte Carlo (HMC) No-U-Turn Sampler (NUTS) that will sample from the marginal loglikelihood. This means that individual effects are marginalized out and then we sample the population level parameters. This does not resample populations like Bootstrap so inference may be more stable if many resampled populations lead to extreme estimates, and it differs from SIR in that it does not need a proposal distribution to be specified.
This method requires slightly more understanding from the user when setting the options that can be found through the docstring of MarginalMCMC. Some knowledge of Bayesian inference is advised.
As sampling based inference can be computationally intensive we exclude the actual invocation of this method from this tutorial.
4 Concluding Remarks
This tutorial showcased a typical Pumas workflow for parameter inference in Pumas models. We showed the different methods supported by Pumas for calculating parameter uncertainty.
References
Dosne, Anne-Gaëlle, Martin Bergstrand, Kajsa Harling, and Mats O Karlsson. 2016. “Improving the Estimation of Parameter Uncertainty Distributions in Nonlinear Mixed Effects Models Using Sampling Importance Resampling.”Journal of Pharmacokinetics and Pharmacodynamics 43 (6): 583–96. https://doi.org/10.1007/s10928-016-9487-8.
Dosne, Anne-Gaëlle, Martin Bergstrand, and Mats O Karlsson. 2017. “An Automated Sampling Importance Resampling Procedure for Estimating Parameter Uncertainty.”Journal of Pharmacokinetics and Pharmacodynamics 44 (6): 509–20. https://doi.org/10.1007/s10928-017-9542-0.
Dosne, Anne-Gaëlle, Ronald Niebecker, and Mats O Karlsson. 2016. “dOFV Distributions: A New Diagnostic for the Adequacy of Parameter Uncertainty in Nonlinear Mixed-Effects Models Applied to the Bootstrap.”Journal of Pharmacokinetics and Pharmacodynamics 43 (6): 597–608. https://doi.org/10.1007/s10928-016-9496-7.