Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Classification/HeuristicLab.Problems.DataAnalysis.Classification.Views/3.3/RocCurvesView.cs @ 4417

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

adapted classification analyzer and readded views project (ticket #939)

File size: 10.8 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.Drawing;
25using System.Linq;
26using System.Text;
27using System.Windows.Forms;
28using System.Windows.Forms.DataVisualization.Charting;
29using HeuristicLab.Common;
30using HeuristicLab.MainForm;
31using HeuristicLab.MainForm.WindowsForms;
32namespace HeuristicLab.Problems.DataAnalysis.Classification.Views {
33  [View("ROC Curves View")]
34  [Content(typeof(SymbolicClassificationSolution))]
35  public partial class RocCurvesView : AsynchronousContentView {
36    private const string xAxisTitle = "False Positive Rate";
37    private const string yAxisTitle = "True Positive Rate";
38    private const string TrainingSamples = "Training";
39    private const string TestSamples = "Test";
40    private Dictionary<string, List<ROCPoint>> cachedRocPoints;
41
42    public RocCurvesView() {
43      InitializeComponent();
44
45      cachedRocPoints = new Dictionary<string, List<ROCPoint>>();
46
47      cmbSamples.Items.Add(TrainingSamples);
48      cmbSamples.Items.Add(TestSamples);
49      cmbSamples.SelectedIndex = 0;
50
51      chart.ChartAreas[0].AxisX.Minimum = 0.0;
52      chart.ChartAreas[0].AxisX.Maximum = 1.0;
53      chart.ChartAreas[0].AxisX.MajorGrid.Interval = 0.2;
54      chart.ChartAreas[0].AxisY.Minimum = 0.0;
55      chart.ChartAreas[0].AxisY.Maximum = 1.0;
56      chart.ChartAreas[0].AxisY.MajorGrid.Interval = 0.2;
57
58      chart.ChartAreas[0].AxisX.Title = xAxisTitle;
59      chart.ChartAreas[0].AxisY.Title = yAxisTitle;
60    }
61
62    public new SymbolicClassificationSolution Content {
63      get { return (SymbolicClassificationSolution)base.Content; }
64      set { base.Content = value; }
65    }
66
67    protected override void RegisterContentEvents() {
68      base.RegisterContentEvents();
69      Content.EstimatedValuesChanged += new EventHandler(Content_EstimatedValuesChanged);
70      Content.ProblemDataChanged += new EventHandler(Content_ProblemDataChanged);
71    }
72    protected override void DeregisterContentEvents() {
73      base.DeregisterContentEvents();
74      Content.EstimatedValuesChanged -= new EventHandler(Content_EstimatedValuesChanged);
75      Content.ProblemDataChanged -= new EventHandler(Content_ProblemDataChanged);
76    }
77
78    private void Content_EstimatedValuesChanged(object sender, EventArgs e) {
79      UpdateChart();
80    }
81    private void Content_ProblemDataChanged(object sender, EventArgs e) {
82      UpdateChart();
83    }
84
85    protected override void OnContentChanged() {
86      base.OnContentChanged();
87      chart.Series.Clear();
88      if (Content != null) UpdateChart();
89    }
90
91    private void UpdateChart() {
92      if (InvokeRequired) Invoke((Action)UpdateChart);
93      else {
94        chart.Series.Clear();
95        chart.Annotations.Clear();
96        cachedRocPoints.Clear();
97
98        int slices = 100;
99        int samplesStart = Content.ProblemData.TrainingSamplesStart.Value;
100        int samplesEnd = Content.ProblemData.TrainingSamplesEnd.Value;
101
102        if (cmbSamples.SelectedItem.ToString() == TrainingSamples) {
103          samplesStart = Content.ProblemData.TrainingSamplesStart.Value;
104          samplesEnd = Content.ProblemData.TrainingSamplesEnd.Value;
105        } else if (cmbSamples.SelectedItem.ToString() == TestSamples) {
106          samplesStart = Content.ProblemData.TestSamplesStart.Value;
107          samplesEnd = Content.ProblemData.TestSamplesEnd.Value;
108        } else throw new InvalidOperationException();
109
110        double[] estimatedValues = Content.EstimatedValues.Skip(samplesStart).Take(samplesEnd - samplesStart).ToArray();
111        double[] targetClassValues = Content.ProblemData.Dataset.GetVariableValues(Content.ProblemData.TargetVariable.Value)
112          .Skip(samplesStart).Take(samplesEnd - samplesStart).ToArray();
113        double minThreshold = estimatedValues.Min();
114        double maxThreshold = estimatedValues.Max();
115        double thresholdIncrement = (maxThreshold - minThreshold) / slices;
116        minThreshold -= thresholdIncrement;
117        maxThreshold += thresholdIncrement;
118
119        List<double> classValues = Content.ProblemData.SortedClassValues.ToList();
120
121        foreach (double classValue in classValues) {
122          List<ROCPoint> rocPoints = new List<ROCPoint>();
123          int positives = targetClassValues.Where(c => c.IsAlmost(classValue)).Count();
124          int negatives = samplesEnd - samplesStart - positives;
125
126          for (double lowerThreshold = minThreshold; lowerThreshold < maxThreshold; lowerThreshold += thresholdIncrement) {
127            for (double upperThreshold = lowerThreshold + thresholdIncrement; upperThreshold < maxThreshold; upperThreshold += thresholdIncrement) {
128              int truePositives = 0;
129              int falsePositives = 0;
130
131              for (int row = 0; row < estimatedValues.Length; row++) {
132                if (lowerThreshold < estimatedValues[row] && estimatedValues[row] < upperThreshold) {
133                  if (targetClassValues[row].IsAlmost(classValue)) truePositives++;
134                  else falsePositives++;
135                }
136              }
137
138              double truePositiveRate = ((double)truePositives) / positives;
139              double falsePositiveRate = ((double)falsePositives) / negatives;
140
141              ROCPoint rocPoint = new ROCPoint(truePositiveRate, falsePositiveRate, lowerThreshold, upperThreshold);
142              if (!rocPoints.Any(x => x.truePositiveRate >= rocPoint.truePositiveRate && x.falsePositiveRate <= rocPoint.falsePositiveRate)) {
143                rocPoints.RemoveAll(x => x.falsePositiveRate >= rocPoint.falsePositiveRate && x.truePositiveRate <= rocPoint.truePositiveRate);
144                rocPoints.Add(rocPoint);
145              }
146            }
147          }
148
149          string className = Content.ProblemData.ClassNames.ElementAt(classValues.IndexOf(classValue));
150          cachedRocPoints[className] = rocPoints.OrderBy(x => x.falsePositiveRate).ToList(); ;
151
152          Series series = new Series(className);
153          series.ChartType = SeriesChartType.Line;
154          series.MarkerStyle = MarkerStyle.Diamond;
155          series.MarkerSize = 5;
156          chart.Series.Add(series);
157          FillSeriesWithDataPoints(series, cachedRocPoints[className]);
158
159          double auc = CalculateAreaUnderCurve(series);
160          series.LegendToolTip = "AUC: " + auc;
161        }
162      }
163    }
164
165    private void FillSeriesWithDataPoints(Series series, IEnumerable<ROCPoint> rocPoints) {
166      series.Points.Add(new DataPoint(0, 0));
167      foreach (ROCPoint rocPoint in rocPoints) {
168        DataPoint point = new DataPoint();
169        point.XValue = rocPoint.falsePositiveRate;
170        point.YValues[0] = rocPoint.truePositiveRate;
171        point.Tag = rocPoint;
172
173        StringBuilder sb = new StringBuilder();
174        sb.AppendLine("True Positive Rate: " + rocPoint.truePositiveRate);
175        sb.AppendLine("False Positive Rate: " + rocPoint.falsePositiveRate);
176        sb.AppendLine("Upper Threshold: " + rocPoint.upperThreshold);
177        sb.AppendLine("Lower Threshold: " + rocPoint.lowerThreshold);
178        point.ToolTip = sb.ToString();
179
180        series.Points.Add(point);
181      }
182      series.Points.Add(new DataPoint(1, 1));
183    }
184
185    private double CalculateAreaUnderCurve(Series series) {
186      if (series.Points.Count < 1) throw new ArgumentException("Could not calculate area under curve if less than 1 data points were given.");
187
188      double auc = 0.0;
189      for (int i = 1; i < series.Points.Count; i++) {
190        double width = series.Points[i].XValue - series.Points[i - 1].XValue;
191        double y1 = series.Points[i - 1].YValues[0];
192        double y2 = series.Points[i].YValues[0];
193
194        auc += (y1 + y2) * width / 2;
195      }
196
197      return auc;
198    }
199
200    private void cmbSamples_SelectedIndexChanged(object sender, System.EventArgs e) {
201      if (Content != null)
202        UpdateChart();
203    }
204
205
206    #region show / hide series
207    private void ToggleSeries(Series series) {
208      if (series.Points.Count == 0)
209        FillSeriesWithDataPoints(series, cachedRocPoints[series.Name]);
210      else
211        series.Points.Clear();
212    }
213    private void chart_MouseDown(object sender, MouseEventArgs e) {
214      HitTestResult result = chart.HitTest(e.X, e.Y);
215      if (result.ChartElementType == ChartElementType.LegendItem) {
216        if (result.Series != null) ToggleSeries(result.Series);
217      }
218    }
219    private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {
220      foreach (LegendItem legendItem in e.LegendItems) {
221        var series = chart.Series[legendItem.SeriesName];
222        if (series != null) {
223          bool seriesIsInvisible = series.Points.Count == 0;
224          foreach (LegendCell cell in legendItem.Cells)
225            cell.ForeColor = seriesIsInvisible ? Color.Gray : Color.Black;
226        }
227      }
228    }
229    private void chart_MouseMove(object sender, MouseEventArgs e) {
230      HitTestResult result = chart.HitTest(e.X, e.Y);
231      if (result.ChartElementType == ChartElementType.LegendItem)
232        this.Cursor = Cursors.Hand;
233      else
234        this.Cursor = Cursors.Default;
235
236      string newTooltipText = string.Empty;
237      if (result.ChartElementType == ChartElementType.DataPoint)
238        newTooltipText = ((DataPoint)result.Object).ToolTip;
239
240      string oldTooltipText = this.toolTip.GetToolTip(chart);
241      if (newTooltipText != oldTooltipText)
242        this.toolTip.SetToolTip(chart, newTooltipText);
243    }
244    #endregion
245
246
247    private class ROCPoint {
248      public ROCPoint(double truePositiveRate, double falsePositiveRate, double lowerThreshold, double upperThreshold) {
249        this.truePositiveRate = truePositiveRate;
250        this.falsePositiveRate = falsePositiveRate;
251        this.lowerThreshold = lowerThreshold;
252        this.upperThreshold = upperThreshold;
253
254      }
255      public double truePositiveRate { get; private set; }
256      public double falsePositiveRate { get; private set; }
257      public double lowerThreshold { get; private set; }
258      public double upperThreshold { get; private set; }
259    }
260
261  }
262}
Note: See TracBrowser for help on using the repository browser.