Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I do have noisy data and want to smooth them by a Savitzky-Golay filter because I want to keep the magnitude of the signal.

a) Is there a ready-to-use Filter available for that?

b) what are appropriate values for m (the half width) and for the coefficients for 3000-4000 data points?

Example data: data

share|improve this question
3  
There is one here library.wolfram.com/infocenter/MathSource/789 –  Nasser Nov 19 '13 at 22:55
    
Smoothing data depends on lots of things - for example if you are recording the data in cm^-1, but you actually need the spectrum in nm. Describe the specific applications and also the sampling rate as there are parameters that depend on it. Also have you considered using other tools to smooth your data ? –  Sektor Nov 19 '13 at 22:55
    
@Nasser: found that link, but the m-file can not be downloaded. –  Shukoff Nov 19 '13 at 23:01
2  
the m-file can not be downloaded It worked for me, just downloaded it the m file and the .nb file. The trick is to right-click->Save Link As... and not to click on it. –  Nasser Nov 19 '13 at 23:11
5  
Here is a link to some code posted on mathgroup that I have used before and found ok: forums.wolfram.com/mathgroup/archive/2012/Feb/msg00036.html –  Mike Honeychurch Nov 19 '13 at 23:46
show 9 more comments

1 Answer 1

up vote 10 down vote accepted

The following code will filter noisy data…

SGKernel[left_?NonNegative, right_?NonNegative, degree_?NonNegative, derivative_? NonNegative] :=
Module[{i, j, k, l, matrix, vector},
    matrix = Table[  (* matrix is symmetric *)
        l = i + j;
        If[l == 0,
            left + right + 1,
        (*Else*)
            Sum[k^l, {k, -left, right}]
        ],
        {i, 0, degree},
        {j, 0, degree}
    ];
    vector = LinearSolve[
        matrix, 
        MapAt[1&, Table[0, {degree+1}], derivative+1]
    ];
    (* vector = Inverse[matrix][[derivative + 1]]; *)
    Table[
        vector.Table[If[i == 0, 1, k^i], {i, 0, degree}],
        {k, -left, right}
    ]
] /; derivative <= degree <= left+right


SGSmooth[list_?VectorQ, window_, degree_, derivative_:0]:=
    Module[{pairs},
        pairs = MapThread[List, {Range[Length[list]], list}];
        Map[Last, SGSmooth[pairs, window, degree, derivative]]
    ] 


SGSmooth[list_?MatrixQ, window_, degree_, derivative_:0]:=
    Module[{kernel, list1, list2, margin, space, smoothData},

        (* determine a symmetric margin at the ends of the raw dataset.
        The window width is split in half to make a symmetric window 
        around a data point of interest *)
        margin = Floor[window/2];

        (* take only the 1st column of data in the list to be smoothed (the
        independant Values) and extract the data from the list by removing
        half the window width 'i.e., margin' from the ends of the list *)
        list1 = Take[Map[First, list], {margin + 1, Length[list] - margin}];

        (* take only the 2nd column of data in the list to be smoothed 
        (the dependent Values) and Map them into list2 *)
        list2 = Map[Last, list];

        (* get the kernel coefficients for the left and right margins, the 
           degree, and the requested derivative *)
        kernel = SGKernel[margin, margin, degree, derivative];

        (* correlation of the kernel with the list of dependent values *)
        list2 = ListCorrelate[kernel, list2];

        (* Data _should_ be equally spaced, but... calculate spacing anyway by getting
           the minimum of all the differences in the truncated list1, remove the first 
           and last points of list1 *)
        space = Min[Drop[list1, 1] - Drop[list1, -1]];

        (* condition the dependant values based on spacing and the derivative *)
        list2 = list2*(derivative!/space^derivative);

        (* recombine the correlated (x-y) data pairs (that is list1 and list2), 
        put these values back together again to form the smooth data list *)
        smoothData=Transpose[{list1, list2}]


    ] /; derivative <= degree <= 2*Floor[window/2] && $VersionNumber >= 4.0

I did not apply this to your data, but you can do that later. This example is applied to noisy random data.

Using a noisy sine data function…

dataFunction[x_] := Sin[x] + Random[Real, {-\[Pi], \[Pi]}];

Build a table of noisy tabular data from 0 to 2[Pi]…

dataTable = Table[{x, dataFunction[x]}, {x, 0, 2 \[Pi], .01}];

Animate the smoothing operations. Notice the smoothed dataset shrinks with increasing 'window width'. This is an artifact of the ListCorrelate function used in the SGSmooth function. ListCorrelate uses an end buffered dataset.

NOTE: The red line is the filtered data set…

 Manipulate[
 If[showRawData,
  Show[
   ListPlot[dataTable, PlotRange -> {{0, 2 \[Pi]}, {-5.0, 5.0}}],
   ListPlot[
    {
     SGSmooth[dataTable, win, order]
     },
    PlotRange -> {{0, 2 \[Pi]}, {-5.0, 5.0}},
    PlotStyle -> {{Red, Thick}, {Green, Thick}},
    Joined -> True]
   ], (* ELSE just plot smooth data *)
  ListPlot[
   {
    SGSmooth[dataTable, win, order]
    },
   PlotRange -> {{0, 2 \[Pi]}, {-5.0, 5.0}},
   PlotStyle -> {{Red, Thick}, {Green, Thick}},
   Joined -> True]
  ],

 {{win, 100, "window width"}, 2, 300, 1, 
  Appearance -> "Labeled"}, {{order, 1, "order of polynomial"}, 1, 9, 
  1, Appearance -> "Labeled"},
 {{showRawData, True, "Raw Data"}, {True, False}},
 SaveDefinitions -> True
 ]

This will create a Manipulate similar to the following:

enter image description here

Hope this helps!

share|improve this answer
    
Great!. Thanks for sharing the filter. The last Definition of SGSmooth has a ; too much preventing any output. –  Markus Roellig Nov 20 '13 at 19:47
    
@MarkusRoellig thank you finding this little typo. I fixed the above code to plot the smoothed data. I had originally used SGSmooth[] with additional embedded code and did not remove the ';'. My apologies... –  Joseph Nov 21 '13 at 1:43
    
@Joseph: Thank you, that answer is great! Just a question: It is described in literature that the calculation of the derivatives can be directly implemented in the SG-filter (see: pubs.acs.org/doi/abs/10.1021/ac50031a048). I guess this would be an expansion of your solution, right? If so, I open another question and let you know? –  Shukoff Nov 25 '13 at 15:06
    
@Shukoff: You can use the existing code as is to calculate the data derivative. That is the beauty of the SG filter. This implementation is very similar to the Numerical Recipes implemetation (wire.tu-bs.de/OLDWEB/mameyer/cmr/savgol.pdf). Just use: SGSmooth[dataTable, win, order, 1] for 1st derivative, SGSmooth[dataTable, win, order, 2] for 2nd derivative, etc... –  Joseph Nov 25 '13 at 15:14
    
@Joseph: your implementation is superb! Thanks! –  Shukoff Nov 25 '13 at 15:16
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.