SavitzkyGolayFilter - Maple Help

SignalProcessing

 SavitzkyGolayFilter
 apply Savitzky-Golay filter to a signal

 Calling Sequence SavitzkyGolayFilter( signal, degree, frameradius, options ) SavitzkyGolayFilter( signal, degree, weights, options )

Parameters

 signal - 1-D rtable or list of real-valued data degree - nonnegative integer for the degree of the polynomials used for filtering frameradius - positive integer for the radius of the frame used for filtering weights - 2-D rtable or list of positive real-valued weights for the least-squares fitting

Options

 • extend: Keyword true or false. Specifies if the signal is to be extended on the left and right by frameradius in order to improve the quality of the filtered signal. The default is false.
 • extrapolation: Keyword periodic, polynomial, or spline. Specifies how the signal is to be extended when extend=true. The default is polynomial.
 • filteredplotoptions: List of additional plot options used to create the plot of the filtered signal. The default is [].
 • plotoptions: List of additional plot options used to create the combined plot of the unfiltered and filtered signals. The default is [].
 • recurrencevariables: List of three unique names for the index, old value, and new value used when constructing the recurrence relation for the filter. The default is [n,x,y].
 • splinepoints: Positive integer other than 1 or the keyword infinity. Specifies the maximum number of data points used to perform spline extrapolation. The default is 10.
 • timerange: Range of the form t1..t2, where t1 and t2 are numeric values. Specifies the time range for the plot and Vector of times. The default is 1..numelems(signal).
 • unfilteredplotoptions: List of additional plot options used to create the plot of the unfiltered signal. The default is [].
 • output: The type of output. Let $m$ be the signal size, $d$ be the degree, $r$ be the frame radius, and $l=2r+1$ be the frame length. The supported options are:
 – coefficients: The float[8] Vector, of size $l$, storing the coefficients of the filter.
 – convolution: The float[8] Vector, of size $m-2r$ when extend=false and $m$ when extend=true, storing the convolution of the filter.
 – derivatives: The float[8] Matrix, of size $m$ by $d$, storing the derivatives of the signal up to degree $d$.
 – differentiator: The float[8] Matrix, of size $l$ by $d+1$, storing the differentiator of the filter.
 – filteredsignal: The float[8] Vector, of size $m$, storing the filtered signal.
 – hankel: The float[8] Matrix, of size $d+1$ by $d+1$, storing the Hankel Matrix of the filter.
 – nrr: The numeric value corresponding to the noise-reduction ratio of the filtered signal.
 – plot: The plot of the unfiltered and filtered signals.
 – projection: The float[8] Matrix, of size $l$ by $l$, storing the projection of the filter.
 – recurrence: The linear recurrence relation corresponding to the filter.
 – times: The float[8] Vector with the time values determined by timerange.
 – unfilteredsignal: The float[8] Vector, of size $m$, storing the unfiltered signal.
 – vandermonde: The float[8] Matrix, of size $l$ by $d+1$, storing the Vandermonde Matrix of the filter.
 – weights: The float[8] Vector, of size $l$, storing the weights.
 – record: Returns a record with the previous options.
 – list of any of the above options: Returns an expression sequence with the corresponding outputs, in the same order. The default is filteredsignal.

