From cbc6c2cdb5f5615e4cb53b09f5a69a31cff68436 Mon Sep 17 00:00:00 2001 From: Justin Needham Date: Wed, 6 May 2015 16:40:57 -0400 Subject: [PATCH] Authored Proposed fix for handling small-width buckets --- src/Numerics/Statistics/Histogram.cs | 61 ++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/src/Numerics/Statistics/Histogram.cs b/src/Numerics/Statistics/Histogram.cs index 5d9de518..1772e2b2 100644 --- a/src/Numerics/Statistics/Histogram.cs +++ b/src/Numerics/Statistics/Histogram.cs @@ -60,7 +60,7 @@ namespace MathNet.Numerics.Statistics /// -1 when the point is less than this bucket, 0 when it is in this bucket and 1 otherwise. public int Compare(Bucket bkt1, Bucket bkt2) { - return bkt2.Width == 0.0 + return bkt2.IsSinglePoint ? -bkt1.Contains(bkt2.UpperBound) : -bkt2.Contains(bkt1.UpperBound); } @@ -81,6 +81,9 @@ namespace MathNet.Numerics.Statistics /// /// The number of datapoints in the bucket. /// + /// + /// Value may be NaN if this was constructed as a argument. + /// public double Count { get; set; } /// @@ -103,6 +106,18 @@ namespace MathNet.Numerics.Statistics Count = count; } + /// + /// Constructs a Bucket that can be used as an argument for + /// when performing a Binary search. + /// + /// Value to look for + public Bucket(double targetValue) + { + LowerBound = targetValue; + UpperBound = targetValue; + Count = double.NaN; + } + /// /// Creates a copy of the Bucket with the lowerbound, upperbound and counts exactly equal. /// @@ -120,6 +135,15 @@ namespace MathNet.Numerics.Statistics get { return UpperBound - LowerBound; } } + /// + /// True if this is a single point argument for + /// when performing a Binary search. + /// + private bool IsSinglePoint + { + get { return double.IsNaN(Count); } + } + /// /// Default comparer. /// @@ -132,8 +156,10 @@ namespace MathNet.Numerics.Statistics /// This method check whether a point is contained within this bucket. /// /// The point to check. - /// 0 if the point falls within the bucket boundaries; -1 if the point is - /// smaller than the bucket, +1 if the point is larger than the bucket. + /// + /// 0 if the point falls within the bucket boundaries; + /// -1 if the point is smaller than the bucket, + /// +1 if the point is larger than the bucket. public int Contains(double x) { if (LowerBound < x) @@ -152,6 +178,11 @@ namespace MathNet.Numerics.Statistics /// /// Comparison of two disjoint buckets. The buckets cannot be overlapping. /// + /// + /// 0 if UpperBound and LowerBound are bit-for-bit equal + /// 1 if This bucket is lower that the compared bucket + /// -1 otherwise + /// public int CompareTo(Bucket bucket) { if (UpperBound > bucket.LowerBound && LowerBound < bucket.LowerBound) @@ -159,8 +190,8 @@ namespace MathNet.Numerics.Statistics throw new ArgumentException(Resources.PartialOrderException); } - if (UpperBound.AlmostEqual(bucket.UpperBound) - && LowerBound.AlmostEqual(bucket.LowerBound)) + if (UpperBound.Equals(bucket.UpperBound) + && LowerBound.Equals(bucket.LowerBound)) { return 0; } @@ -174,9 +205,12 @@ namespace MathNet.Numerics.Statistics } /// - /// Checks whether two Buckets are equal; this method tolerates a difference in lowerbound, upperbound - /// and count given by . + /// Checks whether two Buckets are equal. /// + /// + /// UpperBound and LowerBound are compared bit-for-bit, but This method tolerates a + /// difference in Count given by . + /// public override bool Equals(object obj) { if (!(obj is Bucket)) @@ -185,8 +219,8 @@ namespace MathNet.Numerics.Statistics } var bucket = (Bucket) obj; - return LowerBound.AlmostEqual(bucket.LowerBound) - && UpperBound.AlmostEqual(bucket.UpperBound) + return LowerBound.Equals(bucket.LowerBound) + && UpperBound.Equals(bucket.UpperBound) && Count.AlmostEqual(bucket.Count); } @@ -257,10 +291,13 @@ namespace MathNet.Numerics.Statistics // Add buckets for each bin; the smallest bucket's lowerbound must be slightly smaller // than the minimal element. - AddBucket(new Bucket(lower.Decrement(), lower + width)); + double fNextLowerBound = lower + width; + AddBucket(new Bucket(lower.Decrement(), fNextLowerBound)); for (int n = 1; n < nbuckets; n++) { - AddBucket(new Bucket(lower + n * width, lower + (n + 1) * width)); + AddBucket(new Bucket( + fNextLowerBound, + fNextLowerBound = (lower + (n + 1) * width))); } AddData(data); @@ -380,7 +417,7 @@ namespace MathNet.Numerics.Statistics LazySort(); // Binary search for the bucket index. - int index = _buckets.BinarySearch(new Bucket(v, v), Bucket.DefaultPointComparer); + int index = _buckets.BinarySearch(new Bucket(v), Bucket.DefaultPointComparer); if (index < 0) {