2010-03-05 2 views
7

히스토그램 계산을 위해 저장소를 생성해야합니다. 언어는 C#입니다. 기본적으로 십진수의 배열을 가져 와서 히스토그램 플롯을 생성해야합니다.히스토그램 찾기 십진수 데이터에 대한 비닝 알고리즘

괜찮은 라이브러리를 찾지 못해서 지금 당장이 작업을 수행 할 수 없으므로 라이브러리 또는 알고리즘을 찾고 데이터 비닝을 수행하는 데 도움이됩니다.

그래서 ...

  • 소수점 데이터와 출력 비닝 히스토그램의 배열에 걸릴 것입니다 거기에 어떤 C#을 라이브러리가 있습니까

    ?
  • 생성 된 히스토그램에 사용할 빈을 작성하기위한 일반 알고리즘이 있습니까?

답변

13

여기는 내가 사용하는 간단한 버킷 기능입니다. 당신이 @JakePearson를 사용하여 홀수 결과 허용

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#으로 번역해야했습니다. 고마워. –

+1

@Jake Pearson : System.Linq 네임 스페이스를 가져 오는 경우 최소 및 최대 값을 찾기 위해 첫 번째 foreach 루프가 필요하지 않습니다. 대신 다음과 같이 작성하십시오. min = source.Min(); 및 max = source.Max(). Im는 그것이 CPU에 얼마나 더 효과적인지 모르지만 읽기는 약간 적습니다. –

+0

양호한 통화가 업데이트되었습니다. –

4

내가있어 등 소수, INT 두 번에 대한 다음과 같은 기능의 다른 버전을 구현해야합니다, 그래서 슬프게도, .NET의 제네릭은 숫자 형 contraint을 지원하지 않습니다 대답. 그것은 엣지 케이스와 관련이 있습니다.

다음은 그의 메소드를 테스트하는 데 사용한 코드입니다. 확장 메서드를 약간 변경하여 int[]을 반환하고 decimal 대신 double을 수락했습니다.

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.Next(0, maxValue + 1); 

double value = rand.NextDouble() * (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; 
    } 

유일한 문제는 현재의 입력 데이터 세트는 최소 및 최대 값들의 많은 경우, 비닝 방법은 그 값과 집합을 잘못 할 얻어진 그래프의 많은 제외 것이다.