Free cookie consent management tool by TermsFeed Policy Generator

source: addons/HeuristicLab.DataImporter/HeuristicLab.DataImporter.Command/ChangeValues/FilterSavitzkyGolayCommand.cs @ 16566

Last change on this file since 16566 was 16566, checked in by gkronber, 5 years ago

#2520: updated HeuristicLab.DataImporter addon for new Persistence (introduced in 3.3.16)

File size: 6.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2013 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using HeuristicLab.DataImporter.Command.View;
26using HeuristicLab.DataImporter.Data;
27using HeuristicLab.DataImporter.Data.CommandBase;
28using HeuristicLab.DataImporter.Data.Model;
29using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
30using HEAL.Attic;
31
32namespace HeuristicLab.DataImporter.Command {
33  [StorableType("28A12839-2A01-45BB-836F-05A2E71BF672")]
34  [ViewableCommandInfoAttribute("Savitzky Golay", 1, ColumnGroupState.DoubleColumnSelected, "Change Values", Position = 2,
35    OptionsView = typeof(FilterSavitzkyGolayCommandView))]
36  public class FilterSavitzkyGolayCommand : ColumnGroupCommandWithAffectedColumnsBase {
37    private Dictionary<int, DoubleColumn> oldColumns;
38    private ICollection<int> oldSortedColumnIndices;
39
40    [Storable]
41    public int WindowLeft { get; set; }
42    [Storable]
43    public int WindowRight { get; set; }
44    [Storable]
45    public int Order { get; set; }
46    [Storable]
47    public int OrderOfDerivative { get; set; }
48
49    [StorableConstructor]
50    protected FilterSavitzkyGolayCommand(bool deserializing)
51      : base(deserializing) {
52      oldColumns = new Dictionary<int, DoubleColumn>();
53    }
54
55    public FilterSavitzkyGolayCommand(DataSet dataSet, string columnGroupName, int[] affectedColumns)
56      : base(dataSet, columnGroupName, affectedColumns) {
57      oldColumns = new Dictionary<int, DoubleColumn>();
58      this.WindowLeft = -16;
59      this.WindowRight = 16;
60      this.Order = 2;
61      this.OrderOfDerivative = 0;
62    }
63
64    public override string Description {
65      get { return "Filter Column - Savitzky Golay"; }
66    }
67
68    public override void Execute() {
69      base.Execute();
70      if (this.WindowLeft >= WindowRight)
71        throw new CommandExecutionException("Window size for filter commands must no be smaller than 1.", this);
72      foreach (int col in AffectedColumns) {
73        DoubleColumn doubleCol = ColumnGroup.GetColumn(col) as DoubleColumn;
74        if (doubleCol == null) throw new CommandExecutionException("Filtering is only supported for double columns.", this);
75      }
76      DoubleColumn column;
77      oldSortedColumnIndices = new List<int>(ColumnGroup.SortedColumnIndexes);
78      foreach (int col in AffectedColumns) {
79        if (ColumnGroup.GetColumn(col) is DoubleColumn) {
80          column = (DoubleColumn)ColumnGroup.GetColumn(col);
81          oldColumns.Add(col, column);
82          ColumnGroup.ReplaceColumn(col, CalcNewColumn(column, this.WindowLeft, this.WindowRight, this.OrderOfDerivative, this.Order));
83        }
84      }
85      ColumnGroup.SortedColumnIndexes = oldSortedColumnIndices;
86      ColumnGroup.FireChanged();
87      ColumnGroup = null;
88    }
89
90    public override void UndoExecute() {
91      base.UndoExecute();
92      foreach (KeyValuePair<int, DoubleColumn> pair in oldColumns)
93        ColumnGroup.ReplaceColumn(pair.Key, pair.Value);
94
95      ColumnGroup.SortedColumnIndexes = oldSortedColumnIndices;
96      oldSortedColumnIndices = null;
97      oldColumns.Clear();
98      ColumnGroup.FireChanged();
99      ColumnGroup = null;
100    }
101
102    private DoubleColumn CalcNewColumn(DoubleColumn oldColumn, int windowLeft, int windowRight, int derivativeOrder, int order) {
103      double[] c;
104      SavitzkyGolay(Math.Abs(windowLeft), Math.Abs(windowRight), derivativeOrder, order, out c);
105
106      DoubleColumn newCol = (DoubleColumn)oldColumn.CreateCopyOfColumnWithoutValues();
107      double[] data = oldColumn.ValuesEnumerable.Cast<double?>().Select(x => x.HasValue ? x.Value : double.NaN).ToArray();
108      double[] ans = new double[oldColumn.TotalValuesCount];
109      alglib.convr1d(data, data.Length, c, c.Length, out ans);
110      for (int i = Math.Abs(windowRight); i < ans.Length - Math.Abs(windowLeft); i++) {
111        if (!double.IsNaN(ans[i]))
112          newCol.AddValue(ans[i]);
113        else
114          newCol.AddValueOrNull(null);
115      }
116      newCol.SortOrder = oldColumn.SortOrder;
117      return newCol;
118    }
119
120    /// <summary>
121    /// Calculates coefficients for Savitzky-Golay filtering. (Numerical Recipes, page 769), one important change is that the coefficients are returned in normal order instead of wraparound order
122    /// </summary>
123    /// <param name="nl">number of samples to the left</param>
124    /// <param name="nr">number of samples to the right</param>
125    /// <param name="ld">order of derivative (smoothing=0)</param>
126    /// <param name="order">order of the polynomial to fit</param>
127    /// <param name="c">resulting coefficients for convolution, in correct order (t-nl, ... t-1, t+0, t+1, ... t+nr)</param>
128    private void SavitzkyGolay(int nl, int nr, int ld, int order, out double[] c) {
129      int np = nl + nr + 1;
130
131      int j, k, imj, ipj, kk, mm;
132      double fac = 0;
133      double sum = 0;
134      if (nl < 0 || nr < 0 || ld > order || nl + nr < order) throw new ArgumentException();
135
136      double[,] a = new double[order + 1, order + 1];
137      double[] b = new double[order + 1];
138      c = new double[np];
139
140      for (ipj = 0; ipj <= (order << 1); ipj++) {
141        sum = (ipj > 0 ? 0.0 : 1.0);
142        for (k = 1; k <= nr; k++) sum += Math.Pow((double)k, (double)ipj);
143        for (k = 1; k <= nl; k++) sum += Math.Pow((double)-k, (double)ipj);
144        mm = Math.Min(ipj, 2 * order - ipj);
145        for (imj = -mm; imj <= mm; imj += 2)
146          a[(ipj + imj) / 2, (ipj - imj) / 2] = sum;
147      }
148      for (j = 0; j < order + 1; j++) b[j] = 0;
149      b[ld] = 1.0;
150      alglib.densesolverreport rep;
151      int info;
152      double[] x = new double[b.Length];
153      alglib.rmatrixsolve(a, b.Length, b, out info, out rep, out x);
154
155      for (kk = 0; kk < np; kk++) c[kk] = 0.0;
156      for (k = -nl; k <= nr; k++) {
157        sum = x[0];
158        fac = 1.0;
159        for (mm = 1; mm <= order; mm++) sum += x[mm] * (fac *= k);
160        kk = k + nl;
161        c[kk] = sum;
162      }
163    }
164  }
165}
Note: See TracBrowser for help on using the repository browser.