Input Data
Required columns
The input dataset (passed to pumas_be or preprocess_be) must contain one row per observation with the following columns:
| Column | Meaning |
|---|---|
id | Subject identifier. |
sequence | The dosing sequence assigned to the subject (e.g. RT, TR). |
period | The dosing period within the study. |
| endpoint | The pharmacokinetic endpoint (e.g. AUC, Cmax, Tmax). |
While the names id, sequence, and period are the defaults, column names are configurable. For this, id, sequence, and period are passed as keyword arguments, and the endpoint column is selected with endpoint (default :AUC). The endpoint is expected to be on the natural scale by default (and this implies it will be internally log-transformed). However, if it is already log-transformed, then pass logtransformed = true.
The sequence column encodes the formulations in dosing order. Formulations are labelled by single characters, and the reference is the alphabetically first character (R in RT/TR; A in ADBC; R with tests S and T in RST). See Study Designs for the supported sequence sets.
Preprocessing
The preprocess_be function standardizes a raw dataset into the canonical schema used internally. It runs as the first step of pumas_be, so it applies to every analysis whether or not it is called directly. A raw dataset may carry several PK parameters (e.g. AUC and Cmax), but each analysis concerns a single endpoint, chosen via endpoint. Preprocessing therefore reduces the data to that one endpoint: the standardized dataset has a single endpoint column, and all downstream steps operate on it alone. It:
- selects the design variables and the chosen endpoint;
- applies the natural-log transformation to the endpoint (unless
logtransformed = true); - renames variables to their canonical names;
- drops missing observations;
- derives the
formulationfor each row from thesequenceandperiod; - converts variables to the formats used for analysis (categorical factors).
The standardized dataset has exactly these columns, in order:
id id_within_sequence sequence period formulation endpointwhere endpoint is now log-transformed and id_within_sequence is an index of the subject within its sequence. The is_preprocessed function returns true for a dataset already in this form. A call to pumas_be invokes preprocess_be internally, so explicit preprocessing is only needed for the lower-level workflow. Even when using the one-call workflow, the standardized dataset is retained on the output and can be inspected via out.data.
Missing data
The preprocess_be function drops individual observations with a missing endpoint (and, on the natural scale, non-positive values) rather than dropping a subject outright. A subject missing one period therefore still contributes its remaining observations: the analysis is available-case, not complete-case, and the mixed models accommodate the resulting imbalance.
Because dropping is decided from the endpoint being analyzed, a raw dataset carrying two or more endpoints can yield a different set of retained observations for each. An observation with a present AUC but a missing Cmax, for example, is kept when endpoint = :AUC but dropped when endpoint = :Cmax. The two endpoints are therefore run on slightly different data, one more reason each endpoint is a separate analysis (see Workflow).
Inspecting a design
The detect_design function reports the design name, number of sequences/periods/formulations, the reference and test formulations, replication type, and whether the design supports FDA RSABE / EMA ABEL. It reads only the sequence column, so it applies to a raw or a standardized dataset alike.
Example
Taking PJ2017_3_12, the same dataset previewed at the start of these docs:
using Bioequivalence, PharmaDatasets
be_data = dataset(joinpath("bioequivalence", "RT_TR", "PJ2017_3_12"))
first(be_data, 10)| Row | id | sequence | period | AUC | Cmax |
|---|---|---|---|---|---|
| Int64 | String3 | Int64 | Float64? | Float64? | |
| 1 | 1 | RT | 1 | 58.16 | 2.589 |
| 2 | 1 | RT | 2 | 79.34 | 2.827 |
| 3 | 2 | TR | 1 | 150.12 | 5.145 |
| 4 | 2 | TR | 2 | 142.29 | 3.216 |
| 5 | 3 | RT | 1 | 69.68 | 2.48 |
| 6 | 3 | RT | 2 | 85.59 | 4.407 |
| 7 | 4 | TR | 1 | 36.95 | 2.442 |
| 8 | 4 | TR | 2 | 5.0 | 0.498 |
| 9 | 5 | RT | 1 | 121.84 | 5.319 |
| 10 | 5 | RT | 2 | missing | missing |
The preprocess_be function standardizes it to the canonical schema (AUC selected and log-transformed, formulation derived, id_within_sequence added):
pp_data = preprocess_be(be_data; endpoint = :AUC)
first(pp_data, 10)| Row | id | id_within_sequence | sequence | period | formulation | endpoint |
|---|---|---|---|---|---|---|
| Cat… | Cat… | Cat… | Cat… | Cat… | Float64 | |
| 1 | 1 | 1 | RT | 1 | R | 4.0632 |
| 2 | 1 | 1 | RT | 2 | T | 4.37374 |
| 3 | 2 | 1 | TR | 1 | T | 5.01143 |
| 4 | 2 | 1 | TR | 2 | R | 4.95787 |
| 5 | 3 | 2 | RT | 1 | R | 4.24391 |
| 6 | 3 | 2 | RT | 2 | T | 4.44957 |
| 7 | 4 | 2 | TR | 1 | T | 3.60957 |
| 8 | 4 | 2 | TR | 2 | R | 1.60944 |
| 9 | 5 | 3 | RT | 1 | R | 4.80271 |
| 10 | 6 | 3 | TR | 1 | T | 3.1999 |
The detect_design function reports the design as a named tuple; here its fields are listed one per line:
design = detect_design(be_data)
for (field, value) in pairs(design)
println(rpad(field, 22), " = ", repr(value))
enddesign = "RT|TR"
num_sequences = 2
num_periods = 2
num_formulations = 2
reference_formulation = 'R'
test_formulations = ['T']
replicated = non_replicated
supports_rsabe = false
supports_abel = false
crossover = true