df <- as.data.frame(o_nca)
names(df)[1] "Subject" "start" "end" "PPTESTCD" "PPORRES" "PPANMETH" "exclude"
PKNCA does not build a complete submission dataset for you, but it comes close: its tidy, long-format result table maps directly onto the CDISC PP (Pharmacokinetic Parameters) domain, and as of version 0.12.2 as.data.frame() can translate parameter codes into CDISC standard codes directly (see the CDISC output format section below). This page documents that mapping and describes how to prepare outputs for regulatory submissions. For PKNCA’s regulator-facing overview, see PKNCA – an R package for noncompartmental analysis.
The CDISC PP domain stores one parameter value per row, with the parameter code in PPTESTCD and the original result in PPORRES. When a units table is provided, PKNCA additionally fills its own standardized-result column, PPSTRES; the corresponding CDISC SDTM variables are PPSTRESC (character) and PPSTRESN (numeric). PKNCA’s as.data.frame() output follows exactly this long-format convention.
[1] "Subject" "start" "end" "PPTESTCD" "PPORRES" "PPANMETH" "exclude"
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 |
PPSTAT / PPREASND (or a supplemental qualifier) |
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. For the full units workflow, see the Units chapter and Unit Assignment and Conversion with PKNCA.
# A tibble: 10 × 7
Subject start end PPTESTCD PPORRES PPANMETH exclude
<ord> <dbl> <dbl> <chr> <dbl> <chr> <chr>
1 1 0 24 auclast 92.4 "AUC: lin up/log dow… <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. The PPTESTCD and PPORRES columns are already CDISC-named, so no renaming is needed:
# A tibble: 6 × 8
Subject start end PPTESTCD PPORRES PPANMETH exclude PPSTRESN
<ord> <dbl> <dbl> <chr> <dbl> <chr> <chr> <dbl>
1 1 0 24 auclast 92.4 "AUC: lin up/log down" <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.
The long-format extraction above keeps PKNCA’s own parameter codes in PPTESTCD — auclast, cmax, lambda.z, and so on. For submission datasets, as.data.frame() can translate those codes for you: out_format = "cdisc" replaces each PPTESTCD with its CDISC submission code (auclast becomes AUCLST, clast.obs becomes CLST, lambda.z becomes LAMZ) and inserts a PPTEST column carrying the standard parameter label. Route-dependent parameters resolve to the correct code based on the route recorded in the dose data.
Subject PPTESTCD PPTEST PPORRES
1 1 AUCLST AUC to Last Nonzero Conc 92.3654416
2 1 CMAX Max Conc 10.5000000
3 1 TMAX Time of CMAX 1.1200000
4 1 TLST Time of Last Nonzero Conc 24.3700000
5 1 CLST Last Nonzero Conc 3.2800000
6 1 LAMZ Lambda z 0.0484570
7 1 R2 R Squared 0.9999997
8 1 R2ADJ R Squared Adjusted 0.9999995
When an interval-based parameter is requested (for example aucint.last, whose CDISC code AUCINT contains “INT”), the CDISC output also gains PPSTINT and PPENINT columns giving the interval start and end as ISO 8601 durations relative to the last dose time (e.g. PT0H, PT12H).
Custom parameters registered with add.interval.col() can declare their own CDISC codes through its pptestcd_cdisc and pptest_cdisc arguments; a parameter without a declared code keeps its PKNCA name in the CDISC output. See the Writing Custom Parameter Functions chapter.
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 × 7
# ℹ 7 variables: Subject <ord>, start <dbl>, end <dbl>, PPTESTCD <chr>,
# PPORRES <dbl>, PPANMETH <chr>, 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(). The rule functions supply their own exclusion reason text (e.g. exclude_nca_min.hl.r.squared(0.9) records r.squared < 0.9), so no reason= argument is needed. For regulatory submissions, include all rows in the PP domain — both included and excluded. Exclusion is typically represented via PPSTAT/PPREASND (status and reason not done) or a supplemental qualifier; PKNCA’s exclude column provides the source text for whichever convention your submission uses. Exclusion workflows are covered in the Post-Processing chapter and vignette.
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 group/interval and each parameter gets a column, with cells formatted as “point [spread]” (geometric mean [CV%] by default):
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 calling summary().
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.1 (2026-06-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] dplyr_1.2.1 PKNCA_0.12.1.9000
loaded via a namespace (and not attached):
[1] jsonlite_2.0.0 compiler_4.6.1 brio_1.1.5 tidyselect_1.2.1
[5] Rcpp_1.1.2 tidyr_1.3.2 yaml_2.3.12 fastmap_1.2.0
[9] lattice_0.22-9 R6_2.6.1 generics_0.1.4 knitr_1.51
[13] htmlwidgets_1.6.4 backports_1.5.1 conflicted_1.2.0 checkmate_2.3.4
[17] tibble_3.3.1 units_1.0-1 pillar_1.11.1 rlang_1.3.0
[21] utf8_1.2.6 testthat_3.3.2 cachem_1.1.0 xfun_0.60
[25] otel_0.2.0 memoise_2.0.1 cli_3.6.6 withr_3.0.3
[29] magrittr_2.0.5 digest_0.6.39 grid_4.6.1 lifecycle_1.0.5
[33] nlme_3.1-169 vctrs_0.7.3 evaluate_1.0.5 glue_1.8.1
[37] rmarkdown_2.31 purrr_1.2.2 tools_4.6.1 pkgconfig_2.0.3
[41] 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() · as.data.frame.PKNCAresults() · exclude() · exclude_nca_*() · summary() · PKNCA.options()
---
title: "Regulatory and CDISC Context"
---
```{r setup, include=FALSE}
library(PKNCA)
library(dplyr)
conflicted::conflicts_prefer(dplyr::filter, dplyr::select, .quiet = TRUE)
```
PKNCA does not build a complete submission dataset for you, but it comes close: its tidy, long-format result table maps directly onto the CDISC PP (Pharmacokinetic Parameters) domain, and as of version 0.12.2 `as.data.frame()` can translate parameter codes into CDISC standard codes directly (see the CDISC output format section below). This page documents that mapping and describes how to prepare outputs for regulatory submissions. For PKNCA's regulator-facing overview, see [PKNCA – an R package for noncompartmental analysis](https://humanpred.github.io/pknca/articles/v31-FDA-introduction.html).
```{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)
o_nca <- pk.nca(d_data)
```
---
## CDISC PP domain alignment
The CDISC PP domain stores one parameter value per row, with the parameter code in `PPTESTCD` and the original result in `PPORRES`. When a units table is provided, PKNCA additionally fills its own standardized-result column, `PPSTRES`; the corresponding CDISC SDTM variables are `PPSTRESC` (character) and `PPSTRESN` (numeric). 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` | `PPSTAT` / `PPREASND` (or a supplemental qualifier) | 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. For the full units workflow, see the [Units chapter](units.qmd) and [Unit Assignment and Conversion with PKNCA](https://humanpred.github.io/pknca/articles/v07-unit-conversion.html).
```{r result-head}
head(df, 10)
```
---
## 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.
---
## 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. The `PPTESTCD` and `PPORRES` columns are already CDISC-named, so no renaming is needed:
```{r extract-individual}
pp_domain_draft <- as.data.frame(o_nca)
# PPSTRESN is typically the same as PPORRES for numeric results
pp_cdisc <- pp_domain_draft |>
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.
---
## CDISC output format (≥ 0.12.2)
The long-format extraction above keeps PKNCA's own parameter codes in `PPTESTCD` — `auclast`, `cmax`, `lambda.z`, and so on. For submission datasets, `as.data.frame()` can translate those codes for you: `out_format = "cdisc"` replaces each `PPTESTCD` with its CDISC submission code (`auclast` becomes `AUCLST`, `clast.obs` becomes `CLST`, `lambda.z` becomes `LAMZ`) and inserts a `PPTEST` column carrying the standard parameter label. Route-dependent parameters resolve to the correct code based on the route recorded in the dose data.
```{r cdisc-format}
as.data.frame(o_nca, out_format = "cdisc") |>
select(Subject, PPTESTCD, PPTEST, PPORRES) |>
head(8)
```
When an interval-based parameter is requested (for example `aucint.last`, whose CDISC code `AUCINT` contains "INT"), the CDISC output also gains `PPSTINT` and `PPENINT` columns giving the interval start and end as ISO 8601 durations relative to the last dose time (e.g. `PT0H`, `PT12H`).
Custom parameters registered with `add.interval.col()` can declare their own CDISC codes through its `pptestcd_cdisc` and `pptest_cdisc` arguments; a parameter without a declared code keeps its PKNCA name in the CDISC output. See the [Writing Custom Parameter Functions chapter](custom-parameters.qmd).
---
## 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}
# How many rows are flagged for exclusion?
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()`. The rule functions supply their own exclusion reason text (e.g. `exclude_nca_min.hl.r.squared(0.9)` records `r.squared < 0.9`), so no `reason=` argument is needed. For regulatory submissions, include all rows in the PP domain — both included and excluded. Exclusion is typically represented via `PPSTAT`/`PPREASND` (status and reason not done) or a supplemental qualifier; PKNCA's `exclude` column provides the source text for whichever convention your submission uses. Exclusion workflows are covered in the [Post-Processing chapter](postprocessing.qmd) and [vignette](https://humanpred.github.io/pknca/articles/v07-post-processing.html).
```{r exclude-nca-example}
# Apply standard exclusion rules and inspect excluded rows
o_nca_excl <- o_nca |>
exclude(FUN = exclude_nca_min.hl.r.squared(0.9)) |>
exclude(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()
```
---
## Summary table for reports
`summary(o_nca)` returns a wide-format table suitable for clinical study reports. Each row is a group/interval and each parameter gets a column, with cells formatted as "point [spread]" (geometric mean [CV%] by default):
```{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 calling `summary()`.
---
## 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: force the plain linear trapezoidal rule everywhere
PKNCA.options(auc.method = "linear")
```
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) · [as.data.frame.PKNCAresults()](https://humanpred.github.io/pknca/reference/as.data.frame.PKNCAresults.html) · [exclude()](https://humanpred.github.io/pknca/reference/exclude.html) · [exclude_nca_*()](https://humanpred.github.io/pknca/reference/exclude_nca.html) · [summary()](https://humanpred.github.io/pknca/reference/summary.PKNCAresults.html) · [PKNCA.options()](https://humanpred.github.io/pknca/reference/PKNCA.options.html)
:::