Free cookie consent management tool by TermsFeed Policy Generator

source: stable/HeuristicLab.Analysis.Views/3.3/HistogramControl.cs @ 12551

Last change on this file since 12551 was 12154, checked in by ascheibe, 10 years ago

#2351 merged r12134,r12135,r12139,r12140,r12143,r12145 into stable

File size: 8.5 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 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.Analysis.Statistics;
29
30namespace HeuristicLab.Analysis.Views {
31  public partial class HistogramControl : UserControl {
32    protected static readonly string SeriesName = "Histogram";
33    protected static readonly decimal bandwidthMin = 0.0000000000001m;
34    protected Dictionary<string, List<double>> points;
35    protected bool suppressUpdate;
36
37    public int NumberOfBins {
38      get { return (int)binsNumericUpDown.Value; }
39      set { binsNumericUpDown.Value = value; }
40    }
41
42    public int MinimumNumberOfBins {
43      get { return (int)binsNumericUpDown.Minimum; }
44      set { binsNumericUpDown.Minimum = value; }
45    }
46
47    public int MaximumNumberOfBins {
48      get { return (int)binsNumericUpDown.Maximum; }
49      set { binsNumericUpDown.Maximum = value; }
50    }
51
52    public int IncrementNumberOfBins {
53      get { return (int)binsNumericUpDown.Increment; }
54      set { binsNumericUpDown.Increment = value; }
55    }
56
57    public bool CalculateExactBins {
58      get { return exactCheckBox.Checked; }
59      set { exactCheckBox.Checked = value; }
60    }
61
62    public bool ShowExactCheckbox {
63      get { return exactCheckBox.Visible; }
64      set { exactCheckBox.Visible = value; }
65    }
66
67    public HistogramControl() {
68      InitializeComponent();
69      points = new Dictionary<string, List<double>>();
70    }
71
72    protected void InitNewRow(string name) {
73      if (!points.ContainsKey(name)) {
74        points.Add(name, new List<double>());
75      }
76    }
77
78    protected void InitSeries(string name) {
79      if (!chart.Series.Any(x => x.Name == name)) {
80        Series s = chart.Series.Add(name);
81        s.ChartType = SeriesChartType.Column;
82        s.BorderColor = Color.Black;
83        s.BorderWidth = 1;
84        s.BorderDashStyle = ChartDashStyle.Solid;
85      }
86    }
87
88    public void AddPoint(double point) {
89      InitNewRow(SeriesName);
90      InitSeries(SeriesName);
91      points[SeriesName].Add(point);
92      UpdateHistogram();
93    }
94
95    public void AddPoints(IEnumerable<double> points) {
96      InitNewRow(SeriesName);
97      InitSeries(SeriesName);
98      this.points[SeriesName].AddRange(points);
99      UpdateHistogram();
100    }
101
102    public void AddPoint(string name, double point, bool replace = false) {
103      InitNewRow(name);
104      InitSeries(name);
105      if (replace) {
106        points[name].Clear();
107      }
108      points[name].Add(point);
109      UpdateHistogram();
110    }
111
112    public void AddPoints(string name, IEnumerable<double> points, bool replace = false) {
113      InitNewRow(name);
114      InitSeries(name);
115      if (replace) {
116        this.points[name].Clear();
117      }
118      this.points[name].AddRange(points);
119      UpdateHistogram();
120    }
121
122    public void ClearPoints() {
123      points.Clear();
124      UpdateHistogram();
125    }
126
127    protected void UpdateHistogram(double bandwith = double.NaN) {
128      if (InvokeRequired) {
129        Invoke((Action<double>)UpdateHistogram, bandwith);
130        return;
131      }
132
133      double overallMin = double.MaxValue;
134      double overallMax = double.MinValue;
135      int bins = (int)binsNumericUpDown.Value;
136
137      chart.Series.Clear();
138      foreach (var point in points) {
139        if (!point.Value.Any()) continue;
140
141        Series histogramSeries = new Series(point.Key);
142        chart.Series.Add(histogramSeries);
143        double minValue = point.Value.Min();
144        double maxValue = point.Value.Max();
145        double intervalWidth = (maxValue - minValue) / bins;
146        if (intervalWidth <= 0) continue;
147
148        if (!exactCheckBox.Checked) {
149          intervalWidth = HumanRoundRange(intervalWidth);
150          minValue = Math.Floor(minValue / intervalWidth) * intervalWidth;
151          maxValue = Math.Ceiling(maxValue / intervalWidth) * intervalWidth;
152        }
153
154        double current = minValue, intervalCenter = intervalWidth / 2.0;
155        int count = 0;
156        foreach (double v in point.Value.OrderBy(x => x)) {
157          while (v > current + intervalWidth) {
158            histogramSeries.Points.AddXY(current + intervalCenter, count);
159            current += intervalWidth;
160            count = 0;
161          }
162          count++;
163        }
164        histogramSeries.Points.AddXY(current + intervalCenter, count);
165        histogramSeries["PointWidth"] = "1";
166
167        overallMax = Math.Max(overallMax, maxValue);
168        overallMin = Math.Min(overallMin, minValue);
169      }
170
171      chart.ApplyPaletteColors();
172
173      int i = 0;
174      foreach (var point in points) {
175        if (!point.Value.Any()) continue;
176
177        var histogramSeries = chart.Series[i];
178        CalculateDensity(histogramSeries, point.Value, bandwith);
179
180        i++;
181      }
182
183
184      ChartArea chartArea = chart.ChartAreas[0];
185      // don't show grid lines for second y-axis
186      chartArea.AxisY2.MajorGrid.Enabled = false;
187      chartArea.AxisY2.MinorGrid.Enabled = false;
188      chartArea.AxisY.Title = "Frequency";
189
190      double overallIntervalWidth = (overallMax - overallMin) / bins;
191      double axisInterval = overallIntervalWidth;
192      while ((overallMax - overallMin) / axisInterval > 10.0) {
193        axisInterval *= 2.0;
194      }
195      chartArea.AxisX.Interval = axisInterval;
196    }
197
198    protected void CalculateDensity(Series series, List<double> row, double bandwidth = double.NaN) {
199      string densitySeriesName = "Density " + series.Name;
200      double stepWidth = series.Points[1].XValue - series.Points[0].XValue;
201      var rowArray = row.ToArray();
202
203      if (chart.Series.Any(x => x.Name == densitySeriesName)) {
204        var ds = chart.Series.Single(x => x.Name == densitySeriesName);
205        chart.Series.Remove(ds);
206      }
207
208      if (double.IsNaN(bandwidth)) {
209        bandwidth = KernelDensityEstimator.EstimateBandwidth(rowArray);
210        decimal bwDecimal = (decimal)bandwidth;
211        if (bwDecimal < bandwidthMin) {
212          bwDecimal = bandwidthMin;
213          bandwidth = decimal.ToDouble(bwDecimal);
214        }
215        suppressUpdate = true;
216        bandwidthNumericUpDown.Value = bwDecimal;
217      }
218      var density = KernelDensityEstimator.Density(rowArray, rowArray.Length, stepWidth, bandwidth);
219
220      Series newSeries = new Series(densitySeriesName);
221      newSeries.Color = series.Color;
222      newSeries.ChartType = SeriesChartType.FastLine;
223      newSeries.BorderWidth = 2;
224      foreach (var d in density) {
225        newSeries.Points.Add(new DataPoint(d.Item1, d.Item2));
226      }
227
228      // densities should be plotted on the second axis (different scale)
229      newSeries.YAxisType = AxisType.Secondary;
230      chart.Series.Add(newSeries);
231    }
232
233    protected double HumanRoundRange(double range) {
234      double base10 = Math.Pow(10.0, Math.Floor(Math.Log10(range)));
235      double rounding = range / base10;
236      if (rounding <= 1.5) rounding = 1;
237      else if (rounding <= 2.25) rounding = 2;
238      else if (rounding <= 3.75) rounding = 2.5;
239      else if (rounding <= 7.5) rounding = 5;
240      else rounding = 10;
241      return rounding * base10;
242    }
243
244    private void binsNumericUpDown_ValueChanged(object sender, EventArgs e) {
245      UpdateHistogram();
246    }
247
248    private void exactCheckBox_CheckedChanged(object sender, EventArgs e) {
249      UpdateHistogram();
250    }
251
252    private void bandwidthNumericUpDown_ValueChanged(object sender, EventArgs e) {
253      if (!suppressUpdate) {
254        UpdateHistogram(decimal.ToDouble(bandwidthNumericUpDown.Value));
255      }
256      suppressUpdate = false;
257    }
258  }
259}
Note: See TracBrowser for help on using the repository browser.