source: branches/DataPreprocessing/HeuristicLab.DataPreprocessing/3.4/Implementations/TransactionalPreprocessingData.cs @ 11002

Last change on this file since 11002 was 11002, checked in by mleitner, 8 years ago

Refactoring

File size: 11.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;
24using System.Collections.Generic;
25using System.Globalization;
26using System.Linq;
27using HeuristicLab.Common;
28using HeuristicLab.Core;
29using HeuristicLab.Data;
30using HeuristicLab.Problems.DataAnalysis;
31using HeuristicLab.Problems.DataAnalysis.Transformations;
32
33namespace HeuristicLab.DataPreprocessing {
34
35  [Item("PreprocessingData", "Represents data used for preprocessing.")]
36  public class TransactionalPreprocessingData : PreprocessingData, ITransactionalPreprocessingData {
37
38    private class Snapshot {
39      public IList<IList> VariableValues { get; set; }
40      public IList<string> VariableNames { get; set; }
41
42      public IntRange TrainingPartition { get; set; }
43      public IntRange TestPartition { get; set; }
44      public IList<ITransformation> Transformations { get; set; }
45      public DataPreprocessingChangedEventType ChangedType { get; set; }
46
47      public int ChangedColumn { get; set; }
48      public int ChangedRow { get; set; }
49    }
50
51    private const int MAX_UNDO_DEPTH = 5;
52
53    private readonly IList<Snapshot> undoHistory = new List<Snapshot>();
54    private readonly Stack<DataPreprocessingChangedEventType> eventStack = new Stack<DataPreprocessingChangedEventType>();
55
56    public bool IsInTransaction { get { return eventStack.Count > 0; } }
57
58    public TransactionalPreprocessingData(IDataAnalysisProblemData problemData)
59      : base(problemData) {
60    }
61
62    private TransactionalPreprocessingData(TransactionalPreprocessingData original, Cloner cloner)
63      : base(original, cloner) {
64     }
65
66    private void SaveSnapshot(DataPreprocessingChangedEventType changedType, int column, int row) {
67      if (IsInTransaction) return;
68
69      var currentSnapshot = new Snapshot {
70        VariableValues = CopyVariableValues(variableValues),
71        VariableNames = new List<string>(variableNames),
72        TrainingPartition = new IntRange(TrainingPartition.Start, TrainingPartition.End),
73        TestPartition = new IntRange(TestPartition.Start, TestPartition.End),
74        Transformations = new List<ITransformation>(transformations),
75        ChangedType = changedType,
76        ChangedColumn = column,
77        ChangedRow = row
78      };
79
80      if (undoHistory.Count >= MAX_UNDO_DEPTH)
81        undoHistory.RemoveAt(0);
82
83      undoHistory.Add(currentSnapshot);
84    }
85
86    #region NamedItem abstract Member Implementations
87
88    public override IDeepCloneable Clone(Cloner cloner) {
89      return new TransactionalPreprocessingData(this, cloner);
90    }
91
92    #endregion
93
94    #region Overridden IPreprocessingData Members
95
96    public override T GetCell<T>(int columnIndex, int rowIndex)
97    {
98      return (T)variableValues[columnIndex][rowIndex];
99    }
100
101    public override void SetCell<T>(int columnIndex, int rowIndex, T value) {
102      SaveSnapshot(DataPreprocessingChangedEventType.ChangeItem, columnIndex, rowIndex);
103      variableValues[columnIndex][rowIndex] = value;
104      if (!IsInTransaction)
105        OnChanged(DataPreprocessingChangedEventType.ChangeItem, columnIndex, rowIndex);
106    }
107
108    public override string GetCellAsString(int columnIndex, int rowIndex)
109    {
110      return variableValues[columnIndex][rowIndex].ToString();
111    }
112
113    public override string GetVariableName(int columnIndex)
114    {
115      return variableNames[columnIndex];
116    }
117
118    public override int GetColumnIndex(string variableName)
119    {
120      return variableNames.IndexOf(variableName);
121    }
122
123    public override bool IsType<T>(int columnIndex)
124    {
125      return variableValues[columnIndex] is List<T>;
126    }
127
128    [Obsolete("use the index based variant, is faster")]
129    public override IList<T> GetValues<T>(string variableName, bool considerSelection)
130    {
131      return GetValues<T>(GetColumnIndex(variableName), considerSelection);
132    }
133
134    public override IList<T> GetValues<T>(int columnIndex, bool considerSelection)
135    {
136      if (considerSelection)
137      {
138        var list = new List<T>();
139        foreach (var rowIdx in selection[columnIndex])
140        {
141          list.Add((T)variableValues[columnIndex][rowIdx]);
142        }
143        return list;
144      }
145      else
146      {
147        return (IList<T>)variableValues[columnIndex];
148      }
149    }
150
151    public override void SetValues<T>(int columnIndex, IList<T> values) {
152      SaveSnapshot(DataPreprocessingChangedEventType.ChangeColumn, columnIndex, -1);
153      if (IsType<T>(columnIndex))
154      {
155        variableValues[columnIndex] = (IList)values;
156      }
157      else
158      {
159        throw new ArgumentException("The datatype of column " + columnIndex + " must be of type " + variableValues[columnIndex].GetType().Name + " but was " + typeof(T).Name);
160      }
161      if (!IsInTransaction)
162        OnChanged(DataPreprocessingChangedEventType.ChangeColumn, columnIndex, -1);
163    }
164
165    public override bool SetValue(string value, int columnIndex, int rowIndex) {
166      bool valid = false;
167      if (IsType<double>(columnIndex)) {
168        double val;
169        valid = double.TryParse(value, out val);
170        SetValueIfValid(columnIndex, rowIndex, valid, val);
171      } else if (IsType<string>(columnIndex)) {
172        valid = value != null;
173        SetValueIfValid(columnIndex, rowIndex, valid, value);
174      } else if (IsType<DateTime>(columnIndex)) {
175        DateTime date;
176        valid = DateTime.TryParse(value, out date);
177        SetValueIfValid(columnIndex, rowIndex, valid, date);
178      } else {
179        throw new ArgumentException("column " + columnIndex + " contains a non supported type.");
180      }
181
182      if (!IsInTransaction)
183        OnChanged(DataPreprocessingChangedEventType.ChangeColumn, columnIndex, -1);
184
185      return valid;
186    }
187   
188    public override bool Validate(string value, out string errorMessage, int columnIndex){
189     if (columnIndex < 0 || columnIndex > VariableNames.Count()) {
190        throw new ArgumentOutOfRangeException("column index is out of range");
191      }
192
193      bool valid = false;
194      errorMessage = string.Empty;
195      if (IsType<double>(columnIndex)) {
196        double val;
197        valid = double.TryParse(value, out val);
198        if (!valid) {
199          errorMessage = "Invalid Value (Valid Value Format: \"" + FormatPatterns.GetDoubleFormatPattern() + "\")";
200        }
201      } else if (IsType<string>(columnIndex)) {
202        valid = value != null;
203        if (!valid) {
204          errorMessage = "Invalid Value (string must not be null)";
205        }
206      } else if (IsType<DateTime>(columnIndex)) {
207        DateTime date;
208        valid = DateTime.TryParse(value, out date);
209        if (!valid) {
210          errorMessage = "Invalid Value (Valid Value Format: \"" + CultureInfo.CurrentCulture.DateTimeFormat + "\"";
211        }
212      } else {
213        throw new ArgumentException("column " + columnIndex + " contains a non supported type.");
214      }
215
216      return valid;
217    }
218
219    private void SetValueIfValid<T>(int columnIndex, int rowIndex, bool valid, T value) {
220      if (valid)
221        SetCell<T>(columnIndex, rowIndex, value);
222    }
223
224    public override bool AreAllStringColumns(IEnumerable<int> columnIndices) {
225      return columnIndices.All(x => IsType<string>(x));
226    }
227
228    public override void DeleteRowsWithIndices(IEnumerable<int> rows) {
229      foreach (int rowIndex in rows) {
230        DeleteRow(rowIndex);
231      }
232    }
233
234    public override void InsertRow(int rowIndex) {
235      SaveSnapshot(DataPreprocessingChangedEventType.DeleteRow, -1, rowIndex);
236      foreach (IList column in variableValues)
237      {
238        Type type = column.GetType().GetGenericArguments()[0];
239        column.Insert(rowIndex, type.IsValueType ? Activator.CreateInstance(type) : null);
240      }
241      if (!IsInTransaction)
242        OnChanged(DataPreprocessingChangedEventType.AddRow, -1, rowIndex);
243    }
244
245    public override void DeleteRow(int rowIndex) {
246      SaveSnapshot(DataPreprocessingChangedEventType.AddRow, -1, rowIndex);
247      foreach (IList column in variableValues)
248      {
249        column.RemoveAt(rowIndex);
250      }
251      if (!IsInTransaction)
252        OnChanged(DataPreprocessingChangedEventType.DeleteRow, -1, rowIndex);
253    }
254
255    public override void InsertColumn<T>(string variableName, int columnIndex) {
256      SaveSnapshot(DataPreprocessingChangedEventType.DeleteColumn, columnIndex, -1);
257      variableValues.Insert(columnIndex, new List<T>(Rows));
258      variableNames.Insert(columnIndex, variableName);
259      if (!IsInTransaction)
260        OnChanged(DataPreprocessingChangedEventType.AddColumn, columnIndex, -1);
261    }
262
263    public override void DeleteColumn(int columnIndex) {
264      SaveSnapshot(DataPreprocessingChangedEventType.AddColumn, columnIndex, -1);
265      variableValues.RemoveAt(columnIndex);
266      variableNames.RemoveAt(columnIndex);
267      if (!IsInTransaction)
268        OnChanged(DataPreprocessingChangedEventType.DeleteColumn, columnIndex, -1);
269    }
270
271    public override Dataset ExportToDataset()
272    {
273      IList<IList> values = new List<IList>();
274
275      for (int i = 0; i < Columns; ++i)
276      {
277        values.Add(variableValues[i]);
278      }
279
280      var dataset = new Dataset(variableNames, values);
281      return dataset;
282    }
283
284    public override void ClearSelection()
285    {
286      Selection = new Dictionary<int, IList<int>>();
287    }
288
289    public override event EventHandler SelectionChanged;
290
291    protected override void OnSelectionChanged()
292    {
293      var listeners = SelectionChanged;
294      if (listeners != null) listeners(this, EventArgs.Empty);
295    }
296
297
298    #endregion
299
300    #region TransactionalPreprocessingData members
301
302    public bool IsUndoAvailable {
303      get { return undoHistory.Count > 0; }
304    }
305
306    public void Undo() {
307      if (IsUndoAvailable) {
308        Snapshot previousSnapshot = undoHistory[undoHistory.Count - 1];
309        variableValues = previousSnapshot.VariableValues;
310        variableNames = previousSnapshot.VariableNames;
311        TrainingPartition = previousSnapshot.TrainingPartition;
312        TestPartition = previousSnapshot.TestPartition;
313        transformations = previousSnapshot.Transformations;
314        undoHistory.Remove(previousSnapshot);
315        OnChanged(previousSnapshot.ChangedType,
316          previousSnapshot.ChangedColumn,
317          previousSnapshot.ChangedRow);
318      }
319    }
320
321    public void InTransaction(Action action, DataPreprocessingChangedEventType type = DataPreprocessingChangedEventType.Any) {
322      BeginTransaction(type);
323      action();
324      EndTransaction();
325    }
326
327    public void BeginTransaction(DataPreprocessingChangedEventType type) {
328      SaveSnapshot(type, -1, -1);
329      eventStack.Push(type);
330    }
331
332    public void EndTransaction() {
333      if (eventStack.Count == 0)
334        throw new InvalidOperationException("There is no open transaction that can be ended.");
335
336      var @event = eventStack.Pop();
337      OnChanged(@event, -1, -1);
338    }
339
340    #endregion
341  }
342}
Note: See TracBrowser for help on using the repository browser.