Workflow
An analysis can be run in one call or as a sequence of lower-level steps. Both produce the same inference and the same result summary table; they differ only in how much of the pipeline is exposed.
Whether carrying out the one-call workflow or the lower-level workflow, each analysis concerns a single pharmacokinetic endpoint, selected with the endpoint argument. A study that requires bioequivalence to be shown for both Cmax and AUC should therefore be run as two separate analyses on the same dataset (one per endpoint), each returning its own result.
One-call workflow (the typical usage)
The pumas_be function runs preprocessing, inference, assessment, and result-table construction in a single call, returning a BioequivalenceEndpointOutput. This is the recommended entry point for most analyses.
using Bioequivalence, PharmaDatasets
be_data = dataset(joinpath("bioequivalence", "RTRT_TRTR", "PJ2017_4_4"))
out = pumas_be(be_data, FDA_HighlyVariable; endpoint = :AUC)| Observation Counts | ||||
| Sequence ╲ Period | 1 | 2 | 3 | 4 |
| RTRT | 27 | 27 | 26 | 26 |
| TRTR | 27 | 27 | 26 | 26 |
| Paradigm: Replicated crossover (supports ABE, ABEL, and RSABE) | ||||
| Model: Mixed model with REML (unequal variance) | ||||
| Criteria: FDA RSABE for HV | ||||
| Endpoint: AUC | ||||
| Formulations: Reference(R), Test(T) | ||||
| Results(AUC) | Assessment | Criteria | ||
| R | Geometric Marginal Mean | 339.9 | ||
| Geometric Naive Mean | 340.8 | |||
| T | Geometric Marginal Mean | 375.9 | ||
| Geometric Naive Mean | 376.7 | |||
| Geometric Mean T/R Ratio (%) | 110.6 | Pass | GMR ∈ [80, 125] | |
| Degrees of Freedom | 140.7 | |||
| 90% Confidence Interval (%) | [102.9, 118.9] | |||
| Variability | CVᵣ (%) | σ̂ᵣ | 35.4 | 0.3436 | OK for RS | CVᵣ ≥ Minimal CVᵣ |
| CVₜ (%) | σ̂ₜ | 27.87 | 0.2735 | |||
| Variability Ratio (%) | 79.6 | |||
| ANOVA | Formulation (p-value) | 0.02205 | ||
| Sequence (p-value) | 0.9038 | |||
| Period (p-value) | 0.1555 | |||
| Reference Scaling Params | Reference Scaling Constant (θ | k = √θ) | 0.7967 | 0.8926 | ||
| Minimal CVᵣ for Reference Scaling (%) | 30.0 | 0.294 | |||
| Reference Scaling Analysis | Geometric Mean T/R Ratio (%) | 111 | ||
| Standard Error (Log Scale) | 0.0438 | |||
| 90% Confidence Interval (%) | [103.2, 119.5] | |||
| Degrees of Freedom | 50 | |||
| Howe's Approx RSABE Stat (95%) | -0.05172 | Pass | ≤ 0 | |
The first positional argument is the dataset; the optional second positional argument is the regulatory criterion (default StandardBioequivalenceCriterion). See the pumas_be docstring for the full argument list.
Displaying out (above) renders the result summary table, the typical output. The same result can also be introspected programmatically through out.result_table (an OrderedDict) or out.result (a DataFrame); see Output.
The out.result_table field gives a symbolic description of the summary table:
out.result_tableOrderedCollections.OrderedDict{Symbol, Any} with 35 entries:
:PARADIGM => replicated_crossover_supports_abe_abel_and_rsabe
:INFERENCE_MODEL_TYPE => unequal_variance_mixed_model_reml
:CRITERIA => FDA_HighlyVariable
:SECTION_REFERENCE => "R"
:MARG_GM_REF => 339.947
:NAIVE_GM_REF => 340.767
:SECTION_FORMULATION_1 => "T"
:MARG_GM_TRT => 375.865
:NAIVE_GM_TRT => 376.685
:GMR => 1.106
:DF => 140.7
:CI => (1.029, 1.189)
:POINT_ESTIMATE_PASS => true
:POINT_ESTIMATE_RULE => "GMR ∈ [80, 125]"
:SECTION_MODEL_OUT_1 => "Variability"
:CVᵣ => 0.354
:CVₜ => 0.2787
:SIGMA_RATIO => 0.796
:SECTION_MODEL_OUT_2 => "ANOVA"
:ANOVA_P_VALUE_FORMULATION => 0.02205
:ANOVA_P_VALUE_SEQUENCE => 0.90383
:ANOVA_P_VALUE_PERIOD => 0.15552
:SECTION_MODEL_OUT_3 => "Reference Scaling Params"
:REFERENCE_SCALE => 0.796689
:CV_MIN_VALUE => 0.3
⋮ => ⋮You can then programmatically access specific fields, for example out.result_table[:GMR]:
out.result_table[:GMR]1.106Another representation of the output is in a DataFrame via the result field, in particular holding the raw per-comparison inference (the estimate δ, its standard error, the confidence bounds, the GMR, and the variability estimates):
out.result| Row | Parameter | δ | SE | lnLB | lnUB | trt_margGM | ref_margGM | trt_naiveGM | ref_naiveGM | GMR | LB | UB | CVᵣ | CVₜ | σ_ratio | σ⁺ | cb | dof |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| String | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | Float64 | |
| 1 | T - R | 0.1004 | 0.04338 | 0.0286 | 0.1723 | 375.865 | 339.947 | 376.685 | 340.767 | 1.106 | 1.029 | 1.189 | 0.354 | 0.2787 | 0.796 | 1.007 | -0.05172 | 140.7 |
Exponentiating the log-scale estimate δ recovers the GMR shown above:
round(exp(out.result[1, :δ]), digits = 3)1.106Lower-level workflow
While using pumas_be is typically fine for most cases, the same analysis can be dissected and run as five steps:
preprocess_be → run_be → assess_be → set_result_table! → result_summary_table
| Step | Function | Role |
|---|---|---|
| 1 | preprocess_be | Standardize the dataset (see Input Data). |
| 2 | run_be | Fit the model and compute the inference. |
| 3 | assess_be | Evaluate the inference against a criterion. |
| 4 | set_result_table! | Render the assessment into the output's result table. |
| 5 | result_summary_table | Pretty-print the populated table. |
While these five steps are more lengthy, in cases where intermediate results are needed they can be of use.
More details on each of the steps
1. Preprocessing.
pp_data = preprocess_be(be_data; endpoint = :AUC)The preprocess_be function takes a raw dataset (with id, sequence, period, and an endpoint column) and returns a standardized DataFrame with columns id, id_within_sequence, sequence, period, formulation, and endpoint, where the endpoint is log-transformed and missing observations are dropped (see Input Data).
2. Inference.
out = run_be(pp_data; reference_scale = reference_scale_default(FDA_HighlyVariable))A dataset in the form returned by preprocess_be is the required input to run_be, which fits the model and returns a BioequivalenceEndpointOutput carrying the inference, but not yet any assessment.
The run_be function is criterion-agnostic: when calling through pumas_be each criterion sets the inference options it needs implicitly, whereas run_be has fixed defaults of its own (an omitted reference_scale is treated as the FDA highly-variable constant θ ≈ 0.7967, which together with the model defaults happens to match FDA_HighlyVariable), so to reproduce any other criterion you must pass its options explicitly. Pass the criterion's reference-scaling constant via reference_scale_default. The EMA criteria additionally default to the subjects-fixed linear model, so EMA_HighlyVariable also requires custom_model = LinearModel, wsv_model = additive_sequence_wsv_model, and homogeneity = true (see Models):
out = run_be(
pp_data;
reference_scale = reference_scale_default(EMA_HighlyVariable),
custom_model = LinearModel,
wsv_model = additive_sequence_wsv_model,
homogeneity = true,
)The run_be function also accepts endpoint_name to label the endpoint in the output, which pumas_be sets from endpoint.
3. Assessment.
decision = assess_be(FDA_HighlyVariable, out)The assess_be function takes a regulatory criterion and the run_be output and returns a BioequivalenceEndpointDecision carrying the per-comparison assessments. It does not mutate out (see Output).
4. Rendering the result table.
set_result_table!(decision)The set_result_table! function renders the decision into out.result_table (a symbolic OrderedDict description of the result summary table), mutating the output in place.
5. The summary table.
result_summary_table(out)The result_summary_table function produces a table object that displays as plain text in the terminal and as a formatted table in rich environments such as these docs.
Worked example: the five steps reproduce the one-call result
Running the five steps on the dataset from the one-call workflow above yields the same analysis as pumas_be(be_data, FDA_HighlyVariable; endpoint = :AUC):
# Step 1
pp_data = preprocess_be(be_data; endpoint = :AUC)
# Step 2
out_steps = run_be(
pp_data;
reference_scale = reference_scale_default(FDA_HighlyVariable),
endpoint_name = "AUC",
)
# Step 3
decision = assess_be(FDA_HighlyVariable, out_steps)
# Step 4
set_result_table!(decision)
# Step 5
result_summary_table(out_steps)| Paradigm: Replicated crossover (supports ABE, ABEL, and RSABE) | ||||
| Model: Mixed model with REML (unequal variance) | ||||
| Criteria: FDA RSABE for HV | ||||
| Endpoint: AUC | ||||
| Formulations: Reference(R), Test(T) | ||||
| Results(AUC) | Assessment | Criteria | ||
| R | Geometric Marginal Mean | 339.9 | ||
| Geometric Naive Mean | 340.8 | |||
| T | Geometric Marginal Mean | 375.9 | ||
| Geometric Naive Mean | 376.7 | |||
| Geometric Mean T/R Ratio (%) | 110.6 | Pass | GMR ∈ [80, 125] | |
| Degrees of Freedom | 140.7 | |||
| 90% Confidence Interval (%) | [102.9, 118.9] | |||
| Variability | CVᵣ (%) | σ̂ᵣ | 35.4 | 0.3436 | OK for RS | CVᵣ ≥ Minimal CVᵣ |
| CVₜ (%) | σ̂ₜ | 27.87 | 0.2735 | |||
| Variability Ratio (%) | 79.6 | |||
| ANOVA | Formulation (p-value) | 0.02205 | ||
| Sequence (p-value) | 0.9038 | |||
| Period (p-value) | 0.1555 | |||
| Reference Scaling Params | Reference Scaling Constant (θ | k = √θ) | 0.7967 | 0.8926 | ||
| Minimal CVᵣ for Reference Scaling (%) | 30.0 | 0.294 | |||
| Reference Scaling Analysis | Geometric Mean T/R Ratio (%) | 111 | ||
| Standard Error (Log Scale) | 0.0438 | |||
| 90% Confidence Interval (%) | [103.2, 119.5] | |||
| Degrees of Freedom | 50 | |||
| Howe's Approx RSABE Stat (95%) | -0.05172 | Pass | ≤ 0 | |
This is the same table shown for out at the top of the page, which can be checked directly:
out_steps.result_table == out.result_tabletrueTwo details are worth noting. The endpoint_name argument is passed explicitly because run_be does not see the endpoint argument; without it the Endpoint row of the summary would not be labelled AUC. And reference_scale is passed explicitly for clarity, although an omitted reference_scale already defaults to the FDA highly-variable constant (see run_be); for FDA_HighlyVariable this is the only inference option the criterion sets, whereas EMA_HighlyVariable also requires the model options shown above.