Description

 • The SavitzkyGolayFilter command applies the Savitzky-Golay Filter to a 1-D signal with real-valued data, which is useful for smoothing noisy data and estimating derivatives. The method is also known as the Least-Squares Smoothing, Polynomial Smoothing Filter, and Locally Weighted Scatterplot Smoothing (LOWESS).
 • The filter works by applying polynomial fitting to a succession of frames/windows containing a fixed number of points. More specifically, for each interior data point of the signal, say ${x}_{k}$ which corresponds to time ${t}_{k}$, the frame consists of the points $\left[{x}_{k-r},..,{x}_{k+r}\right]$, where $r$ is the frame radius. The total number of points in the frame is the frame length, $l=2r+1$, and the index $k$ satisfies $r+1\le k$ and $k\le m+r$, where $m$ is the size of the signal. The filtered value ${y}_{k}$ of ${x}_{k}$ is determined by the value of the best-fit polynomial of degree $d$ through the points in the frame at time ${t}_{k}$.
 • To determine the filtered signal, Maple does the following:
 1 Suppose the frame consists of $r$ nodes ${n}_{i}$ spaced uniformly between $-1$ and $1$.
 2 Let $p$ be the polynomial of degree $d$ having coefficients $c$ with respect to the basis defined by the columns of a Vandermonde Matrix $V$, with dimensions $l$ by $d+1$. The elements of $V$ are such that the value of $p$ at node ${n}_{i}$ is element $i$ of $y=V·c$. The Vandermonde Matrix used by Maple has elements defined by ${V}_{i,j}={\left(\frac{i-r-1}{r}\right)}^{j-1}$.
 3 It can be shown that the optimal choice of $c$ is given by the the middle row of the Projection Matrix $P=F·{V}^{\mathrm{Typesetting}:-\mathrm{_Hold}\left(\left[\mathrm{%T}\right]\right)}$, where the Differentiator Matrix is given by $F=V·\mathrm{Typesetting}:-\mathrm{_Hold}\left(\left[\mathrm{%^}\left(H,-1\right)\right]\right)$ and the Hankel Matrix is given by $H={V}^{\mathrm{Typesetting}:-\mathrm{_Hold}\left(\left[\mathrm{%T}\right]\right)}·W·V$. More specifically, if $x$ is the signal over the frame, the objective function to minimize is given by $\mathrm{\phi }={\left(x-V·c\right)}^{\mathrm{Typesetting}:-\mathrm{_Hold}\left(\left[\mathrm{%T}\right]\right)}·W·\left(x-V·c\right)$.
 4 For each point of the unfiltered signal, say ${x}_{k}$ with $r+1\le k$ and $k\le m+r$, the filtered value ${y}_{k}$ is found using a frame centered at ${x}_{k}$ and computing the value of the best-fit polynomial at the center node using the above method. All the filtered values together between indices $r+1$ and $m+r$, namely the steady-state signal, are computed as the valid convolution of the unfiltered signal and the coefficients Vector.
 5 To find the filtered signal on the left and right of the steady-state signal, namely the input-on transient and input-off transient, respectively, let $u$ be the first $r$ elements of the unfiltered signal $x$ in reverse order, $v$ be the last $r$ elements of $x$ in reverse order, $Q$ be the first $r$ rows of $P$ in reverse order, and $R$ be the last $r$ rows of $P$ in reverse order. The input-on transient is given by $R·u$ and the input-off transient is given by $Q·v$.
 • When extend=false, the original signal is extended on the left and right by $r$ so that the overall signal has size $m+2r$. This is done so that the resulting steady-state of the filtered signal is of size $m$, which will be used as the filtered version of the original signal.
 • The noise-reduction ratio is computed as the sum of the squares of the coefficients. Note that the sum of the coefficients is equal to $1$.
 • The Derivatives Matrix is found by convolving the unfiltered signal with the appropriately weighted columns of the Differentiator Matrix. When extend=false the original unfiltered signal is used with the convolution shape being same. On the other hand, when extend=true, the extended unfiltered signal is used with the convolution shape being valid.
 • The frame length $l$ and radius $r$ satisfy $l=2r+1$. Moreover, the size of the signal $m$ must satisfy $l\le m$, and the degree of the filtering polynomials $d$ must satisfy $d<2r$.
 • When a weights container is passed, its size corresponds to $l$ and must be an odd integer larger than 1. Internally, the weights will be normalized with respect to root mean square.
 • The signal and weights rtables cannot have an indexing function and must use Fortran ordering and rectangular storage.
 • Maple attempts to coerce signal and weights to an rtable of float[8] datatype, and an error is thrown if this is not possible. For this reason, it is most efficient for the passed containers to be rtables of this datatype.
 • The SavitzkyGolayFilter command is not thread safe.

Examples

 > $\mathrm{with}\left(\mathrm{SignalProcessing}\right):$

Example 1

 > $X≔\mathrm{GenerateSignal}\left({ⅇ}^{-0.5t}\mathrm{sin}\left(2t\right),t=0..2\mathrm{Pi},200,'\mathrm{noisedeviation}'=0.05\right)$
 > $d≔3$
 ${d}{≔}{3}$ (1)
 > $r≔25$
 ${r}{≔}{25}$ (2)
 > $\mathrm{SavitzkyGolayFilter}\left(X,d,r\right)$
 > $\mathrm{SavitzkyGolayFilter}\left(X,d,r,'\mathrm{output}'='\mathrm{plot}'\right)$

