Skip to content

Commit 784b768

Browse files
committed
Re-implement the Clopper-Pearson interval using the beta distribution
This requires less conversion of arguments before and after the underlying call to the regularized beta function.
1 parent 3eccfe4 commit 784b768

2 files changed

Lines changed: 25 additions & 14 deletions

File tree

commons-math-legacy/src/main/java/org/apache/commons/math4/legacy/stat/interval/ClopperPearsonInterval.java

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717
package org.apache.commons.math4.legacy.stat.interval;
1818

19-
import org.apache.commons.statistics.distribution.FDistribution;
19+
import org.apache.commons.statistics.distribution.BetaDistribution;
2020

2121
/**
2222
* Implements the Clopper-Pearson method for creating a binomial proportion confidence interval.
@@ -37,22 +37,17 @@ public ConfidenceInterval createInterval(int numberOfTrials,
3737
double lowerBound = 0;
3838
double upperBound = 1;
3939

40-
final double alpha = 0.5 * (1 - confidenceLevel);
40+
// alpha = 1 - confidence level
41+
final double halfAlpha = 0.5 * (1 - confidenceLevel);
42+
final int n = numberOfTrials;
43+
final int x = numberOfSuccesses;
4144

4245
if (numberOfSuccesses > 0) {
43-
final FDistribution distributionLowerBound = FDistribution.of(2.0 * (numberOfTrials - numberOfSuccesses + 1),
44-
2.0 * numberOfSuccesses);
45-
final double fValueLowerBound = distributionLowerBound.inverseSurvivalProbability(alpha);
46-
lowerBound = numberOfSuccesses /
47-
(numberOfSuccesses + (numberOfTrials - numberOfSuccesses + 1) * fValueLowerBound);
46+
lowerBound = BetaDistribution.of(x, n - x + 1).inverseCumulativeProbability(halfAlpha);
4847
}
4948

5049
if (numberOfSuccesses < numberOfTrials) {
51-
final FDistribution distributionUpperBound = FDistribution.of(2.0 * (numberOfSuccesses + 1),
52-
2.0 * (numberOfTrials - numberOfSuccesses));
53-
final double fValueUpperBound = distributionUpperBound.inverseSurvivalProbability(alpha);
54-
upperBound = (numberOfSuccesses + 1) * fValueUpperBound /
55-
(numberOfTrials - numberOfSuccesses + (numberOfSuccesses + 1) * fValueUpperBound);
50+
upperBound = BetaDistribution.of(x + 1, n - x).inverseSurvivalProbability(halfAlpha);
5651
}
5752

5853
return new ConfidenceInterval(lowerBound, upperBound, confidenceLevel);

commons-math-legacy/src/test/java/org/apache/commons/math4/legacy/stat/interval/ClopperPearsonIntervalTest.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,32 @@ public void testCase3() {
9292
check(expected, createBinomialConfidenceInterval().createInterval(trials, successes, confidenceLevel));
9393
}
9494

95+
@Test
96+
public void testCase4() {
97+
final int successes = 20;
98+
final int trials = 400;
99+
final double confidenceLevel = 0.95;
100+
101+
// Check using scipy.stats.beta
102+
// k, n, alpha = 20, 400, 0.05
103+
// p_l, p_u = beta.ppf([alpha / 2, 1 - alpha / 2], [k, k + 1], [n - k + 1, n - k])
104+
final ConfidenceInterval expected = new ConfidenceInterval(0.030805241143265938,
105+
0.07616697275514255,
106+
confidenceLevel);
107+
108+
check(expected, createBinomialConfidenceInterval().createInterval(trials, successes, confidenceLevel));
109+
}
110+
95111
private void check(ConfidenceInterval expected,
96112
ConfidenceInterval actual) {
97113
final double relTol = 1.0e-6; // Reasonable relative tolerance for floating point comparison
98114
// Compare bounds using a relative tolerance
99115
Assert.assertEquals(expected.getLowerBound(),
100116
actual.getLowerBound(),
101-
relTol * (1.0 + Math.abs(expected.getLowerBound())));
117+
relTol * expected.getLowerBound());
102118
Assert.assertEquals(expected.getUpperBound(),
103119
actual.getUpperBound(),
104-
relTol * (1.0 + Math.abs(expected.getUpperBound())));
120+
relTol * expected.getUpperBound());
105121
// The confidence level must be exact
106122
Assert.assertEquals(expected.getConfidenceLevel(),
107123
actual.getConfidenceLevel(),

0 commit comments

Comments
 (0)