All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openejb.math.stat.descriptive.rank.Percentile Maven / Gradle / Ivy

There is a newer version: 4.7.5
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.openejb.math.stat.descriptive.rank;

import java.io.Serializable;
import java.util.Arrays;

import org.apache.openejb.math.MathRuntimeException;
import org.apache.openejb.math.stat.descriptive.AbstractUnivariateStatistic;

/**
 * Provides percentile computation.
 * 

* There are several commonly used methods for estimating percentiles (a.k.a. * quantiles) based on sample data. For large samples, the different methods * agree closely, but when sample sizes are small, different methods will give * significantly different results. The algorithm implemented here works as follows: *

    *
  1. Let n be the length of the (sorted) array and * 0 < p <= 100 be the desired percentile.
  2. *
  3. If n = 1 return the unique array element (regardless of * the value of p); otherwise
  4. *
  5. Compute the estimated percentile position * pos = p * (n + 1) / 100 and the difference, d * between pos and floor(pos) (i.e. the fractional * part of pos). If pos >= n return the largest * element in the array; otherwise
  6. *
  7. Let lower be the element in position * floor(pos) in the array and let upper be the * next element in the array. Return lower + d * (upper - lower) *
  8. *

*

* To compute percentiles, the data must be (totally) ordered. Input arrays * are copied and then sorted using {@link java.util.Arrays#sort(double[])}. * The ordering used by Arrays.sort(double[]) is the one determined * by {@link java.lang.Double#compareTo(Double)}. This ordering makes * Double.NaN larger than any other value (including * Double.POSITIVE_INFINITY). Therefore, for example, the median * (50th percentile) of * {0, 1, 2, 3, 4, Double.NaN} evaluates to 2.5.

*

* Since percentile estimation usually involves interpolation between array * elements, arrays containing NaN or infinite values will often * result in NaN or infinite values returned.

*

* Note that this implementation is not synchronized. If * multiple threads access an instance of this class concurrently, and at least * one of the threads invokes the increment() or * clear() method, it must be synchronized externally.

* * @version $Revision: 811685 $ $Date: 2009-09-05 10:36:48 -0700 (Sat, 05 Sep 2009) $ */ public class Percentile extends AbstractUnivariateStatistic implements Serializable { /** Serializable version identifier */ private static final long serialVersionUID = -1231216485095130416L; /** Determines what percentile is computed when evaluate() is activated * with no quantile argument */ private double quantile = 0.0; /** * Constructs a Percentile with a default quantile * value of 50.0. */ public Percentile() { this(50.0); } /** * Constructs a Percentile with the specific quantile value. * @param p the quantile * @throws IllegalArgumentException if p is not greater than 0 and less * than or equal to 100 */ public Percentile(final double p) { setQuantile(p); } /** * Copy constructor, creates a new {@code Percentile} identical * to the {@code original} * * @param original the {@code Percentile} instance to copy */ public Percentile(Percentile original) { copy(original, this); } /** * Returns an estimate of the pth percentile of the values * in the values array. *

* Calls to this method do not modify the internal quantile * state of this statistic.

*

*

    *
  • Returns Double.NaN if values has length * 0
  • *
  • Returns (for any value of p) values[0] * if values has length 1
  • *
  • Throws IllegalArgumentException if values * is null or p is not a valid quantile value (p must be greater than 0 * and less than or equal to 100)
  • *

*

* See {@link Percentile} for a description of the percentile estimation * algorithm used.

* * @param values input array of values * @param p the percentile value to compute * @return the percentile value or Double.NaN if the array is empty * @throws IllegalArgumentException if values is null * or p is invalid */ public double evaluate(final double[] values, final double p) { test(values, 0, 0); return evaluate(values, 0, values.length, p); } /** * Returns an estimate of the quantileth percentile of the * designated values in the values array. The quantile * estimated is determined by the quantile property. *

*

    *
  • Returns Double.NaN if length = 0
  • *
  • Returns (for any value of quantile) * values[begin] if length = 1
  • *
  • Throws IllegalArgumentException if values * is null, or start or length * is invalid
  • *

*

* See {@link Percentile} for a description of the percentile estimation * algorithm used.

* * @param values the input array * @param start index of the first array element to include * @param length the number of elements to include * @return the percentile value * @throws IllegalArgumentException if the parameters are not valid * */ @Override public double evaluate( final double[] values, final int start, final int length) { return evaluate(values, start, length, quantile); } /** * Returns an estimate of the pth percentile of the values * in the values array, starting with the element in (0-based) * position begin in the array and including length * values. *

* Calls to this method do not modify the internal quantile * state of this statistic.

*

*

    *
  • Returns Double.NaN if length = 0
  • *
  • Returns (for any value of p) values[begin] * if length = 1
  • *
  • Throws IllegalArgumentException if values * is null , begin or length is invalid, or * p is not a valid quantile value (p must be greater than 0 * and less than or equal to 100)
  • *

*

* See {@link Percentile} for a description of the percentile estimation * algorithm used.

* * @param values array of input values * @param p the percentile to compute * @param begin the first (0-based) element to include in the computation * @param length the number of array elements to include * @return the percentile value * @throws IllegalArgumentException if the parameters are not valid or the * input array is null */ public double evaluate(final double[] values, final int begin, final int length, final double p) { test(values, begin, length); if ((p > 100) || (p <= 0)) { throw MathRuntimeException.createIllegalArgumentException( "out of bounds quantile value: {0}, must be in (0, 100]", p); } if (length == 0) { return Double.NaN; } if (length == 1) { return values[begin]; // always return single value for n = 1 } double n = length; double pos = p * (n + 1) / 100; double fpos = Math.floor(pos); int intPos = (int) fpos; double dif = pos - fpos; double[] sorted = new double[length]; System.arraycopy(values, begin, sorted, 0, length); Arrays.sort(sorted); if (pos < 1) { return sorted[0]; } if (pos >= n) { return sorted[length - 1]; } double lower = sorted[intPos - 1]; double upper = sorted[intPos]; return lower + dif * (upper - lower); } /** * Returns the value of the quantile field (determines what percentile is * computed when evaluate() is called with no quantile argument). * * @return quantile */ public double getQuantile() { return quantile; } /** * Sets the value of the quantile field (determines what percentile is * computed when evaluate() is called with no quantile argument). * * @param p a value between 0 < p <= 100 * @throws IllegalArgumentException if p is not greater than 0 and less * than or equal to 100 */ public void setQuantile(final double p) { if (p <= 0 || p > 100) { throw MathRuntimeException.createIllegalArgumentException( "out of bounds quantile value: {0}, must be in (0, 100]", p); } quantile = p; } /** * {@inheritDoc} */ @Override public Percentile copy() { Percentile result = new Percentile(); copy(this, result); return result; } /** * Copies source to dest. *

Neither source nor dest can be null.

* * @param source Percentile to copy * @param dest Percentile to copy to * @throws NullPointerException if either source or dest is null */ public static void copy(Percentile source, Percentile dest) { dest.quantile = source.quantile; } }