Example 2

 • Consider the following signal:
 > $f≔\mathrm{sin}\left(t\right)+\frac{1\mathrm{cos}\left(3t\right)}{5}$
 ${f}{≔}{\mathrm{sin}}{}\left({t}\right){+}\frac{{\mathrm{cos}}{}\left({3}{}{t}\right)}{{5}}$ (3)
 > $\mathrm{t1}≔0$
 ${\mathrm{t1}}{≔}{0}$ (4)
 > $\mathrm{t2}≔2\mathrm{Pi}$
 ${\mathrm{t2}}{≔}{2}{}{\mathrm{\pi }}$ (5)
 > $m≔200$
 ${m}{≔}{200}$ (6)
 > $T,X≔\mathrm{GenerateSignal}\left(f,t=\mathrm{t1}..\mathrm{t2},m,'\mathrm{noisedeviation}'=0.2,'\mathrm{output}'=\left['\mathrm{times}','\mathrm{signal}'\right]\right):$
 • Use of non-constant weights can improve the smoothing:
 > $d≔2$
 ${d}{≔}{2}$ (7)
 > $r≔10$
 ${r}{≔}{10}$ (8)
 > $W≔⟨\mathrm{seq}\left(1..r+1\right),\mathrm{seq}\left(r..1,-1\right)⟩:$
 > $\mathrm{SavitzkyGolayFilter}\left(X,d,r,'\mathrm{timerange}'=\mathrm{t1}..\mathrm{t2},'\mathrm{plotoptions}'=\left['\mathrm{title}'="Unweighted Savitzky-Golay Filtered Signal"\right],'\mathrm{output}'='\mathrm{plot}'\right)$
 > $\mathrm{SavitzkyGolayFilter}\left(X,d,W,'\mathrm{timerange}'=\mathrm{t1}..\mathrm{t2},'\mathrm{plotoptions}'=\left['\mathrm{title}'="Weighted Savitzky-Golay Filtered Signal"\right],'\mathrm{output}'='\mathrm{plot}'\right)$

Example 3

 • The Savitzky-Golay Filter can be used to determine the derivatives of a signal. For example, consider the following signal:
 > $g≔t→2\mathrm{cos}\left(t\right)+\mathrm{sin}\left(3t\right)$
 ${g}{≔}{t}{↦}{2}{\cdot }{\mathrm{cos}}{}\left({t}\right){+}{\mathrm{sin}}{}\left({3}{\cdot }{t}\right)$ (9)
 > $\mathrm{t1}≔-2\mathrm{Pi}$
 ${\mathrm{t1}}{≔}{-}{2}{}{\mathrm{\pi }}$ (10)
 > $\mathrm{t2}≔2\mathrm{Pi}$
 ${\mathrm{t2}}{≔}{2}{}{\mathrm{\pi }}$ (11)
 > $m≔500$
 ${m}{≔}{500}$ (12)
 > $T,X≔\mathrm{GenerateSignal}\left(g,\mathrm{t1}..\mathrm{t2},m,'\mathrm{includefinishtime}'='\mathrm{false}','\mathrm{output}'=\left['\mathrm{times}','\mathrm{signal}'\right]\right):$
 • Here, we will compare the un-extended and extended cases with the actual derivatives:
 > $d≔3$
 ${d}{≔}{3}$ (13)
 > $r≔20$
 ${r}{≔}{20}$ (14)
 > $\mathrm{Y1}≔\mathrm{SavitzkyGolayFilter}\left(X,d,r,'\mathrm{timerange}'=\mathrm{t1}..\mathrm{t2},'\mathrm{extend}'='\mathrm{false}','\mathrm{output}'='\mathrm{derivatives}'\right):$
 > $\mathrm{Y2}≔\mathrm{SavitzkyGolayFilter}\left(X,d,r,'\mathrm{timerange}'=\mathrm{t1}..\mathrm{t2},'\mathrm{extend}'='\mathrm{true}','\mathrm{extrapolation}'='\mathrm{periodic}','\mathrm{output}'='\mathrm{derivatives}'\right):$
 > $\mathrm{dg}≔\mathrm{Vector}\left(d,k→{\mathrm{D}}^{\left(k\right)}\left(g\right)\right):$
 > $Z≔\mathrm{Matrix}\left(m,d,\left(i,j\right)→{\mathrm{dg}}_{j}\left({T}_{i}\right),'\mathrm{datatype}'='{\mathrm{float}}_{8}'\right):$
 • As we can see, both versions are accurate but the extended one is more accurate at the ends of the time interval:
 > $\mathrm{dataplot}\left(T,\mathrm{Y1},'\mathrm{style}'='\mathrm{line}','\mathrm{legend}'=\left[\mathrm{seq}\left(\mathrm{sprintf}\left("%s derivative",\mathrm{convert}\left(i,'\mathrm{english}','\mathrm{ordinal}'\right)\right),i=1..d\right)\right],'\mathrm{title}'="Savitzky-Golay Derivatives Without Extension",'\mathrm{view}'=\left['\mathrm{DEFAULT}',\mathrm{min}\left(Z\right)..\mathrm{max}\left(Z\right)\right]\right)$
 > $\mathrm{dataplot}\left(T,\mathrm{Y2},'\mathrm{style}'='\mathrm{line}','\mathrm{legend}'=\left[\mathrm{seq}\left(\mathrm{sprintf}\left("%s derivative",\mathrm{convert}\left(i,'\mathrm{english}','\mathrm{ordinal}'\right)\right),i=1..d\right)\right],'\mathrm{title}'="Savitzky-Golay Derivatives With Extension",'\mathrm{view}'=\left['\mathrm{DEFAULT}',\mathrm{min}\left(Z\right)..\mathrm{max}\left(Z\right)\right]\right)$
 • Using relative root mean square error, we can quantify the accuracy:
 > $\left[\mathrm{seq}\left(\mathrm{RelativeRootMeanSquareError}\left({\mathrm{Y1}}_{\left(\right)..\left(\right),j},{Z}_{\left(\right)..\left(\right),j}\right),j=1..d\right)\right]$
 $\left[{0.343537779779587105}{,}{0.393736670852185722}{,}{0.940391637995256491}\right]$ (15)
 > $\left[\mathrm{seq}\left(\mathrm{RelativeRootMeanSquareError}\left({\mathrm{Y2}}_{\left(\right)..\left(\right),j},{Z}_{\left(\right)..\left(\right),j}\right),j=1..d\right)\right]$
 $\left[{0.0104018750855234855}{,}{0.158908529185620623}{,}{0.130136176920078145}\right]$ (16)
 • As expected, though, the unextended version is very accurate away from the endpoints:
 > $\left[\mathrm{seq}\left(\mathrm{RelativeRootMeanSquareError}\left({\mathrm{Y1}}_{r+1..-\left(r+1\right),j},{Z}_{r+1..-\left(r+1\right),j}\right),j=1..d\right)\right]$
 $\left[{0.0102865451746987236}{,}{0.159079567157564300}{,}{0.130110621919648534}\right]$ (17)

