1  #region License Information


2  /* HeuristicLab


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


36  #region variables


37  protected const string ALL_SAMPLES = "All samples";


38  protected const string TRAINING_SAMPLES = "Training samples";


39  protected const string TEST_SAMPLES = "Test samples";


40  /// <summary>


41  /// used to reduce code duplication


42  /// </summary>


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


44  /// <summary>


45  /// approximate amount of bins


46  /// </summary>


47  protected const double bins = 25;


48  /// <summary>


49  /// keeps for all series a list for every bin with the position of the bin, the relative frequency of the


50  /// residuals and the beginning and the end of the interval of the bin


51  /// </summary>


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


53  #endregion


54 


55  public new IRegressionSolution Content {


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


57  set { base.Content = value; }


58  }


59 


60  public RegressionSolutionResidualHistogram()


61  : base() {


62  InitializeComponent();


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


64  foreach (string series in ALL_SERIES) {


65  chart.Series.Add(series);


66  chart.Series[series].LegendText = series;


67  chart.Series[series].ChartType = SeriesChartType.Column;


68  chart.Series[series]["PointWidth"] = "0.9";


69  chart.Series[series].BorderWidth = 1;


70  chart.Series[series].BorderDashStyle = ChartDashStyle.Solid;


71  chart.Series[series].BorderColor = Color.Black;


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


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


74  }


75  //configure axis


76  chart.CustomizeAllChartAreas();


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


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


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


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


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


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


83  chart.ChartAreas[0].CursorY.IsUserSelectionEnabled = true;


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


85  chart.ChartAreas[0].AxisY.IsStartedFromZero = true;


86  }


87 


88  private void RedrawChart() {


89  foreach (string series in ALL_SERIES) {


90  chart.Series[series].Points.Clear();


91  relativeFrequencies[series].Clear();


92  }


93  if (Content != null) {


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


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


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


97  max = HumanRoundMax(max);


98  intervalWidth = HumanRoundMax(intervalWidth);


99 


100  foreach (string series in ALL_SERIES) {


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


102  if (!series.Equals(ALL_SAMPLES))


103  ShowValues(chart.Series[series], relativeFrequencies[series]);


104  }


105 


106  ChartArea chartArea = chart.ChartAreas[0];


107  chartArea.AxisX.Minimum = max  intervalWidth;


108  chartArea.AxisX.Maximum = max + intervalWidth;


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


110  chartArea.AxisY.Interval = 0.1;


111  chartArea.AxisX.Interval = intervalWidth;


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


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


114  if (curBins % 2 == 0)


115  chartArea.AxisX.IntervalOffset = intervalWidth;


116  else


117  chartArea.AxisX.IntervalOffset = intervalWidth / 2;


118  }


119  }


120 


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


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


123 


124  foreach (string series in ALL_SERIES) {


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


126  }


127  IRegressionProblemData problemdata = Content.ProblemData;


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


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


130 


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


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


133  residuals[ALL_SAMPLES].Add(residual);


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


135  residuals[TRAINING_SAMPLES].Add(residual);


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


137  residuals[TEST_SAMPLES].Add(residual);


138  }


139  return residuals;


140  }


141 


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


143  //Series residualSeries = chart.Series[series];


144  double intervalCenter = intervalWidth / 2.0;


145  double sampleCount = residualValues.Count();


146  double current = max;


147 


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


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


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


151  current += intervalWidth;


152  }


153  //ShowValues(residualSeries, relativeFrequencies[series]);


154  }


155 


156  private double HumanRoundMax(double max) {


157  double base10;


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


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


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


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


162  return rounding;


163  }


164 


165  #region events


166  protected override void RegisterContentEvents() {


167  base.RegisterContentEvents();


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


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


170  }


171  protected override void DeregisterContentEvents() {


172  base.DeregisterContentEvents();


173  Content.ModelChanged = new EventHandler(Content_ModelChanged);


174  Content.ProblemDataChanged = new EventHandler(Content_ProblemDataChanged);


175  }


176 


177  protected override void OnContentChanged() {


178  base.OnContentChanged();


179  RedrawChart();


180  }


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


182  RedrawChart();


183  }


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


185  RedrawChart();


186  }


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


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


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


190  ToggleSeriesData(result.Series);


191  }


192  }


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


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


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


196  Cursor = Cursors.Hand;


197  else


198  Cursor = Cursors.Default;


199  }


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


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


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


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


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


205  }


206  #endregion


207 


208  private void ToggleSeriesData(Series series) {


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


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


211  series.Points.Clear();


212  }


213  } else if (Content != null) {


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


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


216  chart.Refresh();


217  }


218  }


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


220  DataPointCollection seriesPoints = series.Points;


221 


222  foreach (var valueList in relativeSeriesFrequencies) {


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


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


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


226  }


227  }


228  }


229  }

