Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Problems.DataAnalysis.Views/3.4/Classification/DiscriminantFunctionClassificationSolutionThresholdView.cs @ 6479

Last change on this file since 6479 was 5975, checked in by mkommend, 14 years ago

#1313: Updated view names to use spaces instead of !camelCasing.

File size: 11.1 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
21
22using System;
23using System.Collections.Generic;
24using System.Drawing;
25using System.Linq;
26using System.Windows.Forms;
27using System.Windows.Forms.DataVisualization.Charting;
28using HeuristicLab.Common;
29using HeuristicLab.Core.Views;
30using HeuristicLab.MainForm;
31using HeuristicLab.MainForm.WindowsForms;
32
33namespace HeuristicLab.Problems.DataAnalysis.Views {
34  [View("Classification Threshold")]
35  [Content(typeof(IDiscriminantFunctionClassificationSolution), true)]
36  public sealed partial class DiscriminantFunctionClassificationSolutionThresholdView : ItemView, IDiscriminantFunctionClassificationSolutionEvaluationView {
37    private const double TrainingAxisValue = 0.0;
38    private const double TestAxisValue = 10.0;
39    private const double TrainingTestBorder = (TestAxisValue - TrainingAxisValue) / 2;
40    private const string TrainingLabelText = "Training Samples";
41    private const string TestLabelText = "Test Samples";
42
43    public new IDiscriminantFunctionClassificationSolution Content {
44      get { return (IDiscriminantFunctionClassificationSolution)base.Content; }
45      set { base.Content = value; }
46    }
47
48    private Dictionary<double, Series> classValueSeriesMapping;
49    private Random random;
50    private bool updateInProgress;
51
52    public DiscriminantFunctionClassificationSolutionThresholdView()
53      : base() {
54      InitializeComponent();
55
56      classValueSeriesMapping = new Dictionary<double, Series>();
57      random = new Random();
58      updateInProgress = false;
59
60      this.chart.CustomizeAllChartAreas();
61      this.chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;
62      this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
63      this.chart.ChartAreas[0].AxisX.Minimum = TrainingAxisValue - TrainingTestBorder;
64      this.chart.ChartAreas[0].AxisX.Maximum = TestAxisValue + TrainingTestBorder;
65      AddCustomLabelToAxis(this.chart.ChartAreas[0].AxisX);
66
67      this.chart.ChartAreas[0].AxisY.Title = "Estimated Values";
68      this.chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;
69      this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;
70    }
71
72    private void AddCustomLabelToAxis(Axis axis) {
73      CustomLabel trainingLabel = new CustomLabel();
74      trainingLabel.Text = TrainingLabelText;
75      trainingLabel.FromPosition = TrainingAxisValue - TrainingTestBorder;
76      trainingLabel.ToPosition = TrainingAxisValue + TrainingTestBorder;
77      axis.CustomLabels.Add(trainingLabel);
78
79      CustomLabel testLabel = new CustomLabel();
80      testLabel.Text = TestLabelText;
81      testLabel.FromPosition = TestAxisValue - TrainingTestBorder;
82      testLabel.ToPosition = TestAxisValue + TrainingTestBorder;
83      axis.CustomLabels.Add(testLabel);
84    }
85
86    protected override void RegisterContentEvents() {
87      base.RegisterContentEvents();
88      Content.ModelChanged += new EventHandler(Content_ModelChanged);
89      Content.ProblemDataChanged += new EventHandler(Content_ProblemDataChanged);
90    }
91    protected override void DeregisterContentEvents() {
92      base.DeregisterContentEvents();
93      Content.ModelChanged -= new EventHandler(Content_ModelChanged);
94      Content.ProblemDataChanged -= new EventHandler(Content_ProblemDataChanged);
95    }
96
97    private void Content_ProblemDataChanged(object sender, EventArgs e) {
98      UpdateChart();
99    }
100    private void Content_ModelChanged(object sender, EventArgs e) {
101      Content.Model.ThresholdsChanged += new EventHandler(Model_ThresholdsChanged);
102      UpdateChart();
103    }
104    private void Model_ThresholdsChanged(object sender, EventArgs e) {
105      AddThresholds();
106    }
107    protected override void OnContentChanged() {
108      base.OnContentChanged();
109      UpdateChart();
110    }
111
112    private void UpdateChart() {
113      if (InvokeRequired) Invoke((Action)UpdateChart);
114      else if (!updateInProgress) {
115        updateInProgress = true;
116        chart.Series.Clear();
117        classValueSeriesMapping.Clear();
118        if (Content != null) {
119          IEnumerator<string> classNameEnumerator = Content.ProblemData.ClassNames.GetEnumerator();
120          IEnumerator<double> classValueEnumerator = Content.ProblemData.ClassValues.OrderBy(x => x).GetEnumerator();
121          while (classNameEnumerator.MoveNext() && classValueEnumerator.MoveNext()) {
122            Series series = new Series(classNameEnumerator.Current);
123            series.ChartType = SeriesChartType.FastPoint;
124            series.Tag = classValueEnumerator.Current;
125            chart.Series.Add(series);
126            classValueSeriesMapping.Add(classValueEnumerator.Current, series);
127            FillSeriesWithDataPoints(series);
128          }
129          AddThresholds();
130        }
131        chart.ChartAreas[0].RecalculateAxesScale();
132        updateInProgress = false;
133      }
134    }
135
136    private void FillSeriesWithDataPoints(Series series) {
137      List<double> estimatedValues = Content.EstimatedValues.ToList();
138      foreach (int row in Content.ProblemData.TrainingIndizes) {
139        double estimatedValue = estimatedValues[row];
140        double targetValue = Content.ProblemData.Dataset[Content.ProblemData.TargetVariable, row];
141        if (targetValue.IsAlmost((double)series.Tag)) {
142          double jitterValue = random.NextDouble() * 2.0 - 1.0;
143          DataPoint point = new DataPoint();
144          point.XValue = TrainingAxisValue + 0.01 * jitterValue * JitterTrackBar.Value * (TrainingTestBorder * 0.9);
145          point.YValues[0] = estimatedValue;
146          point.Tag = new KeyValuePair<double, double>(TrainingAxisValue, jitterValue);
147          series.Points.Add(point);
148        }
149      }
150
151      foreach (int row in Content.ProblemData.TestIndizes) {
152        double estimatedValue = estimatedValues[row];
153        double targetValue = Content.ProblemData.Dataset[Content.ProblemData.TargetVariable, row];
154        if (targetValue == (double)series.Tag) {
155          double jitterValue = random.NextDouble() * 2.0 - 1.0;
156          DataPoint point = new DataPoint();
157          point.XValue = TestAxisValue + 0.01 * jitterValue * JitterTrackBar.Value * (TrainingTestBorder * 0.9);
158          point.YValues[0] = estimatedValue;
159          point.Tag = new KeyValuePair<double, double>(TestAxisValue, jitterValue);
160          series.Points.Add(point);
161        }
162      }
163
164      UpdateCursorInterval();
165    }
166
167    private void AddThresholds() {
168      chart.Annotations.Clear();
169      int classIndex = 1;
170      foreach (double threshold in Content.Model.Thresholds) {
171        if (!double.IsInfinity(threshold)) {
172          HorizontalLineAnnotation annotation = new HorizontalLineAnnotation();
173          annotation.AllowMoving = true;
174          annotation.AllowResizing = false;
175          annotation.LineWidth = 2;
176          annotation.LineColor = Color.Red;
177
178          annotation.IsInfinitive = true;
179          annotation.ClipToChartArea = chart.ChartAreas[0].Name;
180          annotation.Tag = classIndex;  //save classIndex as Tag to avoid moving the threshold accross class bounderies
181
182          annotation.AxisX = chart.ChartAreas[0].AxisX;
183          annotation.AxisY = chart.ChartAreas[0].AxisY;
184          annotation.Y = threshold;
185
186          chart.Annotations.Add(annotation);
187          classIndex++;
188        }
189      }
190    }
191
192    private void JitterTrackBar_ValueChanged(object sender, EventArgs e) {
193      foreach (Series series in chart.Series) {
194        foreach (DataPoint point in series.Points) {
195          double value = ((KeyValuePair<double, double>)point.Tag).Key;
196          double jitterValue = ((KeyValuePair<double, double>)point.Tag).Value; ;
197          point.XValue = value + 0.01 * jitterValue * JitterTrackBar.Value * (TrainingTestBorder * 0.9);
198        }
199      }
200    }
201
202    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
203      foreach (LegendItem legendItem in e.LegendItems) {
204        var series = chart.Series[legendItem.SeriesName];
205        if (series != null) {
206          bool seriesIsInvisible = series.Points.Count == 0;
207          foreach (LegendCell cell in legendItem.Cells)
208            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
209        }
210      }
211    }
212
213    private void chart_MouseMove(object sender, MouseEventArgs e) {
214      HitTestResult result = chart.HitTest(e.X, e.Y);
215      if (result.ChartElementType == ChartElementType.LegendItem)
216        this.Cursor = Cursors.Hand;
217      else
218        this.Cursor = Cursors.Default;
219    }
220
221    private void ToggleSeries(Series series) {
222      if (series.Points.Count == 0)
223        FillSeriesWithDataPoints(series);
224      else
225        series.Points.Clear();
226    }
227
228    private void chart_MouseDown(object sender, MouseEventArgs e) {
229      HitTestResult result = chart.HitTest(e.X, e.Y);
230      if (result.ChartElementType == ChartElementType.LegendItem) {
231        if (result.Series != null) ToggleSeries(result.Series);
232      }
233    }
234
235    private void chart_AnnotationPositionChanging(object sender, AnnotationPositionChangingEventArgs e) {
236      int classIndex = (int)e.Annotation.Tag;
237      double[] thresholds = Content.Model.Thresholds.ToArray();
238      thresholds[classIndex] = e.NewLocationY;
239      Content.Model.SetThresholdsAndClassValues(thresholds, Content.Model.ClassValues);
240    }
241
242    private void UpdateCursorInterval() {
243      Series series = chart.Series[0];
244      double[] xValues = (from point in series.Points
245                          where !point.IsEmpty
246                          select point.XValue)
247                    .DefaultIfEmpty(1.0)
248                    .ToArray();
249      double[] yValues = (from point in series.Points
250                          where !point.IsEmpty
251                          select point.YValues[0])
252                    .DefaultIfEmpty(1.0)
253                    .ToArray();
254
255      double xRange = xValues.Max() - xValues.Min();
256      double yRange = yValues.Max() - yValues.Min();
257      if (xRange.IsAlmost(0.0)) xRange = 1.0;
258      if (yRange.IsAlmost(0.0)) yRange = 1.0;
259      double xDigits = (int)Math.Log10(xRange) - 3;
260      double yDigits = (int)Math.Log10(yRange) - 3;
261      double xZoomInterval = Math.Pow(10, xDigits);
262      double yZoomInterval = Math.Pow(10, yDigits);
263      this.chart.ChartAreas[0].CursorX.Interval = xZoomInterval;
264      this.chart.ChartAreas[0].CursorY.Interval = yZoomInterval;
265    }
266  }
267}
Note: See TracBrowser for help on using the repository browser.