Source code for gradeit.filters.savitzky_golay

from collections.abc import Sequence

import numpy as np


[docs] def savgol_filter( x: Sequence[float] | np.ndarray, window_length: int, polyorder: int = 3 ) -> np.ndarray: """Apply a Savitzky-Golay filter to a 1-D signal. Parameters: x: the signal to smooth. window_length: the (odd) length of the filter window. polyorder: the order of the polynomial fit within each window. Returns: The smoothed signal as a float64 array the same length as ``x``. Uses interpolated values at both ends of the signal. """ x = np.asarray(x, dtype=np.float64) if window_length % 2 == 0: raise ValueError("window_length must be odd") if window_length > x.size: raise ValueError("window_length must be <= the size of x for mode='interp'") if polyorder >= window_length: raise ValueError("polyorder must be less than window_length") half = window_length // 2 # Build coefficients for the center of each window. positions = np.arange(-half, half + 1) design = np.vander(positions, polyorder + 1, increasing=True) coeffs = np.linalg.pinv(design)[0] # Smooth interior points. smoothed = np.convolve(x, coeffs[::-1], mode="same") # Fit a polynomial at each end. local_idx = np.arange(window_length) left_fit = np.polynomial.polynomial.polyfit(local_idx, x[:window_length], polyorder) smoothed[:half] = np.polynomial.polynomial.polyval(local_idx[:half], left_fit) right_fit = np.polynomial.polynomial.polyfit(local_idx, x[-window_length:], polyorder) smoothed[-half:] = np.polynomial.polynomial.polyval( local_idx[window_length - half :], right_fit ) return smoothed