Skip to content

Commit 9af52f6

Browse files
committed
make it a package
1 parent 5125237 commit 9af52f6

File tree

6 files changed

+134
-66
lines changed

6 files changed

+134
-66
lines changed

.idea/inspectionProfiles/Project_Default.xml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/profiles_settings.xml

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Example Code.ipynb

+72-38
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#!/usr/bin/env python2
2+
3+
__version__ = '0.2.dev1'
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
from __future__ import division
22
import numpy as np
33
from scipy.special import gammaln
4-
from scipy.misc import comb
4+
from scipy.misc import comb, logsumexp
55
from decorator import decorator
66

77

88
def _dynamicProgramming(f, *args, **kwargs):
9+
if f.data is None:
10+
f.data = args[0]
11+
12+
if not np.array_equal(f.data, args[0]):
13+
f.cache = {}
14+
f.data = args[0]
15+
916
try:
1017
f.cache[args[1:3]]
1118
except KeyError:
@@ -15,10 +22,13 @@ def _dynamicProgramming(f, *args, **kwargs):
1522

1623
def dynamicProgramming(f):
1724
f.cache = {}
25+
f.data = None
1826
return decorator(_dynamicProgramming, f)
1927

2028

21-
def offline_changepoint_detection(data, prior_func, observation_log_likelihood_function, P=None, truncate=-np.inf):
29+
def offline_changepoint_detection(data, prior_func,
30+
observation_log_likelihood_function,
31+
truncate=-np.inf):
2232
"""Compute the likelihood of changepoints on data.
2333
2434
Keyword arguments:
@@ -34,10 +44,8 @@ def offline_changepoint_detection(data, prior_func, observation_log_likelihood_f
3444
Q = np.zeros((n,))
3545
g = np.zeros((n,))
3646
G = np.zeros((n,))
37-
38-
if P is None:
39-
P = np.ones((n,n)) # a log_likelihood won't become one
40-
47+
P = np.ones((n, n)) * -np.inf
48+
4149
# save everything in log representation
4250
for t in range(n):
4351
g[t] = np.log(prior_func(t))
@@ -46,28 +54,24 @@ def offline_changepoint_detection(data, prior_func, observation_log_likelihood_f
4654
else:
4755
G[t] = np.logaddexp(G[t-1], g[t])
4856

49-
if P[n-1, n-1] == 1:
50-
P[n-1, n-1] = observation_log_likelihood_function(data, n-1, n)
51-
57+
P[n-1, n-1] = observation_log_likelihood_function(data, n-1, n)
5258
Q[n-1] = P[n-1, n-1]
5359

5460
for t in reversed(range(n-1)):
5561
P_next_cp = -np.inf # == -log(0)
5662
for s in range(t, n-1):
57-
# don't recompute log likelihoods already saved
58-
if P[t, s] == 1:
59-
P[t, s] = observation_log_likelihood_function(data, t, s + 1)
60-
63+
P[t, s] = observation_log_likelihood_function(data, t, s + 1)
64+
6165
# compute recursion
6266
summand = P[t, s] + Q[s + 1] + g[s + 1 - t]
6367
P_next_cp = np.logaddexp(P_next_cp, summand)
64-
65-
# truncate sum to become approx. linear in time (see Fearnhead, 2006, eq. (3))
68+
69+
# truncate sum to become approx. linear in time (see
70+
# Fearnhead, 2006, eq. (3))
6671
if summand - P_next_cp < truncate:
6772
break
68-
69-
if P[t, n-1] == 1:
70-
P[t, n-1] = observation_log_likelihood_function(data, t, n)
73+
74+
P[t, n-1] = observation_log_likelihood_function(data, t, n)
7175

7276
# (1 - G) is numerical stable until G becomes numerically 1
7377
if G[n-1-t] < -1e-15: # exp(-1e-15) = .99999...
@@ -80,21 +84,17 @@ def offline_changepoint_detection(data, prior_func, observation_log_likelihood_f
8084

8185
Pcp = np.ones((n-1, n-1)) * -np.inf
8286
for t in range(n-1):
83-
if P[0, t] == 1:
84-
P[0, t] = observation_log_likelihood_function(data, 0, t+1)
85-
Pcp[0, t] = P[0, t] + Q[t + 1] + g[t] - Q[0]
87+
Pcp[0, t] = P[0, t] + Q[t + 1] + g[t] - Q[0]
8688
for j in range(1, n-1):
8789
for t in range(j, n-1):
88-
for tau in range(j-1, t):
89-
if P[tau+1, t] == 1:
90-
P[tau+1, t] = observation_log_likelihood_function(data, tau+1, t+1)
91-
tmp_cond = Pcp[j-1, tau] + P[tau+1, t] + Q[t + 1] + g[t - tau] - Q[tau + 1]
92-
Pcp[j, t] = np.logaddexp(Pcp[j, t], tmp_cond)
90+
tmp_cond = Pcp[j-1, j-1:t] + P[j:t+1, t] + Q[t + 1] + g[0:t-j+1] - Q[j:t+1]
91+
Pcp[j, t] = logsumexp(tmp_cond)
9392

9493
return Q, P, Pcp
9594

9695
@dynamicProgramming
9796
def gaussian_obs_log_likelihood(data, t, s):
97+
s += 1
9898
n = s - t
9999
mean = data[t:s].sum(0) / n
100100

@@ -104,8 +104,8 @@ def gaussian_obs_log_likelihood(data, t, s):
104104
betaT = 1 + 0.5 * ((data[t:s] - mean) ** 2).sum(0) + ((n)/(1 + n)) * (mean**2 / 2)
105105
scale = (betaT*(nuT + 1))/(alphaT * nuT)
106106

107-
# splitting the PDF of the student distribution up is /much/ faster. (~ factor 20)
108-
# using sum over for loop is even more worthwhile
107+
# splitting the PDF of the student distribution up is /much/ faster.
108+
# (~ factor 20) using sum over for loop is even more worthwhile
109109
prob = np.sum(np.log(1 + (data[t:s] - muT)**2/(nuT * scale)))
110110
lgA = gammaln((nuT + 1) / 2) - np.log(np.sqrt(np.pi * nuT * scale)) - gammaln(nuT/2)
111111

@@ -115,8 +115,10 @@ def gaussian_obs_log_likelihood(data, t, s):
115115
def const_prior(r, l):
116116
return 1/(l)
117117

118+
118119
def geometric_prior(t, p):
119120
return p * ((1 - p) ** (t - 1))
120121

122+
121123
def neg_binominal_prior(t, k, p):
122124
return comb(t - k, k - 1) * p ** k * (1 - p) ** (t - k)

setup.py

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env python2
2+
3+
from distutils.core import setup
4+
import bayesian_changepoint_detection
5+
6+
setup(name='bayesian_changepoint_detection',
7+
version=bayesian_changepoint_detection.__version__,
8+
description='Some Bayesian changepoint detection algorithms',
9+
author='Johannes Kulick',
10+
author_email='johannes.kulick@ipvs.uni-stuttgart.de',
11+
url='http://github.com/hildensia/bayesian_changepoint_detection',
12+
packages = ['bayesian_changepoint_detection'],
13+
requires=['scipy', 'numpy']
14+
)

0 commit comments

Comments
 (0)