This code generates an example of Wald's sequential probability ratio test (SPRT).
In [1]:
# set up imports
import numpy
import matplotlib.pyplot as plt
import scipy.stats
%matplotlib inline
In [2]:
alpha=0.05 # likelihood of deciding A when A is true
beta=1.-alpha # likelihood of deciding B when B is true
A=numpy.log(beta/alpha)
B=numpy.log((1.-beta)/(1.-alpha))
In [3]:
# get the log likelihood ratio for the data under either positive or negative motion
def gauss_LLR(xbar, sigma=1, mu=[10,-10]):
"""
Gaussian log likelihood ratio ratio
"""
return numpy.log(scipy.stats.norm.pdf(xbar,loc=mu[0],scale=sigma)/scipy.stats.norm.pdf(xbar,loc=mu[1],scale=sigma))
In [40]:
# generate some data from a noisy diffusion process
def mkdata(drift=0.001,noise_sd=0.01,npts=1000):
data=numpy.zeros(npts)
cumul=numpy.zeros(npts)
for i in range(1,npts):
data[i]=drift+numpy.random.randn()*noise_sd
cumul[i]=numpy.sum(data[:(i+1)])
return data,cumul
In [41]:
# create simulated data, fit SPRT and return # of time steps
def fit_sprt(data):
npts=data.shape[0]
ll=[0]
S=[0]
#cumulative sum of log-likelihood ratio
for i in range(1,npts):
ll.append(gauss_LLR(data[i]))
S=numpy.sum(ll)
if S>A:
#print('upward decision: %d steps (S=%f)'%(i,S))
return([1,i])
elif S<B:
#print('downward decision: %d steps (S=%f)'%(i,S))
return([-1,i])
return([numpy.nan,i])
nruns=100
npts=1000
outcome=[]
cumul=numpy.zeros((nruns,npts))
for r in range(nruns):
data,c=mkdata(npts=npts)
sprt=fit_sprt(data)
outcome.append(sprt)
cumul[r,:sprt[1]]=c[:sprt[1]]
outcome=numpy.array(outcome)
print('accuracy=%0.3f'%numpy.mean(outcome[:,0]==1))
print('proportion not converged=%f'%numpy.mean(numpy.isnan(outcome[:,0])))
In [51]:
correct=outcome[:,0]==1
plt.figure(figsize=(12,8))
plt.subplot(3,1,1)
h=plt.hist(outcome[correct,1],50)
plt.subplot(3,1,2)
for i in range(cumul.shape[0]):
plt.plot(cumul[i,:outcome[i,1]])
plt.subplot(3,1,3)
plt.hist(outcome[~correct,1],bins=h[1])
Out[51]:
In [ ]: