using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ScottPlot.Statistics { /// /// This module holds an array of values and provides popultation statistics (mean, median, standard deviation, etc) /// public class Population { public double[] values { get; private set; } public double[] sortedValues { get; private set; } public double min { get; private set; } public double max { get; private set; } public double median { get; private set; } public double sum { get; private set; } public int count { get; private set; } public double mean { get; private set; } public double stDev { get; private set; } public double plus3stDev { get; private set; } public double minus3stDev { get; private set; } public double plus2stDev { get; private set; } public double minus2stDev { get; private set; } public double stdErr { get; private set; } public double Q1 { get; private set; } public double Q3 { get; private set; } public double IQR { get; private set; } public double[] lowOutliers { get; private set; } public double[] highOutliers { get; private set; } public double maxNonOutlier { get; private set; } public double minNonOutlier { get; private set; } public int n { get { return values.Length; } } public double span { get { return sortedValues.Last() - sortedValues.First(); } } /// /// Generate random values with a normal distribution /// public Population(Random rand, int pointCount, double mean = .5, double stdDev = .5) { values = DataGen.RandomNormal(rand, pointCount, mean, stdDev); Recalculate(); } /// /// Calculate population stats from the given array of values /// public Population(double[] values) { if (values is null) throw new ArgumentException("values cannot be null"); this.values = values; Recalculate(); } [Obsolete("This constructor overload is deprecated. Please remove the fullAnalysis argument.")] public Population(double[] values, bool fullAnalysis = true) { if (values is null) throw new ArgumentException("values cannot be null"); this.values = values; if (fullAnalysis) Recalculate(); } public void Recalculate() { count = values.Length; int QSize = (int)Math.Floor(count / 4.0); sortedValues = new double[count]; Array.Copy(values, 0, sortedValues, 0, count); Array.Sort(sortedValues); min = sortedValues.First(); max = sortedValues.Last(); median = sortedValues[count / 2]; Q1 = sortedValues[QSize]; Q3 = sortedValues[sortedValues.Length - QSize - 1]; IQR = Q3 - Q1; double lowerBoundary = Q1 - 1.5 * IQR; double upperBoundary = Q3 + 1.5 * IQR; int minNonOutlierIndex = 0; int maxNonOutlierIndex = 0; for (int i = 0; i < sortedValues.Length; i++) { if (sortedValues[i] < lowerBoundary) { } else { minNonOutlierIndex = i; break; } } for (int i = sortedValues.Length - 1; i >= 0; i--) { if (sortedValues[i] > upperBoundary) { } else { maxNonOutlierIndex = i; break; } } lowOutliers = new double[minNonOutlierIndex]; highOutliers = new double[sortedValues.Length - maxNonOutlierIndex - 1]; Array.Copy(sortedValues, 0, lowOutliers, 0, lowOutliers.Length); Array.Copy(sortedValues, maxNonOutlierIndex + 1, highOutliers, 0, highOutliers.Length); minNonOutlier = sortedValues[minNonOutlierIndex]; maxNonOutlier = sortedValues[maxNonOutlierIndex]; sum = values.Sum(); mean = sum / count; double sumVariancesSquared = 0; for (int i = 0; i < values.Length; i++) { double pointVariance = Math.Abs(mean - values[i]); double pointVarianceSquared = Math.Pow(pointVariance, 2); sumVariancesSquared += pointVarianceSquared; } double meanVarianceSquared = sumVariancesSquared / values.Length; stDev = Math.Sqrt(meanVarianceSquared); plus2stDev = mean + stDev * 2; minus2stDev = mean - stDev * 2; plus3stDev = mean + stDev * 3; minus3stDev = mean - stDev * 3; stdErr = stDev / Math.Sqrt(count); } public double[] GetDistribution(double[] xs, bool normalize) { double[] ys = new double[xs.Length]; for (int i = 0; i < xs.Length; i++) ys[i] = Math.Exp(-.5 * Math.Pow((xs[i] - mean) / stDev, 2)); if (normalize) { double sum = ys.Sum(); for (int i = 0; i < ys.Length; i++) ys[i] /= sum; } return ys; } } }