Free cookie consent management tool by TermsFeed Policy Generator

source: branches/2389-EpsLexicase/HeuristicLab.Optimization.Views/3.3/RunCollectionViews/RunCollectionResultAggregationView.cs

Last change on this file was 15940, checked in by bburlacu, 6 years ago

#2921: Implement result aggregation view.

File size: 9.0 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Windows.Forms;
5using HeuristicLab.Common;
6using HeuristicLab.Core;
7using HeuristicLab.Data;
8using HeuristicLab.MainForm;
9using HeuristicLab.MainForm.WindowsForms;
10
11namespace HeuristicLab.Optimization.Views.RunCollectionViews {
12  [View("Result Aggregation")]
13  [Content(typeof(RunCollection), false)]
14  public partial class RunCollectionResultAggregationView : AsynchronousContentView {
15    private enum AggregationMethod { Minimum, Maximum, Average, FirstQuartile, Median, ThirdQuartile, IQR, StdDev }
16
17    private readonly Dictionary<AggregationMethod, Func<IEnumerable<double>, double>> aggregator = new Dictionary<AggregationMethod, Func<IEnumerable<double>, double>> {
18      { AggregationMethod.Minimum, x => x.Min() },
19      { AggregationMethod.Maximum, x => x.Max() },
20      { AggregationMethod.Average, x => x.Average() },
21      { AggregationMethod.FirstQuartile, x => x.Quantile(0.25) },
22      { AggregationMethod.Median, x => x.Median() },
23      { AggregationMethod.ThirdQuartile, x => x.Quantile(0.75) },
24      { AggregationMethod.IQR, x => x.Quantile(0.75) - x.Quantile(0.25) },
25      { AggregationMethod.StdDev, x => x.StandardDeviation() },
26    };
27
28    public RunCollectionResultAggregationView() {
29      InitializeComponent();
30    }
31
32    public new RunCollection Content {
33      get { return (RunCollection)base.Content; }
34      set { base.Content = value; }
35    }
36
37    protected override void OnContentChanged() {
38      var results = GetCompatibleResults(Content);
39
40      statisticsCheckedList.Items.Clear();
41      resultsCheckedList.Items.Clear();
42
43      foreach (var name in Enum.GetNames(typeof(AggregationMethod))) {
44        statisticsCheckedList.Items.Add(name, false);
45      }
46
47      foreach (var name in GetCompatibleResults(Content).OrderBy(x => x))
48        resultsCheckedList.Items.Add(name, false);
49
50      firstCriterionComboBox.Items.Clear();
51      secondCriterionComboBox.Items.Clear();
52
53      firstCriterionComboBox.Items.AddRange(Content.ParameterNames.OrderBy(x => x).ToArray());
54      secondCriterionComboBox.Items.AddRange(Content.ParameterNames.OrderBy(x => x).ToArray());
55
56      base.OnContentChanged();
57    }
58
59    private static IEnumerable<string> GetCompatibleResults(RunCollection runs) {
60      var results = new HashSet<string>(runs.First().Results.Where(x => IsCompatible(x.Value)).Select(x => x.Key));
61
62      foreach (var run in runs.Skip(1)) {
63        results.IntersectWith(run.Results.Keys);
64      }
65
66      return results;
67    }
68
69    // return only results of a numeric type
70    private static bool IsCompatible(IItem item) {
71      return item is IntValue || item is DoubleValue || item is PercentValue || item is TimeSpanValue;
72    }
73
74    private void AggregateResults() {
75      if (!(statisticsCheckedList.CheckedItems.Count > 0 && resultsCheckedList.CheckedItems.Count > 0 && !string.IsNullOrEmpty(firstCriterionComboBox.Text)))
76        return;
77
78      var groups = new Dictionary<string, IEnumerable<IRun>>();
79      var first = Content.GroupBy(run => Content.GetValue(run, firstCriterionComboBox.Text).ToString());
80
81      if (!string.IsNullOrEmpty(secondCriterionComboBox.Text) && secondCriterionComboBox.Text != firstCriterionComboBox.Text) {
82        foreach (var f in first) {
83          foreach (var g in f.GroupBy(run => Content.GetValue(run, secondCriterionComboBox.Text).ToString())) {
84            var key = f.Key + " " + g.Key;
85            groups.Add(key, g);
86          }
87        }
88      } else {
89        groups = first.ToDictionary(x => x.Key, x => x.AsEnumerable());
90      }
91
92      var aggregatedDict = new Dictionary<string, List<Tuple<string, double>>>();
93
94      foreach (var result in resultsCheckedList.CheckedItems) {
95        var resultName = result.ToString();
96        foreach (var group in groups) {
97          var values = GetValues(group.Value, resultName);
98
99          var key = group.Key;
100          if (!aggregatedDict.ContainsKey(key)) {
101            aggregatedDict[key] = new List<Tuple<string, double>>();
102          }
103
104          foreach (var i in statisticsCheckedList.CheckedIndices) {
105            var name = resultName + " " + (AggregationMethod)i;
106            aggregatedDict[key].Add(Tuple.Create(name, aggregator[(AggregationMethod)i](values)));
107          }
108        }
109      }
110      var groupNames = aggregatedDict.Keys.ToList();
111      var resultNames = aggregatedDict.First().Value.Select(x => x.Item1).ToList();
112
113      if (!aggregatedDict.Values.Skip(1).All(x => resultNames.SequenceEqual(x.Select(y => y.Item1)))) {
114        throw new Exception("Inconsistent results across groups.");
115      }
116
117      var rows = resultNames.Count;
118      var cols = groups.Count;
119
120      var matrix = new DoubleMatrix(rows, cols);
121
122      var col = 0;
123      foreach (var groupName in groupNames) {
124        var row = 0;
125        foreach (var resultPair in aggregatedDict[groupName]) {
126          matrix[row, col] = resultPair.Item2;
127          ++row;
128        }
129        ++col;
130      }
131      matrix.RowNames = resultNames;
132      matrix.ColumnNames = groupNames;
133
134      aggregatedResultsMatrixView.Content = transposeMatrixCheckBox.Checked ? Transpose(matrix) : matrix;
135    }
136
137    private IEnumerable<double> GetValues(IEnumerable<IRun> runs, string resultName) {
138      var values = new List<double>();
139
140      foreach (var run in runs) {
141        var value = Content.GetValue(run, resultName);
142        var doubleValue = value as DoubleValue;
143        var intValue = value as IntValue;
144        var percentValue = value as PercentValue;
145        var timeSpanValue = value as TimeSpanValue;
146
147        if (doubleValue != null) {
148          values.Add(doubleValue.Value);
149        } else if (intValue != null) {
150          values.Add(intValue.Value);
151        } else if (percentValue != null) {
152          values.Add(percentValue.Value);
153        } else if (timeSpanValue != null) {
154          values.Add(timeSpanValue.Value.TotalSeconds);
155        }
156      }
157      return values;
158    }
159
160    private static DoubleMatrix Transpose(DoubleMatrix matrix) {
161      var transposed = new DoubleMatrix(matrix.Columns, matrix.Rows) {
162        RowNames = matrix.ColumnNames,
163        ColumnNames = matrix.RowNames
164      };
165
166      for (int i = 0; i < matrix.Rows; ++i) {
167        for (int j = 0; j < matrix.Columns; ++j) {
168          transposed[j, i] = matrix[i, j];
169        }
170      }
171
172      return transposed;
173    }
174
175    #region events
176    private void aggregateByComboBox_SelectedIndexChanged(object sender, EventArgs e) {
177      AggregateResults();
178    }
179
180    private void resultsCheckedList_ItemCheck(object sender, ItemCheckEventArgs e) {
181      BeginInvoke((MethodInvoker)AggregateResults); // delayed execution
182    }
183
184    private void statisticsToCalculateCheckBox_ItemCheck(object sender, ItemCheckEventArgs e) {
185      BeginInvoke((MethodInvoker)AggregateResults); // delayed execution
186    }
187
188    private void criterionComboBox_SelectedIndexChanged(object sender, EventArgs e) {
189      AggregateResults();
190    }
191
192    private void transposeMatrixCheckBox_CheckedChanged(object sender, EventArgs e) {
193      if (aggregatedResultsMatrixView.Content == null)
194        return;
195
196      var matrix = (DoubleMatrix)aggregatedResultsMatrixView.Content;
197      aggregatedResultsMatrixView.Content = Transpose(matrix);
198    }
199
200    private void orderByStatisticCheckbox_CheckedChanged(object sender, EventArgs e) {
201      if (!orderByStatisticCheckbox.Checked) {
202        AggregateResults();
203        return;
204      }
205
206      // we reorder the matrix rows (or columns, if transposed) so that every n_th rows (column) become successive
207      // the number of rows (columns, if transposed) in the matrix is a multiple of n
208      var matrix = (DoubleMatrix)aggregatedResultsMatrixView.Content;
209      var rowNames = matrix.RowNames.ToList();
210      var columnNames = matrix.ColumnNames.ToList();
211
212      var ordered = new DoubleMatrix(matrix.Rows, matrix.Columns);
213      var orderedNames = new List<string>();
214
215      var transposed = transposeMatrixCheckBox.Checked;
216      var count = statisticsCheckedList.CheckedItems.Count;
217      var outerCount = transposed ? matrix.Columns : matrix.Rows;
218      var innerCount = transposed ? matrix.Rows : matrix.Columns;
219
220      for (int i = 0; i < count; ++i) {
221        for (int j = i; j < outerCount; j += count) {
222          var c = orderedNames.Count;
223          for (int k = 0; k < innerCount; ++k) {
224            if (transposed)
225              ordered[k, c] = matrix[k, j];
226            else
227              ordered[c, k] = matrix[j, k];
228          }
229          orderedNames.Add(transposed ? columnNames[j] : rowNames[j]);
230        }
231      }
232
233      if (transposed) {
234        ordered.RowNames = matrix.RowNames;
235        ordered.ColumnNames = orderedNames;
236      } else {
237        ordered.ColumnNames = matrix.ColumnNames;
238        ordered.RowNames = orderedNames;
239      }
240
241      aggregatedResultsMatrixView.Content = ordered;
242    }
243    #endregion
244  }
245}
Note: See TracBrowser for help on using the repository browser.