
Statistical Methods in bbssr
Gosuke Homma
2026-10-28
Source:vignettes/bbssr-statistical-methods.Rmd
bbssr-statistical-methods.RmdNotation
Let denote the number of responders in group , so that and independently. Realized counts are written and , and is the total number of responders. The null hypothesis is , and the common value under the null is denoted and treated as a nuisance parameter.
Every test in the package is defined through its rejection region, a subset of the grid of possible outcomes. Once that region is available, the power at any pair of response probabilities follows from a single sum,
The type I error rate is the same sum evaluated at
,
maximized over
.
The tests of non-inferiority with a margin, Blackwelder and
Farrington-Manning, have the null hypothesis
instead, or
with a margin on the scale of the risk ratio, and the
bbssr-non-inferiority vignette describes them. The rest of
this vignette concerns the hypothesis of equal response
probabilities.
Conditional tests
Conditioning on removes the nuisance parameter. Under the null the count then follows a hypergeometric distribution, and the one-sided Fisher p-value is
The mid-p variant replaces the contribution of the observed table by half of it, giving . This is no longer a valid p-value in the strict sense, so the mid-p test can exceed the nominal level, but it removes much of the conservatism that conditioning introduces.
Two-sided conventions
A two-sided version of a discrete conditional test is not unique.
Three conventions are available through the tsmethod
argument.
The minlike convention sums the null probabilities of
all tables that are no more likely than the observed one,
This is the convention of
stats::fisher.test.
The central convention doubles the smaller of the two
one-sided tail probabilities and truncates at one,
The blaker convention orders the tables by the smaller
of their two one-sided tail probabilities and sums the null
probabilities of all tables that are at least as extreme as the observed
one in this ordering,
This is formula (2) of Mehrotra, Chan
and Berger (2003), and the convention 'blaker' of
exact2x2::exact2x2(). The tables of the other tail that it
adds form a tail of their own, whose probability is no larger than the
tail probability of the observed table. The p-value is therefore at most
twice that tail probability and never exceeds the central one. When the
two groups are of equal size the conditional distribution is symmetric,
and the three conventions give the same p-value.
Fay and Hunsberger (2021, Section 8) illustrate the differences with 8 responders out of 14 against 1 out of 7.
ex <- vapply(c('minlike', 'central', 'blaker'), function(ts) {
attr(BinaryRR(14, 7, 0.05, 'Fisher', alternative = 'two.sided', tsmethod = ts),
'p.value')[9, 2]
}, numeric(1))
round(ex, 3)
#> minlike central blaker
#> 0.159 0.157 0.087The table with 4 responders out of 14 against 5 out of 7 is exactly
as likely as the observed one, so the minlike convention
counts it. Its lower tail probability, 0.0805, exceeds the upper tail
probability of the observed table, 0.0783, so the blaker
convention leaves it out.
Under the mid-p correction, the minlike and
blaker conventions count the tables tied with the observed
one in their ordering, the observed one included, with half of their
probability, which is the definition of the mid-p value in Fay and
Hunsberger (2021, Section 9). The central convention
doubles the smaller of the two one-sided mid-p values.
The bound by the central p-value holds for the exact Fisher p-value only. It fails for the mid-p value, and for the Boschloo test, whose ordering of the outcomes changes with the convention, as the two outcomes below show: 5 responders out of 8 against none out of 5 for the mid-p test, and 2 out of 4 against none out of 3 for the Boschloo test.
two_sided_p <- function(N1, N2, Test, x1, x2, ts) {
attr(BinaryRR(N1, N2, 0.05, Test, alternative = 'two.sided', tsmethod = ts),
'p.value')[x1 + 1, x2 + 1]
}
data.frame(
Test = c('Fisher-midP', 'Boschloo'),
blaker = c(two_sided_p(8, 5, 'Fisher-midP', 5, 0, 'blaker'),
two_sided_p(4, 3, 'Boschloo', 2, 0, 'blaker')),
central = c(two_sided_p(8, 5, 'Fisher-midP', 5, 0, 'central'),
two_sided_p(4, 3, 'Boschloo', 2, 0, 'central'))
)
#> Test blaker central
#> 1 Fisher-midP 0.05361305 0.04351204
#> 2 Boschloo 0.30541895 0.21874043The central convention has a property that the
minlike and blaker conventions lack. Its
two-sided rejection region at level
is exactly the union of the two one-sided rejection regions at level
.
N1 <- 9
N2 <- 7
alpha <- 0.02
two <- BinaryRR(N1, N2, 2 * alpha, 'Fisher',
alternative = 'two.sided', tsmethod = 'central')
upper <- BinaryRR(N1, N2, alpha, 'Fisher')
lower <- t(BinaryRR(N2, N1, alpha, 'Fisher'))
identical(as.vector(two), as.vector(upper | lower))
#> [1] TRUEThe minlike and central conventions give
different regions of the same nominal size.
minlike <- BinaryRR(N1, N2, 0.05, 'Fisher', alternative = 'two.sided',
tsmethod = 'minlike')
central <- BinaryRR(N1, N2, 0.05, 'Fisher', alternative = 'two.sided',
tsmethod = 'central')
data.frame(
tsmethod = c('minlike', 'central'),
rejected = c(sum(minlike), sum(central)),
rejected.by.this.only = c(sum(minlike & !central), sum(central & !minlike))
)
#> tsmethod rejected rejected.by.this.only
#> 1 minlike 22 2
#> 2 central 20 0The chi-squared and Z-pooled tests order outcomes by
when the alternative is two-sided, so tsmethod does not
apply to them.
Unconditional tests
Conditioning is not the only way to eliminate . An exact unconditional test keeps the full binomial model and maximizes the null tail probability over the nuisance parameter,
where is an ordering statistic. The Z-pooled test uses the two-sample Z statistic with a pooled variance estimator,
with larger values more extreme. The Boschloo test uses the Fisher
p-value itself as the ordering statistic, with smaller values more
extreme. Boschloo (1970) described this test as Fisher’s test at a
raised conditional level, the largest level at which the probability of
rejection under the null hypothesis does not exceed
at any
.
The probability
does not decrease as
increases, so rejecting when
is at most the raised level is the same as rejecting when the
unconditional p-value above is at most
.
For a two-sided alternative the article gives each of the two parts of
the rejection region half of the raised level, which is the
central convention. The default
tsmethod = 'minlike' orders the outcomes by the two-sided
p-value of stats::fisher.test instead, and
tsmethod = 'blaker' by the p-value of formula (2) of
Mehrotra, Chan and Berger (2003), which is how they define the two-sided
Boschloo test.
The supremum is approximated by a search over the grid of
n.grid equally spaced values of
from 0 to 1, where n.grid defaults to 100. A grid that
contains a coarser grid can only find a maximum at least as large, so
refining the grid in this way can only raise the p-values. The grids of
21 and 2001 points below are nested, since the spacing of the first is a
multiple of that of the second.
coarse <- attr(BinaryRR(12, 12, 0.025, 'Boschloo', n.grid = 21), 'p.value')
fine <- attr(BinaryRR(12, 12, 0.025, 'Boschloo', n.grid = 2001), 'p.value')
data.frame(all.p.values.increased = all(fine >= coarse - 1e-12),
largest.increase = max(fine - coarse))
#> all.p.values.increased largest.increase
#> 1 TRUE 0.00222195Refining the maximum between the grid points
The tail probability of each outcome is a polynomial in
,
and its maximum over the grid is a lower bound of the maximum over the
unit interval. A p-value computed on the grid can therefore fall below
the exact p-value, and the test can then exceed its nominal level. With
ref.pvalue = TRUE the grid is extended by points equally
spaced on the arcsine square-root scale, which resolve the narrow local
maxima near 0 and 1, and every local maximum on the extended grid is
refined by a safeguarded Newton iteration between its two neighbouring
grid points. A refined p-value is never smaller than the p-value on the
grid.
The size of a fixed-sample design is the largest rejection
probability over
,
which the function below evaluates on a fine grid and refines with
optimize() around the largest grid value.
size_fixed <- function(RR) {
N1 <- attr(RR, 'N1')
N2 <- attr(RR, 'N2')
m <- matrix(as.vector(RR), N1 + 1L, N2 + 1L)
f <- function(t) sum(outer(dbinom(0:N1, N1, t), dbinom(0:N2, N2, t)) * m)
theta <- seq(0.001, 0.999, by = 0.001)
v <- vapply(theta, f, numeric(1))
k <- which.max(v)
lower <- theta[max(k - 1L, 1L)]
upper <- theta[min(k + 1L, length(theta))]
max(v[k], optimize(f, c(lower, upper), maximum = TRUE)$objective)
}
grid.rr <- BinaryRR(32, 32, 0.025, 'Z-pool')
refined.rr <- BinaryRR(32, 32, 0.025, 'Z-pool', ref.pvalue = TRUE)
data.frame(
p.values = c('grid', 'refined'),
rejected = c(sum(grid.rr), sum(refined.rr)),
size = signif(c(size_fixed(grid.rr), size_fixed(refined.rr)), 4)
)
#> p.values rejected size
#> 1 grid 342 0.02501
#> 2 refined 340 0.02334With 32 patients per group the refinement removes 2 outcomes from the
rejection region of the Z-pooled test at the one-sided level 0.025, and
the size of the test changes from 0.02501 to 0.02334. Every function
that computes a rejection region accepts ref.pvalue. The
default is FALSE, which keeps the results of earlier
versions, and the refinement takes longer than the grid alone.
Ties in the ordering statistic
The ordering statistic takes the same value at several outcomes far more often than one might expect. With the outcomes and both have a Fisher p-value of .
stat <- attr(BinaryRR(7, 7, 0.025, 'Fisher'), 'p.value')
c(cell_5_1 = stat[6, 2], cell_6_2 = stat[7, 3])
#> cell_5_1 cell_6_2
#> 0.05128205 0.05128205The tail event is defined by “at least as extreme as”, so both outcomes belong to each other’s tail set and must receive the same p-value. Accumulating the null probabilities in an arbitrary order within a tie group would give them different values, and the decision at those outcomes would depend on how the sorting routine happens to break the tie. The package groups tied values explicitly and assigns each group the tail probability accumulated up to its last member.
The Berger-Boos procedure
Maximizing over the whole unit interval is wasteful, because values of far from the observed pooled proportion are implausible. Berger and Boos (1994) proposed maximizing over a percent confidence interval for instead, and adding to compensate,
The package uses an exact Clopper-Pearson interval based on
responders among
patients, so the interval differs from outcome to outcome. Setting
bb.gamma to a positive value activates the procedure.
Berger and Boos (1994) write
for
and suggest a small value such as 0.001 or 0.0001. The resulting test
still controls the type I error rate at the nominal level.
plain <- BinaryRR(15, 15, 0.025, 'Boschloo', n.grid = 200)
bb <- BinaryRR(15, 15, 0.025, 'Boschloo', n.grid = 200, bb.gamma = 1e-4)
p.plain <- attr(plain, 'p.value')
p.bb <- attr(bb, 'p.value')
data.frame(rejected.plain = sum(plain), rejected.berger.boos = sum(bb),
largest.decrease = max(p.plain - p.bb),
largest.increase = max(p.bb - p.plain))
#> rejected.plain rejected.berger.boos largest.decrease largest.increase
#> 1 61 61 0.005134331 0.0001087237Two forces act in opposite directions. Restricting the search lowers the maximum, and the additive raises the result. Which one wins varies from outcome to outcome, so the rejection region can grow, shrink or stay as it is. The gain is largest when the outcome is extreme, because the confidence interval for then excludes the value at which the unrestricted maximum is attained.
Relationships between the tests
The conditional distribution of a p-value satisfies for any fixed . Averaging over gives for every , so the Boschloo p-value never exceeds the Fisher p-value at the same outcome. The Boschloo rejection region therefore contains the Fisher rejection region, which is the sense in which Boschloo’s test is uniformly more powerful.
fisher <- BinaryRR(15, 15, 0.025, 'Fisher')
boschloo <- BinaryRR(15, 15, 0.025, 'Boschloo', n.grid = 200)
data.frame(rejected.fisher = sum(fisher), rejected.boschloo = sum(boschloo),
fisher.region.contained = all(as.vector(boschloo)[as.vector(fisher)]))
#> rejected.fisher rejected.boschloo fisher.region.contained
#> 1 51 61 TRUEThe type I error rates show how much of the nominal level each test actually spends.
max_type1 <- function(RR, n.grid = 401) {
N1 <- attr(RR, 'N1')
N2 <- attr(RR, 'N2')
m <- matrix(as.vector(RR), N1 + 1L, N2 + 1L)
theta <- seq(0, 1, length.out = n.grid)
max(vapply(theta, function(t) {
sum(outer(dbinom(0:N1, N1, t), dbinom(0:N2, N2, t)) * m)
}, numeric(1)))
}
tests <- c('Chisq', 'Fisher', 'Fisher-midP', 'Z-pool', 'Boschloo')
t1 <- vapply(tests, function(tst) {
max_type1(BinaryRR(30, 30, 0.025, tst, n.grid = 200), n.grid = 801)
}, numeric(1))
data.frame(
Test = tests, max.type1 = round(t1, 5), exceeds.alpha = t1 > 0.025,
row.names = NULL
)
#> Test max.type1 exceeds.alpha
#> 1 Chisq 0.02770 TRUE
#> 2 Fisher 0.01370 FALSE
#> 3 Fisher-midP 0.02595 TRUE
#> 4 Z-pool 0.02346 FALSE
#> 5 Boschloo 0.02344 FALSEThe Fisher test spends the least, which is the cost of conditioning.
The two unconditional tests spend much more while staying below the
level, which is where their extra power comes from. The chi-squared and
mid-p tests carry no such guarantee, and the exceeds.alpha
column shows what that means at this configuration.
Blinded sample size re-estimation
At the interim analysis,
and
patients have been observed in the two groups and the total number of
responders
is known. The first index is the stage and the second the group, as in
the bbssr-type1-error vignette. The blinded estimate of the
pooled response probability is
.
With an allocation ratio of
to 1 and an assumed treatment effect
,
group-specific probabilities are recovered as
and these enter the sample size calculation in place of the original
assumptions. Nothing in this chain requires knowledge of which patient
received which treatment. With effect = 'RR' or
effect = 'OR' the assumed effect is a risk ratio or an odds
ratio, and the recovered probabilities are those with the pooled value
and that ratio.
The sample size is re-estimated in one of two ways, chosen by
ss.method. The default 'exact' steps from the
normal approximation, one patient at a time, to the sample size at which
the exact power of the test first attains the target, as
BinarySampleSize() does, and this search needs
probabilities inside the unit interval, so the recovered probabilities
are truncated to it. The argument search selects instead
the smallest size that attains the target power, or the smallest size
from which every size up to a limit attains it, as in
BinarySampleSize(), and the limit is returned in the column
N2.limit of the attribute reestimation. The
other three methods use the normal approximation, which gives the size
of group 2, before rounding, as
where
under 'standard', formula (21.3) of Kieser (2020), and
under 'null.variance', formula (1) of Friede and Kieser
(2004). Under 'alternative.variance' both terms use
.
A two-sided alternative uses
in place of
,
and for the risk difference the denominator is
.
Group 1 receives
patients. With a non-inferiority margin the difference and the variance
change, as the bbssr-non-inferiority vignette describes.
The approximation uses the recovered probabilities before truncation,
with each Bernoulli variance truncated at zero, so that the assumed
effect
is kept as in formula (2) of Friede and Kieser (2004). The
bbssr-reestimation-rules vignette compares the methods and
the rules that turn
into whole numbers.
n.int <- 12
N.plan <- 24Two rules govern what happens next. The unrestricted rule takes the re-estimated sample size as it stands, allowing the trial to end up smaller than planned. The restricted rule raises it to the planned sample size first, so the trial can only grow. The table below applies both to a trial planned at 24 patients per group with an interim analysis at 12, with the default exact search.
interim <- data.frame(S = c(2, 4, 6, 8, 10))
interim$pooled <- round(interim$S / (2 * n.int), 3)
interim$unrestricted <- vapply(interim$S, function(s) {
BinaryBSSR(n1 = n.int, n2 = n.int, S = s, Delta.A = 0.3, r = 1,
alpha = 0.025, tar.power = 0.8, Test = 'Chisq')$N.final
}, numeric(1))
interim$restricted <- vapply(interim$S, function(s) {
BinaryBSSR(n1 = n.int, n2 = n.int, S = s, Delta.A = 0.3, r = 1,
alpha = 0.025, tar.power = 0.8, Test = 'Chisq',
restricted = TRUE, N1 = N.plan, N2 = N.plan)$N.final
}, numeric(1))
interim
#> S pooled unrestricted restricted
#> 1 2 0.083 46 48
#> 2 4 0.167 40 48
#> 3 6 0.250 62 62
#> 4 8 0.333 76 76
#> 5 10 0.417 82 82The unrestricted column is not monotone in . At a pooled rate of 0.083 the recovered control probability is negative and is truncated at zero, which shrinks the recovered risk difference below and pushes the sample size back up.
BinaryPowerBSSR() evaluates a design by averaging the
conditional power over the distribution of the interim outcome,
where the conditional power is computed from the rejection region of the final sample size that the interim outcome leads to. The sum runs over every possible interim outcome, so the same rejection region is required many times and is cached.
Setting
in this sum gives the type I error rate of the design at
,
and BinaryTypeIErrorBSSR() evaluates it over
.
The bbssr-type1-error vignette covers this calculation, the
adjusted nominal level and the decomposition of the rate by the interim
outcome.
The largest type I error rate
The largest type I error rate is the largest rejection probability on
the boundary of the null hypothesis. Without a margin the boundary is
.
With a margin, on the scale of the risk difference or of the risk ratio,
both response probabilities on the boundary are linear functions
and
of the pooled response probability
,
see the bbssr-non-inferiority vignette.
BinaryTypeIErrorBSSR() and
BinaryAlphaAdjBSSR() find the largest value over an
interval of
together with an upper bound, as described in this section.
Let and be the interim numbers of responders, as in the sum above, their total, and and the final sample sizes that leads to. Given that of the patients of group 1 respond, the number of responders among the first is hypergeometric whatever the response probability, where is the binomial probability and , and likewise for group 2. Let index the distinct pairs of final sample sizes and be the rejection region at these sizes. Summing over the interim outcomes and the final responder counts, the type I error rate is where the inner sum runs over the interim outcomes that lead to . The weights do not depend on . The fixed-sample design is the case of a single without interim patients, with .
On an interval write with . Then , and the type I error rate is a polynomial in , which the package writes in the Bernstein basis Three identities give its coefficients without subtraction. First, a response probability is that of a patient who responds with probability with probability and with probability otherwise. The number of the patients of the first kind is binomial with parameters and , so where and are independent, with and for group 1 and likewise for group 2. Second, the product of two basis polynomials is a multiple of a basis polynomial, so the term of is a polynomial of degree with the coefficients Third, a polynomial of degree with the coefficients is one of degree with the coefficients where . The terms of the different final sample sizes are raised to the largest degree in this way and added. Every quantity in these steps is non-negative.
The basis polynomials are non-negative and sum to one, so on the rate lies between the smallest and the largest of its coefficients , and and are its values at and . The coefficients on the two halves of the interval follow from the algorithm of de Casteljau. With and for , the coefficients on the first half are and those on the second half are . The package halves the interval repeatedly and keeps the largest value found at the ends of the pieces. A piece is set aside once its largest coefficient is at most , and the largest coefficient of the pieces set aside is an upper bound of the rate on the whole interval. If fewer than subdivisions suffice, up to rounding error. After subdivisions the remaining pieces are set aside with their largest coefficients. The bound is then still valid, and a warning reports that it can exceed by more than .
The interval runs from the smallest to the largest value of
theta, restricted with a margin to the values at which both
response probabilities lie in the unit interval.
BinaryTypeIErrorBSSR() with the default
maximize = 'certified' reports
,
or the largest value on the grid if that is larger, as TIE,
and
,
or TIE if that is larger by rounding error, as
bound. BinaryAlphaAdjBSSR() accepts a level
only when
does not exceed the target level. The bbssr-validation
vignette compares the result with a dense grid.
Choosing a test
Boschloo is the default recommendation when the
computation is affordable, since it is exact and uniformly more powerful
than Fisher. Z-pool is close behind and
slightly cheaper. Mehrotra, Chan and Berger (2003) recommend these two
tests over Fisher’s test, and caution that an exact unconditional test
based on another statistic, such as the difference in proportions, can
be less powerful than Fisher’s test. Fisher is exact but
conservative, and is the conventional choice when a regulator expects
the classical procedure. Chisq is useful for exploration
and for the starting value of a sample size search, but does not control
the type I error rate exactly at small sample sizes.
Fisher-midP sits between Fisher and the
unconditional tests and is worth considering when exact control is not a
formal requirement. Blackwelder and
Farrington-Manning are the tests to use with a
non-inferiority margin. Their rejection regions and power are computed
exactly, but the tests themselves are asymptotic, so their size at a
given sample size is worth checking.
References
Berger, R. L. and Boos, D. D. (1994). P values maximized over a confidence set for the nuisance parameter. Journal of the American Statistical Association, 89, 1012-1016.
Boschloo, R. D. (1970). Raised conditional level of significance for the 2 × 2-table when testing the equality of two probabilities. Statistica Neerlandica, 24, 1-9.
Fay, M. P. and Hunsberger, S. A. (2021). Practical valid inferences for the two-sample binomial problem. Statistics Surveys, 15, 72-110.
Friede, T. and Kieser, M. (2004). Sample size recalculation for binary data in internal pilot study designs. Pharmaceutical Statistics, 3, 269-279.
Kieser, M. (2020). Methods and Applications of Sample Size Calculation and Recalculation in Clinical Trials. Springer.
Mehrotra, D. V., Chan, I. S. F. and Berger, R. L. (2003). A cautionary note on exact unconditional inference for a difference between two independent binomial proportions. Biometrics, 59, 441-450.