/// <summary> /// Returns the largest quantile such that ((quantile - lowerItem)/(upperItem - lowerItem) + lowerIndex)/(n-1) <= probability. /// </summary> /// <param name="probability">A number between 0 and 1.</param> /// <param name="lowerIndex"></param> /// <param name="lowerItem">Must be finite.</param> /// <param name="upperItem">Must be finite and at least lowerItem.</param> /// <param name="n">Must be greater than 1</param> /// <returns></returns> internal static double GetQuantile(double probability, double lowerIndex, double lowerItem, double upperItem, long n) { if (probability < 0) { throw new ArgumentOutOfRangeException(nameof(probability), probability, $"{nameof(probability)} < 0"); } if (probability > 1) { throw new ArgumentOutOfRangeException(nameof(probability), probability, $"{nameof(probability)} > 1"); } if (n <= 1) { throw new ArgumentOutOfRangeException(nameof(n), n, "n <= 1"); } double pos = MMath.LargestDoubleProduct(probability, n - 1); double frac = MMath.LargestDoubleSum(-lowerIndex, pos); if (upperItem < lowerItem) { throw new ArgumentOutOfRangeException(nameof(upperItem), upperItem, $"{nameof(upperItem)} ({upperItem}) < {nameof(lowerItem)} ({lowerItem})"); } if (upperItem == lowerItem) { return(lowerItem); } // The above check ensures diff > 0 double diff = upperItem - lowerItem; double offset = MMath.LargestDoubleProduct(frac, diff); return(MMath.LargestDoubleSum(lowerItem, offset)); }
/// <summary> /// Returns the largest value x such that GetProbLessThan(x) <= probability. /// </summary> /// <param name="probability">A real number in [0,1].</param> /// <returns></returns> public double GetQuantile(double probability) { if (probability < 0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability < 0"); } if (probability > 1.0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability > 1.0"); } int n = quantiles.Length; if (probability < 1.0 / (n + 1.0)) { return(lowerGaussian.GetQuantile(probability)); } if (probability > n / (n + 1.0)) { return(upperGaussian.GetQuantile(probability)); } if (n == 1) { return(quantiles[0]); // probability must be 0.5 } double pos = MMath.LargestDoubleProduct(n + 1, probability) - 1; int lower = (int)Math.Floor(pos); if (lower == n - 1) { return(quantiles[lower]); } return(OuterQuantiles.GetQuantile(probability, lower + 1, quantiles[lower], quantiles[lower + 1], n + 2)); }
/// <summary> /// Returns the largest value x such that GetProbLessThan(x) <= probability. /// </summary> /// <param name="probability">A real number in [0,1].</param> /// <returns></returns> public double GetQuantile(double probability) { if (probability < 0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability < 0"); } if (probability > 1.0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability > 1.0"); } // The zero-based index of item in the sorted List<T>, if item is found; // otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item // or, if there is no larger element, the bitwise complement of Count. int index = Array.BinarySearch(probabilities, probability); if (index >= 0) { return(quantiles[index]); } else { // Linear interpolation int largerIndex = ~index; if (largerIndex == 0) { return(quantiles[largerIndex]); } int smallerIndex = largerIndex - 1; if (largerIndex == probabilities.Length) { return(quantiles[smallerIndex]); } double upperItem = quantiles[largerIndex]; double lowerItem = quantiles[smallerIndex]; double diff = upperItem - lowerItem; if (diff > double.MaxValue) { double scale = System.Math.Max(System.Math.Abs(upperItem), System.Math.Abs(lowerItem)); double lowerItemOverScale = lowerItem / scale; diff = upperItem / scale - lowerItemOverScale; double slope = diff / (probabilities[largerIndex] - probabilities[smallerIndex]); double frac = MMath.LargestDoubleSum(-probabilities[smallerIndex], probability); double offset = MMath.LargestDoubleProduct(frac, slope); return(MMath.LargestDoubleSum(lowerItemOverScale, offset) * scale); } else { double slope = diff / (probabilities[largerIndex] - probabilities[smallerIndex]); // Solve for the largest x such that probabilities[smallerIndex] + (x - quantiles[smallerIndex]) / slope <= probability. double frac = MMath.LargestDoubleSum(-probabilities[smallerIndex], probability); double offset = MMath.LargestDoubleProduct(frac, slope); return(MMath.LargestDoubleSum(lowerItem, offset)); } } }
/// <summary> /// Returns the largest quantile such that ((quantile - lowerItem)/(upperItem - lowerItem) + lowerIndex)/(n-1) <= probability. /// </summary> /// <param name="probability"></param> /// <param name="lowerIndex"></param> /// <param name="lowerItem"></param> /// <param name="upperItem"></param> /// <param name="n"></param> /// <returns></returns> internal static double GetQuantile(double probability, long lowerIndex, double lowerItem, double upperItem, long n) { if (n == 1) { throw new ArgumentOutOfRangeException("n == 1"); } double pos = MMath.LargestDoubleProduct(n - 1, probability); double frac = MMath.LargestDoubleSum(-lowerIndex, pos); double diff = upperItem - lowerItem; if (diff == 0) { return(lowerItem); } double offset = MMath.LargestDoubleProduct(diff, frac); return(MMath.LargestDoubleSum(lowerItem, offset)); }
/// <summary> /// Returns the largest value x such that GetProbLessThan(x) <= probability. /// </summary> /// <param name="probability">A real number in [0,1].</param> /// <returns></returns> public double GetQuantile(double probability) { if (probability < 0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability < 0"); } if (probability > 1.0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability > 1.0"); } // The zero-based index of item in the sorted List<T>, if item is found; // otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item // or, if there is no larger element, the bitwise complement of Count. int index = Array.BinarySearch(probabilities, probability); if (index >= 0) { return(quantiles[index]); } else { // Linear interpolation int largerIndex = ~index; if (largerIndex == 0) { return(quantiles[largerIndex]); } int smallerIndex = largerIndex - 1; if (largerIndex == probabilities.Length) { return(quantiles[smallerIndex]); } double slope = (quantiles[largerIndex] - quantiles[smallerIndex]) / (probabilities[largerIndex] - probabilities[smallerIndex]); // Solve for the largest x such that probabilities[smallerIndex] + (x - quantiles[smallerIndex]) / slope <= probability. double frac = MMath.LargestDoubleSum(-probabilities[smallerIndex], probability); double offset = MMath.LargestDoubleProduct(slope, frac); return(MMath.LargestDoubleSum(quantiles[smallerIndex], offset)); } }
/// <summary> /// Returns the largest value x such that GetProbLessThan(x) <= probability. /// </summary> /// <param name="probability">A real number in [0,1].</param> /// <param name="quantiles">Numbers in increasing order corresponding to the quantiles for probabilities (0,...,n-1)/(n-1).</param> /// <returns></returns> public static double GetQuantile(double probability, double[] quantiles) { if (probability < 0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability < 0"); } if (probability > 1.0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability > 1.0"); } int n = quantiles.Length; if (quantiles == null) { throw new ArgumentNullException(nameof(quantiles)); } if (n == 0) { throw new ArgumentException("quantiles array is empty", nameof(quantiles)); } if (probability == 1.0) { return(MMath.NextDouble(quantiles[n - 1])); } if (n == 1) { return(quantiles[0]); } double pos = MMath.LargestDoubleProduct(n - 1, probability); int lower = (int)Math.Floor(pos); int upper = (int)Math.Ceiling(pos); if (upper == lower) { upper = lower + 1; } return(GetQuantile(probability, lower, quantiles[lower], quantiles[upper], n)); }
/// <summary> /// Returns the largest value x such that GetProbLessThan(x) <= probability. /// </summary> /// <param name="probability">A real number in [0,1].</param> /// <returns></returns> public double GetQuantile(double probability) { if (probability < 0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability < 0"); } if (probability > 1.0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability > 1.0"); } // compute the min and max of the retained items double lowerBound = double.PositiveInfinity, upperBound = double.NegativeInfinity; for (int bufferIndex = 0; bufferIndex < buffers.Length; bufferIndex++) { double[] buffer = buffers[bufferIndex]; int count = countInBuffer[bufferIndex]; for (int i = 0; i < count; i++) { double item = buffer[i]; if (item < lowerBound) { lowerBound = item; } if (item > upperBound) { upperBound = item; } } } if (probability == 1.0) { return(MMath.NextDouble(upperBound)); } if (probability == 0.0) { return(lowerBound); } if (double.IsPositiveInfinity(lowerBound)) { throw new Exception("QuantileEstimator has no data"); } if (lowerBound == upperBound) { return(upperBound); } // use bisection while (true) { double x = MMath.Average(lowerBound, upperBound); double lowerItem; double upperItem; long lowerRank; long lowerWeight, minLowerWeight, upperWeight, minUpperWeight; long itemCount; GetAdjacentItems(x, out lowerItem, out upperItem, out lowerRank, out lowerWeight, out upperWeight, out minLowerWeight, out minUpperWeight, out itemCount); if (InterpolationType == 0) { double probabilityLessThanLowerItem = (double)(lowerRank - lowerWeight) / itemCount; if (probability < probabilityLessThanLowerItem) { upperBound = MMath.PreviousDouble(lowerItem); } else { double probabilityLessThanUpperItem = (double)lowerRank / itemCount; if (probability < probabilityLessThanUpperItem) { return(lowerItem); } double probabilityLessThanOrEqualUpperItem = (double)(lowerRank + upperWeight) / itemCount; if (probability < probabilityLessThanOrEqualUpperItem) { return(upperItem); } lowerBound = MMath.NextDouble(upperItem); } } else if (InterpolationType == 1) { // Find frac such that (lowerRank - 0.5 + frac) / itemCount == probability double scaledProbability = MMath.LargestDoubleProduct(itemCount, probability); if (scaledProbability < 0.5) { return(lowerBound); } if (scaledProbability >= itemCount - 0.5) { return(upperBound); } // probability of lowerItem ranges from (lowerRank-lowerWeight+0.5) / itemCount // to (lowerRank - 0.5) / itemCount //if (scaledProbability == lowerRank - lowerWeight + 0.5) return lowerItem; if ((scaledProbability > lowerRank - lowerWeight + 0.5) && (scaledProbability < lowerRank - 0.5)) { return(lowerItem); } // probability of upperItem ranges from (lowerRank + 0.5) / itemCount // to (lowerRank + upperWeight - 0.5) / itemCount if (scaledProbability == lowerRank + 0.5) { return(upperItem); } if ((scaledProbability > lowerRank + 0.5) && (scaledProbability < lowerRank + upperWeight - 0.5)) { return(upperItem); } double frac = scaledProbability - (lowerRank - 0.5); if (frac < 0) { upperBound = MMath.PreviousDouble(x); } else if (frac > 1) { lowerBound = MMath.NextDouble(x); } else { return(OuterQuantiles.GetQuantile(probability, lowerRank - 0.5, lowerItem, upperItem, itemCount + 1)); } } else { double scaledProbability = MMath.LargestDoubleProduct(itemCount - 1, probability); // probability of lowerItem ranges from (lowerRank-lowerWeight) / (itemCount - 1) // to (lowerRank - 1) / (itemCount - 1). if (scaledProbability == lowerRank - lowerWeight) { return(lowerItem); } if ((scaledProbability > lowerRank - lowerWeight) && (scaledProbability < lowerRank - 1)) { return(lowerItem); } // probability of upperItem ranges from lowerRank / (itemCount - 1) // to (lowerRank + upperWeight-1) / (itemCount - 1) if (scaledProbability == lowerRank) { return(upperItem); } if ((scaledProbability > lowerRank) && (scaledProbability < lowerRank + upperWeight - 1)) { return(upperItem); } // solve for frac in (lowerRank-1 + frac) / (itemCount - 1) == probability double frac = scaledProbability - (lowerRank - 1); if (frac < 0) { upperBound = MMath.PreviousDouble(x); } else if (frac > 1) { lowerBound = MMath.NextDouble(x); } else { return(OuterQuantiles.GetQuantile(probability, lowerRank - 1, lowerItem, upperItem, itemCount)); } } } }
/// <summary> /// Returns the largest value x such that GetProbLessThan(x) <= probability. /// </summary> /// <param name="probability">A real number in [0,1].</param> /// <returns></returns> public double GetQuantile(double probability) { if (probability < 0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability < 0"); } if (probability > 1.0) { throw new ArgumentOutOfRangeException(nameof(probability), "probability > 1.0"); } // compute the min and max of the retained items double lowerBound = double.PositiveInfinity, upperBound = double.NegativeInfinity; for (int bufferIndex = 0; bufferIndex < buffers.Length; bufferIndex++) { double[] buffer = buffers[bufferIndex]; int count = countInBuffer[bufferIndex]; for (int i = 0; i < count; i++) { double item = buffer[i]; if (item < lowerBound) { lowerBound = item; } if (item > upperBound) { upperBound = item; } } } if (probability == 1.0) { return(MMath.NextDouble(upperBound)); } if (double.IsPositiveInfinity(lowerBound)) { throw new Exception("QuantileEstimator has no data"); } if (lowerBound == upperBound) { return(upperBound); } // use bisection while (true) { double x = (lowerBound + upperBound) / 2; double lowerItem; double upperItem; long lowerIndex; long lowerWeight, minLowerWeight, upperWeight, minUpperWeight; long itemCount; GetAdjacentItems(x, out lowerItem, out upperItem, out lowerIndex, out lowerWeight, out upperWeight, out minLowerWeight, out minUpperWeight, out itemCount); double scaledProbability = MMath.LargestDoubleProduct(itemCount - 1, probability); // probability of lowerItem ranges from (lowerIndex - lowerWeight + 1) / (itemCount - 1) // to lowerIndex / (itemCount - 1). if ((scaledProbability >= lowerIndex - lowerWeight + 1) && (scaledProbability < lowerIndex)) { return(lowerItem); } // probability of upperItem ranges from (lowerIndex + 1) / (itemCount - 1) // to (lowerIndex + upperWeight) / (itemCount - 1) if ((scaledProbability >= lowerIndex + 1) && (scaledProbability <= lowerIndex + upperWeight)) { return(upperItem); } // solve for frac in (lowerIndex + frac) / (itemCount - 1) == probability double frac = scaledProbability - lowerIndex; if (frac < 0) { upperBound = x; } else if (frac > 1) { lowerBound = x; } else { return(OuterQuantiles.GetQuantile(probability, lowerIndex, lowerItem, upperItem, itemCount)); } } }