14  Units Assignment and Conversion

14.1 Why units matter

By default PKNCA returns bare numbers. When you provide units, every row in as.data.frame() gains CDISC-style columns that mirror the SDTM PK-Parameters domain:

Column Meaning
PPORRESU Original reporting unit (the unit the result is expressed in)
PPSTRESU Standardised unit (populated when a conversion is requested)
PPSTRES Result expressed in the standardised unit

This makes the output submission-ready and prevents silent unit mismatches when combining results from multiple studies or analytes.


14.2 Assigning units at object creation

Units can be supplied directly to PKNCAconc() and PKNCAdose() as string literals or as the name of a column in your data frame that holds the unit string per row.

14.2.1 Arguments in PKNCAconc()

Verified with:

formals(getS3method("PKNCAconc", "data.frame")) |> names()

The unit-related arguments are:

  • concu — concentration unit (e.g. "mg/L")
  • timeu — time unit (e.g. "h")
  • amountu — amount unit, used for urine/fecal data (e.g. "mg")
  • concu_pref — preferred output unit for concentration (triggers conversion)
  • timeu_pref — preferred output unit for time (triggers conversion)
  • amountu_pref — preferred output unit for amount (triggers conversion)

14.2.2 Arguments in PKNCAdose()

  • doseu — dose unit (e.g. "mg")
  • doseu_pref — preferred output unit for dose (triggers conversion)

14.2.3 Example: units as string literals

Theoph_df <- as.data.frame(Theoph)
d_dose <- Theoph_df |>
  dplyr::filter(Time == 0) |>
  dplyr::mutate(amt = Dose * Wt)

o_conc <- PKNCAconc(Theoph_df, conc ~ Time | Subject,
                    concu = "mg/L", timeu = "h")

o_dose <- PKNCAdose(d_dose, amt ~ Time | Subject,
                    doseu = "mg")

14.2.4 Example: units from a data column

Theoph_units <- Theoph_df |>
  dplyr::mutate(conc_unit = "mg/L", time_unit = "h")

o_conc_col <- PKNCAconc(Theoph_units, conc ~ Time | Subject,
                        concu = "conc_unit", timeu = "time_unit")

14.3 pknca_units_table()

This function builds a complete mapping from every NCA parameter to its reporting unit, derived from the four base units you supply.

In PKNCA ≥ 0.12.2, pknca_units_table() is an S3 generic. The most useful new method is pknca_units_table.PKNCAdata(), which extracts units directly from a fully assembled PKNCAdata object — useful when units are already embedded in PKNCAconc and PKNCAdose:

# Units already embedded in the data objects
o_conc_u <- PKNCAconc(as.data.frame(Theoph) |> dplyr::rename(time=Time, subject=Subject),
                       conc ~ time | subject, concu = "mg/L", timeu = "h")
o_dose_u <- PKNCAdose(
  as.data.frame(Theoph) |>
    dplyr::group_by(Subject) |>
    dplyr::summarise(dose = Dose[1]*Wt[1], .groups="drop") |>
    dplyr::rename(subject=Subject) |>
    dplyr::mutate(time = 0),
  dose ~ time | subject, route = "extravascular",
  doseu = "mg", timeu = "h"
)
o_data_u <- PKNCAdata(o_conc_u, o_dose_u)

# Extract units from the PKNCAdata object (≥ 0.12.2)
u_from_data <- pknca_units_table(o_data_u)
head(u_from_data, 6)
  PPORRESU           PPTESTCD
1 unitless          r.squared
2 unitless      adj.r.squared
3 unitless    lambda.z.corrxy
4 unitless     tobit_residual
5 unitless adj_tobit_residual
6 fraction                  f

14.3.1 Arguments (default method)

args(getS3method("pknca_units_table", "default"))
function (concu, doseu, amountu, timeu, concu_pref = NULL, doseu_pref = NULL, 
    amountu_pref = NULL, timeu_pref = NULL, conversions = data.frame(), 
    ...) 
