SAVGOL_FILTER
The Savitzky-Golay filter is a digital filter that can be applied to a set of digital data points for the purpose of smoothing the data—that is, to increase the precision of the data without distorting the signal tendency. This is achieved, in a process known as convolution, by fitting successive sub-sets of adjacent data points with a low-degree polynomial by the method of linear least squares.
The filter is particularly effective at preserving higher moments of the signal, such as peak heights and widths, which might be flattened by other smoothing techniques like moving averages.
y_i = \sum_{j=-k}^k C_j x_{i+j}
where C_j are the filter coefficients derived from the polynomial fit.
Excel Usage
=SAVGOL_FILTER(x, window_length, polyorder, deriv, delta, axis, savgol_mode, cval)
x(list[list], required): The data to be filtered (Excel range).window_length(int, required): The length of the filter window (must be positive and odd).polyorder(int, required): The order of the polynomial used to fit the samples.deriv(int, optional, default: 0): The order of the derivative to compute.delta(float, optional, default: 1): The spacing of the samples (used if deriv > 0).axis(int, optional, default: -1): The axis along which the filter is applied.savgol_mode(str, optional, default: “interp”): Extension mode for padded signal.cval(float, optional, default: 0): Value to fill past edges if mode is ‘constant’.
Returns (list[list]): The filtered data as a 2D array.
Example 1: Basic smoothing
Inputs:
| x | window_length | polyorder | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 2 | 5 | 2 | 1 | 0 | 1 | 4 | 9 | 5 | 2 |
Excel formula:
=SAVGOL_FILTER({2,2,5,2,1,0,1,4,9}, 5, 2)
Expected output:
| Result | ||||||||
|---|---|---|---|---|---|---|---|---|
| 1.65714 | 3.17143 | 3.54286 | 2.85714 | 0.657143 | 0.171429 | 1 | 4 | 9 |
Example 2: Nearest mode
Inputs:
| x | window_length | polyorder | savgol_mode | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 2 | 5 | 2 | 1 | 0 | 1 | 4 | 9 | 5 | 2 | nearest |
Excel formula:
=SAVGOL_FILTER({2,2,5,2,1,0,1,4,9}, 5, 2, "nearest")
Expected output:
| Result | ||||||||
|---|---|---|---|---|---|---|---|---|
| 1.74286 | 3.02857 | 3.54286 | 2.85714 | 0.657143 | 0.171429 | 1 | 4.6 | 7.97143 |
Example 3: First derivative
Inputs:
| x | window_length | polyorder | deriv | ||||
|---|---|---|---|---|---|---|---|
| 0 | 1 | 4 | 9 | 16 | 5 | 2 | 1 |
Excel formula:
=SAVGOL_FILTER({0,1,4,9,16}, 5, 2, 1)
Expected output:
| Result | ||||
|---|---|---|---|---|
| -4.4406e-15 | 2 | 4 | 6 | 8 |
Python Code
Show Code
import numpy as np
from scipy.signal import savgol_filter as scipy_savgol
def savgol_filter(x, window_length, polyorder, deriv=0, delta=1, axis=-1, savgol_mode='interp', cval=0):
"""
Apply a Savitzky-Golay filter to a signal array for smoothing.
See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.savgol_filter.html
This example function is provided as-is without any representation of accuracy.
Args:
x (list[list]): The data to be filtered (Excel range).
window_length (int): The length of the filter window (must be positive and odd).
polyorder (int): The order of the polynomial used to fit the samples.
deriv (int, optional): The order of the derivative to compute. Default is 0.
delta (float, optional): The spacing of the samples (used if deriv > 0). Default is 1.
axis (int, optional): The axis along which the filter is applied. Default is -1.
savgol_mode (str, optional): Extension mode for padded signal. Valid options: Mirror, Constant, Nearest, Wrap, Interp. Default is 'interp'.
cval (float, optional): Value to fill past edges if mode is 'constant'. Default is 0.
Returns:
list[list]: The filtered data as a 2D array.
"""
try:
def to2d(x):
return [[x]] if not isinstance(x, list) else x
x_arr = np.array(to2d(x))
# Handle potential conversion issues
if x_arr.dtype.kind not in 'iuf':
# Try to convert to float, ignoring non-numeric
x_flat = x_arr.flatten()
cleaned = []
for val in x_flat:
try:
cleaned.append(float(val))
except (TypeError, ValueError):
cleaned.append(0.0) # Or should we use nan? filter usually fails on nan.
x_arr = np.array(cleaned).reshape(x_arr.shape)
result = scipy_savgol(
x_arr,
window_length=int(window_length),
polyorder=int(polyorder),
deriv=int(deriv),
delta=float(delta),
axis=int(axis),
mode=savgol_mode,
cval=float(cval)
)
return result.tolist()
except Exception as e:
return f"Error: {str(e)}"