Frage

brauche ich bins für die Zwecke zur Erzeugung eines Histogramms berechnet wird. Die Sprache ist C #. Grundsätzlich muß ich in einem Array von Dezimalzahlen nehmen und ein Histogramm Plot aus derjenigediejenigedasjenige zu erzeugen.

Haben Sie nicht in der Lage gewesen, eine anständige Bibliothek finden diese geradezu zu tun, so jetzt bin ich gerade auf der Suche nach entweder einer Bibliothek oder einem Algorithmus, um Hilfe zu mir tue den Binning der Daten.

So ...

  • Gibt es C # -Bibliotheken gibt, die in einer Reihe von Dezimalzahl Daten und gibt ein binned Histogramm nehmen?
  • Gibt es generischer Algorithmus für den Aufbau der Bins in erzeugt ein Histogramm verwendet werden?
War es hilfreich?

Lösung

Hier ist ein einfacher Eimer Funktion ich verwende. Leider hat .NET Generika nicht einen numerischen Typ contraint unterstützen, so dass Sie für dezimal eine andere Version der folgenden Funktion wird implementieren, int, double, etc.

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;
}

Andere Tipps

Ich habe ungeradee Ergebnisse mit @JakePearson Antwort akzeptiert. Es hat mit einem Rand Fall zu tun.

Hier ist der Code, den ich verwenden, seine Methode zu testen. Ich änderte immer so leicht die Erweiterungsmethode, eine int[] Rückkehr und die Annahme double statt 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;
    }
}

Alles funktioniert gut bei der Verwendung von 10.000.000 zufälligen Doppel Werte zwischen 0 und 100 (exklusiv). Jeder Eimer hat in etwa die gleiche Anzahl von Werten, der Sinn macht gegeben, dass Random gibt eine Normalverteilung.

gutes Ergebnis

Aber wenn ich den Wert Generation Linie geändert von

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

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

und Sie erhalten das folgende Ergebnis, das doppelt zählt der letzte Eimer.

Odd Ergebnis

Es scheint, dass, wenn ein Wert ist gleich wie eine der Grenzen eines Eimers, der Code, wie es legt den Wert in dem falschen Eimer geschrieben wird. Dieser Artefakt scheint nicht mit zufälligen double Werten als die Chance auf eine Zufallszahl passieren zu einer Grenze eines Eimers gleich ist selten und nicht offensichtlich sein.

So wie ich dies korrigiert ist zu definieren, welche Seite des Eimers Grenze ist inklusive vs. exklusiv.

Denken Sie an

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

vs.

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

Sie können nicht beide Grenzen haben inklusive, da die Methode würde wissen nicht, welche Eimer es in setzen, wenn Sie einen Wert haben, genau an einer Grenze entsprechen.

    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;
    }

Das einzige Problem ist jetzt, wenn der Eingabedatensatz eine Menge von Min- und Max-Wert hat, wird das Binning-Verfahren viele dieser Werte ausschließen und die resultierende Graph wird die Datenmenge falsch darstellen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top