00001 import numpy as np 00002 from math import factorial 00003 import pylab 00004 00005 class savitzky_golay(): 00006 r"""Smooth (and optionally differentiate) data with a Savitzky-Golay filter. 00007 The Savitzky-Golay filter removes high frequency noise from data. 00008 It has the advantage of preserving the original shape and 00009 features of the signal better than other types of filtering 00010 approaches, such as moving averages techniques. 00011 Parameters 00012 ---------- 00013 y : array_like, shape (N,) 00014 the values of the time history of the signal. 00015 window_size : int 00016 the length of the window. Must be an odd integer number. 00017 order : int 00018 the order of the polynomial used in the filtering. 00019 Must be less then `window_size` - 1. 00020 deriv: int 00021 the order of the derivative to compute (default = 0 means only smoothing) 00022 Returns 00023 ------- 00024 ys : ndarray, shape (N) 00025 the smoothed signal (or it's n-th derivative). 00026 Notes 00027 ----- 00028 The Savitzky-Golay is a type of low-pass filter, particularly 00029 suited for smoothing noisy data. The main idea behind this 00030 approach is to make for each point a least-square fit with a 00031 polynomial of high order over a odd-sized window centered at 00032 the point. 00033 Examples 00034 -------- 00035 t = np.linspace(-4, 4, 500) 00036 y = np.exp( -t**2 ) + np.random.normal(0, 0.05, t.shape) 00037 ysg = savitzky_golay(y, window_size=31, order=4) 00038 import matplotlib.pyplot as plt 00039 plt.plot(t, y, label='Noisy signal') 00040 plt.plot(t, np.exp(-t**2), 'k', lw=1.5, label='Original signal') 00041 plt.plot(t, ysg, 'r', label='Filtered signal') 00042 plt.legend() 00043 plt.show() 00044 References 00045 ---------- 00046 .. [1] A. Savitzky, M. J. E. Golay, Smoothing and Differentiation of 00047 Data by Simplified Least Squares Procedures. Analytical 00048 Chemistry, 1964, 36 (8), pp 1627-1639. 00049 .. [2] Numerical Recipes 3rd Edition: The Art of Scientific Computing 00050 W.H. Press, S.A. Teukolsky, W.T. Vetterling, B.P. Flannery 00051 Cambridge University Press ISBN-13: 9780521880688 00052 """ 00053 00054 def __init__(self, window_size, order, deriv=0, rate=1): 00055 00056 self.window_size = window_size 00057 self.order = order 00058 self.deriv = deriv 00059 self.rate = rate 00060 00061 try: 00062 self.window_size = np.abs(np.int(self.window_size)) 00063 self.order = np.abs(np.int(self.order)) 00064 except ValueError, msg: 00065 raise ValueError("window_size and order have to be of type int") 00066 if self.window_size % 2 != 1 or self.window_size < 1: 00067 raise TypeError("window_size size must be a positive odd number") 00068 if self.window_size < self.order + 2: 00069 raise TypeError("window_size is too small for the polynomials order") 00070 00071 def filter(self, y): 00072 00073 self.order_range = range(self.order+1) 00074 half_window = (self.window_size -1) // 2 00075 # precompute coefficients 00076 b = np.mat([[k**i for i in self.order_range] for k in range(-half_window, half_window+1)]) 00077 m = np.linalg.pinv(b).A[self.deriv] * self.rate**self.deriv * factorial(self.deriv) 00078 # pad the signal at the extremes with 00079 # values taken from the signal itself 00080 firstvals = y[0] - np.abs( y[1:half_window+1][::-1] - y[0] ) 00081 lastvals = y[-1] + np.abs(y[-half_window-1:-1][::-1] - y[-1]) 00082 y = np.concatenate((firstvals, y, lastvals)) 00083 return np.convolve( m[::-1], y, mode='valid') 00084 00085 if __name__ == "__main__": 00086 00087 # create some sample twoD data 00088 x = np.linspace(-3,3,100) 00089 Z = np.exp( -(x)) 00090 00091 # add noise 00092 Zn = Z + np.random.normal( 0, 0.2, Z.shape ) 00093 00094 sg = savitzky_golay(window_size=29, order=4) 00095 00096 l = sg.filter(Zn) 00097 00098 pylab.plot(l) 00099 00100 pylab.plot(Zn) 00101 00102 pylab.show()