Bioequivalence Analysis (BE)

Bioequivalence.jl is a package for performing bioequivalence analysis.

The full API is available in the next section and provides the signatures and examples for using all the available functionality.

Quickstart

In order to use Bioequivalence.jl, add the package through

using Pkg
Pkg.add("Bioequivalence")

or using the package REPL

]add Bioequivalence

You can then load the library through

using Bioequivalence

A bioequivalence study is an instance of the type BioequivalenceStudy and can be constructed through the pumas_be function.

What is bioequivalence?

Bioequivalence is a concept in pharmacokinetics that captures the idea that various pharmaceutical products administrated in a similar manner (e.g., same molar dose of the same active ingredient, route of administration) can be expected to have, for all intents and purposes, the same effect on individuals in a defined population.

Clinical studies collect data which can be analyzed such as through noncompartmental analysis to obtain insightful descriptives about the contentration curve also known as pharmacokinetic endpoints. These endpoints relate to the rate (e.g., maximum concentration, time of peak concentration) and extent of absorption (e.g., area under the curve). Bioequivalence relies on the study design and pharmacokinetic endpoints from clinical trials or simulation models to make a determination about the expected effects of formulations.

Three major types of bioequivalence are regularly used:

  1. Average (ABE): are the mean values of the distributions of the pharmacokinetic endpoints for the reference and the test formulations similar enough? The concept is the most popular with a rise in adoption in the early 1990's by the United States and the European Union. It is considered to be the easiest criterion for a new formulation to achieve bioequivalence. It is required by most regulatories agencies for the product to be approved under a bioequivalence process.

  2. Population (PBE): are the distributions of the pharmacokinetic endpoints for the reference and the test formulations similar enough? In this case, it is not longer comparing just the expected value of the distributions but the full distribution. PBE is especially important for determining prescribability or the decision to assign a patient one of formulations as part of a treatment for the first time.

  3. Individual (IBE): are the distributions of the pharmacokinetic endpoints for the reference and the test formulations similar enough across a large proportion of the intended population? IBE is particularly relevant for switchability or the decision to substitute an ongoing regimen (change formulation) without detrimental effects to the patient.

PBE and IBE can be assessed through two different methods:

  1. constant scaling: the regulatory agency provides a value to be used in determining PBE or IBE.

  2. reference scaling: the estimated total variance of the reference formulation in determining PBE or IBE.

  3. mixed scaling: use reference scaling when the estimated total variance of the reference formulation is greater than that of the test formulation and the constant scaling otherwise.

Note

One argument for using the mixed scaling is that if the estimate of the total variance of the test formulation is greater than of the reference it bioequivalence would be very conservative.

Info

The reference scaling system is most used when working with highly variable drugs (HVD), those with intrasubject variability > 30%, and narrow therapeutic index drugs (NTI), those drugs where small differences in dose or blood concentration may lead to serious therapeutic failures and/or adverse drug reactions that are life-threatening or result in persistent or significant disability or incapacity.

Designs

There are three major categories of bioequivalence study desings.

Nonparametric for endpoints such as time of maximum concentration which typically do not have (or can easily transformed) a normal-like distribution.

Parallel designs which are typically used when crossover designs are not feasible.

Crossover (replicated and nonreplicated) designs which are the most commonly used by the industry.

Designs are fully characterized by:

  • Subjects: participants in the study (each is assigned to a sequence)
  • Formulations: the different formulations being compared (i.e., reference and additional test formulations)
  • Periods: each dosing period at which each subject is administrated a formulation based on the sequence it has been assigned to
  • Sequences: a dosing regimen which establishes what formulation is given at each period
Warning

The periods should be spaced enough such that there are no carryover effects from dosings in the previous periods.

Nonparametric analysis (i.e., x formulations, y sequences, z periods)

The nonparametric design performs a Wilcoxon signed rank test of the null hypothesis that the distribution of the reference formulation and the distribution of an alternative formulation have the same median.

When there are no tied ranks and ≤ 50 samples, or tied ranks and ≤ 15 samples, it will perform an exact signed rank test or approximate it otherwise.

Since the study design can be inferred from the data argument (i.e., based on sequences, formulations, and periods), the inferred study design approach will be automatically selected. Once can manually overwrite the method for the nonparametric option by selecting nonparametric = true in pumas_be.

