la generación de histogramas simple de datos enteros en C #
Pregunta
Como parte de un banco de pruebas que estoy construyendo, estoy buscando una clase simple para calcular un histograma de valores enteros (número de iteraciones tomadas de un algoritmo para resolver un problema). La respuesta debería llamarse algo como esto:
Histogram my_hist = new Histogram();
for( uint i = 0; i < NUMBER_OF_RESULTS; i++ )
{
myHist.AddValue( some_result );
}
for( uint j = 0; j < myHist.NumOfBins; j++ )
{
Console.WriteLine( "{0} occurred {1} times", myHist.BinValues[j], myHist.BinCounts[j] );
}
Me sorprendió un poco de google no se presentó una buena solución, pero tal vez no busco las cosas correctas. ¿Hay una solución genérica por ahí o se trata de la pena rodar mi propia?
Solución
Se puede usar SortedDictionary
uint[] items = new uint[] {5, 6, 1, 2, 3, 1, 5, 2}; // sample data
SortedDictionary<uint, int> histogram = new SortedDictionary<uint, int>();
foreach (uint item in items) {
if (histogram.ContainsKey(item)) {
histogram[item]++;
} else {
histogram[item] = 1;
}
}
foreach (KeyValuePair<uint, int> pair in histogram) {
Console.WriteLine("{0} occurred {1} times", pair.Key, pair.Value);
}
Esto dejará a cabo contenedores vacíos, aunque
Otros consejos
Sobre la base de la sugerencia de BastardSaint me ocurrió con un envoltorio limpio y bastante genérico:
public class Histogram<TVal> : SortedDictionary<TVal, uint>
{
public void IncrementCount(TVal binToIncrement)
{
if (ContainsKey(binToIncrement))
{
this[binToIncrement]++;
}
else
{
Add(binToIncrement, 1);
}
}
}
Así que ahora puedo hacer:
const uint numOfInputDataPoints = 5;
Histogram<uint> hist = new Histogram<uint>();
// Fill the histogram with data
for (uint i = 0; i < numOfInputDataPoints; i++)
{
// Grab a result from my algorithm
uint numOfIterationsForSolution = MyAlorithm.Run();
// Add the number to the histogram
hist.IncrementCount( numOfIterationsForSolution );
}
// Report the results
foreach (KeyValuePair<uint, uint> histEntry in hist.AsEnumerable())
{
Console.WriteLine("{0} occurred {1} times", histEntry.Key, histEntry.Value);
}
Me tomó un tiempo para encontrar la manera de hacer que los medicamentos genéricos (para empezar yo sólo hizo caso omiso de la constructora SortedDictionary
lo que significaba que sólo se podía utilizar para llaves uint
).
Puede utilizar LINQ:
var items = new[] {5, 6, 1, 2, 3, 1, 5, 2};
items
.GroupBy(i => i)
.Select(g => new {
Item = g.Key,
Count = g.Count()
})
.OrderBy(g => g.Item)
.ToList()
.ForEach(g => {
Console.WriteLine("{0} occurred {1} times", g.Item, g.Count);
});