using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ScottPlot
{
[Obsolete("ScottPlot.Histogram is now ScottPlot.Statistics.Histogram", true)]
public class Histogram
{
public Histogram(double[] values, double? min = null, double? max = null, double? binSize = null, double? binCount = null, bool ignoreOutOfBounds = true)
{
throw new NotImplementedException("ScottPlot.Histogram is now ScottPlot.Statistics.Histogram");
}
};
}
namespace ScottPlot.Statistics
{
// TODO: This class needs refactoring to improve names.
// Use numpy.histogram as a reference: https://numpy.org/doc/stable/reference/generated/numpy.histogram.html
public class Histogram
{
///
/// Lower edges of bins used to create the histogram
///
public readonly double[] bins;
///
/// Total number of values in each bin.
///
public readonly double[] counts;
///
/// Fractional number of values in each bin.
/// The total of all values in this array is 1.0.
///
public readonly double[] countsFrac;
///
/// Cumulative total number of values in each bin.
/// The returned array will start near 0.0 and end near 1.0.
///
public readonly double[] cumulativeCounts;
///
/// Probability density (fraction) for each bin based on the mean and standard deviation of the population.
/// The sum of all these values is 1.0
///
public readonly double[] probability;
///
/// This is the probability density curve normalized to its peak, so its maximum value is 1.0
///
public readonly double[] countsFracCurve;
///
/// Cumulative probability density fraction for each bin
///
public readonly double[] cumulativeFrac;
///
/// Distance between each bin
///
public readonly double binSize;
///
/// Population mean
///
public readonly double mean;
///
/// Population standard deviation
///
public readonly double stdev;
///
/// Compute the histogram of a set of data.
/// Bins are identically sized and evenly spaced.
///
/// input data
/// manually-defined lower edge of first bin
/// manually-defined upper edge of last bin
/// manually-defined width of each bin
/// resize bins as needed so this number of bins is achieved
/// if True, values below min or above max will be ignored
public Histogram(double[] values, double? min = null, double? max = null, double? binSize = null, double? binCount = null, bool ignoreOutOfBounds = true)
{
var population = new Population(values);
mean = population.mean;
stdev = population.stDev;
min = (min is null) ? population.minus3stDev : min.Value;
max = (max is null) ? population.plus3stDev : max.Value;
if (min >= max)
throw new ArgumentException($"max ({max}) cannot be greater than min ({min})");
if ((binCount != null) && (binSize != null))
throw new ArgumentException("binCount and binSize cannot both be given");
double defaultBinCount = 100;
double span = max.Value - min.Value;
if (binSize == null)
{
if (binCount == null)
binSize = span / defaultBinCount;
else
binSize = span / binCount;
}
if (ignoreOutOfBounds == false)
{
// add an extra bin on each side of the histogram
min -= binSize;
max += binSize;
}
bins = BinBySize((double)binSize, (double)min, (double)max);
this.binSize = bins[1] - bins[0];
counts = GetHistogram(values, bins, ignoreOutOfBounds);
cumulativeCounts = GetCumulative(counts);
countsFrac = GetNormalized(counts);
cumulativeFrac = GetCumulative(countsFrac);
countsFracCurve = population.GetDistribution(bins, false);
probability = population.GetDistribution(bins, true);
}
private static double[] GetNormalized(double[] values)
{
double[] countsFrac = new double[values.Length];
for (int i = 0; i < countsFrac.Length; i++)
countsFrac[i] = values[i] / values.Sum();
return countsFrac;
}
private static double[] GetCumulative(double[] values)
{
double[] cumulaltive = new double[values.Length];
cumulaltive[0] = values[0];
for (int i = 1; i < cumulaltive.Length; i++)
cumulaltive[i] = cumulaltive[i - 1] + values[i];
return cumulaltive;
}
public static double[] BinBySize(double binSize, double min, double max)
{
double span = (double)max - (double)min;
int binCount = (int)(span / binSize);
double[] bins = new double[binCount];
for (int i = 0; i < bins.Length; i++)
bins[i] = i * (double)binSize + (double)min;
return bins;
}
private static double[] GetHistogram(double[] values, double[] bins, bool ignoreOutOfBounds = true)
{
double binSize = bins[1] - bins[0];
double[] counts = new double[bins.Length];
for (int i = 0; i < values.Length; i++)
{
int index = (int)((values[i] - bins[0]) / binSize);
if (index < 0)
{
if (!ignoreOutOfBounds)
counts[0] += 1;
}
else if (index >= counts.Length)
{
if (!ignoreOutOfBounds)
counts[counts.Length - 1] += 1;
}
else
{
counts[index] += 1;
}
}
return counts;
}
}
}