Parallel design (i.e., x formulations, y periods, z sequences, e.g. R|S|T)

Perform a Welch's t-test (i.e., unequal variance two-sample t-test) of the null hypothesis that the distribution of the reference formulation and the distribution of an alternative formulation comes have equal means. The number of degrees of freedom of the test uses the Welch-Satterthwaite equation:

\[ ν_{χ'} ≈ \frac{\left(\sum_{i=1}^n k_i s_i^2\right)^2}{\sum_{i=1}^n \frac{(k_i s_i^2)^2}{ν_i}}\]

Crossover designs

Crossover designs are divided into two categories:

  1. Replicated

  2. Nonreplicated

Nonreplicated designs have subjects assigned to distinct formulations in each period.

Replicated crossover designs are those with subjects receiving the same formulation more than once. A key feature of replicated designs is that it allows to estimate within-subject variances per formulation which are a component for assessing PBE and IPE.

Common crossover designs:

NameNumber of FormulationsNumber of PeriodsNumber of SequencesExampleReplicated
2x2222RT|TRfalse
Balaam222RR|RT|TR|TTtrue
Dual232RTT|TRRtrue
Inner242RRTT|TTRRtrue
Outer242RTRT|TRTRtrue
Williams 3336RST|RTS|SRT|STR|TRS|TSRfalse
Williams 4444ADBC|BACD|CBDA|DCABfalse

Replicated designs are preferred, particularly the inner and outer designs.

If employing the dual design, it is recommended to have a larger sample size in order to achieve the same level of statistical power.

Note

In the United States, there is a minimum requirement of at least 12 evaluable subjects for any bioequivalence study.

For analyzing more than two formulations at a time, the Williams designs, a generalized latin square, is the preferred design given its statistical power.

There are two ways to analyze crossover designs:

Linear model

Performs a linear regression with the following model

\[ \ln\left(endpoint\right) = β₀ + β₁ formulation + β₂ sequence + β₃ period + β₄ id + ε\]

where βⱼ, j ∈ [1, 2, 3, 4], are vectors for features where formulation uses the dummy variable coding and sequence and period use contrast coding.

Linear mixed model

log(endpoint) ~ formulation + sequence + period + (1 | id)
Info

The linear mixed model corresponds to

proc mixed data = data method = ml;
class sequence subject period formulation;
model ln_endpoint = sequence period formulation;
random subject(sequence);

in SAS.

Tip

One can request to use the restricted maximum likelihood (REML) objective to match SAS default value through passing the reml = true argument to pumas_be.

Tip

Per the Food and Drug Administration (US regulatory agency) guidance, replicated crossover designs should employ the linear mixed model approach while nonreplicated crossover desings should employ a linear model (linear mixed models for nonreplicated crossover desings are also acceptable).

Validation

Each design has been tested using various sources including:

  • Chow, Shein-Chung, and Jen-pei Liu. 2009. Design and Analysis of Bioavailability and Bioequivalence Studies. 3rd ed. Chapman & Hall/CRC Biostatistics Series 27. Boca Raton: CRC Press. DOI: 10.1201/9781420011678.
  • Fuglsang, Anders, Helmut Schütz, and Detlew Labes. 2015. "Reference Datasets for Bioequivalence Trials in a Two-Group Parallel Design." The AAPS Journal 17 (2): 400–404. DOI: 10.1208/s12248-014-9704-6.
  • Patterson, Scott D, and Byron Jones. 2017. Bioequivalence and Statistics in Clinical Pharmacology. 2nd ed. Chapman & Hall/CRC Biostatistics Series. DOI: 10.1201/9781315374161.
  • Schütz, Helmut, Detlew Labes, and Anders Fuglsang. 2014. "Reference Datasets for 2-Treatment, 2-Sequence, 2-Period Bioequivalence Studies." The AAPS Journal 16 (6): 1292–97. DOI: 10.1208/s12248-014-9661-0.

API

Public

Bioequivalence.BioequivalenceStudyType
BioequivalenceStudy

Return a bioequivalence study.

See also: pumas_be.

Fields

  • data::DataFrame data used for the study
  • data_stats::NamedTuple
    • total::Int refers to the number of observations the data passed to the function had.
    • used_for_analysis::Int refers to the number of observations used for fitting the model (e.g., drop missing values)
    • treatment::DataFrame gives a DataFrame with the summary statistics of the statistical model's response by treatment
    • sequence::DataFrame gives a DataFrame with the summary statistics of the statistical model's response by sequence
    • period::DataFrame gives a DataFrame with the summary statistics of the statistical model's response by period
  • design::NamedTuple number of subjects in each sequence
  • model statistical models used for the analysis
  • model_stats statistics for the model
    • Wald test for joint-statistical significance of the fixed effects
  • result::DataFrame results for inference

Examples

julia> data = dataset(joinpath("bioequivalence", "RST_RTS_SRT_STR_TRS_TSR", "PJ2017_4_5"))
186×5 DataFrame
 Row │ id     sequence  period  AUC      Cmax
     │ Int64  String3   Int64   Int64?   Int64?
─────┼───────────────────────────────────────────
   1 │     1  SRT            1     7260     1633
   2 │     1  SRT            2     6463     1366
   3 │     1  SRT            3     8759     2141
   4 │     2  RTS            1     3457      776
   5 │     2  RTS            2     6556     2387
   6 │     2  RTS            3     4081     1355
   7 │     4  TSR            1     4006     1326
   8 │     4  TSR            2     4879     1028
   9 │     4  TSR            3     3817     1052
  10 │     5  STR            1     4250      945
  11 │     5  STR            2     3487     1041
  ⋮  │   ⋮       ⋮        ⋮        ⋮        ⋮
 177 │    61  RTS            3     3779     1144
 178 │    62  SRT            1     5787     1461
 179 │    62  SRT            2     7069     1995
 180 │    62  SRT            3     6530     1236
 181 │    63  TRS            1     2204      495
 182 │    63  TRS            2     2927      770
 183 │    63  TRS            3  missing  missing
 184 │    67  RST            1     4045     1025
 185 │    67  RST            2     7865     2668
 186 │    67  RST            3  missing  missing
                                 165 rows omitted

julia> output = pumas_be(data, endpoint = :Cmax)
Design: RST|RTS|SRT|STR|TRS|TSR

Sequences: RST|RTS|SRT|STR|TRS|TSR (6)
Periods: 1:3 (3)
Subjects per Sequence: (RST = 9, RTS = 11, SRT = 11, STR = 10, TRS = 11, TSR = 10)

Average Bioequivalence
─────────────────────────────────────────────────────────────────────────────
              δ         SE      lnLB      lnUB     GMR      LB      UB     CV
─────────────────────────────────────────────────────────────────────────────
S - R  0.466225  0.0525563  0.379094  0.553357  159.4   146.1   173.91  29.69
T - R  0.261828  0.0525354  0.174731  0.348925  129.93  119.09  141.75  29.69
─────────────────────────────────────────────────────────────────────────────

julia> output.data_stats.treatment
3×10 DataFrame
 Row │ formulation  exp_mean  mean     std       min      q25      median   q75      max      n
     │ Cat…         Float64   Float64  Float64   Float64  Float64  Float64  Float64  Float64  Int64
─────┼──────────────────────────────────────────────────────────────────────────────────────────────
   1 │ R             837.478  6.7304   0.466938  5.89715  6.3257   6.66568  7.09589  7.71334     62
   2 │ S            1339.96   7.20039  0.419893  6.09131  6.92952  7.20117  7.5251   8.02027     61
   3 │ T            1078.08   6.98294  0.473224  5.75574  6.62539  7.00851  7.29641  7.90286     61

julia> output.data_stats.sequence
6×10 DataFrame
 Row │ sequence  exp_mean  mean     std       min      q25      median   q75      max      n
     │ Cat…      Float64   Float64  Float64   Float64  Float64  Float64  Float64  Float64  Int64
─────┼───────────────────────────────────────────────────────────────────────────────────────────
   1 │ RST        997.282  6.90503  0.493722  5.90263  6.6385   6.92755  7.17642  7.88908     26
   2 │ RTS       1084.03   6.98844  0.499403  6.09131  6.4677   7.05618  7.32449  7.77779     33
   3 │ SRT       1187.43   7.07954  0.482725  5.89715  6.63068  7.11964  7.45124  7.90286     33
   4 │ STR        869.299  6.76769  0.404857  6.04501  6.4758   6.8663   6.95607  7.71913     30
   5 │ TRS       1180.5    7.07369  0.466733  6.20456  6.71254  7.11698  7.39368  7.96797     32
   6 │ TSR       1071.51   6.97682  0.556906  5.75574  6.69448  7.02452  7.28049  8.02027     30

julia> output.data_stats.period
3×10 DataFrame
 Row │ period  exp_mean  mean     std       min      q25      median   q75      max      n
     │ Cat…    Float64   Float64  Float64   Float64  Float64  Float64  Float64  Float64  Int64
─────┼─────────────────────────────────────────────────────────────────────────────────────────
   1 │ 1        1009.89  6.9176   0.498246  5.75574  6.46653  6.97018  7.28186  7.72356     62
   2 │ 2        1108.56  7.01082  0.460876  5.89715  6.63035  7.06641  7.31235  8.02027     62
   3 │ 3        1076.82  6.98176  0.516518  6.03787  6.63167  6.99805  7.30986  7.96797     60

julia> output.model
StatsModels.TableRegressionModel{GLM.LinearModel{GLM.LmResp{Vector{Float64}}, GLM.DensePredChol{Float64, LinearAlgebra.CholeskyPivoted{Float64, Matrix{Float64}}}}, Matrix{Float64}}

endpoint ~ 1 + formulation + sequence + period + id

Coefficients:
──────────────────────────────────────────────────────────────────────────────────────
                      Coef.   Std. Error       t  Pr(>|t|)     Lower 95%     Upper 95%
──────────────────────────────────────────────────────────────────────────────────────
(Intercept)      6.73182       0.0770531   87.37    <1e-99    6.57923       6.8844
formulation: S   0.466225      0.0525563    8.87    <1e-14    0.362149      0.570301
formulation: T   0.261828      0.0525354    4.98    <1e-05    0.157794      0.365862
sequence: RTS   -0.267159      0.154501    -1.73    0.0864   -0.573112      0.0387952
sequence: SRT    0.454448      0.154501     2.94    0.0039    0.148494      0.760401
sequence: STR   -0.537532      0.154501    -3.48    0.0007   -0.843486     -0.231579
sequence: TRS   -0.43362       0.183196    -2.37    0.0196   -0.796398     -0.0708417
sequence: TSR    0.27436       0.154501     1.78    0.0783   -0.0315937     0.580314
period: 2        0.0504953     0.0302861    1.67    0.0981   -0.00947942    0.11047
period: 3        0.00727282    0.0306504    0.24    0.8128   -0.0534233     0.067969
id: 2            0.507159      0.237319     2.14    0.0347    0.0372028     0.977114
id: 4           -0.220947      0.237319    -0.93    0.3537   -0.690903      0.249009
id: 5            0.385905      0.237319     1.63    0.1066   -0.0840506     0.855861
id: 6            0.53118       0.266187     2.00    0.0483    0.00405669    1.0583
id: 7           -0.169298      0.237319    -0.71    0.4770   -0.639254      0.300658
id: 8           -0.850534      0.237319    -3.58    0.0005   -1.32049      -0.380578
id: 9           -0.292758      0.237319    -1.23    0.2198   -0.762714      0.177198
id: 10           0.402387      0.237319     1.70    0.0926   -0.0675684     0.872343
id: 11           0.612382      0.237319     2.58    0.0111    0.142426      1.08234
id: 12           0.825139      0.266187     3.10    0.0024    0.298016      1.35226
id: 13          -0.884971      0.237319    -3.73    0.0003   -1.35493      -0.415015
id: 14           0.0108808     0.237319     0.05    0.9635   -0.459075      0.480837
id: 15          -0.85675       0.237319    -3.61    0.0005   -1.32671      -0.386794
id: 16           0.336305      0.237319     1.42    0.1591   -0.133651      0.806261
id: 17           0.957573      0.266187     3.60    0.0005    0.43045       1.4847
id: 18           0.501641      0.237319     2.11    0.0366    0.0316852     0.971597
id: 19           0.44782       0.266187     1.68    0.0951   -0.0793033     0.974943
id: 20           1.01442       0.237319     4.27    <1e-04    0.544469      1.48438
id: 21          -1.07752       0.237319    -4.54    <1e-04   -1.54748      -0.607566
id: 22           0.0161758     0.237319     0.07    0.9458   -0.45378       0.486132
id: 23          -0.968346      0.237319    -4.08    <1e-04   -1.4383       -0.49839
id: 24          -0.620657      0.237319    -2.62    0.0101   -1.09061      -0.150702
id: 25           0.241019      0.237319     1.02    0.3119   -0.228937      0.710974
id: 26          -0.58826       0.237319    -2.48    0.0146   -1.05822      -0.118304
id: 27          -0.115719      0.237319    -0.49    0.6267   -0.585675      0.354237
id: 28          -0.781828      0.237319    -3.29    0.0013   -1.25178      -0.311872
id: 29           0.798856      0.266187     3.00    0.0033    0.271733      1.32598
id: 30          -0.0276964     0.237319    -0.12    0.9073   -0.497652      0.442259
id: 31           0.296141      0.237319     1.25    0.2146   -0.173815      0.766096
id: 32           0.479757      0.266187     1.80    0.0740   -0.0473659     1.00688
id: 33          -0.465854      0.237319    -1.96    0.0520   -0.93581       0.00410209
id: 34           0.174728      0.237319     0.74    0.4630   -0.295228      0.644684
id: 35          -0.500386      0.237319    -2.11    0.0371   -0.970342     -0.0304301
id: 36           0.332375      0.237319     1.40    0.1640   -0.13758       0.802331
id: 37           0.241332      0.237319     1.02    0.3113   -0.228624      0.711288
id: 39          -1.20503       0.237319    -5.08    <1e-05   -1.67498      -0.735073
id: 40          -0.451024      0.237319    -1.90    0.0598   -0.92098       0.0189318
id: 41           0.0824414     0.237319     0.35    0.7289   -0.387514      0.552397
id: 42           0.582803      0.266187     2.19    0.0305    0.0556799     1.10993
id: 43           0.121696      0.237319     0.51    0.6091   -0.348259      0.591652
id: 44          -0.896732      0.237319    -3.78    0.0002   -1.36669      -0.426776
id: 45          -0.66548       0.237319    -2.80    0.0059   -1.13544      -0.195524
id: 46           0.0234445     0.266187     0.09    0.9300   -0.503678      0.550567
id: 47           0.708353      0.237319     2.98    0.0035    0.238397      1.17831
id: 48          -0.0258779     0.237319    -0.11    0.9134   -0.495834      0.444078
id: 49           0.933102      0.266187     3.51    0.0006    0.405979      1.46022
id: 50          -0.56925       0.237319    -2.40    0.0180   -1.03921      -0.0992941
id: 51          -0.176366      0.237319    -0.74    0.4589   -0.646322      0.29359
id: 52           0.0         NaN          NaN       NaN     NaN           NaN
id: 53           0.227455      0.237319     0.96    0.3398   -0.242501      0.697411
id: 54           0.114108      0.237319     0.48    0.6315   -0.355848      0.584064
id: 55           0.90436       0.237319     3.81    0.0002    0.434404      1.37432
id: 56           0.0         NaN          NaN       NaN     NaN           NaN
id: 57          -0.402746      0.237319    -1.70    0.0923   -0.872702      0.06721
id: 58           0.0         NaN          NaN       NaN     NaN           NaN
id: 59           0.543237      0.237319     2.29    0.0239    0.0732815     1.01319
id: 60           0.180587      0.266187     0.68    0.4988   -0.346536      0.70771
id: 61           0.0         NaN          NaN       NaN     NaN           NaN
id: 62          -0.0939777     0.237319    -0.40    0.6928   -0.563934      0.375978
id: 63           0.0         NaN          NaN       NaN     NaN           NaN
id: 67          -0.0600292     0.266231    -0.23    0.8220   -0.587238      0.46718
──────────────────────────────────────────────────────────────────────────────────────

julia> output.model_stats.Wald
────────────────────────────────────────────────────────
                 Wald              Distribution  p-value
────────────────────────────────────────────────────────
formulation  39.5883   FDist(ν1=2.0, ν2=118.0)    <1e-13
sequence      7.25755  FDist(ν1=5.0, ν2=118.0)    <1e-05
period        2.17662  FDist(ν1=2.0, ν2=118.0)    0.1180
subj          5.2503   FDist(ν1=56.0, ν2=118.0)   <1e-13
────────────────────────────────────────────────────────

julia> output.model_stats.lsmeans
──────────────────────────────────────────────────────────────────────────────
   exp_Mean     Mean  Standard Deviation  t-statistic    Distribution  p-value
──────────────────────────────────────────────────────────────────────────────
R   838.669  6.73182           0.0770531      87.366   TDist(ν=118.0)   <1e-99
S  1336.81   7.19804           0.0785901      91.5896  TDist(ν=118.0)   <1e-99
T  1089.69   6.99364           0.0771388      90.6631  TDist(ν=118.0)   <1e-99
──────────────────────────────────────────────────────────────────────────────
Bioequivalence.generate_designMethod
generate_design(
    sequences::AbstractVector{<:AbstractString},
    amt::Union{Number,AbstractVector{<:Number}},
    subjects_per_sequence::Union{<:Integer,AbstractVector{<:Integer}},
    ) -> DataFrame

Returns a DataFrame with id, sequence, period, amt, evid, cmt, and time. It can be used to quickly set up data for Pumas, NCA, and Bioequivalence. In order to add covariates, use innerjoin to join the result of this function with another DataFrame with covariates.

Examples

julia> skeleton = generate_design(["RT", "TR"], [0, 50], 10)
40×8 DataFrame
 Row │ id     sequence  period  formulation  amt    time   evid   cmt
     │ Int64  Cat…      Int64   Char         Int64  Int64  Int64  Int64
─────┼──────────────────────────────────────────────────────────────────
   1 │     1  RT             1  R                0      0      4      1
   2 │     1  RT             2  T               50      0      4      1
   3 │     2  RT             1  R                0      0      4      1
   4 │     2  RT             2  T               50      0      4      1
   5 │     3  RT             1  R                0      0      4      1
   6 │     3  RT             2  T               50      0      4      1
   7 │     4  RT             1  R                0      0      4      1
   8 │     4  RT             2  T               50      0      4      1
   9 │     5  RT             1  R                0      0      4      1
  10 │     5  RT             2  T               50      0      4      1
  11 │     6  RT             1  R                0      0      4      1
  ⋮  │   ⋮       ⋮        ⋮          ⋮         ⋮      ⋮      ⋮      ⋮
  31 │    16  TR             1  T               50      0      4      1
  32 │    16  TR             2  R                0      0      4      1
  33 │    17  TR             1  T               50      0      4      1
  34 │    17  TR             2  R                0      0      4      1
  35 │    18  TR             1  T               50      0      4      1
  36 │    18  TR             2  R                0      0      4      1
  37 │    19  TR             1  T               50      0      4      1
  38 │    19  TR             2  R                0      0      4      1
  39 │    20  TR             1  T               50      0      4      1
  40 │    20  TR             2  R                0      0      4      1
                                                         19 rows omitted

julia> skeleton = generate_design(["RTRT", "TRTR"], [50, 75], [12, 10])
88×8 DataFrame
 Row │ id     sequence  period  formulation  amt    time   evid   cmt
     │ Int64  Cat…      Int64   Char         Int64  Int64  Int64  Int64
─────┼──────────────────────────────────────────────────────────────────
   1 │     1  RTRT           1  R               50      0      4      1
   2 │     1  RTRT           2  T               75      0      4      1
   3 │     1  RTRT           3  R               50      0      4      1
   4 │     1  RTRT           4  T               75      0      4      1
   5 │     2  RTRT           1  R               50      0      4      1
   6 │     2  RTRT           2  T               75      0      4      1
   7 │     2  RTRT           3  R               50      0      4      1
   8 │     2  RTRT           4  T               75      0      4      1
   9 │     3  RTRT           1  R               50      0      4      1
  10 │     3  RTRT           2  T               75      0      4      1
  11 │     3  RTRT           3  R               50      0      4      1
  ⋮  │   ⋮       ⋮        ⋮          ⋮         ⋮      ⋮      ⋮      ⋮
  79 │    20  TRTR           3  T               75      0      4      1
  80 │    20  TRTR           4  R               50      0      4      1
  81 │    21  TRTR           1  T               75      0      4      1
  82 │    21  TRTR           2  R               50      0      4      1
  83 │    21  TRTR           3  T               75      0      4      1
  84 │    21  TRTR           4  R               50      0      4      1
  85 │    22  TRTR           1  T               75      0      4      1
  86 │    22  TRTR           2  R               50      0      4      1
  87 │    22  TRTR           3  T               75      0      4      1
  88 │    22  TRTR           4  R               50      0      4      1
                                                         67 rows omitted
Bioequivalence.pumas_beMethod
pumas_be(
    data::AbstractDataFrame;
    endpoint::Union{AbstractString,Symbol,Integer} = "AUC",
    logtransformed::Bool = false,
    reference_scale::Real = (log(1.25) / 0.25)^2,
    cv_max::Real = Inf,
    id::Union{AbstractString,Symbol,Integer} = "id",
    sequence::Union{AbstractString,Symbol,Integer} = "sequence",
    period::Union{AbstractString,Symbol,Integer} = "period",
    nonparametric::Bool = occursin(r"(?i)tmax", string(endpoint)),
    reml::Bool = true
    ) -> BioequivalenceStudy

BioequivalenceStudy constructor.

See also: BioequivalenceStudy.

Arguments

  • data: must have id, sequence, period, and an endpoint.
  • endpoint: which variable is the endpoint?
  • logtransformed: has the endpoint been log transformed?
  • id: which variable is the subject identifier?
  • sequence: which variable is the sequence?
  • period: which variable is the period?
  • nonparametric: whether to use a nonparametric (default if endpoint includes tmax ignoring case) or parametric model.
  • reference_scale: 𝜃 for reference scale (FDA ≈ 0.797, FDA/NTI ≈ 1.11, EMA = 0.76)
  • cv_max: maximum within subject variability for reference scaling (FDA = Inf, FDA/NTI = 21.42, EMA = 50)
  • reml: whether the linear mixed model should use restricted maximum likelihood or maximum likelihood.

Current designs include: nonparametric, parallel, and various crossover designs

DescriptionDesignTreatmentsPeriodsSequencesWays
RTTR2x2222
RRRTTRTTBalaam2
RTRTRTFully replicate 3-period, 2-sequence 2-way232
RTRTRRPartial reference-replicate 3-way design232
RTTTRRFully replicate 3-period, 2-sequence 4-way232
RRTRTRTRRPartial reference-replicate 3-way23
RTRTTRTRFully replicate 4-period, 2-sequence, 2-way242
RRTTTTRRFully replicate 4-period, 2-sequence, 4-way A242
RTTRTRRTFully replicate 4-period, 2-sequence, 4-way B242
RRTTRTTRTRRTTTRRFully replicate 4-period, 4-sequence, 4-way A2
RTRTRTTRTRRTTRTRFully replicate 4-period, 4-sequence, 4-way B2
RRTTParallel2>12
RSTRTSSRTSTRTRSTSR
ADBCBACDCBDADCABWiliams 44

Examples

julia> data = dataset(joinpath("bioequivalence", "RT_TR", "SLF2014_1"))
36×4 DataFrame
 Row │ id     sequence  period  AUC
     │ Int64  String3   Int64   Float64
─────┼──────────────────────────────────
   1 │     1  RT             1   181.09
   2 │     1  RT             2   210.14
   3 │     2  RT             1   114.48
   4 │     2  RT             2    98.72
   5 │     3  TR             1   225.95
   6 │     3  TR             2   241.09
   7 │     4  RT             1   176.91
   8 │     4  RT             2   186.65
   9 │     5  TR             1   147.01
  10 │     5  TR             2   139.56
  11 │     6  TR             1    97.53
  ⋮  │   ⋮       ⋮        ⋮        ⋮
  27 │    14  TR             1   179.96
  28 │    14  TR             2   181.09
  29 │    15  TR             1   173.86
  30 │    15  TR             2   206.66
  31 │    16  RT             1   144.0
  32 │    16  RT             2   143.25
  33 │    17  RT             1   185.1
  34 │    17  RT             2   192.22
  35 │    18  TR             1   117.99
  36 │    18  TR             2   125.5
                         15 rows omitted

julia> output = pumas_be(data)
Design: RT|TR

Sequences: RT|TR (2)
Periods: 1:2 (2)
Subjects per Sequence: (RT = 9, TR = 9)

Average Bioequivalence
───────────────────────────────────────────────────────────────────────────────
                δ        SE        lnLB         lnUB    GMR     LB     UB    CV
───────────────────────────────────────────────────────────────────────────────
T - R  -0.0503868  0.026658  -0.0969286  -0.00384499  95.09  90.76  99.62  8.01
───────────────────────────────────────────────────────────────────────────────

julia> data = dataset(joinpath("bioequivalence", "RTT_TRR", "PJ2017_4_1"))
285×5 DataFrame
 Row │ id     sequence  period  AUC       Cmax
     │ Int64  String3   Int64   Float64?  Float64?
─────┼─────────────────────────────────────────────
   1 │   101  TRR            1     12.26     0.511
   2 │   101  TRR            2     16.19     0.688
   3 │   101  TRR            3     11.34     0.533
   4 │   102  TRR            1    397.98    13.27
   5 │   102  TRR            2    267.63     7.933
   6 │   102  TRR            3    487.55    12.952
   7 │   103  TRR            1    243.81    16.771
   8 │   103  TRR            2    141.7      6.926
   9 │   103  TRR            3    198.44     9.257
  10 │   109  TRR            1    182.52     8.816
  11 │   109  TRR            2    112.34     4.921
  ⋮  │   ⋮       ⋮        ⋮        ⋮         ⋮
 276 │   186  RTT            3     87.63     4.87
 277 │   190  RTT            1     82.78     3.88
 278 │   190  RTT            2    164.56     7.37
 279 │   190  RTT            3    213.98     7.01
 280 │   191  RTT            1     98.86     4.59
 281 │   191  RTT            2     99.02     2.96
 282 │   191  RTT            3     75.48     2.38
 283 │   194  RTT            1     21.29     1.51
 284 │   194  RTT            2     46.3      2.74
 285 │   194  RTT            3     15.41     1.41
                                   264 rows omitted

julia> output = pumas_be(data)
Design: RTT|TRR

Sequences: RTT|TRR (2)
Periods: 1:3 (3)
Subjects per Sequence: (RTT = 46, TRR = 48)
Reference scaled using 𝜃 = 0.797

Average Bioequivalence
────────────────────────────────────────────────────────────────────────────────────────────────────────
              δ        SE     lnLB    lnUB    GMR     LB      UB    CVᵣ    CVₜ  σ_ratio     σ⁺        cb
────────────────────────────────────────────────────────────────────────────────────────────────────────
T - R  -0.02654  0.069061  -0.1408  0.0878  97.38  86.86  109.17  42.75  69.65    1.535  1.983  -0.09243
────────────────────────────────────────────────────────────────────────────────────────────────────────

Private

Bioequivalence.abeFunction
abe(::Type{T},
    data::AbstractDataFrame,
    test::AbstractChar,
    reference::AbstractChar) where T <: Union{ApproximateSignedRankTest,
                                              UnequalVarianceTTest}::DataFrame

Average Bioequivalence Bioequivalence Modeling

Bioequivalence.compute_cbFunction
compute_cb(
    data::AbstractDataFrame,
    σw₀::Real,
    𝛥::Real,
    σwᵣ::Real = σw(data).σwᵣ,
    ) -> Float64

Return the 95% upper confidence bound for (T̄ₜ - Ȳᵣ)² - 𝜃 * σwᵣ².

Bioequivalence.detect_designMethod
detect_design(sequences::AbstractVector)::Tuple{Symbol,Vector{Char}}

The study design: Parallel, Crossover, Balaam, Higher or throws an error. Sequences based on the implied treatment and order.

Bioequivalence.lsmeansFunction
lsmeans(
    model::Union{LinearMixedModel, TableRegressionModel{<:LinearModel}},
    treatments::AbstractVector{<:Char}
    ) -> CoefTable

Return the least squares geometric means for the treatments.

Bioequivalence.walds_testsMethod
walds_tests(model::LinearMixedModel) -> CoefTable

Wald test provdes a statistical test assessing whether the model parameters are jointly statistically significant from zero.

Bioequivalence.walds_testsMethod
walds_tests(model::TableRegressionModel{<:LinearModel})::CoefTable

Wald test provides a statistical test assessing whether the model parameters are jointly statistically significant from zero.

Bioequivalence.σwMethod
σw(data::AbstractDataFrame)

Return the within-subject variability for the reference, test, ratio, and upper bound.