Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
09/14/08 21:18:30 (16 years ago)
Author:
gkronber
Message:

implemented basic histogram (without 'brushing'). #271 (Functionality to show frequency distribution of any variable (histograms))

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.CEDMA.Charting/Histogram.cs

    r560 r571  
    1 #region License Information
     1#region License Information
    22/* HeuristicLab
    33 * Copyright (C) 2002-2008 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
     
    2222using System;
    2323using System.Collections.Generic;
     24using System.Text;
     25using System.Drawing;
    2426using System.Linq;
    25 using System.Text;
    26 using HeuristicLab.Core;
    27 using System.Xml;
    28 using HeuristicLab.CEDMA.DB.Interfaces;
     27using HeuristicLab.Charting;
     28using System.Windows.Forms;
    2929
    3030namespace HeuristicLab.CEDMA.Charting {
    31   public class Histogram {
    32     private int buckets;
    33     public int Buckets {
    34       get { return buckets; }
    35       set {
    36         buckets = value;
    37         ResetBucketBounds();
     31  public class Histogram : Chart {
     32    private static readonly Color defaultColor = Color.Blue;
     33    private static readonly Color selectionColor = Color.Red;
     34
     35    private double minX;
     36    private double maxX;
     37    private double maxFrequency;
     38    private const int N_BUCKETS = 50;
     39    private List<Record> records;
     40    private ResultList results;
     41    private Dictionary<IPrimitive, List<Record>> primitiveToRecordsDictionary;
     42    private Dictionary<Record, IPrimitive> recordToPrimitiveDictionary;
     43    private Group bars;
     44    private double[] limits;
     45    private int[] buckets;
     46    private string dimension;
     47
     48    public Histogram(ResultList results, double x1, double y1, double x2, double y2)
     49      : this(results, new PointD(x1, y1), new PointD(x2, y2)) {
     50    }
     51
     52    public Histogram(ResultList results, PointD lowerLeft, PointD upperRight)
     53      : base(lowerLeft, upperRight) {
     54      records = new List<Record>();
     55      primitiveToRecordsDictionary = new Dictionary<IPrimitive, List<Record>>();
     56      recordToPrimitiveDictionary = new Dictionary<Record, IPrimitive>();
     57      this.results = results;
     58      foreach(Record r in results.Records) {
     59        records.Add(r);
     60        r.OnSelectionChanged += new EventHandler(Record_OnSelectionChanged);
     61      }
     62      results.OnRecordAdded += new EventHandler<RecordAddedEventArgs>(results_OnRecordAdded);
     63      results.Changed += new EventHandler(results_Changed);
     64      limits = new double[N_BUCKETS - 1];
     65      buckets = new int[N_BUCKETS];
     66    }
     67
     68    void results_Changed(object sender, EventArgs e) {
     69      Repaint();
     70      EnforceUpdate();
     71    }
     72
     73    void results_OnRecordAdded(object sender, RecordAddedEventArgs e) {
     74      lock(records) {
     75        e.Record.OnSelectionChanged += new EventHandler(Record_OnSelectionChanged);
     76        records.Add(e.Record);
    3877      }
    3978    }
    4079
    41     private double[] limit;
    42 
    43     private List<double> values;
    44 
    45     public Histogram(int buckets) {
    46       this.buckets = buckets;
    47       values = new List<double>();
     80    void Record_OnSelectionChanged(object sender, EventArgs e) {
     81      Record r = (Record)sender;
     82      IPrimitive primitive;
     83      recordToPrimitiveDictionary.TryGetValue(r, out primitive);
     84      if(primitive != null) {
     85        ((FixedSizeCircle)primitive).UpdateEnabled = false;
     86        bars.UpdateEnabled = false;
     87        if(r.Selected) {
     88          int alpha = primitive.Pen.Color.A;
     89          primitive.Pen.Color = Color.FromArgb(alpha, selectionColor);
     90          primitive.Brush = primitive.Pen.Brush;
     91          primitive.IntoForeground();
     92        } else {
     93          int alpha = primitive.Pen.Color.A;
     94          primitive.Pen.Color = Color.FromArgb(alpha, defaultColor);
     95          primitive.Brush = primitive.Pen.Brush;
     96          primitive.IntoBackground();
     97        }
     98        ((FixedSizeCircle)primitive).UpdateEnabled = true;
     99        bars.UpdateEnabled = true;
     100      }
    48101    }
    49102
    50     public double LowerValue(int bucketIndex) {
    51       return limit[bucketIndex];
     103    public void ShowFrequency(string dimension) {
     104      if(this.dimension != dimension) {
     105        this.dimension = dimension;
     106        ResetViewSize();
     107        Repaint();
     108        ZoomToViewSize();
     109      }
    52110    }
    53111
    54     public double UpperValue(int bucketIndex) {
    55       return limit[bucketIndex+1];
     112    private void Repaint() {
     113      if(dimension == null) return;
     114      UpdateEnabled = false;
     115      Group.Clear();
     116      primitiveToRecordsDictionary.Clear();
     117      recordToPrimitiveDictionary.Clear();
     118      bars = new Group(this);
     119      Group.Add(new Axis(this, 0, 0, AxisType.Both));
     120      UpdateViewSize(0, 0);
     121      var values = records.Select(r => r.Get(dimension)).Where(
     122        x => !double.IsNaN(x) && !double.IsInfinity(x) && x != double.MinValue && x != double.MaxValue).OrderBy(x => x);
     123      IEnumerable<IGrouping<double,double>> frequencies;
     124      double bucketSize;
     125      if(dimension == Record.TARGET_VARIABLE || dimension == Record.TREE_HEIGHT || dimension == Record.TREE_SIZE) {
     126        frequencies = values.GroupBy(x => x);
     127        bucketSize = 1.0;
     128      } else {
     129        double min = values.ElementAt((int)(values.Count() * 0.05));
     130        double max = values.ElementAt((int)(values.Count() * 0.95));
     131        bucketSize = (max - min) / N_BUCKETS;
     132        frequencies = values.GroupBy(x => Math.Min(Math.Max(min, Math.Floor((x - min) / bucketSize) * bucketSize + min), max));
     133      }
     134      Pen defaultPen = new Pen(defaultColor);
     135      Brush defaultBrush = defaultPen.Brush;
     136      foreach(IGrouping<double, double> g in frequencies) {
     137        double freq = g.Count();
     138        double lower = g.Key;
     139        double upper = g.Key+bucketSize;
     140        HeuristicLab.Charting.Rectangle bar = new HeuristicLab.Charting.Rectangle(this, lower, 0, upper, freq, defaultPen, defaultBrush);
     141        primitiveToRecordsDictionary[bar] = records;
     142        if(lower == frequencies.First().Key) bar.ToolTipText = " x < "+upper+" : "+freq;
     143        else if(lower ==frequencies.Last().Key) bar.ToolTipText = "x >= "+lower+" : "+freq;
     144        else bar.ToolTipText = "x in ["+lower+" .. "+upper+"[ : "+freq;
     145        bars.Add(bar);
     146        UpdateViewSize(lower, freq);
     147        UpdateViewSize(upper, freq);
     148      }
     149      Group.Add(bars);
     150      UpdateEnabled = true;
    56151    }
    57152
    58     public int Frequency(int bucketIndex) {
    59       return values.Count(x => x >= LowerValue(bucketIndex) && x < UpperValue(bucketIndex));
     153    private void ZoomToViewSize() {
     154      if(minX < maxX) {
     155        // enlarge view by 5% on each side
     156        double width = maxX - minX;
     157        minX = minX - width * 0.05;
     158        maxX = maxX + width * 0.05;
     159        double minY = 0 - maxFrequency * 0.05;
     160        double maxY = maxFrequency + maxFrequency * 0.05;
     161        ZoomIn(minX, minY, maxX, maxY);
     162      }
    60163    }
    61164
    62     public void AddValues(IEnumerable<double> xs) {
    63       values.AddRange(xs.Where(x=>
    64         !double.IsInfinity(x) && !double.IsNaN(x) && double.MaxValue!=x && double.MinValue !=x));
    65       ResetBucketBounds();
     165    private void UpdateViewSize(double x, double freq) {
     166      if(x < minX) minX = x;
     167      if(x > maxX) maxX = x;
     168      if(freq  > maxFrequency) maxFrequency = freq;
    66169    }
    67170
    68     private void ResetBucketBounds() {
    69       limit = new double[buckets+1];
    70       values.Sort();
    71       double min = values[10];
    72       double max = values[values.Count-10];
     171    private void ResetViewSize() {
     172      minX = double.PositiveInfinity;
     173      maxX = double.NegativeInfinity;
     174      maxFrequency = double.NegativeInfinity;
     175    }
    73176
    74       double step = (max - min) / buckets;
    75       double cur = min;
    76       for(int i = 0; i < buckets+1; i++) {
    77         limit[i] = cur;
    78         cur += step;
     177    internal List<Record> GetRecords(Point point) {
     178      List<Record> records = null;
     179      IPrimitive p = bars.GetPrimitive(TransformPixelToWorld(point));
     180      if(p != null) {
     181        primitiveToRecordsDictionary.TryGetValue(p, out records);
     182      }
     183      return records;
     184    }
     185
     186    public override void MouseClick(Point point, MouseButtons button) {
     187      if(button == MouseButtons.Left) {
     188        List<Record> rs = GetRecords(point);
     189        if(rs != null) rs.ForEach(r => r.ToggleSelected());
     190        results.FireChanged();
     191      } else {
     192        base.MouseClick(point, button);
    79193      }
    80194    }
Note: See TracChangeset for help on using the changeset viewer.