1  #region License Information


2  /* HeuristicLab


3  * Copyright (C) 20022011 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 


22  using System;


23  using System.Collections.Generic;


24  using System.Drawing;


25  using System.Linq;


26  using System.Windows.Forms;


27  using System.Windows.Forms.DataVisualization.Charting;


28  using HeuristicLab.MainForm;


29  using HeuristicLab.MainForm.WindowsForms;


30 


31  namespace HeuristicLab.Problems.DataAnalysis.Views {


32  [View("Residual Histogram")]


33  [Content(typeof(IRegressionSolution))]


34  public partial class RegressionSolutionResidualHistogram : DataAnalysisSolutionEvaluationView {


35  protected const string ALL_SAMPLES = "All samples";


36  protected const string TRAINING_SAMPLES = "Training samples";


37  protected const string TEST_SAMPLES = "Test samples";


38 


39  //to reduce code duplication


40  protected static string[] ALL_SERIES = new string[] { ALL_SAMPLES, TRAINING_SAMPLES, TEST_SAMPLES };


41 


42  //approximate amount of bins


43  protected const double bins = 25;


44 


45  /*


46  * keeps for all series a list for all bins with the position of the bin, the relative frequency of the


47  * residuals and the beginning and the end of the interval of the bin


48  */


49  protected Dictionary<string, List<List<double>>> relativeFrequencies;


50 


51  public new IRegressionSolution Content {


52  get { return (IRegressionSolution)base.Content; }


53  set { base.Content = value; }


54  }


55 


56  public RegressionSolutionResidualHistogram()


57  : base() {


58  InitializeComponent();


59 


60  relativeFrequencies = new Dictionary<string, List<List<double>>>();


61 


62  foreach (string series in ALL_SERIES) {


63  this.chart.Series.Add(series);


64  this.chart.Series[series].LegendText = series;


65  this.chart.Series[series].ChartType = SeriesChartType.Column;


66  this.chart.Series[series]["PointWidth"] = "0.9";


67  this.chart.Series[series].BorderWidth = 1;


68  this.chart.Series[series].BorderDashStyle = ChartDashStyle.Solid;


69  this.chart.Series[series].BorderColor = Color.Black;


70  this.chart.Series[series].ToolTip = series + " Y = #VALY from #CUSTOMPROPERTY(from) to #CUSTOMPROPERTY(to)";


71 


72  relativeFrequencies[series] = new List<List<double>>();


73  }


74 


75  //configure axis


76  this.chart.CustomizeAllChartAreas();


77  this.chart.ChartAreas[0].AxisX.Title = "Residuals";


78  this.chart.ChartAreas[0].CursorX.IsUserSelectionEnabled = true;


79  this.chart.ChartAreas[0].AxisX.ScaleView.Zoomable = true;


80  this.chart.ChartAreas[0].CursorX.Interval = 1;


81  this.chart.ChartAreas[0].CursorY.Interval = 1;


82 


83  this.chart.ChartAreas[0].AxisY.Title = "Relative Frequency";


84  this.chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;


85  this.chart.ChartAreas[0].AxisY.ScaleView.Zoomable = true;


86  this.chart.ChartAreas[0].AxisY.IsStartedFromZero = true;


87  }


88 


89  private void RedrawChart() {


90 


91  foreach (string series in ALL_SERIES) {


92  this.chart.Series[series].Points.Clear();


93  this.relativeFrequencies[series].Clear();


94  }


95 


96  if (Content != null) {


97 


98  Dictionary<string, List<double>> residuals = CalculateResiduals();


99 


100  double max = Math.Max(Math.Abs(residuals[ALL_SAMPLES].Min()), Math.Abs(residuals[ALL_SAMPLES].Max()));


101  double intervalWidth = (max * 2.0) / bins;


102 


103  max = HumanRoundMax(max);


104  intervalWidth = HumanRoundMax(intervalWidth);


105 


106  foreach (string series in ALL_SERIES) {


107  CalculateFrequencies(residuals[series], series, max, intervalWidth);


108  }


109 


110  ChartArea chartArea = chart.ChartAreas[0];


111 


112  chartArea.AxisX.Minimum = max  intervalWidth;


113  chartArea.AxisX.Maximum = max + intervalWidth;


114  chartArea.AxisY.Maximum = relativeFrequencies[ALL_SAMPLES].Select(x => x.ElementAt(1)).Max() + 0.02;


115  chartArea.AxisY.Interval = 0.1;


116 


117  chartArea.AxisX.Interval = intervalWidth;


118  int curBins = (int)Math.Round(max / intervalWidth);


119  //shifts the x axis label so that zero is in the middle


120  if (curBins % 2 == 0)


121  chartArea.AxisX.IntervalOffset = intervalWidth;


122  else


123  chartArea.AxisX.IntervalOffset = intervalWidth / 2;


124  }


125  }


126 


