1  #region License Information


2  /* HeuristicLab


3  * Copyright (C) 20022015 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  /// approximate amount of bins


42  /// </summary>


43  protected const double bins = 25;


44  #endregion


45 


46  public new IRegressionSolution Content {


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


48  set { base.Content = value; }


49  }


50 


51  public RegressionSolutionResidualHistogram()


52  : base() {


53  InitializeComponent();


54  foreach (string series in new List<String>() { ALL_SAMPLES, TRAINING_SAMPLES, TEST_SAMPLES }) {


55  chart.Series.Add(series);


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


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


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


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


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


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


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


63  }


64  //configure axis


65  chart.CustomizeAllChartAreas();


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


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


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


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


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


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


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


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


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


75  }


76 


77  private void RedrawChart() {


78  foreach (Series series in chart.Series) {


79  series.Points.Clear();


80  }


81  if (Content != null) {


82  List<double> residuals = CalculateResiduals(Content);


83 


84  double max = 0.0;


85  foreach (Series series in chart.Series) {


86  CalculateFrequencies(residuals, series);


87  double seriesMax = series.Points.Select(p => p.YValues.First()).Max();


88  max = max < seriesMax ? seriesMax : max;


89  }


90 


91  // ALL_SAMPLES has to be calculated to know its highest frequency, but it is not shown in the beginning


92  chart.Series.First(s => s.Name.Equals(ALL_SAMPLES)).Points.Clear();


93 


94  double roundedMax, intervalWidth;


95  CalculateResidualParameters(residuals, out roundedMax, out intervalWidth);


96 


97  ChartArea chartArea = chart.ChartAreas[0];


98  chartArea.AxisX.Minimum = roundedMax  intervalWidth;


99  chartArea.AxisX.Maximum = roundedMax + intervalWidth;


100  // get the highest frequency of a residual of any series


101  chartArea.AxisY.Maximum = max;


102  if (chartArea.AxisY.Maximum < 0.1) {


103  chartArea.AxisY.Interval = 0.01;


104  chartArea.AxisY.Maximum = Math.Ceiling(chartArea.AxisY.Maximum * 100) / 100;


105  } else {


106  chartArea.AxisY.Interval = 0.1;


107  chartArea.AxisY.Maximum = Math.Ceiling(chartArea.AxisY.Maximum * 10) / 10;


108  }


109  chartArea.AxisX.Interval = intervalWidth;


110  int curBins = (int)Math.Round((roundedMax * 2) / intervalWidth);


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


112  if (curBins % 2 == 0)


113  chartArea.AxisX.IntervalOffset = intervalWidth;


114  else


115  chartArea.AxisX.IntervalOffset = intervalWidth / 2;


116  }


117  }


118 


119  private List<double> CalculateResiduals(IRegressionSolution solution) {


120  List<double> residuals = new List<double>();


121 


122  IRegressionProblemData problemdata = solution.ProblemData;


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


124  List<double> estimatedValues = solution.EstimatedValues.ToList();


125 


126  for (int i = 0; i < solution.ProblemData.Dataset.Rows; i++) {


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


128  residuals.Add(residual);


129  }


130  return residuals;


131  }


132 


133  private void CalculateFrequencies(List<double> residualValues, Series series) {


134  double roundedMax, intervalWidth;


135  CalculateResidualParameters(residualValues, out roundedMax, out intervalWidth);


136 


137  IEnumerable<double> relevantResiduals = residualValues;


138  IRegressionProblemData problemdata = Content.ProblemData;


139  if (series.Name.Equals(TRAINING_SAMPLES)) {


140  relevantResiduals = residualValues.Skip(problemdata.TrainingPartition.Start).Take(problemdata.TrainingPartition.Size);


141  } else if (series.Name.Equals(TEST_SAMPLES)) {


142  relevantResiduals = residualValues.Skip(problemdata.TestPartition.Start).Take(problemdata.TestPartition.Size);


143  }


144 


145  double intervalCenter = intervalWidth / 2.0;


146  double sampleCount = relevantResiduals.Count();


147  double current = roundedMax;


148  DataPointCollection seriesPoints = series.Points;


149 


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


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


152  seriesPoints.AddXY(current, help.Count() / sampleCount);


153  seriesPoints[seriesPoints.Count  1]["from"] = (current  intervalCenter).ToString();


154  seriesPoints[seriesPoints.Count  1]["to"] = (current + intervalCenter).ToString();


155  current += intervalWidth;


156  }


157  }


158 


159  private void ToggleSeriesData(Series series) {


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


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


162  series.Points.Clear();


163  }


164  } else if (Content != null) {


165  List<double> residuals = CalculateResiduals(Content);


166  CalculateFrequencies(residuals, series);


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


168  chart.Refresh();


169  }


170  }


171 


172  private static void CalculateResidualParameters(List<double> residuals, out double roundedMax, out double intervalWidth) {


173  double realMax = Math.Max(Math.Abs(residuals.Min()), Math.Abs(residuals.Max()));


174  roundedMax = HumanRoundMax(realMax);


175  intervalWidth = (roundedMax * 2.0) / bins;


176  intervalWidth = HumanRoundMax(intervalWidth);


177  // sets roundedMax to a value, so that zero will be in the middle of the x axis


178  double help = realMax / intervalWidth;


179  help = help % 1 < 0.5 ? (int)help : (int)help + 1;


180  roundedMax = help * intervalWidth;


181  }


182 


183  private static double HumanRoundMax(double max) {


184  double base10;


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


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


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


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


189  return rounding;


190  }


191 


192  #region events


193  protected override void RegisterContentEvents() {


194  base.RegisterContentEvents();


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


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


197  }


198  protected override void DeregisterContentEvents() {


199  base.DeregisterContentEvents();


200  Content.ModelChanged = new EventHandler(Content_ModelChanged);


201  Content.ProblemDataChanged = new EventHandler(Content_ProblemDataChanged);


202  }


203 


204  protected override void OnContentChanged() {


205  base.OnContentChanged();


206  RedrawChart();


207  }


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


209  RedrawChart();


210  }


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


212  RedrawChart();


213  }


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


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


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


217  ToggleSeriesData(result.Series);


218  }


219  }


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


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


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


223  Cursor = Cursors.Hand;


224  else


225  Cursor = Cursors.Default;


226  }


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


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


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


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


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


232  }


233  #endregion


234  }


235  }