NULL

All four of concu, doseu, amountu, timeu are required. PKNCA derives composite units automatically (e.g. auclast gets h*mg/L from timeu="h" and concu="mg/L").

14.3.2 Output columns

The returned data frame has exactly two columns when no preferred units are requested:

Column Content
PPORRESU Reporting unit for this parameter
PPTESTCD Parameter code (e.g. "cmax", "auclast", "tmax")

When preferred units are requested, two additional columns appear: PPSTRESU (target unit) and conversion_factor.

u <- pknca_units_table(concu = "mg/L", timeu = "h",
                       doseu = "mg", amountu = "mg")

# The table covers all NCA parameters
nrow(u)
[1] 203
# Sample rows
u[u$PPTESTCD %in% c("cmax", "auclast", "tmax", "cl.obs", "vz.obs"), ]
       PPORRESU PPTESTCD
27            h     tmax
78         mg/L     cmax
117   mg/(mg/L)   vz.obs
132      h*mg/L  auclast
192 mg/(h*mg/L)   cl.obs

14.4 Passing the units table to PKNCAdata()

Pass the table via the units argument. If units is provided, any units given directly to PKNCAconc() or PKNCAdose() are ignored.

# Build objects (units here will be overridden by the table below)
o_conc2 <- PKNCAconc(Theoph_df, conc ~ Time | Subject)
o_dose2  <- PKNCAdose(d_dose, amt ~ Time | Subject)

u <- pknca_units_table(concu = "mg/L", timeu = "h",
                       doseu = "mg", amountu = "mg")

o_data <- PKNCAdata(o_conc2, o_dose2, units = u)

# Run NCA
res <- pk.nca(o_data)

# Units appear in the PPORRESU column
df <- as.data.frame(res)
df |>
  dplyr::filter(Subject == "1",
                PPTESTCD %in% c("auclast", "cmax", "tmax", "cl.obs")) |>
  dplyr::select(PPTESTCD, PPORRES, PPORRESU)
# A tibble: 3 × 3
  PPTESTCD PPORRES PPORRESU
  <chr>      <dbl> <chr>   
1 auclast    92.4  h*mg/L  
2 cmax       10.5  mg/L    
3 tmax        1.12 h       

14.5 Preferred units

Setting concu_pref, timeu_pref, doseu_pref, or amountu_pref in pknca_units_table() tells PKNCA to convert results to those preferred units. PKNCA calculates the conversion factor automatically for metric prefixes and time unit changes.

When preferred units are active the results data frame gains:

  • PPSTRESU — the preferred unit
  • PPSTRES — the result in the preferred unit
u_pref <- pknca_units_table(
  concu = "mg/L", timeu  = "h",
  doseu = "mg",   amountu = "mg",
  concu_pref = "ug/L",   # mg/L → ug/L  (× 1 000)
  timeu_pref = "day"     # h   → day   (× 1/24)
)

# Inspect what the table says for key parameters
u_pref[u_pref$PPTESTCD %in% c("cmax", "auclast", "tmax"), ]
    PPORRESU PPTESTCD PPSTRESU conversion_factor
27         h     tmax      day      4.166667e-02
78      mg/L     cmax     ug/L      1.000000e+03
132   h*mg/L  auclast day*ug/L      4.166667e+01
o_data_pref <- PKNCAdata(o_conc2, o_dose2, units = u_pref)
res_pref <- pk.nca(o_data_pref)

as.data.frame(res_pref) |>
  dplyr::filter(Subject == "1",
                PPTESTCD %in% c("auclast", "cmax", "tmax")) |>
  dplyr::select(PPTESTCD, PPORRES, PPORRESU, PPSTRES, PPSTRESU)
# A tibble: 3 × 5
  PPTESTCD PPORRES PPORRESU    PPSTRES PPSTRESU
  <chr>      <dbl> <chr>         <dbl> <chr>   
