Пример #1
0
        /// <summary>
        /// Estimates the specified quantile
        /// </summary>
        /// <param name="quantile">The quantile to esimtate. Must be between 0 and 1.</param>
        /// <returns>The value for the estimated quantile</returns>
        public double Quantile(double quantile)
        {
            if (quantile < 0 || quantile > 1)
            {
                throw new ArgumentOutOfRangeException("Quantile must be between 0 and 1");
            }

            if (_centroids.Count == 0)
            {
                throw new InvalidOperationException("Cannot call Quantile() method until first Adding values to the digest");
            }

            if (_centroids.Count == 1)
            {
                return(_centroids.First().Value.Mean);
            }

            int      i    = 0;
            double   t    = 0;
            double   q    = quantile * _count;
            Centroid last = null;

            foreach (Centroid centroid in _centroids.Values)
            {
                last = centroid;
                double k = centroid.Count;

                if (q < t + k)
                {
                    double delta;
                    if (i == 0)
                    {
                        Centroid successor = _centroids.Successor(centroid.Mean).Value;
                        delta = successor.Mean - centroid.Mean;
                    }
                    else if (i == _centroids.Count - 1)
                    {
                        Centroid predecessor = _centroids.Predecessor(centroid.Mean).Value;
                        delta = centroid.Mean - predecessor.Mean;
                    }
                    else
                    {
                        Centroid successor   = _centroids.Successor(centroid.Mean).Value;
                        Centroid predecessor = _centroids.Predecessor(centroid.Mean).Value;
                        delta = (successor.Mean - predecessor.Mean) / 2;
                    }

                    double estimated = centroid.Mean + ((q - t) / k - .5) * delta;

                    // If estimated value is higher than max, return max
                    return(Math.Min(estimated, this.Max));
                }

                t += k;
                i++;
            }

            return(last.Mean);
        }
Пример #2
0
        /// <summary>
        /// Estimates the specified quantile
        /// </summary>
        /// <param name="quantile">The quantile to esimtate. Must be between 0 and 1.</param>
        /// <returns>The value for the estimated quantile</returns>
        public double Quantile(double quantile)
        {
            if (quantile < 0 || quantile > 1)
            {
                throw new ArgumentOutOfRangeException("Quantile must be between 0 and 1");
            }

            if (_centroids.Count == 0)
            {
                throw new InvalidOperationException("Cannot call Quantile() method until first Adding values to the digest");
            }

            if (_centroids.Count == 1)
            {
                return(_centroids.First().Value.Mean);
            }

            double index = quantile * _count;

            if (index < 1)
            {
                return(Min);
            }
            if (index > Count - 1)
            {
                return(Max);
            }

            Centroid currentNode   = _centroids.First().Value;
            Centroid lastNode      = _centroids.Last().Value;
            double   currentWeight = currentNode.Count;

            if (currentWeight == 2 && index <= 2)
            {
                // first node is a double weight with one sample at min, sou we can infer location of other sample
                return(2 * currentNode.Mean - Min);
            }

            if (_centroids.Last().Value.Count == 2 && index > Count - 2)
            {
                // likewise for last centroid
                return(2 * lastNode.Mean - Max);
            }

            double weightSoFar = currentWeight / 2.0;

            if (index < weightSoFar)
            {
                return(WeightedAvg(Min, weightSoFar - index, currentNode.Mean, index - 1));
            }

            foreach (Centroid nextNode in _centroids.Values.Skip(1))
            {
                double nextWeight = nextNode.Count;
                double dw         = (currentWeight + nextWeight) / 2.0;

                if (index < weightSoFar + dw)
                {
                    double leftExclusion  = 0;
                    double rightExclusion = 0;
                    if (currentWeight == 1)
                    {
                        if (index < weightSoFar + 0.5)
                        {
                            return(currentNode.Mean);
                        }
                        else
                        {
                            leftExclusion = 0.5;
                        }
                    }
                    if (nextWeight == 1)
                    {
                        if (index >= weightSoFar + dw - 0.5)
                        {
                            return(nextNode.Mean);
                        }
                        else
                        {
                            rightExclusion = 0.5;
                        }
                    }
                    // centroids i and i+1 bracket our current point
                    // we interpolate, but the weights are diminished if singletons are present
                    double weight1 = index - weightSoFar - leftExclusion;
                    double weight2 = weightSoFar + dw - index - rightExclusion;
                    return(WeightedAvg(currentNode.Mean, weight2, nextNode.Mean, weight1));
                }

                weightSoFar  += dw;
                currentNode   = nextNode;
                currentWeight = nextWeight;
            }

            double w1 = index - weightSoFar;
            double w2 = Count - 1 - index;

            return(WeightedAvg(currentNode.Mean, w2, Max, w1));
        }