Skip to content

feat: add bioequivalence inferential statistics functions#547

Open
billdenney wants to merge 6 commits into
mainfrom
bioequivalence-functions
Open

feat: add bioequivalence inferential statistics functions#547
billdenney wants to merge 6 commits into
mainfrom
bioequivalence-functions

Conversation

@billdenney

Copy link
Copy Markdown
Member

Add fitbe_models(), fitbe_table(), and fitbe_calculate() to compute average bioequivalence statistics from NCA results: per-endpoint linear mixed-effects models on log-transformed values, geometric mean ratios with 90 percent confidence intervals, Satterthwaite degrees of freedom, and intra-subject CV. The functions consume the long-format output of a PKNCAresults object directly.

The statistical engine packages (lme4, lmerTest, emmeans) are Suggests and guarded with requireNamespace(); tests skip when they are absent. Adds the v50-bioequivalence vignette, tests, and documentation. Formalizes the prototype workflow from PR 490.

Add fitbe_models(), fitbe_table(), and fitbe_calculate() to compute average
bioequivalence statistics from NCA results: per-endpoint linear mixed-effects
models on log-transformed values, geometric mean ratios with 90 percent
confidence intervals, Satterthwaite degrees of freedom, and intra-subject CV.
The functions consume the long-format output of a PKNCAresults object directly.

The statistical engine packages (lme4, lmerTest, emmeans) are Suggests and
guarded with requireNamespace(); tests skip when they are absent. Adds the
v50-bioequivalence vignette, tests, and documentation. Formalizes the
prototype workflow from PR 490.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@billdenney

Copy link
Copy Markdown
Member Author

Based on the functions @Sang-j111 build in #490

billdenney and others added 4 commits June 10, 2026 21:24
Starting-point design for extending the bioequivalence functions on this
branch (PR #547) into the full reference-scaled / regulatory assessment space
of replicateBE (EMA/HC/GCC ABEL) and PowerTOST (FDA RSABE/NTID), plus a
regulatory-comparison assessor and the nlmixr2mbbe integration contract.
Build-ignored under design/.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… layer

Add a regulatory bioequivalence decision and reference-scaling layer on top of
the average-BE (fitbe_*) foundation, covering all major frameworks:

* be_assess() - full pass/fail assessment for ABE, EMA/HC/GCC expanding limits
  (ABEL), and the FDA reference-scaled RSABE/NTID/HVNTID frameworks. Accepts a
  PKNCAresults object or a tidy long data.frame.
* be_compare() - assess one dataset under several frameworks side by side.
* be_regulator() / be_expand_limits() - internalized regulatory constants and
  scaled acceptance limits (single source of truth).
* be_design() - crossover design classification and framework feasibility.
* be_within_var() - within-subject variability (swR/swT, CVwR/CVwT, swT:swR
  ratio CI) by ANOVA (default) or a mixed model.
* print/format/summary/as.data.frame methods for the new classes.

All regulatory constants and criteria are internalized, so PKNCA does not depend
on PowerTOST or replicateBE; the implementation was validated against those
packages during development and the tests pin the resulting expected values
(within-subject variances match replicateBE's estimator and the ABEL limits
match its scaled-limit output exactly). The FDA RSABE/NTID/HVNTID criteria
implement the linearized Howe/Hyslop bound from the guidances.

Extends the v50-bioequivalence vignette with a reference-scaling section.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Unify all nine bioequivalence functions (fitbe_* and be_*) under the
capitalized @family Bioequivalence tag so they group under a single
"Bioequivalence" heading.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Comment thread R/bioequivalence.R
group_cols <- NULL
if (inherits(object, "PKNCAresults")) {
assert_PKNCAresults(object)
data <- as.data.frame(as.data.frame(object, filter_excluded = TRUE))

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please only use as.data.frame() once.

Replace every function defined inside another bioequivalence function with a
top-level helper or a base-R primitive, so no closures are created per call and
the helpers are independently testable:

* be_within_var(): arm_var_anova() -> .be_arm_var() / .be_arm_var_na()
* be_design(): the per-subject pattern lambda -> .be_subject_pattern(); the
  replication-count lambdas vectorized (tapply over a logical); the first-value
  lambda -> `[`; drop the unused per_order line
* .be_isc(): the per-subject contrast lambda -> .be_subject_ilat()
* be_compare(): tryCatch() with an inline error handler -> try() + inherits()
* summary.be_compare(): the cell-selection lambda -> `[`

No behavior change; all bioequivalence tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant