Skip to content

openpkflow.be

Bioequivalence analysis: paired 2x2 TOST, formal complete balanced TR/RT 2x2 crossover ANOVA, and research-grade replicate-design screening. FDA partial-replicate RSABE is deliberately NOT_EVALUABLE until its external-reference validation gate is complete. EMA ABEL, full-replicate RSABE, NTI decisions, and unbalanced/incomplete formal designs are not supported in the formal workflow.

Public API

Symbol Type Description
BEStudy class Entry point: __init__(df, parameter, ...), .analyze() -> BEResult, .from_nca_results()
BEResult dataclass Analysis result: gmr, gmr_lower_90ci, gmr_upper_90ci, bioequivalent, cv_intra_pct, subjects_df, .summary(), .report()
BETOSTResult dataclass Low-level TOST output from be_tost()
formal_be_anova(data, parameter, ...) function Formal complete balanced TR/RT 2x2 crossover ANOVA
FormalBEResult dataclass Formal ANOVA table, LSMeans, treatment contrast, residual CV, CI, and decision
be_tost(reference, test, ...) function Core TOST computation
replicate_be(data, value_col, ...) function Long-format replicate-design BE screening
ReplicateBEResult dataclass Replicate BE output with GMR, 90% CI, CVwR, scaled limits, and caveat
ema_scaled_limits(swr, ...) function EMA-style scaled limits from reference within-subject SD
BEStudy.to_bioeqpy_dataframe() method Export BioEqPy-ready long-format BE input
BEStudy.to_bioeqpy_csv(path) method Write BioEqPy-ready CSV input

formal_be_anova()

from openpkflow.be import formal_be_anova

result = formal_be_anova(
    data,
    parameter="AUCinf",
    subject_col="subject",
    sequence_col="sequence",
    period_col="period",
    treatment_col="treatment",
)

The formal initial release supports only complete balanced 2x2 studies:

Column Required values
subject One identifier with exactly two observations
sequence Both TR and RT, equal subject allocation
period 1, 2
treatment Must agree with sequence/period (T or R)
endpoint Finite, positive values for log analysis

The result provides sequence, subject-within-sequence, period, treatment, and residual ANOVA rows. Sequence uses subject-within-sequence as its denominator; period and treatment use residual MSE. The workflow rejects unbalanced, incomplete, duplicated, or rank-deficient input rather than silently changing the estimand.

openpkflow be anova formal_be.csv --parameter AUCinf --report formal_be.html

The independently executable R cross-check is scripts/be_anova_crossval.R; its fixture is tests/validation/data/be_anova_balanced_2x2.csv.

FDA partial-replicate RSABE gate

from openpkflow.be import fda_partial_replicate_rsabe

gate = fda_partial_replicate_rsabe(data, value_col="Cmax")
assert gate.decision == "NOT_EVALUABLE"

The initial planned design is FDA partial replicate 2x2x3 (TRR, RTR, RRT). It will remain non-decisional until pinned external fixtures validate the replicate model, reference within-subject variability, upper confidence bound, point-estimate constraint, fallback behavior, and final decision.

BEStudy

BEStudy(
    df,                      # wide-format DataFrame
    parameter="AUCinf",      # label for output
    *,
    reference_col="reference",
    test_col="test",
    subject_col="subject",
    sequence_col="sequence", # or None; silently dropped if default name absent
)

Required DataFrame columns: subject_col, reference_col, test_col.

sequence_col: - Default "sequence" — silently dropped if absent from the DataFrame. - Any other name — raises ValueError if absent. - Pass None explicitly to indicate no sequence column.

.analyze()

result = study.analyze(
    be_lower=0.80,   # FDA/EMA standard lower limit
    be_upper=1.25,   # FDA/EMA standard upper limit
    alpha=0.05,      # one-sided significance level (90% CI)
)

Returns a BEResult.

.from_nca_results()

BEStudy.from_nca_results(
    reference_results,   # NCASummaryResults
    test_results,        # NCASummaryResults
    parameter="AUCinf",  # "AUCinf", "AUClast", or "Cmax"
)

Matches subjects by ID. Raises ValueError if no common subjects are found.

.to_bioeqpy_dataframe()

from bioeqpy import analyze

table = study.to_bioeqpy_dataframe()
results = analyze(table, parameters=["AUCinf"])

Exports long-format columns expected by BioEqPy: subject, sequence, period, treatment, and the selected PK parameter. The current export supports standard TR/RT 2x2 crossover studies and requires a sequence column.

.to_bioeqpy_csv(path)

Writes the same BioEqPy-ready long-format table to CSV.

BEResult

Attribute Type Description
parameter str PK parameter label
n int Number of subjects
gmr float Geometric Mean Ratio (test/reference)
gmr_lower_90ci float Lower bound of 90% CI
gmr_upper_90ci float Upper bound of 90% CI
be_lower float Acceptance limit (lower)
be_upper float Acceptance limit (upper)
bioequivalent bool True if 90% CI within limits
cv_intra_pct float Intra-subject CV%
subjects_df DataFrame Per-subject table (subject, reference, test, ratio, log_diff)

.summary()

Returns an ASCII table with GMR, 90% CI, acceptance limits, CV%, and verdict.

.report(path, format=None)

Writes an HTML or Markdown report. Format inferred from file extension; override with format="html" or format="markdown".

replicate_be()

from openpkflow.be import replicate_be

result = replicate_be(
    data,                  # long-format DataFrame
    value_col="AUCinf",    # positive PK metric values
    subject_col="subject",
    sequence_col="sequence",
    period_col="period",
    treatment_col="treatment",
)

Required long-format columns:

Column Meaning
subject Subject identifier
sequence Randomized sequence, e.g. TRTR, RTRT, TRR, RTR, RRT
period Period number
treatment T or R
value column Positive PK parameter value such as AUCinf or Cmax

The result reports:

  • conventional GMR and 90% CI,
  • conventional 80-125% ABE decision,
  • reference within-subject SD (swr) and CVwR%,
  • EMA-style scaled limits when CVwR exceeds 30%, capped at the CVwR=50% limit,
  • FDA-style RSABE point-criterion screening, not the full 95% upper-bound decision.

Research-grade scope

replicate_be() is a transparent screening utility. It does not implement jurisdiction-specific mixed-model degrees of freedom, SAS PROC MIXED parity, FDA RSABE 95% upper confidence bound logic, or NTI decision rules. Use it to explore and QA data, then cross-check formal decisions against validated regulatory workflows.

CLI usage:

openpkflow be replicate replicate_be_partial.csv --parameter Cmax --report replicate.html --json replicate.json

be_tost()

from openpkflow.be.methods import be_tost

result = be_tost(
    reference,          # list of reference values (positive floats)
    test,               # list of test values (positive floats, same length)
    *,
    be_lower=0.80,
    be_upper=1.25,
    alpha=0.05,
)

Raises ValueError for mismatched lengths, n < 2, non-positive values, or invalid acceptance limits.

Acceptance limits reference

Product type be_lower be_upper
Standard (FDA/EMA) 0.80 1.25
NTI (FDA) 0.90 1.1111

Statistical method

Log-transformed TOST (Schuirmann 1987). Within-subject log-differences are used for GMR estimation and CI construction. Intra-subject CV is derived as sqrt(exp(s_d^2) - 1) * 100 (Chow & Liu 2008, eq. 3.3.4).

Reference: Schuirmann DJ (1987). J Pharmacokinet Biopharm 15(6):657-680. FDA guidance: Statistical Approaches to Establishing Bioequivalence (2001).