Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.TimeSeries/HeuristicLab.Problems.DataAnalysis.Views/3.4/TimeSeriesPrognosis/TimeSeriesPrognosisSolutionLineChartView.cs @ 7463

Last change on this file since 7463 was 7463, checked in by gkronber, 12 years ago

#1081 improved formatter, line chart view, simplifier for time series prognosis solutions

File size: 14.9 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2011 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.MainForm.WindowsForms;
29
30namespace HeuristicLab.Problems.DataAnalysis.Views {
31  [View("Line Chart")]
32  [Content(typeof(ITimeSeriesPrognosisSolution))]
33  public partial class TimeSeriesPrognosisSolutionLineChartView : DataAnalysisSolutionEvaluationView {
34    private const string TARGETVARIABLE_SERIES_NAME = "Target Variable";
35    private const string PROGNOSEDVALUES_TRAINING_SERIES_NAME = "Prognosed Values (training)";
36    private const string PROGNOSEDVALUES_TEST_SERIES_NAME = "Prognosed Values (test)";
37    private const string PROGNOSEDVALUES_ALL_SERIES_NAME = "Prognosed Values (all samples)";
38    private int testPrognosisStart;
39
40    public new ITimeSeriesPrognosisSolution Content {
41      get { return (ITimeSeriesPrognosisSolution)base.Content; }
42      set { base.Content = value; }
43    }
44
45    public TimeSeriesPrognosisSolutionLineChartView()
46      : base() {
47      InitializeComponent();
48      //configure axis
49      this.chart.CustomizeAllChartAreas();
50      this.chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
51      this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
52      this.chart.ChartAreas[0].AxisX.IsStartedFromZero = true;
53      this.chart.ChartAreas[0].CursorX.Interval = 1;
54
55      this.chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
56      this.chart.ChartAreas[0].AxisY.ScaleView.Zoomable = true;
57      this.chart.ChartAreas[0].CursorY.Interval = 0;
58    }
59
60    private void UpdateTargetVariables() {
61      // populate combobox
62      targetVariableComboBox.Items.Clear();
63      if (Content != null) {
64        if (testPrognosisStart < Content.ProblemData.TestPartition.Start || testPrognosisStart >= Content.ProblemData.TestPartition.End) {
65          testPrognosisStart = Content.ProblemData.TestPartition.Start;
66        }
67        foreach (var targetVariable in Content.ProblemData.TargetVariables)
68          targetVariableComboBox.Items.Add(targetVariable);
69
70        targetVariableComboBox.SelectedIndex = 0;
71      }
72    }
73
74
75
76    private void RedrawChart() {
77      this.chart.Series.Clear();
78      if (Content != null) {
79        this.chart.ChartAreas[0].AxisX.Minimum = 0;
80        this.chart.ChartAreas[0].AxisX.Maximum = Content.ProblemData.Dataset.Rows - 1;
81        string targetVariable = (string)targetVariableComboBox.SelectedItem;
82        int varIndex = Content.ProblemData.TargetVariables.ToList().IndexOf(targetVariable);
83
84        this.chart.Series.Add(TARGETVARIABLE_SERIES_NAME);
85        this.chart.Series[TARGETVARIABLE_SERIES_NAME].LegendText = targetVariable;
86        this.chart.Series[TARGETVARIABLE_SERIES_NAME].ChartType = SeriesChartType.FastLine;
87        this.chart.Series[TARGETVARIABLE_SERIES_NAME].Points.DataBindXY(Enumerable.Range(0, Content.ProblemData.Dataset.Rows).ToArray(),
88          Content.ProblemData.Dataset.GetDoubleValues(targetVariable).ToArray());
89
90        this.chart.Series.Add(PROGNOSEDVALUES_TRAINING_SERIES_NAME);
91        this.chart.Series[PROGNOSEDVALUES_TRAINING_SERIES_NAME].LegendText = PROGNOSEDVALUES_TRAINING_SERIES_NAME;
92        this.chart.Series[PROGNOSEDVALUES_TRAINING_SERIES_NAME].ChartType = SeriesChartType.FastLine;
93        if (prognosedValuesCheckbox.Checked) {
94          this.chart.Series[PROGNOSEDVALUES_TRAINING_SERIES_NAME].Points
95            .DataBindXY(Content.ProblemData.TrainingIndizes.ToArray(),
96                        Content.PrognosedTrainingValues.SelectMany(x => x).Skip(varIndex).TakeEvery(Content.ProblemData.TargetVariables.Count()).ToArray());
97        } else {
98          this.chart.Series[PROGNOSEDVALUES_TRAINING_SERIES_NAME].Points
99            .DataBindXY(Content.ProblemData.TrainingIndizes.ToArray(),
100                        Content.GetPrognosedValues(Content.ProblemData.TrainingIndizes, 1).SelectMany(x => x.Single()).Skip(varIndex).TakeEvery(Content.ProblemData.TargetVariables.Count()).ToArray());
101        }
102        this.chart.Series[PROGNOSEDVALUES_TRAINING_SERIES_NAME].Tag = Content;
103        this.chart.DataManipulator.InsertEmptyPoints(1, IntervalType.Number, PROGNOSEDVALUES_TRAINING_SERIES_NAME);
104        this.chart.Series[PROGNOSEDVALUES_TRAINING_SERIES_NAME].EmptyPointStyle.BorderWidth = 0;
105        this.chart.Series[PROGNOSEDVALUES_TRAINING_SERIES_NAME].EmptyPointStyle.MarkerStyle = MarkerStyle.None;
106
107
108        this.chart.Series.Add(PROGNOSEDVALUES_TEST_SERIES_NAME);
109        this.chart.Series[PROGNOSEDVALUES_TEST_SERIES_NAME].LegendText = PROGNOSEDVALUES_TEST_SERIES_NAME;
110        this.chart.Series[PROGNOSEDVALUES_TEST_SERIES_NAME].ChartType = SeriesChartType.FastLine;
111        if (prognosedValuesCheckbox.Checked) {
112          int offsetToStart = testPrognosisStart - Content.ProblemData.TestPartition.Start;
113          this.chart.Series[PROGNOSEDVALUES_TEST_SERIES_NAME].Points
114            .DataBindXY(Content.ProblemData.TestIndizes.Skip(offsetToStart).ToArray(),
115                        Content.GetPrognosedValues(Enumerable.Range(testPrognosisStart, 1), Content.ProblemData.TestPartition.End - testPrognosisStart)
116                        .SelectMany(x => x.SelectMany(y => y))
117                        .Skip(varIndex)
118                        .TakeEvery(Content.ProblemData.TargetVariables.Count())
119                        .ToArray());
120        } else {
121          this.chart.Series[PROGNOSEDVALUES_TEST_SERIES_NAME].Points
122            .DataBindXY(Content.ProblemData.TestIndizes.ToArray(),
123                        Content.GetPrognosedValues(Content.ProblemData.TestIndizes, 1)
124                        .SelectMany(x => x.Single())
125                        .Skip(varIndex)
126                        .TakeEvery(Content.ProblemData.TargetVariables.Count())
127                        .ToArray());
128        }
129        this.chart.Series[PROGNOSEDVALUES_TEST_SERIES_NAME].Tag = Content;
130
131        UpdateCursorInterval();
132        this.UpdateStripLines();
133      }
134    }
135
136    private void UpdateCursorInterval() {
137      var estimatedValues = this.chart.Series[PROGNOSEDVALUES_TRAINING_SERIES_NAME].Points.Select(x => x.YValues[0]).DefaultIfEmpty(1.0);
138      var targetValues = this.chart.Series[TARGETVARIABLE_SERIES_NAME].Points.Select(x => x.YValues[0]).DefaultIfEmpty(1.0);
139      double estimatedValuesRange = estimatedValues.Max() - estimatedValues.Min();
140      double targetValuesRange = targetValues.Max() - targetValues.Min();
141      double interestingValuesRange = Math.Min(Math.Max(targetValuesRange, 1.0), Math.Max(estimatedValuesRange, 1.0));
142      double digits = (int)Math.Log10(interestingValuesRange) - 3;
143      double yZoomInterval = Math.Max(Math.Pow(10, digits), 10E-5);
144      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
145    }
146
147    #region events
148    protected override void RegisterContentEvents() {
149      base.RegisterContentEvents();
150      Content.ModelChanged += new EventHandler(Content_ModelChanged);
151      Content.ProblemDataChanged += new EventHandler(Content_ProblemDataChanged);
152    }
153    protected override void DeregisterContentEvents() {
154      base.DeregisterContentEvents();
155      Content.ModelChanged -= new EventHandler(Content_ModelChanged);
156      Content.ProblemDataChanged -= new EventHandler(Content_ProblemDataChanged);
157    }
158
159    protected override void OnContentChanged() {
160      base.OnContentChanged();
161      UpdateTargetVariables();
162    }
163
164    private void Content_ProblemDataChanged(object sender, EventArgs e) {
165      UpdateTargetVariables();
166    }
167    private void Content_ModelChanged(object sender, EventArgs e) {
168      RedrawChart();
169    }
170
171    private void targetVariableComboBox_SelectedIndexChanged(object sender, EventArgs e) {
172      RedrawChart();
173    }
174    private void prognosedValuesCheckbox_CheckedChanged(object sender, EventArgs e) {
175      RedrawChart();
176    }
177
178    private void Chart_MouseDoubleClick(object sender, MouseEventArgs e) {
179      HitTestResult result = chart.HitTest(e.X, e.Y);
180      if (result.ChartArea != null && (result.ChartElementType == ChartElementType.PlottingArea ||
181                                       result.ChartElementType == ChartElementType.Gridlines) ||
182                                       result.ChartElementType == ChartElementType.StripLines) {
183        foreach (var axis in result.ChartArea.Axes)
184          axis.ScaleView.ZoomReset(int.MaxValue);
185      }
186    }
187    #endregion
188
189    private void UpdateStripLines() {
190      this.chart.ChartAreas[0].AxisX.StripLines.Clear();
191
192      int[] attr = new int[Content.ProblemData.Dataset.Rows + 1]; // add a virtual last row that is again empty to simplify loop further down
193      foreach (var row in Content.ProblemData.TrainingIndizes) {
194        attr[row] += 1;
195      }
196      foreach (var row in Content.ProblemData.TestIndizes) {
197        attr[row] += 2;
198      }
199      int start = 0;
200      int curAttr = attr[start];
201      for (int row = 0; row < attr.Length; row++) {
202        if (attr[row] != curAttr) {
203          switch (curAttr) {
204            case 0: break;
205            case 1:
206              this.CreateAndAddStripLine("Training", start, row, Color.FromArgb(40, Color.Green), Color.Transparent);
207              break;
208            case 2:
209              this.CreateAndAddStripLine("Test", start, row, Color.FromArgb(40, Color.Red), Color.Transparent);
210              break;
211            case 3:
212              this.CreateAndAddStripLine("Training and Test", start, row, Color.FromArgb(40, Color.Green), Color.FromArgb(40, Color.Red), ChartHatchStyle.WideUpwardDiagonal);
213              break;
214            default:
215              // should not happen
216              break;
217          }
218          curAttr = attr[row];
219          start = row;
220        }
221      }
222    }
223
224    private void CreateAndAddStripLine(string title, int start, int end, Color color, Color secondColor, ChartHatchStyle hatchStyle = ChartHatchStyle.None) {
225      StripLine stripLine = new StripLine();
226      stripLine.BackColor = color;
227      stripLine.BackSecondaryColor = secondColor;
228      stripLine.BackHatchStyle = hatchStyle;
229      stripLine.Text = title;
230      stripLine.Font = new Font("Times New Roman", 12, FontStyle.Bold);
231      // strip range is [start .. end] inclusive, but we evaluate [start..end[ (end is exclusive)
232      // the strip should be by one longer (starting at start - 0.5 and ending at end + 0.5)
233      stripLine.StripWidth = end - start;
234      stripLine.IntervalOffset = start - 0.5; // start slightly to the left of the first point to clearly indicate the first point in the partition
235      this.chart.ChartAreas[0].AxisX.StripLines.Add(stripLine);
236    }
237
238    private void ToggleSeriesData(Series series) {
239      if (series.Points.Count > 0) {  //checks if series is shown
240        if (this.chart.Series.Any(s => s != series && s.Points.Count > 0)) {
241          series.Points.Clear();
242        }
243      } else if (Content != null) {
244        string targetVariable = (string)targetVariableComboBox.SelectedItem;
245        int varIndex = Content.ProblemData.TargetVariables.ToList().IndexOf(targetVariable);
246
247
248        IEnumerable<int> indizes = null;
249        IEnumerable<double> predictedValues = null;
250        switch (series.Name) {
251          case PROGNOSEDVALUES_TRAINING_SERIES_NAME:
252            indizes = Content.ProblemData.TrainingIndizes.ToArray();
253            predictedValues =
254              Content.PrognosedTrainingValues.SelectMany(x => x).Skip(varIndex).TakeEvery(
255                Content.ProblemData.TargetVariables.Count()).ToArray();
256            break;
257          case PROGNOSEDVALUES_TEST_SERIES_NAME:
258            testPrognosisStart = Content.ProblemData.TestPartition.Start;
259            indizes = Content.ProblemData.TestIndizes.ToArray();
260            predictedValues = Content.PrognosedTestValues.SelectMany(x => x).Skip(varIndex).TakeEvery(
261              Content.ProblemData.TargetVariables.Count()).ToArray();
262            break;
263        }
264        series.Points.DataBindXY(indizes, predictedValues);
265        chart.DataManipulator.InsertEmptyPoints(1, IntervalType.Number, series.Name);
266        chart.Legends[series.Legend].ForeColor = Color.Black;
267        UpdateCursorInterval();
268        chart.Refresh();
269      }
270    }
271
272    private void chart_MouseMove(object sender, MouseEventArgs e) {
273      HitTestResult result = chart.HitTest(e.X, e.Y);
274      if (result.ChartElementType == ChartElementType.LegendItem && result.Series.Name != TARGETVARIABLE_SERIES_NAME)
275        Cursor = Cursors.Hand;
276      else
277        Cursor = Cursors.Default;
278    }
279
280    private void chart_MouseDown(object sender, MouseEventArgs e) {
281      HitTestResult result = chart.HitTest(e.X, e.Y);
282      if (result.ChartElementType == ChartElementType.LegendItem && result.Series.Name != TARGETVARIABLE_SERIES_NAME) {
283        ToggleSeriesData(result.Series);
284      } else if (result.ChartElementType == ChartElementType.Axis || result.ChartElementType == ChartElementType.AxisLabels ||
285        result.ChartElementType == ChartElementType.TickMarks) {
286        chart.ChartAreas[0].CursorX.SetCursorPixelPosition(new Point(e.X, e.Y), true);
287        int pos = (int)Math.Round(chart.ChartAreas[0].CursorX.Position);
288        if (pos >= Content.ProblemData.TestPartition.Start && pos < Content.ProblemData.TestPartition.End) {
289          testPrognosisStart = pos;
290          RedrawChart();
291        }
292      }
293    }
294
295    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
296      if (chart.Series.Count != 4) return;
297      e.LegendItems[0].Cells[1].ForeColor = this.chart.Series[TARGETVARIABLE_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
298      e.LegendItems[1].Cells[1].ForeColor = this.chart.Series[PROGNOSEDVALUES_TRAINING_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
299      e.LegendItems[2].Cells[1].ForeColor = this.chart.Series[PROGNOSEDVALUES_TEST_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
300      e.LegendItems[3].Cells[1].ForeColor = this.chart.Series[PROGNOSEDVALUES_ALL_SERIES_NAME].Points.Count == 0 ? Color.Gray : Color.Black;
301    }
302  }
303}
Note: See TracBrowser for help on using the repository browser.