127  private Dictionary<string, List<double>> CalculateResiduals() {


128  Dictionary<string, List<double>> residuals = new Dictionary<string, List<double>>();


129 


130  foreach (string series in ALL_SERIES) {


131  residuals[series] = new List<double>();


132  }


133 


134  IRegressionProblemData problemdata = Content.ProblemData;


135  List<double> targetValues = problemdata.Dataset.GetDoubleValues(Content.ProblemData.TargetVariable).ToList();


136  List<double> estimatedValues = Content.EstimatedValues.ToList();


137 


138  for (int i = 0; i < Content.ProblemData.Dataset.Rows; i++) {


139  double residual = estimatedValues[i]  targetValues[i];


140  residuals[ALL_SAMPLES].Add(residual);


141  if (i >= problemdata.TrainingPartition.Start && i < problemdata.TrainingPartition.End)


142  residuals[TRAINING_SAMPLES].Add(residual);


143  if (i >= problemdata.TestPartition.Start && i < problemdata.TestPartition.End)


144  residuals[TEST_SAMPLES].Add(residual);


145  }


146 


147  return residuals;


148  }


149 


150  private void CalculateFrequencies(List<double> residualValues, string series, double max, double intervalWidth) {


151  Series residualSeries = this.chart.Series[series];


152 


153  double intervalCenter = intervalWidth / 2.0;


154  double sampleCount = residualValues.Count();


155  double current = max;


156 


157  for (int i = 0; i <= bins; i++) {


158  IEnumerable<double> help = residualValues.Where(x => x >= (current  intervalCenter) && x < (current + intervalCenter));


159  relativeFrequencies[series].Add(new List<double>() { current, help.Count() / sampleCount, current  intervalCenter, current + intervalCenter });


160  current += intervalWidth;


161  }


162 


163  ShowValues(residualSeries, relativeFrequencies[series]);


164  }


165 


166  private void ToggleSeriesData(Series series) {


167  if (series.Points.Count > 0) { //checks if series is shown


168  if (this.chart.Series.Any(s => s != series && s.Points.Count > 0)) {


169  series.Points.Clear();


170  }


171  } else if (Content != null) {


172  ShowValues(series, relativeFrequencies[series.Name]);


173  chart.Legends[series.Legend].ForeColor = Color.Black;


174  chart.Refresh();


175  }


176  }


177 


178  private void ShowValues(Series series, List<List<double>> relativeSeriesFrequencies) {


179  DataPointCollection seriesPoints = series.Points;


180 


181  foreach (var valueList in relativeSeriesFrequencies) {


182  seriesPoints.AddXY(valueList[0], valueList[1]);


183  seriesPoints[seriesPoints.Count  1]["from"] = valueList[2].ToString();


184  seriesPoints[seriesPoints.Count  1]["to"] = valueList[3].ToString();


185  }


186  }


187 


188  private double HumanRoundMax(double max) {


189  double base10;


190  if (max > 0) base10 = Math.Pow(10.0, Math.Floor(Math.Log10(max)));


191  else base10 = Math.Pow(10.0, Math.Ceiling(Math.Log10(max)));


192  double rounding = (max > 0) ? base10 : base10;


193  while (rounding < max) rounding += base10;


194  return rounding;


195  }


196 


197  #region events


198  protected override void RegisterContentEvents() {


199  base.RegisterContentEvents();


200  Content.ModelChanged += new EventHandler(Content_ModelChanged);


201  Content.ProblemDataChanged += new EventHandler(Content_ProblemDataChanged);


202  }


203  protected override void DeregisterContentEvents() {


204  base.DeregisterContentEvents();


205  Content.ModelChanged = new EventHandler(Content_ModelChanged);


206  Content.ProblemDataChanged = new EventHandler(Content_ProblemDataChanged);


207  }


208 


209  protected override void OnContentChanged() {


210  base.OnContentChanged();


211  RedrawChart();


212  }


213  private void Content_ProblemDataChanged(object sender, EventArgs e) {


214  RedrawChart();


215  }


216  private void Content_ModelChanged(object sender, EventArgs e) {


217  RedrawChart();


218  }


219 


220  private void chart_MouseDown(object sender, MouseEventArgs e) {


221  HitTestResult result = chart.HitTest(e.X, e.Y);


222  if (result.ChartElementType == ChartElementType.LegendItem) {


223  ToggleSeriesData(result.Series);


224  }


225  }


226  private void chart_MouseMove(object sender, MouseEventArgs e) {


227  HitTestResult result = chart.HitTest(e.X, e.Y);


228  if (result.ChartElementType == ChartElementType.LegendItem)


229  Cursor = Cursors.Hand;


230  else


231  Cursor = Cursors.Default;


232  }


233  private void chart_CustomizeLegend(object sender, CustomizeLegendEventArgs e) {


234  if (chart.Series.Count != 3) return;


235  e.LegendItems[0].Cells[1].ForeColor = this.chart.Series[ALL_SAMPLES].Points.Count == 0 ? Color.Gray : Color.Black;


236  e.LegendItems[1].Cells[1].ForeColor = this.chart.Series[TRAINING_SAMPLES].Points.Count == 0 ? Color.Gray : Color.Black;


237  e.LegendItems[2].Cells[1].ForeColor = this.chart.Series[TEST_SAMPLES].Points.Count == 0 ? Color.Gray : Color.Black;


238  }


239  #endregion


240  }


241  }

