3

特定のデータセットからデータヒストグラムを作成しようとしています。ヒストグラムを作成するためのさまざまなオプションについて読んだことがありますが、

島崎秀樹; 篠本聡(2007)。「時間ヒストグラムのビンサイズを選択する方法」

上記の方法では、推定を使用して最適なビンの幅と分布を決定します。これは、サンプルデータの分布が異なり、ビンの数と幅を事前に決定するのが難しいため、私の場合に必要です。

誰かがc#でそのような関数を書くための良いソースまたは出発点を推薦するか、または十分に近いc#ヒストグラムコードを持っていることができますか?

どうもありがとう。

4

4 に答える 4

8

以下は、ここからこのアルゴリズムのPythonバージョンについて書いたポートです。APIがいくつかの作業で実行できることは知っていますが、これで十分に始めることができます。このコードの結果は、同じ入力データに対してPythonコードによって生成されたものと同じです。

public class HistSample
{
    public static void CalculateOptimalBinWidth(double[] x)
    {
        double xMax = x.Max(), xMin = x.Min();
        int minBins = 4, maxBins = 50;
        double[] N = Enumerable.Range(minBins, maxBins - minBins)
            .Select(v => (double)v).ToArray();
        double[] D = N.Select(v => (xMax - xMin) / v).ToArray();
        double[] C = new double[D.Length];

        for (int i = 0; i < N.Length; i++)
        {
            double[] binIntervals = LinearSpace(xMin, xMax, (int)N[i] + 1);
            double[] ki = Histogram(x, binIntervals);
            ki = ki.Skip(1).Take(ki.Length - 2).ToArray();

            double mean = ki.Average();
            double variance = ki.Select(v => Math.Pow(v - mean, 2)).Sum() / N[i];

            C[i] = (2 * mean - variance) / (Math.Pow(D[i], 2));
        }

        double minC = C.Min();
        int index = C.Select((c, ix) => new { Value = c, Index = ix })
            .Where(c => c.Value == minC).First().Index;
        double optimalBinWidth = D[index];
    }

    public static double[] Histogram(double[] data, double[] binEdges)
    {
        double[] counts = new double[binEdges.Length - 1];

        for (int i = 0; i < binEdges.Length - 1; i++)
        {
            double lower = binEdges[i], upper = binEdges[i + 1];

            for (int j = 0; j < data.Length; j++)
            {
                if (data[j] >= lower && data[j] <= upper)
                {
                    counts[i]++;
                }
            }
        }

        return counts;
    }

    public static double[] LinearSpace(double a, double b, int count)
    {
        double[] output = new double[count];

        for (int i = 0; i < count; i++)
        {
            output[i] = a + ((i * (b - a)) / (count - 1));
        }

        return output;
    }
}

次のように実行します。

double[] x =
{
    4.37, 3.87, 4.00, 4.03, 3.50, 4.08, 2.25, 4.70, 1.73,
    4.93, 1.73, 4.62, 3.43, 4.25, 1.68, 3.92, 3.68, 3.10,
    4.03, 1.77, 4.08, 1.75, 3.20, 1.85, 4.62, 1.97, 4.50,
    3.92, 4.35, 2.33, 3.83, 1.88, 4.60, 1.80, 4.73, 1.77,
    4.57, 1.85, 3.52, 4.00, 3.70, 3.72, 4.25, 3.58, 3.80,
    3.77, 3.75, 2.50, 4.50, 4.10, 3.70, 3.80, 3.43, 4.00,
    2.27, 4.40, 4.05, 4.25, 3.33, 2.00, 4.33, 2.93, 4.58,
    1.90, 3.58, 3.73, 3.73, 1.82, 4.63, 3.50, 4.00, 3.67,
    1.67, 4.60, 1.67, 4.00, 1.80, 4.42, 1.90, 4.63, 2.93,
    3.50, 1.97, 4.28, 1.83, 4.13, 1.83, 4.65, 4.20, 3.93,
    4.33, 1.83, 4.53, 2.03, 4.18, 4.43, 4.07, 4.13, 3.95,
    4.10, 2.27, 4.58, 1.90, 4.50, 1.95, 4.83, 4.12
};

HistSample.CalculateOptimalBinWidth(x);
于 2013-02-11T09:20:29.493 に答える
1

ヒストグラム機能を確認してください。運が悪いデータ要素がビンの境界(最初または最後のビン以外)に等しい場合、それらは両方の連続するビンでカウントされます。コードはチェックし(下位<= data [j] && data [j] <上位)、xMaxに等しいすべての要素が最後のビンに入る場合を処理する必要があります。

于 2013-07-03T19:35:23.310 に答える
1

nick_w回答の小さな更新。

後で実際にビンが必要な場合。さらに、ヒストグラム関数の二重ループを最適化し、さらにlinspace関数を削除しました。

     /// <summary>
     /// Calculate the optimal bins for the given data
     /// </summary>
     /// <param name="x">The data you have</param>
     /// <param name="xMin">The minimum element</param>
     /// <param name="optimalBinWidth">The width between each bin</param>
     /// <returns>The bins</returns>
     public static int[] CalculateOptimalBinWidth(List<double> x, out double xMin, out double optimalBinWidth)
     {
        var xMax = x.Max();
        xMin = x.Min();
        optimalBinWidth = 0;
        const int MIN_BINS = 1;
        const int MAX_BINS = 20;

        int[] minKi = null;
        var minOffset = double.MaxValue;

        foreach (var n in Enumerable.Range(MIN_BINS, MAX_BINS - MIN_BINS).Select(v => v*5))
        {
           var d = (xMax - xMin)/n;
           var ki = Histogram(x, n, xMin, d);
           var ki2 = ki.Skip(1).Take(ki.Length - 2).ToArray();

           var mean = ki2.Average();
           var variance = ki2.Select(v => Math.Pow(v - mean, 2)).Sum()/n;

           var offset = (2*mean - variance)/Math.Pow(d, 2);

           if (offset < minOffset)
           {
              minKi = ki;
              minOffset = offset;
              optimalBinWidth = d;
           }
        }
        return minKi;
     }

     private static int[] Histogram(List<double> data, int count, double xMin, double d)
     {
        var histogram = new int[count];
        foreach (var t in data)
        {
           var bucket = (int) Math.Truncate((t - xMin)/d);
           if (count == bucket) //fix xMax
              bucket --;
           histogram[bucket]++;
        }
        return histogram;
     }
于 2016-06-13T08:30:38.067 に答える
0

クラス間隔への割り当てを高速化するために、二分探索をお勧めします。

public void Add(double element)
{
  if (element < Bins.First().LeftBound || element > Bins.Last().RightBound)
    return;

  var min = 0;
  var max = Bins.Length - 1;
  var index = 0;

  while (min <= max)
  {
    index = min + ((max - min) / 2);

    if (element >= Bins[index].LeftBound && element < Bins[index].RightBound)
      break;

    if (element < Bins[index].LeftBound)
      max = index - 1;
    else
      min = index + 1;
  }

  Bins[index].Count++;
}

「Bins」は、「Leftbound」、「RightBound」、「Count」などのプロパティを定義する「HistogramItem」タイプのアイテムのリストです。

于 2015-09-24T06:13:47.543 に答える