Free cookie consent management tool by TermsFeed Policy Generator

source: branches/Async/HeuristicLab.Analysis.Views/3.3/HistogramControl.cs @ 12675

Last change on this file since 12675 was 11610, checked in by ascheibe, 10 years ago

#2279

  • moved HistogramControl from ChartControlExtensions project to Analysis.Views. This is needed because we need StandardDeviation for drawing Density plots and I don't want to add a reference to Common
  • added calculation of density for normal distribution
File size: 7.1 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2014 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
34    protected Dictionary<string, List<double>> points;
35    public int NumberOfBins {
36      get { return (int)binsNumericUpDown.Value; }
37      set { binsNumericUpDown.Value = value; }
38    }
39
40    public int MinimumNumberOfBins {
41      get { return (int)binsNumericUpDown.Minimum; }
42      set { binsNumericUpDown.Minimum = value; }
43    }
44
45    public int MaximumNumberOfBins {
46      get { return (int)binsNumericUpDown.Maximum; }
47      set { binsNumericUpDown.Maximum = value; }
48    }
49
50    public int IncrementNumberOfBins {
51      get { return (int)binsNumericUpDown.Increment; }
52      set { binsNumericUpDown.Increment = value; }
53    }
54
55    public bool CalculateExactBins {
56      get { return exactCheckBox.Checked; }
57      set { exactCheckBox.Checked = value; }
58    }
59
60    public bool ShowExactCheckbox {
61      get { return exactCheckBox.Visible; }
62      set { exactCheckBox.Visible = value; }
63    }
64
65    public HistogramControl() {
66      InitializeComponent();
67      points = new Dictionary<string, List<double>>();
68    }
69
70    protected void InitNewRow(string name) {
71      if (!points.ContainsKey(name)) {
72        points.Add(name, new List<double>());
73      }
74    }
75
76    protected void InitSeries(string name) {
77      if (!chart.Series.Any(x => x.Name == name)) {
78        Series s = chart.Series.Add(name);
79        s.ChartType = SeriesChartType.Column;
80        s.BorderColor = Color.Black;
81        s.BorderWidth = 1;
82        s.BorderDashStyle = ChartDashStyle.Solid;
83      }
84    }
85
86    public void AddPoint(double point) {
87      InitNewRow(SeriesName);
88      InitSeries(SeriesName);
89      points[SeriesName].Add(point);
90      UpdateHistogram();
91    }
92
93    public void AddPoints(IEnumerable<double> points) {
94      InitNewRow(SeriesName);
95      InitSeries(SeriesName);
96      this.points[SeriesName].AddRange(points);
97      UpdateHistogram();
98    }
99
100    public void AddPoint(string name, double point, bool replace = false) {
101      InitNewRow(name);
102      InitSeries(name);
103      if (replace) {
104        points[name].Clear();
105      }
106      points[name].Add(point);
107      UpdateHistogram();
108    }
109
110    public void AddPoints(string name, IEnumerable<double> points, bool replace = false) {
111      InitNewRow(name);
112      InitSeries(name);
113      if (replace) {
114        this.points[name].Clear();
115      }
116      this.points[name].AddRange(points);
117      UpdateHistogram();
118    }
119
120    public void ClearPoints() {
121      points.Clear();
122      UpdateHistogram();
123    }
124
125    protected void UpdateHistogram() {
126      if (InvokeRequired) {
127        Invoke((Action)UpdateHistogram, null);
128        return;
129      }
130
131      double overallMin = double.MaxValue;
132      double overallMax = double.MinValue;
133      int bins = (int)binsNumericUpDown.Value;
134
135      chart.Series.Clear();
136
137      foreach (var point in points) {
138        if (!point.Value.Any()) continue;
139
140        Series histogramSeries = new Series(point.Key);
141        chart.Series.Add(histogramSeries);
142
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        CalculateDensity(histogramSeries, point.Value);
168
169        overallMax = Math.Max(overallMax, maxValue);
170        overallMin = Math.Min(overallMin, minValue);
171      }
172
173      ChartArea chartArea = chart.ChartAreas[0];
174      chartArea.AxisY.Title = "Frequency";
175
176      double overallIntervalWidth = (overallMax - overallMin) / bins;
177      double axisInterval = overallIntervalWidth;
178      while ((overallMax - overallMin) / axisInterval > 10.0) {
179        axisInterval *= 2.0;
180      }
181      chartArea.AxisX.Interval = axisInterval;
182    }
183
184    protected void CalculateDensity(Series series, List<double> row) {
185      string densitySeriesName = "Density " + series.Name;
186      double stepWidth = series.Points[1].XValue - series.Points[0].XValue;
187
188      if (chart.Series.Any(x => x.Name == densitySeriesName)) {
189        var ds = chart.Series.Single(x => x.Name == densitySeriesName);
190        chart.Series.Remove(ds);
191      }
192
193      var density = NormalDistribution.Density(row.ToArray(), row.Count, stepWidth);
194
195      Series newSeries = new Series(densitySeriesName);
196      newSeries.ChartType = SeriesChartType.FastLine;
197      newSeries.BorderWidth = 2;
198      foreach (var d in density) {
199        newSeries.Points.Add(new DataPoint(d.Item1, d.Item2));
200      }
201
202      chart.Series.Add(newSeries);
203    }
204
205    protected double HumanRoundRange(double range) {
206      double base10 = Math.Pow(10.0, Math.Floor(Math.Log10(range)));
207      double rounding = range / base10;
208      if (rounding <= 1.5) rounding = 1;
209      else if (rounding <= 2.25) rounding = 2;
210      else if (rounding <= 3.75) rounding = 2.5;
211      else if (rounding <= 7.5) rounding = 5;
212      else rounding = 10;
213      return rounding * base10;
214    }
215
216    private void binsNumericUpDown_ValueChanged(object sender, EventArgs e) {
217      UpdateHistogram();
218    }
219
220    private void exactCheckBox_CheckedChanged(object sender, EventArgs e) {
221      UpdateHistogram();
222    }
223  }
224}
Note: See TracBrowser for help on using the repository browser.