df <- as.data.frame(o_nca)
names(df)[1] "Subject" "start" "end" "PPTESTCD" "PPORRES" "exclude"
PKNCA does not include a dedicated CDISC submission function. What it does provide is a tidy, long-format result table whose structure maps directly onto the CDISC PP (Pharmacokinetic Parameters) domain. This page documents that mapping and describes how to prepare outputs for regulatory submissions.
The CDISC PP domain stores one parameter value per row, with the parameter code in PPTESTCD, the numeric result in PPORRES, and the standardized result in PPSTRES (character) when a units table is provided. PKNCA’s as.data.frame() output follows exactly this long-format convention.
The columns map to CDISC PP variables as follows:
| PKNCA column | CDISC PP variable | Notes |
|---|---|---|
PPTESTCD |
PPTESTCD |
Parameter short name; identical names by convention |
PPORRES |
PPORRES |
Original numeric result (character in CDISC; numeric here) |
start |
— | Interval start time; useful for PPRFTDTC derivation |
end |
— | Interval end time |
exclude |
PPEXCLFL / PPDRVFL |
Non-NA string gives the exclusion reason |
Grouping columns (Subject, etc.) |
USUBJID, PPCAT |
Pass through unchanged |
Units (PPORRESU, PPSTRESU) are available when you configure a units table via pknca_units_table() and pass it to PKNCAdata(). Without a units table, PPORRES carries the raw numeric value and unit derivation must be done externally.
# A tibble: 10 × 6
Subject start end PPTESTCD PPORRES exclude
<ord> <dbl> <dbl> <chr> <dbl> <chr>
1 1 0 24 auclast 92.4 <NA>
2 1 0 Inf cmax 10.5 <NA>
3 1 0 Inf tmax 1.12 <NA>
4 1 0 Inf tlast 24.4 <NA>
5 1 0 Inf clast.obs 3.28 <NA>
6 1 0 Inf lambda.z 0.0485 <NA>
7 1 0 Inf r.squared 1.000 <NA>
8 1 0 Inf adj.r.squared 1.000 <NA>
9 1 0 Inf lambda.z.corrxy -1.000 <NA>
10 1 0 Inf lambda.z.time.first 9.05 <NA>
Whatever grouping structure you define in PKNCAconc() — subject ID, analyte, treatment, period — those column names carry through unchanged into as.data.frame(o_nca). They become the basis for USUBJID, PPCAT (analyte/treatment), and other PP domain identifiers.
# A tibble: 6 × 5
Subject start end PPTESTCD PPORRES
<ord> <dbl> <dbl> <chr> <dbl>
1 1 0 24 auclast 92.4
2 1 0 Inf cmax 10.5
3 1 0 Inf tmax 1.12
4 1 0 Inf tlast 24.4
5 1 0 Inf clast.obs 3.28
6 1 0 Inf lambda.z 0.0485
For multi-analyte or multi-period studies, add those variables to the formula in PKNCAconc():
All grouping variables will appear as columns in the long-format result, ready to be mapped to their CDISC equivalents.
as.data.frame(o_nca) returns the individual (subject-level) long-format table. This is the primary output for PP domain construction:
pp_domain_draft <- as.data.frame(o_nca)
# Rename to CDISC column names as needed
pp_cdisc <- pp_domain_draft |>
rename(
PPORRES = PPORRES, # already CDISC-named
PPTESTCD = PPTESTCD # already CDISC-named
) |>
# PPSTRESN is typically the same as PPORRES for numeric results
mutate(PPSTRESN = PPORRES)
head(pp_cdisc)# A tibble: 6 × 7
Subject start end PPTESTCD PPORRES exclude PPSTRESN
<ord> <dbl> <dbl> <chr> <dbl> <chr> <dbl>
1 1 0 24 auclast 92.4 <NA> 92.4
2 1 0 Inf cmax 10.5 <NA> 10.5
3 1 0 Inf tmax 1.12 <NA> 1.12
4 1 0 Inf tlast 24.4 <NA> 24.4
5 1 0 Inf clast.obs 3.28 <NA> 3.28
6 1 0 Inf lambda.z 0.0485 <NA> 0.0485
The result is in tidy long format: one row per subject per parameter per interval, which is the structure expected by the CDISC PP domain.
exclude ColumnThe exclude column records whether a result was flagged for exclusion and why. When exclude is NA, the result is valid. When it is a non-NA character string, it contains a human-readable exclusion reason.
TRUE
192
# A tibble: 0 × 6
# ℹ 6 variables: Subject <ord>, start <dbl>, end <dbl>, PPTESTCD <chr>,
# PPORRES <dbl>, exclude <chr>
Exclusions are set programmatically via the exclude_nca_* family of functions (e.g., exclude_nca_max.aucinf.pext(), exclude_nca_min.hl.r.squared()), or manually via exclude(). For regulatory submissions, include all rows in the PP domain — both included and excluded — and map the exclude string to PPEXCLFL = "Y" with the reason preserved in a supplemental qualifier.
Loading required namespace: testthat
# A tibble: 0 × 4
# ℹ 4 variables: Subject <ord>, PPTESTCD <chr>, PPORRES <dbl>, exclude <chr>
summary(o_nca) returns a wide-format table suitable for clinical study reports. Each row is a parameter and each column is a summary statistic (geometric mean, CV%, range, etc.):
start end N auclast cmax tmax half.life aucinf.obs
0 24 12 74.6 [24.3] . . . .
0 Inf 12 . 8.65 [17.0] 1.14 [0.630, 3.55] 8.18 [2.12] 115 [28.4]
Caption: auclast, cmax, aucinf.obs: geometric mean and geometric coefficient of variation; tmax: median and range; half.life: arithmetic mean and standard deviation; N: number of subjects
The summary uses the PKNCA.set.summary() configuration for each parameter type. You can customize which statistics are reported and their formatting by calling PKNCA.set.summary() before running pk.nca().
For regulatory analyses, the PKNCA option set used during the run must be documented. PKNCA.options() with no arguments returns the full current option set:
[1] "lin up/log down"
$first
[1] "keep"
$middle
[1] "drop"
$last
[1] "keep"
Set any non-default options at the top of the analysis script, before any data is processed:
For full reproducibility, record the session environment alongside the option snapshot:
R version 4.6.0 (2026-04-24)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 24.04.4 LTS
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
locale:
[1] LC_CTYPE=C.UTF-8 LC_NUMERIC=C LC_TIME=C.UTF-8
[4] LC_COLLATE=C.UTF-8 LC_MONETARY=C.UTF-8 LC_MESSAGES=C.UTF-8
[7] LC_PAPER=C.UTF-8 LC_NAME=C LC_ADDRESS=C
[10] LC_TELEPHONE=C LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C
time zone: UTC
tzcode source: system (glibc)
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggplot2_4.0.3 dplyr_1.2.1 PKNCA_0.12.2
loaded via a namespace (and not attached):
[1] gtable_0.3.6 jsonlite_2.0.0 compiler_4.6.0 brio_1.1.5
[5] tidyselect_1.2.1 Rcpp_1.1.1-1.1 tidyr_1.3.2 scales_1.4.0
[9] yaml_2.3.12 fastmap_1.2.0 lattice_0.22-9 R6_2.6.1
[13] generics_0.1.4 knitr_1.51 backports_1.5.1 htmlwidgets_1.6.4
[17] conflicted_1.2.0 checkmate_2.3.4 tibble_3.3.1 units_1.0-1
[21] pillar_1.11.1 RColorBrewer_1.1-3 rlang_1.2.0 testthat_3.3.2
[25] utf8_1.2.6 cachem_1.1.0 xfun_0.58 S7_0.2.2
[29] otel_0.2.0 memoise_2.0.1 cli_3.6.6 withr_3.0.2
[33] magrittr_2.0.5 digest_0.6.39 grid_4.6.0 lifecycle_1.0.5
[37] nlme_3.1-169 vctrs_0.7.3 evaluate_1.0.5 glue_1.8.1
[41] farver_2.1.2 purrr_1.2.2 rmarkdown_2.31 tools_4.6.0
[45] pkgconfig_2.0.3 htmltools_0.5.9
For validated environments, use renv to lock package versions:
Together, PKNCA.options(), sessionInfo(), and renv provide the three layers of reproducibility documentation expected for regulatory NCA submissions.
pkgdown reference: PKNCAconc() · PKNCAdose() · PKNCAdata() · pk.nca() · PKNCA.options()
---
title: "Regulatory and CDISC Context"
---
```{r setup, include=FALSE}
library(PKNCA)
library(dplyr)
library(ggplot2)
conflicted::conflicts_prefer(dplyr::filter, dplyr::select, .quiet = TRUE)
```
PKNCA does not include a dedicated CDISC submission function. What it does provide is a tidy, long-format result table whose structure maps directly onto the CDISC PP (Pharmacokinetic Parameters) domain. This page documents that mapping and describes how to prepare outputs for regulatory submissions.
```{r build-result, include=FALSE}
data("Theoph")
d_conc <- PKNCAconc(Theoph, conc ~ Time | Subject)
d_dose <- PKNCAdose(
Theoph |> group_by(Subject) |>
summarise(Dose = Dose[1] * Wt[1], .groups = "drop") |>
mutate(Time = 0),
Dose ~ Time | Subject
)
d_data <- PKNCAdata(d_conc, d_dose)
suppressMessages(o_nca <- pk.nca(d_data))
```
---
## 1. CDISC PP Domain Alignment
The CDISC PP domain stores one parameter value per row, with the parameter code in `PPTESTCD`, the numeric result in `PPORRES`, and the standardized result in `PPSTRES` (character) when a units table is provided. PKNCA's `as.data.frame()` output follows exactly this long-format convention.
```{r result-columns}
df <- as.data.frame(o_nca)
names(df)
```
The columns map to CDISC PP variables as follows:
| PKNCA column | CDISC PP variable | Notes |
|--------------|-------------------|--------------------------------------------------------|
| `PPTESTCD` | `PPTESTCD` | Parameter short name; identical names by convention |
| `PPORRES` | `PPORRES` | Original numeric result (character in CDISC; numeric here) |
| `start` | — | Interval start time; useful for `PPRFTDTC` derivation |
| `end` | — | Interval end time |
| `exclude` | `PPEXCLFL` / `PPDRVFL` | Non-`NA` string gives the exclusion reason |
| Grouping columns (`Subject`, etc.) | `USUBJID`, `PPCAT` | Pass through unchanged |
Units (`PPORRESU`, `PPSTRESU`) are available when you configure a units table via `pknca_units_table()` and pass it to `PKNCAdata()`. Without a units table, `PPORRES` carries the raw numeric value and unit derivation must be done externally.
```{r result-head}
head(df, 10)
```
---
## 2. Grouping Columns as CDISC Identifiers
Whatever grouping structure you define in `PKNCAconc()` — subject ID, analyte, treatment, period — those column names carry through unchanged into `as.data.frame(o_nca)`. They become the basis for `USUBJID`, `PPCAT` (analyte/treatment), and other PP domain identifiers.
```{r grouping-cols}
# Grouping columns appear alongside PPTESTCD and PPORRES
df |>
select(Subject, start, end, PPTESTCD, PPORRES) |>
head(6)
```
For multi-analyte or multi-period studies, add those variables to the formula in `PKNCAconc()`:
```{r multi-group-example, eval=FALSE}
# Example: analyte + period grouping
d_conc_multi <- PKNCAconc(
my_data,
conc ~ Time | USUBJID / ANALYTE / PERIOD
)
```
All grouping variables will appear as columns in the long-format result, ready to be mapped to their CDISC equivalents.
---
## 3. Extracting Results for Submission
`as.data.frame(o_nca)` returns the individual (subject-level) long-format table. This is the primary output for PP domain construction:
```{r extract-individual}
pp_domain_draft <- as.data.frame(o_nca)
# Rename to CDISC column names as needed
pp_cdisc <- pp_domain_draft |>
rename(
PPORRES = PPORRES, # already CDISC-named
PPTESTCD = PPTESTCD # already CDISC-named
) |>
# PPSTRESN is typically the same as PPORRES for numeric results
mutate(PPSTRESN = PPORRES)
head(pp_cdisc)
```
The result is in tidy long format: one row per subject per parameter per interval, which is the structure expected by the CDISC PP domain.
---
## 4. The `exclude` Column
The `exclude` column records whether a result was flagged for exclusion and why. When `exclude` is `NA`, the result is valid. When it is a non-`NA` character string, it contains a human-readable exclusion reason.
```{r exclude-col}
# All possible exclusion reasons in this result
table(is.na(df$exclude))
# Show any excluded rows
df |> filter(!is.na(exclude)) |> head()
```
Exclusions are set programmatically via the `exclude_nca_*` family of functions (e.g., `exclude_nca_max.aucinf.pext()`, `exclude_nca_min.hl.r.squared()`), or manually via `exclude()`. For regulatory submissions, include all rows in the PP domain — both included and excluded — and map the `exclude` string to `PPEXCLFL = "Y"` with the reason preserved in a supplemental qualifier.
```{r exclude-nca-example}
# Apply standard exclusion rules and inspect excluded rows
o_nca_excl <- o_nca |>
exclude(reason = "r.squared < 0.9", FUN = exclude_nca_min.hl.r.squared(0.9)) |>
exclude(reason = "%AUCextrap > 20%", FUN = exclude_nca_max.aucinf.pext(20))
df_excl <- as.data.frame(o_nca_excl)
df_excl |> filter(!is.na(exclude)) |> select(Subject, PPTESTCD, PPORRES, exclude) |> head()
```
---
## 5. Summary Table for Reports
`summary(o_nca)` returns a wide-format table suitable for clinical study reports. Each row is a parameter and each column is a summary statistic (geometric mean, CV%, range, etc.):
```{r summary-table}
summary(o_nca)
```
The summary uses the `PKNCA.set.summary()` configuration for each parameter type. You can customize which statistics are reported and their formatting by calling `PKNCA.set.summary()` before running `pk.nca()`.
---
## 6. Reproducibility: Pinning Options and Environment
For regulatory analyses, the PKNCA option set used during the run must be documented. `PKNCA.options()` with no arguments returns the full current option set:
```{r options-snapshot}
# Capture the full option set at the start of an analysis script
analysis_options <- PKNCA.options()
# Confirm a key option
analysis_options$auc.method # trapezoidal method
analysis_options$conc.blq # BLQ handling
```
Set any non-default options at the top of the analysis script, before any data is processed:
```{r set-options, eval=FALSE}
# Example: use log-linear trapezoid instead of linear-up/log-down
PKNCA.options(auc.method = "lin-log")
```
For full reproducibility, record the session environment alongside the option snapshot:
```{r session-info}
# Save at the end of the analysis script
sessionInfo()
```
For validated environments, use `renv` to lock package versions:
```{r renv-example, eval=FALSE}
# Lock the current environment
renv::snapshot()
# Restore a locked environment on a validation system
renv::restore()
```
Together, `PKNCA.options()`, `sessionInfo()`, and `renv` provide the three layers of reproducibility documentation expected for regulatory NCA submissions.
---
::: {.callout-note icon=false appearance="minimal"}
**pkgdown reference:** [PKNCAconc()](https://humanpred.github.io/pknca/reference/PKNCAconc.html) · [PKNCAdose()](https://humanpred.github.io/pknca/reference/PKNCAdose.html) · [PKNCAdata()](https://humanpred.github.io/pknca/reference/PKNCAdata.html) · [pk.nca()](https://humanpred.github.io/pknca/reference/pk.nca.html) · [PKNCA.options()](https://humanpred.github.io/pknca/reference/PKNCA.options.html)
:::