pmxpartab
This R package produces nice looking parameter tables for pharmacometric modeling results with ease. It is completely agnostic to the modeling software that was used.
The creation of the parameter table proceeds in 2 steps:
data.frame
from a list of
outputs and metadata.data.frame
.For the first step, the inputs are:
To keep things generic, both the model outputs and metadata must be provided as R lists (it is a separate problem to extract the outputs from the modeling software into the required list format, although the package does include an auxiliary function to help deal specifically with NONMEM outputs).
For illustration, we will use a YAML description of the outputs and metadata, which can easily be read into R. First, the outputs:
library(yaml)
outputs <- yaml.load("
est:
CL: 0.482334
VC: 0.0592686
nCL: 0.315414
nVC: 0.536025
ERRP: 0.0508497
se:
CL: 0.0138646
VC: 0.0055512
nCL: 0.0188891
nVC: 0.0900352
ERRP: 0.0018285
fixed:
CL: no
VC: no
nCL: no
nVC: no
ERRP: no
shrinkage:
nCL: 9.54556
nVC: 47.8771
")
We see that the outputs are split into separate sections:
est
for estimates, se
for standard errors,
fixed
for indicating which parameters were fixed rather
than estimated, shrinkage
for shrinkage estimates.
Now, the metadata:
meta <- yaml.load("
parameters:
- name: CL
label: 'Clearance'
units: 'L/h'
type: Structural
- name: VC
label: 'Volume'
units: 'L'
type: Structural
trans: 'exp'
- name: nCL
label: 'On Clearance'
type: IIV
trans: 'SD (CV%)'
- name: nVC
label: 'On Volume'
type: IIV
trans: 'SD (CV%)'
- name: ERRP
label: 'Proportional Error'
units: '%'
type: RUV
trans: '%'
")
Here, the first important thing is the name
, which must
match the names of the parameters in the outputs. Then, we have some
optional attributes: a descriptive label
, the
units
if applicable, a transformation trans
,
and a type
.
Putting this all together, we can produce a data.frame
like this:
## name label units type trans fixed est se
## 1 CL Clearance L/h Structural <NA> FALSE 0.482334 0.013864600
## 2 VC Volume L Structural exp FALSE 1.061060 0.005890157
## 3 nCL On Clearance <NA> IIV SD (CV%) FALSE 0.315414 0.018889100
## 4 nVC On Volume <NA> IIV SD (CV%) FALSE 0.536025 0.090035200
## 5 ERRP Proportional Error % RUV % FALSE 5.084970 0.182850000
## rse lci95 uci95 pval shrinkage
## 1 2.874481 0.4551594 0.5095086 0.000000e+00 NA
## 2 0.555120 1.0495781 1.0726679 0.000000e+00 NA
## 3 5.988669 0.2783914 0.3524366 0.000000e+00 9.54556
## 4 16.796829 0.3595560 0.7124940 2.624601e-09 47.87710
## 5 3.595891 4.7265840 5.4433560 0.000000e+00 NA
Finally, our nicely formatted table looks like this:
Parameter | Estimate | RSE% | 95% CI | Shrinkage |
---|---|---|---|---|
Typical Values | ||||
Clearance (L/h) | 0.482 | 2.87 | 0.455 – 0.510 | - |
Volume (L) | 1.06 | 0.555 | 1.05 – 1.07 | - |
Between Subject Variability | ||||
On Clearance | 0.315 (32.3%) | 5.99 | 0.278 – 0.352 | 9.55% |
On Volume | 0.536 (57.7%) | 16.8 | 0.360 – 0.712 | 47.9% |
Residual Error | ||||
Proportional Error (%) | 5.08 | 3.60 | 4.73 – 5.44 | - |
We can also do this in one shot, and add footnotes as well:
footnote <- c(
"CI=confidence interval; RSE=relative standard error.",
"Source: run001")
pmxpartab(pmxparframe(outputs, meta), footnote=footnote)
Parameter | Estimate | RSE% | 95% CI | Shrinkage |
---|---|---|---|---|
Typical Values | ||||
Clearance (L/h) | 0.482 | 2.87 | 0.455 – 0.510 | - |
Volume (L) | 1.06 | 0.555 | 1.05 – 1.07 | - |
Between Subject Variability | ||||
On Clearance | 0.315 (32.3%) | 5.99 | 0.278 – 0.352 | 9.55% |
On Volume | 0.536 (57.7%) | 16.8 | 0.360 – 0.712 | 47.9% |
Residual Error | ||||
Proportional Error (%) | 5.08 | 3.60 | 4.73 – 5.44 | - |
CI=confidence interval; RSE=relative standard error. Source: run001 |
It is also possible to use the pipe syntax:
Parameter | Estimate | RSE% | 95% CI | Shrinkage |
---|---|---|---|---|
Typical Values | ||||
Clearance (L/h) | 0.482 | 2.87 | 0.455 – 0.510 | - |
Volume (L) | 1.06 | 0.555 | 1.05 – 1.07 | - |
Between Subject Variability | ||||
On Clearance | 0.315 (32.3%) | 5.99 | 0.278 – 0.352 | 9.55% |
On Volume | 0.536 (57.7%) | 16.8 | 0.360 – 0.712 | 47.9% |
Residual Error | ||||
Proportional Error (%) | 5.08 | 3.60 | 4.73 – 5.44 | - |
CI=confidence interval; RSE=relative standard error. Source: run001 |
There is some debate on the best way to present the random effects
from a mixed-effects model (i.e., the parameter(s) that describe the
(joint) distribution of the random effects, which are typically assumed
to follow a multivariate normal distribution). Some modelers are
accustomed to seeing the variances and covariances, while others (such
as me) prefer the standard deviations and correlations. In the standard
log-normal case, diagonal elements are often presented in the form of
their (geometric) coefficient of variation, which can be derived from
the standard deviation ω by
$\sqrt{e^{ω^2}-1}$, as is typically
shown as a percentage. In any case, pmxpartab
is agnostic
to this choice, and gives freedom and flexibility in this respect.
In one version (call it the flat version), all estimates are
at the top level of the components est
, se
and
fixed
. Here is an example:
outputs <- yaml.load("
est:
nCL: 3.95926E-01
nVC: 1.42749E+00
nCL_nVC: 8.45393E-02
se:
nCL: 9.57069E-03
nVC: 4.62152E-02
nCL_nVC: 4.26648E-02
")
meta <- yaml.load("
parameters:
- name: 'nCL'
label: 'On CL'
type: IIV
- name: 'nVC'
label: 'On Vc'
type: IIV
- name: 'nCL_nVC'
label: 'Correlation CL-Vc'
type: IIV
")
outputs |> pmxparframe(meta) |> pmxpartab()
Parameter | Estimate | RSE% | 95% CI |
---|---|---|---|
Between Subject Variability | |||
On CL | 0.396 | 2.42 | 0.377 – 0.415 |
On Vc | 1.43 | 3.24 | 1.34 – 1.52 |
Correlation CL-Vc | 0.0845 | 50.5 | 0.000916 – 0.168 |
In another version (call it the structured version),
between-individual random effect parameters are contained in
sub-components of est
, se
and
fixed
: - om
contains the standard deviations
as a named vector - om_cov
contains the variance-covariance
matrix - om_cor
contains the correlation matrix, with
standard deviations on the diagonal
Here is an example:
outputs <- list(
est = list(
om = c(nCL=3.95926E-01, nVC=1.42749E+00),
om_cov = matrix(c(1.56758E-01, 4.77799E-02, 4.77799E-02, 2.03772E+00), 2, 2),
om_cor = matrix(c(3.95926E-01, 8.45393E-02, 8.45393E-02, 1.42749E+00), 2, 2)),
se = list(
om = c(nCL=9.57069E-03, nVC=4.62152E-02),
om_cov = matrix(c(7.57858E-03, 2.47183E-02, 2.47183E-02, 1.31943E-01), 2, 2),
om_cor = matrix(c(9.57069E-03, 4.26648E-02, 4.26648E-02, 4.62152E-02), 2, 2)))
meta <- yaml.load("
parameters:
- name: 'om_cov(nCL,nCL)'
label: 'Variance log(CL)'
type: IIV
- name: 'om_cov(nVC,nVC)'
label: 'Variance log(Vc)'
type: IIV
- name: 'om_cor(nCL,nCL)'
label: 'SD log(CL)'
type: IIV
- name: 'om_cor(nVC,nVC)'
label: 'SD log(Vc)'
type: IIV
- name: 'om_cov(nCL,nVC)'
label: 'Covariance log(CL)-log(Vc)'
type: IIV
- name: 'om_cor(nCL,nVC)'
label: 'Correlation log(CL)-log(Vc)'
type: IIV
")
outputs |> pmxparframe(meta) |> pmxpartab()
Parameter | Estimate | RSE% | 95% CI |
---|---|---|---|
Between Subject Variability | |||
Variance log(CL) | 0.157 | 4.83 | 0.142 – 0.172 |
Variance log(Vc) | 2.04 | 6.48 | 1.78 – 2.30 |
SD log(CL) | 0.396 | 2.42 | 0.377 – 0.415 |
SD log(Vc) | 1.43 | 3.24 | 1.34 – 1.52 |
Covariance log(CL)-log(Vc) | 0.0478 | 51.7 | -0.000668 – 0.0962 |
Correlation log(CL)-log(Vc) | 0.0845 | 50.5 | 0.000916 – 0.168 |
TBD