1 auclast    92.4  h*mg/L    3849.     day*ug/L
2 cmax       10.5  mg/L     10500      ug/L    
3 tmax        1.12 h            0.0467 day     

PPORRES/PPORRESU hold the original value and unit; PPSTRES/PPSTRESU hold the converted value and unit.


14.6 Custom unit conversions

For conversions PKNCA cannot derive automatically (e.g. mass ↔︎ molar, or a non-standard reporting preference), pass a conversions data frame to pknca_units_table().

14.6.1 Required columns

Column Type Meaning
PPORRESU character The original unit to match
PPSTRESU character The target unit
conversion_factor numeric Multiplier (original × factor = target). Use NA to ask PKNCA to compute it.

14.6.2 Example: report concentration as ug/mL instead of mg/L

mg/L and ug/mL are numerically equal (factor = 1), but differ in label.

my_conversions <- data.frame(
  PPORRESU        = c("mg/L",   "h*mg/L"),
  PPSTRESU        = c("ug/mL",  "h*ug/mL"),
  conversion_factor = c(1,       1)
)

u_custom <- pknca_units_table(
  concu = "mg/L", timeu = "h",
  doseu = "mg",   amountu = "mg",
  conversions = my_conversions
)

o_data_custom <- PKNCAdata(o_conc2, o_dose2, units = u_custom)
res_custom    <- pk.nca(o_data_custom)

as.data.frame(res_custom) |>
  dplyr::filter(Subject == "1",
                PPTESTCD %in% c("auclast", "cmax")) |>
  dplyr::select(PPTESTCD, PPORRES, PPORRESU, PPSTRES, PPSTRESU)
# A tibble: 2 × 5
  PPTESTCD PPORRES PPORRESU PPSTRES PPSTRESU
  <chr>      <dbl> <chr>      <dbl> <chr>   
1 auclast     92.4 h*mg/L      92.4 h*ug/mL 
2 cmax        10.5 mg/L        10.5 ug/mL   

14.7 Verifying units in results

Call as.data.frame() on the PKNCAresults object. When a units table was provided, the PPORRESU column is always populated; PPSTRESU and PPSTRES appear only when preferred or custom conversions are in effect.

df_all <- as.data.frame(res)

# Confirm PPORRESU is present for every row
stopifnot("PPORRESU" %in% names(df_all))

# Browse the first several parameter results for subject 1
df_all |>
  dplyr::filter(Subject == "1") |>
  dplyr::select(PPTESTCD, PPORRES, PPORRESU) |>
  head(10)
# A tibble: 10 × 3
   PPTESTCD            PPORRES PPORRESU
   <chr>                 <dbl> <chr>   
 1 auclast             92.4    h*mg/L  
 2 cmax                10.5    mg/L    
 3 tmax                 1.12   h       
 4 tlast               24.4    h       
 5 clast.obs            3.28   mg/L    
 6 lambda.z             0.0485 1/h     
 7 r.squared            1.000  unitless
 8 adj.r.squared        1.000  unitless
 9 lambda.z.corrxy     -1.000  unitless
10 lambda.z.time.first  9.05   h       

14.8 Summary

Goal How
Assign units at data creation concu, timeu, doseu args in PKNCAconc() / PKNCAdose()
Build a complete unit table pknca_units_table(concu, doseu, amountu, timeu)
Pass units to the analysis PKNCAdata(..., units = u)
Convert to preferred output units Add concu_pref, timeu_pref, etc. to pknca_units_table()
Custom / non-metric conversions conversions argument in pknca_units_table()
Inspect units in results as.data.frame(res)$PPORRESU
Extract units from a PKNCAdata object (≥ 0.12.2) pknca_units_table(o_data)
Allow some parameters without units (≥ 0.12.2) PKNCA.options(allow_partial_missing_units = TRUE)