source: trunk/sources/HeuristicLab.Algorithms.DataAnalysis.Views/3.4/GaussianProcessRegressionSolutionLineChartView.cs @ 8562

Last change on this file since 8562 was 8562, checked in by gkronber, 7 years ago

#1902 implemented LinearARD and MaternIso covariance functions.

File size: 15.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 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
21using System;
22using System.Collections.Generic;
23using System.Drawing;
24using System.Linq;
25using System.Windows.Forms;
26using System.Windows.Forms.DataVisualization.Charting;
27using HeuristicLab.MainForm;
28using HeuristicLab.Problems.DataAnalysis.Views;
29
30namespace HeuristicLab.Algorithms.DataAnalysis.Views {
31  [View("Line Chart 2")]
32  [Content(typeof(GaussianProcessRegressionSolution))]
33  public partial class GaussianProcessRegressionSolutionLineChartView : DataAnalysisSolutionEvaluationView {
34    private const string TARGETVARIABLE_SERIES_NAME = "Target Variable";
35    private const string ESTIMATEDVALUES_TRAINING_SERIES_NAME = "Estimated Values (training)";
36    private const string ESTIMATEDVALUES_TEST_SERIES_NAME = "Estimated Values (test)";
37    private const string ESTIMATEDVALUES_ALL_SERIES_NAME = "Estimated Values (all samples)";
38
39    public new GaussianProcessRegressionSolution Content {
40      get { return (GaussianProcessRegressionSolution)base.Content; }
41      set { base.Content = value; }
42    }
43
44    public GaussianProcessRegressionSolutionLineChartView()
45      : base() {
46      InitializeComponent();
47      //configure axis
48      this.chart.CustomizeAllChartAreas();
49      this.chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
50      this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
51      this.chart.ChartAreas[0].AxisX.IsStartedFromZero = true;
52      this.chart.ChartAreas[0].CursorX.Interval = 1;
53
54      this.chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
55      this.chart.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
56      this.chart.ChartAreas[0].CursorY.Interval = 0;
57    }
58
59    private void RedrawChart() {
60      this.chart.Series.Clear();
61      if (Content != null) {
62        this.chart.ChartAreas[0].AxisX.Minimum = 0;
63        this.chart.ChartAreas[0].AxisX.Maximum = Content.ProblemData.Dataset.Rows - 1;
64
65        // training series
66        this.chart.Series.Add(ESTIMATEDVALUES_TRAINING_SERIES_NAME);
67        this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].LegendText = ESTIMATEDVALUES_TRAINING_SERIES_NAME;
68        this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].ChartType = SeriesChartType.Range;
69        this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].EmptyPointStyle.Color = this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].Color;
70        var mean = Content.EstimatedTrainingValues.ToArray();
71        var s2 = Content.EstimatedTrainingVariance.ToArray();
72        var lower = mean.Zip(s2, (m, s) => m - 1.96 * Math.Sqrt(s)).ToArray();
73        var upper = mean.Zip(s2, (m, s) => m + 1.96 * Math.Sqrt(s)).ToArray();
74        this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].Points.DataBindXY(Content.ProblemData.TrainingIndices.ToArray(), lower, upper);
75        this.InsertEmptyPoints(this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME]);
76        this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].Tag = Content;
77
78        // test series
79        this.chart.Series.Add(ESTIMATEDVALUES_TEST_SERIES_NAME);
80        this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME].LegendText = ESTIMATEDVALUES_TEST_SERIES_NAME;
81        this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME].ChartType = SeriesChartType.Range;
82
83        mean = Content.EstimatedTestValues.ToArray();
84        s2 = Content.EstimatedTestVariance.ToArray();
85        lower = mean.Zip(s2, (m, s) => m - 1.96 * Math.Sqrt(s)).ToArray();
86        upper = mean.Zip(s2, (m, s) => m + 1.96 * Math.Sqrt(s)).ToArray();
87        this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME].Points.DataBindXY(Content.ProblemData.TestIndices.ToArray(), lower, upper);
88        this.InsertEmptyPoints(this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME]);
89        this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME].Tag = Content;
90
91        // series of remaining points
92        int[] allIndices = Enumerable.Range(0, Content.ProblemData.Dataset.Rows).Except(Content.ProblemData.TrainingIndices).Except(Content.ProblemData.TestIndices).ToArray();
93        mean = Content.EstimatedValues.ToArray();
94        s2 = Content.EstimatedVariance.ToArray();
95        lower = mean.Zip(s2, (m, s) => m - 1.96 * Math.Sqrt(s)).ToArray();
96        upper = mean.Zip(s2, (m, s) => m + 1.96 * Math.Sqrt(s)).ToArray();
97        List<double> allLower = allIndices.Select(index => lower[index]).ToList();
98        List<double> allUpper = allIndices.Select(index => upper[index]).ToList();
99        this.chart.Series.Add(ESTIMATEDVALUES_ALL_SERIES_NAME);
100        this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME].LegendText = ESTIMATEDVALUES_ALL_SERIES_NAME;
101        this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME].ChartType = SeriesChartType.Range;
102        if (allIndices.Count() > 0) {
103          this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME].Points.DataBindXY(allIndices, allLower, allUpper);
104          this.InsertEmptyPoints(this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME]);
105        }
106        this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME].Tag = Content;
107
108        // target
109        this.chart.Series.Add(TARGETVARIABLE_SERIES_NAME);
110        this.chart.Series[TARGETVARIABLE_SERIES_NAME].LegendText = Content.ProblemData.TargetVariable;
111        this.chart.Series[TARGETVARIABLE_SERIES_NAME].ChartType = SeriesChartType.FastLine;
112        this.chart.Series[TARGETVARIABLE_SERIES_NAME].Points.DataBindXY(Enumerable.Range(0, Content.ProblemData.Dataset.Rows).ToArray(),
113          Content.ProblemData.Dataset.GetDoubleValues(Content.ProblemData.TargetVariable).ToArray());
114
115        this.ToggleSeriesData(this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME]);
116
117        // the series have been added in different order than in the normal line chart
118        // --> adapt coloring;
119        var s1Color = chart.Series[0].Color;
120        var s2Color = chart.Series[1].Color;
121        var s3Color = chart.Series[2].Color;
122        var s4Color = chart.Series[3].Color;
123
124        chart.Series[3].Color = s1Color;
125        chart.Series[0].Color = s2Color;
126        chart.Series[1].Color = s3Color;
127        chart.Series[2].Color = s4Color;
128
129        UpdateCursorInterval();
130        this.UpdateStripLines();
131      }
132    }
133
134    private void InsertEmptyPoints(Series series) {
135      int i = 0;
136      while (i < series.Points.Count - 1) {
137        if (series.Points[i].IsEmpty) {
138          ++i;
139          continue;
140        }
141
142        var p1 = series.Points[i];
143        var p2 = series.Points[i + 1];
144        // check for consecutive indices
145        if ((int)p2.XValue - (int)p1.XValue != 1) {
146          // insert an empty point between p1 and p2 so that the line will be invisible (transparent)
147          var p = new DataPoint((int)((p1.XValue + p2.XValue) / 2), new double[] { 0.0, 0.0 }) { IsEmpty = true };
148          // insert
149          series.Points.Insert(i + 1, p);
150        }
151        ++i;
152      }
153    }
154
155    private void UpdateCursorInterval() {
156      var estimatedValues = this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].Points.Select(x => x.YValues[0]).DefaultIfEmpty(1.0);
157      var targetValues = this.chart.Series[TARGETVARIABLE_SERIES_NAME].Points.Select(x => x.YValues[0]).DefaultIfEmpty(1.0);
158      double estimatedValuesRange = estimatedValues.Max() - estimatedValues.Min();
159      double targetValuesRange = targetValues.Max() - targetValues.Min();
160      double interestingValuesRange = Math.Min(Math.Max(targetValuesRange, 1.0), Math.Max(estimatedValuesRange, 1.0));
161      double digits = (int)Math.Log10(interestingValuesRange) - 3;
162      double yZoomInterval = Math.Max(Math.Pow(10, digits), 10E-5);
163      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
164    }
165
166    #region events
167    protected override void RegisterContentEvents() {
168      base.RegisterContentEvents();
169      Content.ModelChanged += new EventHandler(Content_ModelChanged);
170      Content.ProblemDataChanged += new EventHandler(Content_ProblemDataChanged);
171    }
172    protected override void DeregisterContentEvents() {
173      base.DeregisterContentEvents();
174      Content.ModelChanged -= new EventHandler(Content_ModelChanged);
175      Content.ProblemDataChanged -= new EventHandler(Content_ProblemDataChanged);
176    }
177
178    protected override void OnContentChanged() {
179      base.OnContentChanged();
180      RedrawChart();
181    }
182    private void Content_ProblemDataChanged(object sender, EventArgs e) {
183      RedrawChart();
184    }
185    private void Content_ModelChanged(object sender, EventArgs e) {
186      RedrawChart();
187    }
188
189
190
191    private void Chart_MouseDoubleClick(object sender, MouseEventArgs e) {
192      HitTestResult result = chart.HitTest(e.X, e.Y);
193      if (result.ChartArea != null && (result.ChartElementType == ChartElementType.PlottingArea ||
194                                       result.ChartElementType == ChartElementType.Gridlines) ||
195                                       result.ChartElementType == ChartElementType.StripLines) {
196        foreach (var axis in result.ChartArea.Axes)
197          axis.ScaleView.ZoomReset(int.MaxValue);
198      }
199    }
200    #endregion
201
202    private void UpdateStripLines() {
203      this.chart.ChartAreas[0].AxisX.StripLines.Clear();
204
205      int[] attr = new int[Content.ProblemData.Dataset.Rows + 1]; // add a virtual last row that is again empty to simplify loop further down
206      foreach (var row in Content.ProblemData.TrainingIndices) {
207        attr[row] += 1;
208      }
209      foreach (var row in Content.ProblemData.TestIndices) {
210        attr[row] += 2;
211      }
212      int start = 0;
213      int curAttr = attr[start];
214      for (int row = 0; row < attr.Length; row++) {
215        if (attr[row] != curAttr) {
216          switch (curAttr) {
217            case 0: break;
218            case 1:
219              this.CreateAndAddStripLine("Training", start, row, Color.FromArgb(40, Color.Green), Color.Transparent);
220              break;
221            case 2:
222              this.CreateAndAddStripLine("Test", start, row, Color.FromArgb(40, Color.Red), Color.Transparent);
223              break;
224            case 3:
225              this.CreateAndAddStripLine("Training and Test", start, row, Color.FromArgb(40, Color.Green), Color.FromArgb(40, Color.Red), ChartHatchStyle.WideUpwardDiagonal);
226              break;
227            default:
228              // should not happen
229              break;
230          }
231          curAttr = attr[row];
232          start = row;
233        }
234      }
235    }
236
237    private void CreateAndAddStripLine(string title, int start, int end, Color color, Color secondColor, ChartHatchStyle hatchStyle = ChartHatchStyle.None) {
238      StripLine stripLine = new StripLine();
239      stripLine.BackColor = color;
240      stripLine.BackSecondaryColor = secondColor;
241      stripLine.BackHatchStyle = hatchStyle;
242      stripLine.Text = title;
243      stripLine.Font = new Font("Times New Roman", 12, FontStyle.Bold);
244      // strip range is [start .. end] inclusive, but we evaluate [start..end[ (end is exclusive)
245      // the strip should be by one longer (starting at start - 0.5 and ending at end + 0.5)
246      stripLine.StripWidth = end - start;
247      stripLine.IntervalOffset = start - 0.5; // start slightly to the left of the first point to clearly indicate the first point in the partition
248      this.chart.ChartAreas[0].AxisX.StripLines.Add(stripLine);
249    }
250
251    private void ToggleSeriesData(Series series) {
252      if (series.Points.Count > 0) {  //checks if series is shown
253        if (this.chart.Series.Any(s => s != series && s.Points.Count > 0)) {
254          ClearPointsQuick(series.Points);
255        }
256      } else if (Content != null) {
257
258        IEnumerable<int> indices = null;
259        IEnumerable<double> mean = null;
260        IEnumerable<double> s2 = null;
261        double[] lower = null;
262        double[] upper = null;
263        switch (series.Name) {
264          case ESTIMATEDVALUES_ALL_SERIES_NAME:
265            indices = Enumerable.Range(0, Content.ProblemData.Dataset.Rows).Except(Content.ProblemData.TrainingIndices).Except(Content.ProblemData.TestIndices).ToArray();
266            mean = Content.EstimatedValues.ToArray();
267            s2 = Content.EstimatedVariance.ToArray();
268            lower = mean.Zip(s2, (m, s) => m - 1.96 * Math.Sqrt(s)).ToArray();
269            upper = mean.Zip(s2, (m, s) => m + 1.96 * Math.Sqrt(s)).ToArray();
270            lower = indices.Select(index => lower[index]).ToArray();
271            upper = indices.Select(index => upper[index]).ToArray();
272            break;
273          case ESTIMATEDVALUES_TRAINING_SERIES_NAME:
274            indices = Content.ProblemData.TrainingIndices.ToArray();
275            mean = Content.EstimatedTrainingValues.ToArray();
276            s2 = Content.EstimatedTrainingVariance.ToArray();
277            lower = mean.Zip(s2, (m, s) => m - 1.96 * Math.Sqrt(s)).ToArray();
278            upper = mean.Zip(s2, (m, s) => m + 1.96 * Math.Sqrt(s)).ToArray();
279            break;
280          case ESTIMATEDVALUES_TEST_SERIES_NAME:
281            indices = Content.ProblemData.TestIndices.ToArray();
282            mean = Content.EstimatedTestValues.ToArray();
283            s2 = Content.EstimatedTestVariance.ToArray();
284            lower = mean.Zip(s2, (m, s) => m - s).ToArray();
285            upper = mean.Zip(s2, (m, s) => m + s).ToArray();
286            break;
287        }
288        if (indices.Count() > 0) {
289          series.Points.DataBindXY(indices, lower, upper);
290          this.InsertEmptyPoints(series);
291          chart.Legends[series.Legend].ForeColor = Color.Black;
292          UpdateCursorInterval();
293          chart.Refresh();
294        }
295      }
296    }
297
298    // workaround as per http://stackoverflow.com/questions/5744930/datapointcollection-clear-performance
299    private static void ClearPointsQuick(DataPointCollection points) {
300      points.SuspendUpdates();
301      while (points.Count > 0)
302        points.RemoveAt(points.Count - 1);
303      points.ResumeUpdates();
304    }
305
306    private void chart_MouseMove(object sender, MouseEventArgs e) {
307      HitTestResult result = chart.HitTest(e.X, e.Y);
308      if (result.ChartElementType == ChartElementType.LegendItem && result.Series.Name != TARGETVARIABLE_SERIES_NAME)
309        Cursor = Cursors.Hand;
310      else
311        Cursor = Cursors.Default;
312    }
313    private void chart_MouseDown(object sender, MouseEventArgs e) {
314      HitTestResult result = chart.HitTest(e.X, e.Y);
315      if (result.ChartElementType == ChartElementType.LegendItem && result.Series.Name != TARGETVARIABLE_SERIES_NAME) {
316        ToggleSeriesData(result.Series);
317      }
318    }
319
320    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
321      if (chart.Series.Count != 4) return;
322      e.LegendItems[0].Cells[1].ForeColor = this.chart.Series[ESTIMATEDVALUES_TRAINING_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
323      e.LegendItems[1].Cells[1].ForeColor = this.chart.Series[ESTIMATEDVALUES_TEST_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
324      e.LegendItems[2].Cells[1].ForeColor = this.chart.Series[ESTIMATEDVALUES_ALL_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
325      e.LegendItems[3].Cells[1].ForeColor = this.chart.Series[TARGETVARIABLE_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
326    }
327  }
328}
Note: See TracBrowser for help on using the repository browser.