2010-03-05 45 views
7

我需要爲計算直方圖而生成分箱。語言是C#。基本上我需要輸入一個十進制數字並生成一個直方圖。尋找一個十進制數據的直方圖分組算法

一直沒能找到一個像樣的圖書館來做到這一點,所以現在我只是尋找一個圖書館或算法來幫助我做數據的裝箱。

所以......

  • 是否有任何C#庫,在那裏,將採取十進制數據和輸出分級直方圖的陣列?
  • 是否有通用算法來構建用於生成直方圖的容器?

回答

13

這是我使用的一個簡單的桶功能。可悲的是,.NET泛型不支持數字式contraint所以你必須實行不同的版本爲十進制,整型,雙下列功能等

public static List<int> Bucketize(this IEnumerable<decimal> source, int totalBuckets) 
{ 
    var min = source.Min(); 
    var max = source.Max(); 
    var buckets = new List<int>(); 

    var bucketSize = (max - min)/totalBuckets; 
    foreach (var value in source) 
    { 
     int bucketIndex = 0; 
     if (bucketSize > 0.0) 
     { 
      bucketIndex = (int)((value - min)/bucketSize); 
      if (bucketIndex == totalBuckets) 
      { 
       bucketIndex--; 
      } 
     } 
     buckets[bucketIndex]++; 
    } 
    return buckets; 
} 
+0

我寫了一個使用SAS語言的幾乎相同的算法,並且必須讓我的開發人員將其轉換爲C#。謝謝你。 – 2010-03-05 18:48:43

+1

@Jake Pearson:如果您導入命名空間System.Linq,那麼您將不需要第一個foreach循環來查找最小值和最大值。相反,只需寫:min = source.Min();和max = source.Max()。我不確定它在cpu上的效率是多少,但是它的讀數稍微少一些。 – 2013-03-01 19:06:07

+0

良好的通話,更新。 – 2013-03-02 13:31:27

4

我得到了使用@JakePearson奇接受結果回答。它與一個邊緣案例有關。

這是我用來測試他的方法的代碼。我稍微更改了擴展方法,返回int[]並接受double而不是decimal

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 

     Random rand = new Random(1325165); 

     int maxValue = 100; 
     int numberOfBuckets = 100; 

     List<double> values = new List<double>(); 
     for (int i = 0; i < 10000000; i++) 
     { 
      double value = rand.NextDouble() * (maxValue+1);    
      values.Add(value); 
     } 

     int[] bins = values.Bucketize(numberOfBuckets); 

     PointPairList points = new PointPairList(); 
     for (int i = 0; i < numberOfBuckets; i++) 
     { 
      points.Add(i, bins[i]); 
     } 

     zedGraphControl1.GraphPane.AddBar("Random Points", points,Color.Black); 
     zedGraphControl1.GraphPane.YAxis.Title.Text = "Count"; 
     zedGraphControl1.GraphPane.XAxis.Title.Text = "Value"; 


     zedGraphControl1.AxisChange(); 
     zedGraphControl1.Refresh(); 

    } 
} 

public static class Extension 
{ 
    public static int[] Bucketize(this IEnumerable<double> source, int totalBuckets) 
    { 
     var min = source.Min(); 
     var max = source.Max(); 
     var buckets = new int[totalBuckets]; 

     var bucketSize = (max - min)/totalBuckets; 
     foreach (var value in source) 
     { 
      int bucketIndex = 0; 
      if (bucketSize > 0.0) 
      { 
       bucketIndex = (int)((value - min)/bucketSize); 
       if (bucketIndex == totalBuckets) 
       { 
        bucketIndex--; 
       } 
      } 
      buckets[bucketIndex]++; 
     } 
     return buckets; 
    } 
} 

在0到100(獨佔)之間使用10,000,000個隨機double值時,一切正常。每個桶具有大致相同數量的值,這是合理的,因爲Random返回正態分佈。

Good Result

但是,當我改變了值代線從

double value = rand.NextDouble() * (maxValue+1);    

double value = rand.Next(0, maxValue + 1); 

,你會得到以下結果,其中雙數的最後一個桶。

Odd Result

似乎當的值是相同的剷鬥的邊界之一,因爲它是寫的代碼把值不正確的桶。這個神器似乎並不隨隨機double值發生,因爲隨機數等於一個桶的邊界的機會很少,並且不會很明顯。

我糾正這個問題的方法是定義桶邊界的哪一邊是包含邊界和獨佔邊界。

思考的

0< x <=11< x <=2 ... 99< x <=100

0<= x <11<= x <2 ...99<= x <100

不能同時包含兩個邊界,因爲如果您的值恰好等於邊界,則該方法無法知道將哪個存儲桶放入。

public enum BucketizeDirectionEnum 
    { 
     LowerBoundInclusive, 
     UpperBoundInclusive 
    } 

    public static int[] Bucketize(this IList<double> source, int totalBuckets, BucketizeDirectionEnum inclusivity = BucketizeDirectionEnum.UpperBoundInclusive) 
    { 
     var min = source.Min(); 
     var max = source.Max(); 
     var buckets = new int[totalBuckets]; 
     var bucketSize = (max - min)/totalBuckets; 

     if (inclusivity == BucketizeDirectionEnum.LowerBoundInclusive) 
     { 
      foreach (var value in source) 
      { 
       int bucketIndex = (int)((value - min)/bucketSize); 
       if (bucketIndex == totalBuckets) 
        continue; 
       buckets[bucketIndex]++; 
      } 
     } 
     else 
     { 
      foreach (var value in source) 
      { 
       int bucketIndex = (int)Math.Ceiling((value - min)/bucketSize) - 1; 
       if (bucketIndex < 0) 
        continue; 
       buckets[bucketIndex]++; 
      } 
     } 

     return buckets; 
    } 

唯一的問題是現在如果輸入數據集有很多的最小值和最大值的,分級方法將排除許多這些值,所得圖將歪曲的數據集。