Free cookie consent management tool by TermsFeed Policy Generator

source: branches/ParameterBinding/HeuristicLab.Optimization.Views/3.3/RunCollectionBoxPlotView.cs @ 11299

Last change on this file since 11299 was 4721, checked in by mkommend, 14 years ago

Added possiblity to hide statistics of RunCollectionBoxPlotView (ticket #1135).

File size: 16.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 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 System.Windows.Forms;
26using System.Windows.Forms.DataVisualization.Charting;
27using HeuristicLab.Common;
28using HeuristicLab.Core;
29using HeuristicLab.Data;
30using HeuristicLab.MainForm;
31using HeuristicLab.MainForm.WindowsForms;
32
33namespace HeuristicLab.Optimization.Views {
34  [View("RunCollection BoxPlots")]
35  [Content(typeof(RunCollection), false)]
36  public partial class RunCollectionBoxPlotView : AsynchronousContentView {
37    private enum AxisDimension { Color = 0 }
38    private const string BoxPlotSeriesName = "BoxPlotSeries";
39    private const string BoxPlotChartAreaName = "BoxPlotChartArea";
40
41    private string xAxisValue;
42    private string yAxisValue;
43    private Dictionary<int, Dictionary<object, double>> categoricalMapping;
44    private SortedDictionary<double, Series> seriesCache;
45
46    public RunCollectionBoxPlotView() {
47      InitializeComponent();
48      this.categoricalMapping = new Dictionary<int, Dictionary<object, double>>();
49      this.seriesCache = new SortedDictionary<double, Series>();
50      this.chart.ChartAreas[0].Visible = false;
51      this.chart.Series.Clear();
52      this.chart.ChartAreas.Add(BoxPlotChartAreaName);
53      this.chart.CustomizeAllChartAreas();
54    }
55
56    public new RunCollection Content {
57      get { return (RunCollection)base.Content; }
58      set { base.Content = value; }
59    }
60    public IStringConvertibleMatrix Matrix {
61      get { return this.Content; }
62    }
63
64    #region RunCollection and Run events
65    protected override void RegisterContentEvents() {
66      base.RegisterContentEvents();
67      Content.Reset += new EventHandler(Content_Reset);
68      Content.ColumnNamesChanged += new EventHandler(Content_ColumnNamesChanged);
69      Content.ItemsAdded += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsAdded);
70      Content.ItemsRemoved += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
71      Content.CollectionReset += new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
72      RegisterRunEvents(Content);
73    }
74    protected override void DeregisterContentEvents() {
75      base.DeregisterContentEvents();
76      Content.Reset -= new EventHandler(Content_Reset);
77      Content.ColumnNamesChanged -= new EventHandler(Content_ColumnNamesChanged);
78      Content.ItemsAdded -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsAdded);
79      Content.ItemsRemoved -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_ItemsRemoved);
80      Content.CollectionReset -= new HeuristicLab.Collections.CollectionItemsChangedEventHandler<IRun>(Content_CollectionReset);
81      DeregisterRunEvents(Content);
82    }
83
84    protected virtual void RegisterRunEvents(IEnumerable<IRun> runs) {
85      foreach (IRun run in runs)
86        run.Changed += new EventHandler(run_Changed);
87    }
88    protected virtual void DeregisterRunEvents(IEnumerable<IRun> runs) {
89      foreach (IRun run in runs)
90        run.Changed -= new EventHandler(run_Changed);
91    }
92
93    private void Content_CollectionReset(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
94      DeregisterRunEvents(e.OldItems);
95      RegisterRunEvents(e.Items);
96    }
97    private void Content_ItemsRemoved(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
98      DeregisterRunEvents(e.Items);
99    }
100    private void Content_ItemsAdded(object sender, HeuristicLab.Collections.CollectionItemsChangedEventArgs<IRun> e) {
101      RegisterRunEvents(e.Items);
102    }
103
104    private void Content_Reset(object sender, EventArgs e) {
105      if (InvokeRequired)
106        Invoke(new EventHandler(Content_Reset), sender, e);
107      else {
108        this.categoricalMapping.Clear();
109        UpdateDataPoints();
110      }
111    }
112    private void Content_ColumnNamesChanged(object sender, EventArgs e) {
113      if (InvokeRequired)
114        Invoke(new EventHandler(Content_ColumnNamesChanged), sender, e);
115      else {
116        UpdateComboBoxes();
117      }
118    }
119    private void run_Changed(object sender, EventArgs e) {
120      if (InvokeRequired)
121        this.Invoke(new EventHandler(run_Changed), sender, e);
122      else {
123        IRun run = (IRun)sender;
124        UpdateDataPoints();
125      }
126    }
127    #endregion
128
129    #region update comboboxes, datapoints, runs
130    protected override void OnContentChanged() {
131      base.OnContentChanged();
132      this.categoricalMapping.Clear();
133      UpdateComboBoxes();
134      UpdateDataPoints();
135    }
136
137    private void UpdateComboBoxes() {
138      string selectedXAxis = (string)this.xAxisComboBox.SelectedItem;
139      string selectedYAxis = (string)this.yAxisComboBox.SelectedItem;
140      this.xAxisComboBox.Items.Clear();
141      this.yAxisComboBox.Items.Clear();
142      if (Content != null) {
143        string[] additionalAxisDimension = Enum.GetNames(typeof(AxisDimension));
144        this.xAxisComboBox.Items.AddRange(additionalAxisDimension);
145        this.xAxisComboBox.Items.AddRange(Matrix.ColumnNames.ToArray());
146        this.yAxisComboBox.Items.AddRange(additionalAxisDimension);
147        this.yAxisComboBox.Items.AddRange(Matrix.ColumnNames.ToArray());
148
149        bool changed = false;
150        if (selectedXAxis != null && xAxisComboBox.Items.Contains(selectedXAxis)) {
151          xAxisComboBox.SelectedItem = selectedXAxis;
152          changed = true;
153        }
154        if (selectedYAxis != null && yAxisComboBox.Items.Contains(selectedYAxis)) {
155          yAxisComboBox.SelectedItem = selectedYAxis;
156          changed = true;
157        }
158        if (changed)
159          UpdateDataPoints();
160      }
161    }
162
163    private void UpdateDataPoints() {
164      this.chart.Series.Clear();
165      this.seriesCache.Clear();
166      if (Content != null) {
167        foreach (IRun run in this.Content.Where(r => r.Visible))
168          this.AddDataPoint(run);
169        foreach (Series s in this.seriesCache.Values)
170          this.chart.Series.Add(s);
171
172        UpdateStatistics();
173        if (seriesCache.Count > 0) {
174          Series boxPlotSeries = CreateBoxPlotSeries();
175          this.chart.Series.Add(boxPlotSeries);
176        }
177
178        UpdateAxisLabels();
179      }
180      UpdateNoRunsVisibleLabel();
181    }
182
183    private void UpdateStatistics() {
184      DoubleMatrix matrix = new DoubleMatrix(7, seriesCache.Count);
185      matrix.SortableView = false;
186      List<string> columnNames = new List<string>();
187      foreach (Series series in seriesCache.Values) {
188        DataPoint datapoint = series.Points.FirstOrDefault();
189        if (datapoint != null) {
190          IRun run = (IRun)datapoint.Tag;
191          string selectedAxis = (string)xAxisComboBox.SelectedItem;
192          IItem value = null;
193
194          if (Enum.IsDefined(typeof(AxisDimension), selectedAxis)) {
195            AxisDimension axisDimension = (AxisDimension)Enum.Parse(typeof(AxisDimension), selectedAxis);
196            switch (axisDimension) {
197              case AxisDimension.Color: value = new StringValue(run.Color.ToString());
198                break;
199            }
200          } else value = Content.GetValue(run, selectedAxis);
201          string columnName = string.Empty;
202          if (value is DoubleValue || value is IntValue)
203            columnName = selectedAxis + ": ";
204          columnName += value.ToString();
205          columnNames.Add(columnName);
206        }
207      }
208      matrix.ColumnNames = columnNames;
209      matrix.RowNames = new string[] { "Count", "Average", "Median", "Standard Deviation", "Variance", "25th Percentile", "75th Percentile" };
210
211      for (int i = 0; i < seriesCache.Count; i++) {
212        Series series = seriesCache.ElementAt(i).Value;
213        double[] seriesValues = series.Points.Select(p => p.YValues[0]).OrderBy(d => d).ToArray();
214        matrix[0, i] = seriesValues.Length;
215        matrix[1, i] = seriesValues.Average();
216        matrix[2, i] = seriesValues.Median();
217        matrix[3, i] = seriesValues.StandardDeviation();
218        matrix[4, i] = seriesValues.Variance();
219        matrix[5, i] = seriesValues.Percentile(0.25);
220        matrix[6, i] = seriesValues.Percentile(0.75);
221      }
222      statisticsMatrixView.Content = matrix;
223    }
224
225    private Series CreateBoxPlotSeries() {
226      Series boxPlotSeries = new Series(BoxPlotSeriesName);
227      string seriesNames = string.Concat(seriesCache.Keys.Select(x => x.ToString() + ";").ToArray());
228      seriesNames = seriesNames.Remove(seriesNames.Length - 1); //delete last ; from string
229
230      boxPlotSeries.ChartArea = BoxPlotChartAreaName;
231      boxPlotSeries.ChartType = SeriesChartType.BoxPlot;
232      boxPlotSeries["BoxPlotSeries"] = seriesNames;
233      boxPlotSeries["BoxPlotShowUnusualValues"] = "true";
234      boxPlotSeries["PointWidth"] = "0.4";
235      boxPlotSeries.BackGradientStyle = System.Windows.Forms.DataVisualization.Charting.GradientStyle.VerticalCenter;
236      boxPlotSeries.BackSecondaryColor = System.Drawing.Color.FromArgb(130, 224, 64, 10);
237      boxPlotSeries.BorderColor = System.Drawing.Color.FromArgb(64, 64, 64);
238      boxPlotSeries.Color = System.Drawing.Color.FromArgb(224, 64, 10);
239
240      return boxPlotSeries;
241    }
242
243    private void AddDataPoint(IRun run) {
244      double? xValue;
245      double? yValue;
246
247      if (!xAxisComboBox.DroppedDown)
248        this.xAxisValue = (string)xAxisComboBox.SelectedItem;
249      if (!yAxisComboBox.DroppedDown)
250        this.yAxisValue = (string)yAxisComboBox.SelectedItem;
251
252      xValue = GetValue(run, this.xAxisValue);
253      yValue = GetValue(run, this.yAxisValue);
254
255      if (xValue.HasValue && yValue.HasValue) {
256        if (!this.seriesCache.ContainsKey(xValue.Value))
257          seriesCache[xValue.Value] = new Series(xValue.Value.ToString());
258
259        Series series = seriesCache[xValue.Value];
260        DataPoint point = new DataPoint(xValue.Value, yValue.Value);
261        point.Tag = run;
262        series.Points.Add(point);
263      }
264    }
265    #endregion
266
267    #region get values from run
268    private double? GetValue(IRun run, string columnName) {
269      if (run == null || string.IsNullOrEmpty(columnName))
270        return null;
271
272      if (Enum.IsDefined(typeof(AxisDimension), columnName)) {
273        AxisDimension axisDimension = (AxisDimension)Enum.Parse(typeof(AxisDimension), columnName);
274        return GetValue(run, axisDimension);
275      } else {
276        int columnIndex = Matrix.ColumnNames.ToList().IndexOf(columnName);
277        IItem value = Content.GetValue(run, columnIndex);
278        if (value == null)
279          return null;
280
281        DoubleValue doubleValue = value as DoubleValue;
282        IntValue intValue = value as IntValue;
283        TimeSpanValue timeSpanValue = value as TimeSpanValue;
284        double? ret = null;
285        if (doubleValue != null) {
286          if (!double.IsNaN(doubleValue.Value) && !double.IsInfinity(doubleValue.Value))
287            ret = doubleValue.Value;
288        } else if (intValue != null)
289          ret = intValue.Value;
290        else if (timeSpanValue != null) {
291          ret = timeSpanValue.Value.TotalSeconds;
292        } else
293          ret = GetCategoricalValue(columnIndex, value.ToString());
294
295        return ret;
296      }
297    }
298    private double GetCategoricalValue(int dimension, string value) {
299      if (!this.categoricalMapping.ContainsKey(dimension))
300        this.categoricalMapping[dimension] = new Dictionary<object, double>();
301      if (!this.categoricalMapping[dimension].ContainsKey(value)) {
302        if (this.categoricalMapping[dimension].Values.Count == 0)
303          this.categoricalMapping[dimension][value] = 1.0;
304        else
305          this.categoricalMapping[dimension][value] = this.categoricalMapping[dimension].Values.Max() + 1.0;
306      }
307      return this.categoricalMapping[dimension][value];
308    }
309    private double GetValue(IRun run, AxisDimension axisDimension) {
310      double value = double.NaN;
311      switch (axisDimension) {
312        case AxisDimension.Color: {
313            value = GetCategoricalValue(-1, run.Color.ToString());
314            break;
315          }
316        default: {
317            throw new ArgumentException("No handling strategy for " + axisDimension.ToString() + " is defined.");
318          }
319      }
320      return value;
321    }
322    #endregion
323
324    #region GUI events
325    private void UpdateNoRunsVisibleLabel() {
326      if (this.chart.Series.Count > 0)
327        noRunsLabel.Visible = false;
328      else
329        noRunsLabel.Visible = true;
330    }
331
332    private void AxisComboBox_SelectedIndexChanged(object sender, EventArgs e) {
333      UpdateDataPoints();
334    }
335    private void UpdateAxisLabels() {
336      Axis xAxis = this.chart.ChartAreas[BoxPlotChartAreaName].AxisX;
337      Axis yAxis = this.chart.ChartAreas[BoxPlotChartAreaName].AxisY;
338      int axisDimensionCount = Enum.GetNames(typeof(AxisDimension)).Count();
339      SetCustomAxisLabels(xAxis, xAxisComboBox.SelectedIndex - axisDimensionCount);
340      SetCustomAxisLabels(yAxis, yAxisComboBox.SelectedIndex - axisDimensionCount);
341      if (xAxisComboBox.SelectedItem != null)
342        xAxis.Title = xAxisComboBox.SelectedItem.ToString();
343      if (yAxisComboBox.SelectedItem != null)
344        yAxis.Title = yAxisComboBox.SelectedItem.ToString();
345    }
346
347    private void chart_AxisViewChanged(object sender, System.Windows.Forms.DataVisualization.Charting.ViewEventArgs e) {
348      this.UpdateAxisLabels();
349    }
350
351    private void SetCustomAxisLabels(Axis axis, int dimension) {
352      axis.CustomLabels.Clear();
353      if (categoricalMapping.ContainsKey(dimension)) {
354        foreach (var pair in categoricalMapping[dimension]) {
355          string labelText = pair.Key.ToString();
356          CustomLabel label = new CustomLabel();
357          label.ToolTip = labelText;
358          if (labelText.Length > 25)
359            labelText = labelText.Substring(0, 25) + " ... ";
360          label.Text = labelText;
361          label.GridTicks = GridTickTypes.TickMark;
362          label.FromPosition = pair.Value - 0.5;
363          label.ToPosition = pair.Value + 0.5;
364          axis.CustomLabels.Add(label);
365        }
366      } else if (dimension > 0 && Content.GetValue(0, dimension) is TimeSpanValue) {
367        this.chart.ChartAreas[0].RecalculateAxesScale();
368        Axis correspondingAxis = this.chart.ChartAreas[0].Axes.Where(x => x.Name == axis.Name).SingleOrDefault();
369        if (correspondingAxis == null)
370          correspondingAxis = axis;
371        for (double i = correspondingAxis.Minimum; i <= correspondingAxis.Maximum; i += correspondingAxis.LabelStyle.Interval) {
372          TimeSpan time = TimeSpan.FromSeconds(i);
373          string x = string.Format("{0:00}:{1:00}:{2:00}", (int)time.Hours, time.Minutes, time.Seconds);
374          axis.CustomLabels.Add(i - correspondingAxis.LabelStyle.Interval / 2, i + correspondingAxis.LabelStyle.Interval / 2, x);
375        }
376      } else if (chart.ChartAreas[BoxPlotChartAreaName].AxisX == axis) {
377        double position = 1.0;
378        foreach (Series series in chart.Series) {
379          if (series.Name != BoxPlotSeriesName) {
380            string labelText = series.Points[0].XValue.ToString();
381            CustomLabel label = new CustomLabel();
382            label.FromPosition = position - 0.5;
383            label.ToPosition = position + 0.5;
384            label.GridTicks = GridTickTypes.TickMark;
385            label.Text = labelText;
386            axis.CustomLabels.Add(label);
387            position++;
388          }
389        }
390      }
391    }
392
393    private void chart_MouseMove(object sender, MouseEventArgs e) {
394      string newTooltipText = string.Empty;
395      string oldTooltipText;
396      HitTestResult h = this.chart.HitTest(e.X, e.Y);
397      if (h.ChartElementType == ChartElementType.AxisLabels) {
398        newTooltipText = ((CustomLabel)h.Object).ToolTip;
399      }
400
401      oldTooltipText = this.tooltip.GetToolTip(chart);
402      if (newTooltipText != oldTooltipText)
403        this.tooltip.SetToolTip(chart, newTooltipText);
404    }
405    #endregion
406
407    private void showStatisticsCheckBox_CheckedChanged(object sender, EventArgs e) {
408      splitContainer.Panel2Collapsed = !showStatisticsCheckBox.Checked;
409    }
410
411  }
412}
Note: See TracBrowser for help on using the repository browser.