Question

Je dois générer des bacs pour les fins du calcul d'un histogramme. La langue est C #. Fondamentalement, je dois prendre dans un tableau de nombres décimaux et générer un histogramme de ceux-ci.

n'ont pas été en mesure de trouver une bibliothèque convenable de le faire pur et simple alors maintenant je suis à la recherche d'une bibliothèque ou soit un algorithme pour me aider à faire le binning des données.

...

  • Y a-t-il des bibliothèques C # là-bas qui prendra dans un tableau de données décimales et la sortie d'un histogramme mis en cellule?
  • Y at-il algorithme générique pour la construction des bacs à utiliser dans un histogramme généré?
Était-ce utile?

La solution

Voici une fonction simple seau que j'utilise. Malheureusement, les médicaments génériques .NET ne prend pas en charge un type numérique contraint donc vous devrez implémenter une version différente de la fonction suivante pour les décimales, 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;
}

Autres conseils

J'ai obtenu des résultats étranges à l'aide de réponse acceptée @JakePearson. Il doit faire avec un cas limite.

Voici le code que j'utilisé pour tester sa méthode. J'ai changé la méthode d'extension imperceptiblement, un retour int[] et accepter double au lieu de 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;
    }
}

Tout fonctionne bien lorsque vous utilisez 10.000.000 valeurs doubles aléatoires entre 0 et 100 (exclusif). Chaque seau a à peu près le même nombre de valeurs, ce qui est logique étant donné que Random retourne une distribution normale.

bon résultat

Mais quand je l'ai changé la valeur ligne de génération de

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

à

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

et vous obtenez le résultat suivant, qui doublent compte le dernier seau.

Résultat Odd

Il semble que lorsqu'une valeur est identique à l'une des limites d'un seau, le code tel qu'il est écrit met la valeur dans le seau incorrect. Cet artefact ne semble pas se produire avec des valeurs aléatoires double comme la chance d'un nombre aléatoire étant égale à une limite d'un seau est rare et ne serait pas évident.

La façon dont je corrigeais est de définir de quel côté de la frontière du godet est compris par rapport exclusif.

Pensez

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

vs.

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

Vous ne pouvez pas avoir les deux frontières y compris, que la méthode ne sait pas quel seau pour le mettre si vous avez une valeur qui est exactement égale à une limite.

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

Le seul problème est maintenant si l'ensemble de données d'entrée a beaucoup de valeurs min et max, la méthode de binning exclut un grand nombre de ces valeurs et le graphique résultant travestir l'ensemble de données.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top