Example 4

 • The linear recurrence relation corresponding to the filter can be returned:
 > ${\mathrm{req}}_{1}≔\mathrm{SavitzkyGolayFilter}\left(⟨\mathrm{seq}\left(7+{\left(-1\right)}^{k},k=1..25\right)⟩,2,2,'\mathrm{recurrencevariables}'=\left['i','u','v'\right],'\mathrm{output}'='\mathrm{recurrence}'\right)$
 ${{\mathrm{req}}}_{{1}}{≔}{{v}}_{{i}}{=}{-}{0.0857142857142857}{}{{u}}_{{i}{-}{2}}{+}{0.342857142857143}{}{{u}}_{{i}{-}{1}}{+}{0.485714285714286}{}{{u}}_{{i}}{+}{0.342857142857143}{}{{u}}_{{i}{+}{1}}{-}{0.0857142857142857}{}{{u}}_{{i}{+}{2}}$ (18)
 • In terms of fractions:
 > ${\mathrm{req}}_{2}≔\mathrm{subsindets}\left({\mathrm{req}}_{1},'\mathrm{float}',\mathrm{identify}\right)$
 ${{\mathrm{req}}}_{{2}}{≔}{{v}}_{{i}}{=}{-}\frac{{3}{}{{u}}_{{i}{-}{2}}}{{35}}{+}\frac{{12}{}{{u}}_{{i}{-}{1}}}{{35}}{+}\frac{{17}{}{{u}}_{{i}}}{{35}}{+}\frac{{12}{}{{u}}_{{i}{+}{1}}}{{35}}{-}\frac{{3}{}{{u}}_{{i}{+}{2}}}{{35}}$ (19)

Example 5

 • And finally, an example included merely because it looks cool:
 > $\mathrm{SavitzkyGolayFilter}\left(\mathrm{Array}\left(1..100,k→{\left(-1\right)}^{k}\right),2,15,'\mathrm{output}'='\mathrm{plot}'\right)$

References

 Orfanidis, Sophocles J. Introduction to Signal Processing. Pearson Education, Inc. (2009).

Compatibility

 • The SignalProcessing[SavitzkyGolayFilter] command was introduced in Maple 2023.