Input Data

Required columns

The input dataset (passed to pumas_be or preprocess_be) must contain one row per observation with the following columns:

ColumnMeaning
idSubject identifier.
sequenceThe dosing sequence assigned to the subject (e.g. RT, TR).
periodThe dosing period within the study.
endpointThe 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 formulation for each row from the sequence and period;
  • 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   endpoint

where 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)
10×5 DataFrame
RowidsequenceperiodAUCCmax
Int64String3Int64Float64?Float64?
11RT158.162.589
21RT279.342.827
32TR1150.125.145
42TR2142.293.216
53RT169.682.48
63RT285.594.407
74TR136.952.442
84TR25.00.498
95RT1121.845.319
105RT2missingmissing

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)
10×6 DataFrame
Rowidid_within_sequencesequenceperiodformulationendpoint
Cat…Cat…Cat…Cat…Cat…Float64
111RT1R4.0632
211RT2T4.37374
321TR1T5.01143
421TR2R4.95787
532RT1R4.24391
632RT2T4.44957
742TR1T3.60957
842TR2R1.60944
953RT1R4.80271
1063TR1T3.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))
end
design                 = "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