binomconf

Calculating Binomial Confidence Intervals in Python

Gives exact, optimal confidence intervals

from __future__ import division

import math
import random # only used for n=0 case

from scipy import special


def find_root(y_over_dy, start, steps=10, bounds=(None, None)):
    guess = start
    for i in xrange(steps):
        prev, guess = guess, guess - y_over_dy(guess)
        if bounds[0] is not None and guess < bounds[0]: guess = bounds[0]
        if bounds[1] is not None and guess > bounds[1]: guess = bounds[1]
        if guess == prev:
            break
    return guess

def binomial_conf_interval(x, n, conf=0.95):
    assert 0 <= x <= n and 0 <= conf < 1
    if n == 0:
        left = random.random()*(1 - conf)
        return left, left + conf
    bl = float(special.betaln(x+1, n-x+1))
    def f(left_a):
        left = max(1e-8, float(special.betaincinv(x+1, n-x+1, left_a)))
        right = min(1-1e-8, float(special.betaincinv(x+1, n-x+1, left_a + conf)))
        top = math.exp(
            math.log(right)*(x+1) + math.log(1-right)*(n-x+1)
            + math.log(left) + math.log(1-left) - bl
        ) - math.exp(
            math.log(left)*(x+1) + math.log(1-left)*(n-x+1)
            + math.log(right) + math.log(1-right) - bl
        )
        bottom = (x - n*right)*left*(1-left) - (x - n*left)*right*(1-right)
        return top/bottom
    left_a = find_root(f, (1-conf)/2, bounds=(0, 1-conf))
    return (
        float(special.betaincinv(x+1, n-x+1, left_a)),
        float(special.betaincinv(x+1, n-x+1, left_a + conf)),
    )

minmax = lambda x: (min(x), max(x))

def format_binomial_conf(x, n, conf=0.95, f=lambda x: x):
    if n == 0:
        return '???'
    left, right = minmax(map(f, binomial_conf_interval(x, n, conf)))
    return '~%.1f%% (%.f-%.f%%)' % (100*f(x/n),
        math.floor(100*left), math.ceil(100*right))

print "(95% confidence level)"
print
for x, n in [(1, 2), (0, 10), (3, 4), (1000, 1000), (0, 0)]:
    print "x =", x, "n =", n
    print binomial_conf_interval(x, n)
    print format_binomial_conf(x, n)
    print
Output:
(95% confidence level)

x = 1 n = 2
(0.09429932405024612, 0.9057006759497539)
~50.0% (9-91%)

x = 0 n = 10
(0.0, 0.23840419038085267)
~0.0% (0-24%)

x = 3 n = 4
(0.3298504422802088, 0.9739692154994636)
~75.0% (32-98%)

x = 1000 n = 1000
(0.9970117342468727, 1.0)
~100.0% (99-100%)

x = 0 n = 0
(0.001202340017706206, 0.9512023400177062)
???