Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.CEDMA.Charting/Histogram.cs @ 1038

Last change on this file since 1038 was 575, checked in by gkronber, 16 years ago

fixed a problem with brushing in histograms and added variable selection pressure

File size: 7.4 KB
RevLine 
[571]1#region License Information
[560]2/* HeuristicLab
3 * Copyright (C) 2002-2008 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;
[571]24using System.Text;
25using System.Drawing;
[560]26using System.Linq;
[571]27using HeuristicLab.Charting;
28using System.Windows.Forms;
[560]29
30namespace HeuristicLab.CEDMA.Charting {
[571]31  public class Histogram : Chart {
32    private static readonly Color defaultColor = Color.Blue;
33    private static readonly Color selectionColor = Color.Red;
[575]34    private static readonly Pen defaultPen = new Pen(defaultColor);
35    private static readonly Brush defaultBrush = defaultPen.Brush;
36    private static readonly Pen selectionPen = new Pen(selectionColor);
37    private static readonly Brush selectionBrush = selectionPen.Brush;
[571]38
39    private double minX;
40    private double maxX;
41    private double maxFrequency;
42    private const int N_BUCKETS = 50;
[572]43    private const int MAX_BUCKETS = 100;
[571]44    private List<Record> records;
45    private ResultList results;
46    private Dictionary<IPrimitive, List<Record>> primitiveToRecordsDictionary;
47    private Dictionary<Record, IPrimitive> recordToPrimitiveDictionary;
48    private Group bars;
49    private string dimension;
50
51    public Histogram(ResultList results, double x1, double y1, double x2, double y2)
52      : this(results, new PointD(x1, y1), new PointD(x2, y2)) {
53    }
54
55    public Histogram(ResultList results, PointD lowerLeft, PointD upperRight)
56      : base(lowerLeft, upperRight) {
57      records = new List<Record>();
58      primitiveToRecordsDictionary = new Dictionary<IPrimitive, List<Record>>();
59      recordToPrimitiveDictionary = new Dictionary<Record, IPrimitive>();
60      this.results = results;
61      foreach(Record r in results.Records) {
62        records.Add(r);
[560]63      }
[571]64      results.OnRecordAdded += new EventHandler<RecordAddedEventArgs>(results_OnRecordAdded);
65      results.Changed += new EventHandler(results_Changed);
[560]66    }
67
[571]68    void results_Changed(object sender, EventArgs e) {
[575]69      ResetViewSize();
[571]70      Repaint();
71      EnforceUpdate();
72    }
[560]73
[571]74    void results_OnRecordAdded(object sender, RecordAddedEventArgs e) {
75      lock(records) {
76        records.Add(e.Record);
77      }
78    }
[560]79
[571]80    public void ShowFrequency(string dimension) {
81      if(this.dimension != dimension) {
82        this.dimension = dimension;
83        ResetViewSize();
84        Repaint();
85        ZoomToViewSize();
86      }
[560]87    }
88
[571]89    private void Repaint() {
[573]90      lock(records) {
91        if(dimension == null) return;
92        UpdateEnabled = false;
93        Group.Clear();
94        primitiveToRecordsDictionary.Clear();
95        recordToPrimitiveDictionary.Clear();
96        bars = new Group(this);
97        Group.Add(new Axis(this, 0, 0, AxisType.Both));
98        UpdateViewSize(0, 0);
[575]99        PaintHistogram(records);
[573]100        Group.Add(bars);
101        UpdateEnabled = true;
102      }
[572]103    }
104
[575]105    private void PaintHistogram(IEnumerable<Record> records) {
[572]106      var values = records.Select(r => new { Record = r, Value = r.Get(dimension) }).Where(
107        x => !double.IsNaN(x.Value) && !double.IsInfinity(x.Value) && x.Value != double.MinValue && x.Value != double.MaxValue).OrderBy(x => x.Value);
108      if(values.Count() == 0) return;
109      double bucketSize = 1.0;
110      var frequencies = values.GroupBy(x => x.Value);
111      if(frequencies.Count() > MAX_BUCKETS) {
112        double min = values.ElementAt((int)(values.Count() * 0.05)).Value;
113        double max = values.ElementAt((int)(values.Count() * 0.95)).Value;
[571]114        bucketSize = (max - min) / N_BUCKETS;
[572]115        frequencies = values.GroupBy(x => Math.Min(Math.Max(min, Math.Floor((x.Value - min) / bucketSize) * bucketSize + min), max));
[571]116      }
[572]117      foreach(var g in frequencies) {
[571]118        double freq = g.Count();
[575]119        double selectedFreq = g.Where(r=>r.Record.Selected).Count();
[571]120        double lower = g.Key;
[572]121        double upper = g.Key + bucketSize;
[575]122        HeuristicLab.Charting.Rectangle bar = new HeuristicLab.Charting.Rectangle(this, lower, 0, upper, freq, defaultPen, defaultBrush);
[572]123        primitiveToRecordsDictionary[bar] = g.Select(r => r.Record).ToList();
124        primitiveToRecordsDictionary[bar].ForEach(x => recordToPrimitiveDictionary[x] = bar);
[575]125        HeuristicLab.Charting.Rectangle selectedBar = new HeuristicLab.Charting.Rectangle(this, lower, 0, upper, selectedFreq, selectionPen, selectionBrush);
126        primitiveToRecordsDictionary[selectedBar] = g.Select(r => r.Record).Where(r=>r.Selected).ToList();
127        primitiveToRecordsDictionary[selectedBar].ForEach(x => recordToPrimitiveDictionary[x] = bar);
128        if(lower == frequencies.First().Key) {
129          selectedBar.ToolTipText = " x < " + upper + " : " + selectedFreq;
130           bar.ToolTipText = " x < " + upper + " : " + freq;
131        } else if(lower == frequencies.Last().Key) {
132          selectedBar.ToolTipText = "x >= " + lower + " : " + selectedFreq;
133          bar.ToolTipText = "x >= " + lower + " : " + freq;
134        } else {
135          selectedBar.ToolTipText = "x in [" + lower + " .. " + upper + "[ : " + selectedFreq;
136           bar.ToolTipText = "x in [" + lower + " .. " + upper + "[ : " + freq;
137        }
[571]138        bars.Add(bar);
[575]139        bars.Add(selectedBar);
[571]140        UpdateViewSize(lower, freq);
141        UpdateViewSize(upper, freq);
142      }
[560]143    }
144
[571]145    private void ZoomToViewSize() {
146      if(minX < maxX) {
147        // enlarge view by 5% on each side
148        double width = maxX - minX;
149        minX = minX - width * 0.05;
150        maxX = maxX + width * 0.05;
151        double minY = 0 - maxFrequency * 0.05;
152        double maxY = maxFrequency + maxFrequency * 0.05;
153        ZoomIn(minX, minY, maxX, maxY);
154      }
[560]155    }
156
[571]157    private void UpdateViewSize(double x, double freq) {
158      if(x < minX) minX = x;
159      if(x > maxX) maxX = x;
[572]160      if(freq > maxFrequency) maxFrequency = freq;
[560]161    }
162
[571]163    private void ResetViewSize() {
164      minX = double.PositiveInfinity;
165      maxX = double.NegativeInfinity;
166      maxFrequency = double.NegativeInfinity;
167    }
[560]168
[571]169    internal List<Record> GetRecords(Point point) {
170      List<Record> records = null;
171      IPrimitive p = bars.GetPrimitive(TransformPixelToWorld(point));
172      if(p != null) {
173        primitiveToRecordsDictionary.TryGetValue(p, out records);
[560]174      }
[571]175      return records;
[560]176    }
[571]177
178    public override void MouseClick(Point point, MouseButtons button) {
179      if(button == MouseButtons.Left) {
[573]180        lock(records) {
181          List<Record> rs = GetRecords(point);
182          UpdateEnabled = false;
183          if(rs != null) rs.ForEach(r => r.ToggleSelected());
184          UpdateEnabled = true;
185        }
[571]186        results.FireChanged();
187      } else {
188        base.MouseClick(point, button);
189      }
190    }
[560]191  }
192}
Note: See TracBrowser for help on using the repository browser.