A Brief Introduction to Change Point Detection using Python
By: Kirsten Perry
Tags: Programming, Time Series Analysis, Statistics, Anomaly Detection, Data Science

The ruptures Package

Charles Truong adapted the ruptures package from the R changepoint package. It specifically focuses on offline changepoint detection, where the whole sequence is analyzed. Out of all of the Python changepoint options, it is the best documented. We can install it using the basic pip install command:

pip install ruptures

The package offers a variety of search methods (binary segmentation, Pelt, window-based change detection, dynamic programming, etc.), as well as multiple cost functions to play around with. In this tutorial, we focus specifically on search methods.

Search Method Background

This section provides a brief background on some of the search methods available in the ruptures package, including binary segmentation, PELT, window-based change detection, and dynamic programming.

Pruned Exact Linear Time (PELT) search method: The PELT method is an exact method, and generally produces quick and consistent results. It detects change points through the minimization of costs (4). The algorithm has a computational cost of O(n), where n is the number of data points (4). For more info on the PELT method, check out this paper.

Dynamic programming search method: This is an exact method, which has a considerable computational cost of O(Qn² ), where Q is the max number of change points and n is the number of data points (4). For more info on the dynamic programming search method, check out this paper.

Binary segmentation search method: This method is arguably the most established in literature (4). Binary segmentation is an approximate method with an efficient computational cost of O (n log n), where n is the number of data points (4). The algorithm works by iteratively applying a single change point method to the entire sequence to determine if a split exists. If a split is detected, then the sequence splits into two sub-sequences (5). The same process is then applied to both sub-sequences, and so on (5). For more info on binary segmentation, check out this paper.

Window-based search method: This is a relatively simple approximate search method. The window-based search method “computes the discrepancy between two adjacent windows that move along with signal y” (6). When the two windows are highly dissimilar, a high discrepancy between the two values occurs, which is indicative of a change point (6). Upon generating a discrepancy curve, the algorithm locates optimal change point indices in the sequence (6). For more info on the window-based search method, check out this paper.

Code Example

In the below code, we perform change point detection using the search methods described above. We use the time series for daily WTI oil prices, from 2014 to now, pulled via the Energy Information Administration’s (EIA) API ( see this tutorial for more info on using the EIA API to pull data):

def retrieve_time_series(api, series_ID):

"""

Return the time series dataframe,

based on API and unique Series ID

api: API that we're connected to

series_ID: string. Name of the series that we want

to pull from the EIA API

"""

#Retrieve Data By Series ID

series_search = api.data_by_series(series=series_ID)

##Create a pandas dataframe from the retrieved time series

df = pd.DataFrame(series_search)

return df

"""

Execution in main block

""" #Create EIA API using your specific API key

api_key = 'YOUR API KEY HERE'

api = eia.API(api_key)



#Pull the oil WTI price data

series_ID='PET.RWTC.D'

price_df=retrieve_time_series(api, series_ID)

price_df.reset_index(level=0, inplace=True)

#Rename the columns for easier analysis

price_df.rename(columns={'index':'Date',

price_df.columns[1]:'WTI_Price'},

inplace=True)

#Format the 'Date' column

price_df['Date']=price_df['Date'].astype(str).str[:-3]

#Convert the Date column into a date object

price_df['Date']=pd.to_datetime(price_df['Date'], format='%Y %m%d')

#Subset to only include data going back to 2014

price_df=price_df[(price_df['Date']>='2014-01-01')] #Convert the time series values to a numpy 1D array

points=np.array(price_df['WTI_Price'])



#RUPTURES PACKAGE

#Changepoint detection with the Pelt search method

model="rbf"

algo = rpt.Pelt(model=model).fit(points)

result = algo.predict(pen=10)

rpt.display(points, result, figsize=(10, 6))

plt.title('Change Point Detection: Pelt Search Method')

plt.show()



#Changepoint detection with the Binary Segmentation search method

model = "l2"

algo = rpt.Binseg(model=model).fit(points)

my_bkps = algo.predict(n_bkps=10)

# show results

rpt.show.display(points, my_bkps, figsize=(10, 6))

plt.title('Change Point Detection: Binary Segmentation Search Method')

plt.show()



#Changepoint detection with window-based search method

model = "l2"

algo = rpt.Window(width=40, model=model).fit(points)

my_bkps = algo.predict(n_bkps=10)

rpt.show.display(points, my_bkps, figsize=(10, 6))

plt.title('Change Point Detection: Window-Based Search Method')

plt.show()



#Changepoint detection with dynamic programming search method

model = "l1"

algo = rpt.Dynp(model=model, min_size=3, jump=5).fit(points)

my_bkps = algo.predict(n_bkps=10)

rpt.show.display(points, my_bkps, figsize=(10, 6))

plt.title('Change Point Detection: Dynamic Programming Search Method')

plt.show()

Snapshot of the WTI Oil Price Time Series, pulled via the EIA API

Change Point Detection with Pelt Search Method, WTI Oil Price Time Series, 2014-Present

Change Point Detection with Binary Segmentation Search Method, WTI Oil Price Time Series, 2014-Present

Change Point Detection with Window-Based Search Method, WTI Oil Price Time Series, 2014-Present

Change Point Detection with Dynamic Programming Search Method, WTI Oil Price Time Series, 2014-Present

As you can see in the graphics above, the detected change points in the sequence differ based on the search method used. The optimal search method depends on what you value most when subsetting the time series. The PELT and dynamic programming methods are both exact (as opposed to approximate) methods, so they are generally more accurate.