Quantile.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.apache.commons.statistics.descriptive;

  18. import java.util.Arrays;
  19. import java.util.Objects;
  20. import java.util.function.IntToDoubleFunction;
  21. import org.apache.commons.numbers.arrays.Selection;

  22. /**
  23.  * Provides quantile computation.
  24.  *
  25.  * <p>For values of length {@code n}:
  26.  * <ul>
  27.  * <li>The result is {@code NaN} if {@code n = 0}.
  28.  * <li>The result is {@code values[0]} if {@code n = 1}.
  29.  * <li>Otherwise the result is computed using the {@link EstimationMethod}.
  30.  * </ul>
  31.  *
  32.  * <p>Computation of multiple quantiles and will handle duplicate and unordered
  33.  * probabilities. Passing ordered probabilities is recommended if the order is already
  34.  * known as this can improve efficiency; for example using uniform spacing through the
  35.  * array data, or to identify extreme values from the data such as {@code [0.001, 0.999]}.
  36.  *
  37.  * <p>This implementation respects the ordering imposed by
  38.  * {@link Double#compare(double, double)} for {@code NaN} values. If a {@code NaN} occurs
  39.  * in the selected positions in the fully sorted values then the result is {@code NaN}.
  40.  *
  41.  * <p>The {@link NaNPolicy} can be used to change the behaviour on {@code NaN} values.
  42.  *
  43.  * <p>Instances of this class are immutable and thread-safe.
  44.  *
  45.  * @see #with(NaNPolicy)
  46.  * @see <a href="http://en.wikipedia.org/wiki/Quantile">Quantile (Wikipedia)</a>
  47.  * @since 1.1
  48.  */
  49. public final class Quantile {
  50.     /** Message when the probability is not in the range {@code [0, 1]}. */
  51.     private static final String INVALID_PROBABILITY = "Invalid probability: ";
  52.     /** Message when no probabilities are provided for the varargs method. */
  53.     private static final String NO_PROBABILITIES_SPECIFIED = "No probabilities specified";
  54.     /** Message when the size is not valid. */
  55.     private static final String INVALID_SIZE = "Invalid size: ";
  56.     /** Message when the number of probabilities in a range is not valid. */
  57.     private static final String INVALID_NUMBER_OF_PROBABILITIES = "Invalid number of probabilities: ";

  58.     /** Default instance. Method 8 is recommended by Hyndman and Fan. */
  59.     private static final Quantile DEFAULT = new Quantile(false, NaNPolicy.INCLUDE, EstimationMethod.HF8);

  60.     /** Flag to indicate if the data should be copied. */
  61.     private final boolean copy;
  62.     /** NaN policy for floating point data. */
  63.     private final NaNPolicy nanPolicy;
  64.     /** Transformer for NaN data. */
  65.     private final NaNTransformer nanTransformer;
  66.     /** Estimation type used to determine the value from the quantile. */
  67.     private final EstimationMethod estimationType;

  68.     /**
  69.      * @param copy Flag to indicate if the data should be copied.
  70.      * @param nanPolicy NaN policy.
  71.      * @param estimationType Estimation type used to determine the value from the quantile.
  72.      */
  73.     private Quantile(boolean copy, NaNPolicy nanPolicy, EstimationMethod estimationType) {
  74.         this.copy = copy;
  75.         this.nanPolicy = nanPolicy;
  76.         this.estimationType = estimationType;
  77.         nanTransformer = NaNTransformers.createNaNTransformer(nanPolicy, copy);
  78.     }

  79.     /**
  80.      * Return a new instance with the default options.
  81.      *
  82.      * <ul>
  83.      * <li>{@linkplain #withCopy(boolean) Copy = false}
  84.      * <li>{@linkplain #with(NaNPolicy) NaN policy = include}
  85.      * <li>{@linkplain #with(EstimationMethod) Estimation method = HF8}
  86.      * </ul>
  87.      *
  88.      * <p>Note: The default options configure for processing in-place and including
  89.      * {@code NaN} values in the data. This is the most efficient mode and has the
  90.      * smallest memory consumption.
  91.      *
  92.      * @return the quantile implementation
  93.      * @see #withCopy(boolean)
  94.      * @see #with(NaNPolicy)
  95.      * @see #with(EstimationMethod)
  96.      */
  97.     public static Quantile withDefaults() {
  98.         return DEFAULT;
  99.     }

  100.     /**
  101.      * Return an instance with the configured copy behaviour. If {@code false} then
  102.      * the input array will be modified by the call to evaluate the quantiles; otherwise
  103.      * the computation uses a copy of the data.
  104.      *
  105.      * @param v Value.
  106.      * @return an instance
  107.      */
  108.     public Quantile withCopy(boolean v) {
  109.         return new Quantile(v, nanPolicy, estimationType);
  110.     }

  111.     /**
  112.      * Return an instance with the configured {@link NaNPolicy}.
  113.      *
  114.      * <p>Note: This implementation respects the ordering imposed by
  115.      * {@link Double#compare(double, double)} for {@code NaN} values: {@code NaN} is
  116.      * considered greater than all other values, and all {@code NaN} values are equal. The
  117.      * {@link NaNPolicy} changes the computation of the statistic in the presence of
  118.      * {@code NaN} values.
  119.      *
  120.      * <ul>
  121.      * <li>{@link NaNPolicy#INCLUDE}: {@code NaN} values are moved to the end of the data;
  122.      * the size of the data <em>includes</em> the {@code NaN} values and the quantile will be
  123.      * {@code NaN} if any value used for quantile interpolation is {@code NaN}.
  124.      * <li>{@link NaNPolicy#EXCLUDE}: {@code NaN} values are moved to the end of the data;
  125.      * the size of the data <em>excludes</em> the {@code NaN} values and the quantile will
  126.      * never be {@code NaN} for non-zero size. If all data are {@code NaN} then the size is zero
  127.      * and the result is {@code NaN}.
  128.      * <li>{@link NaNPolicy#ERROR}: An exception is raised if the data contains {@code NaN}
  129.      * values.
  130.      * </ul>
  131.      *
  132.      * <p>Note that the result is identical for all policies if no {@code NaN} values are present.
  133.      *
  134.      * @param v Value.
  135.      * @return an instance
  136.      */
  137.     public Quantile with(NaNPolicy v) {
  138.         return new Quantile(copy, Objects.requireNonNull(v), estimationType);
  139.     }

  140.     /**
  141.      * Return an instance with the configured {@link EstimationMethod}.
  142.      *
  143.      * @param v Value.
  144.      * @return an instance
  145.      */
  146.     public Quantile with(EstimationMethod v) {
  147.         return new Quantile(copy, nanPolicy, Objects.requireNonNull(v));
  148.     }

  149.     /**
  150.      * Generate {@code n} evenly spaced probabilities in the range {@code [0, 1]}.
  151.      *
  152.      * <pre>
  153.      * 1/(n + 1), 2/(n + 1), ..., n/(n + 1)
  154.      * </pre>
  155.      *
  156.      * @param n Number of probabilities.
  157.      * @return the probabilities
  158.      * @throws IllegalArgumentException if {@code n < 1}
  159.      */
  160.     public static double[] probabilities(int n) {
  161.         checkNumberOfProbabilities(n);
  162.         final double c1 = n + 1.0;
  163.         final double[] p = new double[n];
  164.         for (int i = 0; i < n; i++) {
  165.             p[i] = (i + 1.0) / c1;
  166.         }
  167.         return p;
  168.     }

  169.     /**
  170.      * Generate {@code n} evenly spaced probabilities in the range {@code [p1, p2]}.
  171.      *
  172.      * <pre>
  173.      * w = p2 - p1
  174.      * p1 + w/(n + 1), p1 + 2w/(n + 1), ..., p1 + nw/(n + 1)
  175.      * </pre>
  176.      *
  177.      * @param n Number of probabilities.
  178.      * @param p1 Lower probability.
  179.      * @param p2 Upper probability.
  180.      * @return the probabilities
  181.      * @throws IllegalArgumentException if {@code n < 1}; if the probabilities are not in the
  182.      * range {@code [0, 1]}; or {@code p2 <= p1}.
  183.      */
  184.     public static double[] probabilities(int n, double p1, double p2) {
  185.         checkProbability(p1);
  186.         checkProbability(p2);
  187.         if (p2 <= p1) {
  188.             throw new IllegalArgumentException("Invalid range: [" + p1 + ", " + p2 + "]");
  189.         }
  190.         final double[] p = probabilities(n);
  191.         for (int i = 0; i < n; i++) {
  192.             p[i] = (1 - p[i]) * p1 + p[i] * p2;
  193.         }
  194.         return p;
  195.     }

  196.     /**
  197.      * Evaluate the {@code p}-th quantile of the values.
  198.      *
  199.      * <p>Note: This method may partially sort the input values if not configured to
  200.      * {@link #withCopy(boolean) copy} the input data.
  201.      *
  202.      * <p><strong>Performance</strong>
  203.      *
  204.      * <p>It is not recommended to use this method for repeat calls for different quantiles
  205.      * within the same values. The {@link #evaluate(double[], double...)} method should be used
  206.      * which provides better performance.
  207.      *
  208.      * @param values Values.
  209.      * @param p Probability for the quantile to compute.
  210.      * @return the quantile
  211.      * @throws IllegalArgumentException if the probability {@code p} is not in the range {@code [0, 1]}
  212.      * @see #evaluate(double[], double...)
  213.      */
  214.     public double evaluate(double[] values, double p) {
  215.         checkProbability(p);
  216.         // Floating-point data handling
  217.         final int[] bounds = new int[1];
  218.         final double[] x = nanTransformer.apply(values, bounds);
  219.         final int n = bounds[0];
  220.         // Special cases
  221.         if (n <= 1) {
  222.             return n == 0 ? Double.NaN : x[0];
  223.         }
  224.         final double pos = estimationType.index(p, n);
  225.         final int i = (int) pos;

  226.         // Partition and compute
  227.         if (pos > i) {
  228.             Selection.select(x, 0, n, new int[] {i, i + 1});
  229.             return Interpolation.interpolate(x[i], x[i + 1], pos - i);
  230.         }
  231.         Selection.select(x, 0, n, i);
  232.         return x[i];
  233.     }

  234.     /**
  235.      * Evaluate the {@code p}-th quantiles of the values.
  236.      *
  237.      * <p>Note: This method may partially sort the input values if not configured to
  238.      * {@link #withCopy(boolean) copy} the input data.
  239.      *
  240.      * @param values Values.
  241.      * @param p Probabilities for the quantiles to compute.
  242.      * @return the quantiles
  243.      * @throws IllegalArgumentException if any probability {@code p} is not in the range {@code [0, 1]};
  244.      * or no probabilities are specified.
  245.      */
  246.     public double[] evaluate(double[] values, double... p) {
  247.         checkProbabilities(p);
  248.         // Floating-point data handling
  249.         final int[] bounds = new int[1];
  250.         final double[] x = nanTransformer.apply(values, bounds);
  251.         final int n = bounds[0];
  252.         // Special cases
  253.         final double[] q = new double[p.length];
  254.         if (n <= 1) {
  255.             Arrays.fill(q, n == 0 ? Double.NaN : x[0]);
  256.             return q;
  257.         }

  258.         // Collect interpolation positions. We use the output q as storage.
  259.         final int[] indices = computeIndices(n, p, q);

  260.         // Partition
  261.         Selection.select(x, 0, n, indices);

  262.         // Compute
  263.         for (int k = 0; k < p.length; k++) {
  264.             final int i = (int) q[k];
  265.             if (q[k] > i) {
  266.                 q[k] = Interpolation.interpolate(x[i], x[i + 1], q[k] - i);
  267.             } else {
  268.                 q[k] = x[i];
  269.             }
  270.         }
  271.         return q;
  272.     }

  273.     /**
  274.      * Evaluate the {@code p}-th quantile of the values.
  275.      *
  276.      * <p>Note: This method may partially sort the input values if not configured to
  277.      * {@link #withCopy(boolean) copy} the input data.
  278.      *
  279.      * <p><strong>Performance</strong>
  280.      *
  281.      * <p>It is not recommended to use this method for repeat calls for different quantiles
  282.      * within the same values. The {@link #evaluate(int[], double...)} method should be used
  283.      * which provides better performance.
  284.      *
  285.      * @param values Values.
  286.      * @param p Probability for the quantile to compute.
  287.      * @return the quantile
  288.      * @throws IllegalArgumentException if the probability {@code p} is not in the range {@code [0, 1]}
  289.      * @see #evaluate(int[], double...)
  290.      */
  291.     public double evaluate(int[] values, double p) {
  292.         checkProbability(p);
  293.         final int n = values.length;
  294.         // Special cases
  295.         if (n <= 1) {
  296.             return n == 0 ? Double.NaN : values[0];
  297.         }
  298.         final double pos = estimationType.index(p, n);
  299.         final int i = (int) pos;

  300.         // Partition and compute
  301.         final int[] x = copy ? values.clone() : values;
  302.         if (pos > i) {
  303.             Selection.select(x, 0, n, new int[] {i, i + 1});
  304.             return Interpolation.interpolate(x[i], x[i + 1], pos - i);
  305.         }
  306.         Selection.select(x, 0, n, i);
  307.         return x[i];
  308.     }

  309.     /**
  310.      * Evaluate the {@code p}-th quantiles of the values.
  311.      *
  312.      * <p>Note: This method may partially sort the input values if not configured to
  313.      * {@link #withCopy(boolean) copy} the input data.
  314.      *
  315.      * @param values Values.
  316.      * @param p Probabilities for the quantiles to compute.
  317.      * @return the quantiles
  318.      * @throws IllegalArgumentException if any probability {@code p} is not in the range {@code [0, 1]};
  319.      * or no probabilities are specified.
  320.      */
  321.     public double[] evaluate(int[] values, double... p) {
  322.         checkProbabilities(p);
  323.         final int n = values.length;
  324.         // Special cases
  325.         final double[] q = new double[p.length];
  326.         if (n <= 1) {
  327.             Arrays.fill(q, n == 0 ? Double.NaN : values[0]);
  328.             return q;
  329.         }

  330.         // Collect interpolation positions. We use the output q as storage.
  331.         final int[] indices = computeIndices(n, p, q);

  332.         // Partition
  333.         final int[] x = copy ? values.clone() : values;
  334.         Selection.select(x, 0, n, indices);

  335.         // Compute
  336.         for (int k = 0; k < p.length; k++) {
  337.             final int i = (int) q[k];
  338.             if (q[k] > i) {
  339.                 q[k] = Interpolation.interpolate(x[i], x[i + 1], q[k] - i);
  340.             } else {
  341.                 q[k] = x[i];
  342.             }
  343.         }
  344.         return q;
  345.     }

  346.     /**
  347.      * Evaluate the {@code p}-th quantile of the values.
  348.      *
  349.      * <p>This method can be used when the values of known size are already sorted.
  350.      *
  351.      * <pre>{@code
  352.      * short[] x = ...
  353.      * Arrays.sort(x);
  354.      * double q = Quantile.withDefaults().evaluate(x.length, i -> x[i], 0.05);
  355.      * }</pre>
  356.      *
  357.      * @param n Size of the values.
  358.      * @param values Values function.
  359.      * @param p Probability for the quantile to compute.
  360.      * @return the quantile
  361.      * @throws IllegalArgumentException if {@code size < 0}; or if the probability {@code p} is
  362.      * not in the range {@code [0, 1]}.
  363.      */
  364.     public double evaluate(int n, IntToDoubleFunction values, double p) {
  365.         checkSize(n);
  366.         checkProbability(p);
  367.         // Special case
  368.         if (n <= 1) {
  369.             return n == 0 ? Double.NaN : values.applyAsDouble(0);
  370.         }
  371.         final double pos = estimationType.index(p, n);
  372.         final int i = (int) pos;
  373.         final double v1 = values.applyAsDouble(i);
  374.         if (pos > i) {
  375.             final double v2 = values.applyAsDouble(i + 1);
  376.             return Interpolation.interpolate(v1, v2, pos - i);
  377.         }
  378.         return v1;
  379.     }

  380.     /**
  381.      * Evaluate the {@code p}-th quantiles of the values.
  382.      *
  383.      * <p>This method can be used when the values of known size are already sorted.
  384.      *
  385.      * <pre>{@code
  386.      * short[] x = ...
  387.      * Arrays.sort(x);
  388.      * double[] q = Quantile.withDefaults().evaluate(x.length, i -> x[i], 0.25, 0.5, 0.75);
  389.      * }</pre>
  390.      *
  391.      * @param n Size of the values.
  392.      * @param values Values function.
  393.      * @param p Probabilities for the quantiles to compute.
  394.      * @return the quantiles
  395.      * @throws IllegalArgumentException if {@code size < 0}; if any probability {@code p} is
  396.      * not in the range {@code [0, 1]}; or no probabilities are specified.
  397.      */
  398.     public double[] evaluate(int n, IntToDoubleFunction values, double... p) {
  399.         checkSize(n);
  400.         checkProbabilities(p);
  401.         // Special case
  402.         final double[] q = new double[p.length];
  403.         if (n <= 1) {
  404.             Arrays.fill(q, n == 0 ? Double.NaN : values.applyAsDouble(0));
  405.             return q;
  406.         }
  407.         for (int k = 0; k < p.length; k++) {
  408.             final double pos = estimationType.index(p[k], n);
  409.             final int i = (int) pos;
  410.             final double v1 = values.applyAsDouble(i);
  411.             if (pos > i) {
  412.                 final double v2 = values.applyAsDouble(i + 1);
  413.                 q[k] = Interpolation.interpolate(v1, v2, pos - i);
  414.             } else {
  415.                 q[k] = v1;
  416.             }
  417.         }
  418.         return q;
  419.     }

  420.     /**
  421.      * Check the probability {@code p} is in the range {@code [0, 1]}.
  422.      *
  423.      * @param p Probability for the quantile to compute.
  424.      * @throws IllegalArgumentException if the probability is not in the range {@code [0, 1]}
  425.      */
  426.     private static void checkProbability(double p) {
  427.         // Logic negation will detect NaN
  428.         if (!(p >= 0 && p <= 1)) {
  429.             throw new IllegalArgumentException(INVALID_PROBABILITY + p);
  430.         }
  431.     }

  432.     /**
  433.      * Check the probabilities {@code p} are in the range {@code [0, 1]}.
  434.      *
  435.      * @param p Probabilities for the quantiles to compute.
  436.      * @throws IllegalArgumentException if any probabilities {@code p} is not in the range {@code [0, 1]};
  437.      * or no probabilities are specified.
  438.      */
  439.     private static void checkProbabilities(double... p) {
  440.         if (p.length == 0) {
  441.             throw new IllegalArgumentException(NO_PROBABILITIES_SPECIFIED);
  442.         }
  443.         for (final double pp : p) {
  444.             checkProbability(pp);
  445.         }
  446.     }

  447.     /**
  448.      * Check the {@code size} is positive.
  449.      *
  450.      * @param n Size of the values.
  451.      * @throws IllegalArgumentException if {@code size < 0}
  452.      */
  453.     private static void checkSize(int n) {
  454.         if (n < 0) {
  455.             throw new IllegalArgumentException(INVALID_SIZE + n);
  456.         }
  457.     }

  458.     /**
  459.      * Check the number of probabilities {@code n} is strictly positive.
  460.      *
  461.      * @param n Number of probabilities.
  462.      * @throws IllegalArgumentException if {@code c < 1}
  463.      */
  464.     private static void checkNumberOfProbabilities(int n) {
  465.         if (n < 1) {
  466.             throw new IllegalArgumentException(INVALID_NUMBER_OF_PROBABILITIES + n);
  467.         }
  468.     }

  469.     /**
  470.      * Compute the indices required for quantile interpolation.
  471.      *
  472.      * <p>The zero-based interpolation index in {@code [0, n)} is
  473.      * saved into the working array {@code q} for each {@code p}.
  474.      *
  475.      * @param n Size of the data.
  476.      * @param p Probabilities for the quantiles to compute.
  477.      * @param q Working array for quantiles.
  478.      * @return the indices
  479.      */
  480.     private int[] computeIndices(int n, double[] p, double[] q) {
  481.         final int[] indices = new int[p.length << 1];
  482.         int count = 0;
  483.         for (int k = 0; k < p.length; k++) {
  484.             final double pos = estimationType.index(p[k], n);
  485.             q[k] = pos;
  486.             final int i = (int) pos;
  487.             indices[count++] = i;
  488.             if (pos > i) {
  489.                 // Require the next index for interpolation
  490.                 indices[count++] = i + 1;
  491.             }
  492.         }
  493.         if (count < indices.length) {
  494.             return Arrays.copyOf(indices, count);
  495.         }
  496.         return indices;
  497.     }

  498.     /**
  499.      * Estimation methods for a quantile. Provides the nine quantile algorithms
  500.      * defined in Hyndman and Fan (1996)[1] as {@code HF1 - HF9}.
  501.      *
  502.      * <p>Samples quantiles are defined by:
  503.      *
  504.      * <p>\[ Q(p) = (1 - \gamma) x_j + \gamma x_{j+1} \]
  505.      *
  506.      * <p>where \( \frac{j-m}{n} \leq p \le \frac{j-m+1}{n} \), \( x_j \) is the \( j \)th
  507.      * order statistic, \( n \) is the sample size, the value of \( \gamma \) is a function
  508.      * of \( j = \lfloor np+m \rfloor \) and \( g = np + m - j \), and \( m \) is a constant
  509.      * determined by the sample quantile type.
  510.      *
  511.      * <p>Note that the real-valued position \( np + m \) is a 1-based index and
  512.      * \( j \in [1, n] \). If the real valued position is computed as beyond the lowest or
  513.      * highest values in the sample, this implementation will return the minimum or maximum
  514.      * observation respectively.
  515.      *
  516.      * <p>Types 1, 2, and 3 are discontinuous functions of \( p \); types 4 to 9 are continuous
  517.      * functions of \( p \).
  518.      *
  519.      * <p>For the continuous functions, the probability \( p_k \) is provided for the \( k \)-th order
  520.      * statistic in size \( n \). Samples quantiles are equivalently obtained to \( Q(p) \) by
  521.      * linear interpolation between points \( (p_k, x_k) \) and \( (p_{k+1}, x_{k+1}) \) for
  522.      * any \( p_k \leq p \leq p_{k+1} \).
  523.      *
  524.      * <ol>
  525.      * <li>Hyndman and Fan (1996)
  526.      *     <i>Sample Quantiles in Statistical Packages.</i>
  527.      *     The American Statistician, 50, 361-365.
  528.      *     <a href="https://www.jstor.org/stable/2684934">doi.org/10.2307/2684934</a>
  529.      * <li><a href="http://en.wikipedia.org/wiki/Quantile">Quantile (Wikipedia)</a>
  530.      * </ol>
  531.      */
  532.     public enum EstimationMethod {
  533.         /**
  534.          * Inverse of the empirical distribution function.
  535.          *
  536.          * <p>\( m = 0 \). \( \gamma = 0 \) if \( g = 0 \), and 1 otherwise.
  537.          */
  538.         HF1 {
  539.             @Override
  540.             double position0(double p, int n) {
  541.                 // position = np + 0. This is 1-based so adjust to 0-based.
  542.                 return Math.ceil(n * p) - 1;
  543.             }
  544.         },
  545.         /**
  546.          * Similar to {@link #HF1} with averaging at discontinuities.
  547.          *
  548.          * <p>\( m = 0 \). \( \gamma = 0.5 \) if \( g = 0 \), and 1 otherwise.
  549.          */
  550.         HF2 {
  551.             @Override
  552.             double position0(double p, int n) {
  553.                 final double pos = n * p;
  554.                 // Average at discontinuities
  555.                 final int j = (int) pos;
  556.                 final double g = pos - j;
  557.                 if (g == 0) {
  558.                     return j - 0.5;
  559.                 }
  560.                 // As HF1 : ceil(j + g) - 1
  561.                 return j;
  562.             }
  563.         },
  564.         /**
  565.          * The observation closest to \( np \). Ties are resolved to the nearest even order statistic.
  566.          *
  567.          * <p>\( m = -1/2 \). \( \gamma = 0 \) if \( g = 0 \) and \( j \) is even, and 1 otherwise.
  568.          */
  569.         HF3 {
  570.             @Override
  571.             double position0(double p, int n) {
  572.                 // Let rint do the work for ties to even
  573.                 return Math.rint(n * p) - 1;
  574.             }
  575.         },
  576.         /**
  577.          * Linear interpolation of the inverse of the empirical CDF.
  578.          *
  579.          * <p>\( m = 0 \). \( p_k = \frac{k}{n} \).
  580.          */
  581.         HF4 {
  582.             @Override
  583.             double position0(double p, int n) {
  584.                 // np + 0 - 1
  585.                 return n * p - 1;
  586.             }
  587.         },
  588.         /**
  589.          * A piecewise linear function where the knots are the values midway through the steps of
  590.          * the empirical CDF. Proposed by Hazen (1914) and popular amongst hydrologists.
  591.          *
  592.          * <p>\( m = 1/2 \). \( p_k = \frac{k - 1/2}{n} \).
  593.          */
  594.         HF5 {
  595.             @Override
  596.             double position0(double p, int n) {
  597.                 // np + 0.5 - 1
  598.                 return n * p - 0.5;
  599.             }
  600.         },
  601.         /**
  602.          * Linear interpolation of the expectations for the order statistics for the uniform
  603.          * distribution on [0,1]. Proposed by Weibull (1939).
  604.          *
  605.          * <p>\( m = p \). \( p_k = \frac{k}{n + 1} \).
  606.          *
  607.          * <p>This method computes the quantile as per the Apache Commons Math Percentile
  608.          * legacy implementation.
  609.          */
  610.         HF6 {
  611.             @Override
  612.             double position0(double p, int n) {
  613.                 // np + p - 1
  614.                 return (n + 1) * p - 1;
  615.             }
  616.         },
  617.         /**
  618.          * Linear interpolation of the modes for the order statistics for the uniform
  619.          * distribution on [0,1]. Proposed by Gumbull (1939).
  620.          *
  621.          * <p>\( m = 1 - p \). \( p_k = \frac{k - 1}{n - 1} \).
  622.          */
  623.         HF7 {
  624.             @Override
  625.             double position0(double p, int n) {
  626.                 // np + 1-p - 1
  627.                 return (n - 1) * p;
  628.             }
  629.         },
  630.         /**
  631.          * Linear interpolation of the approximate medians for order statistics.
  632.          *
  633.          * <p>\( m = (p + 1)/3 \). \( p_k = \frac{k - 1/3}{n + 1/3} \).
  634.          *
  635.          * <p>As per Hyndman and Fan (1996) this approach is most recommended as it provides
  636.          * an approximate median-unbiased estimate regardless of distribution.
  637.          */
  638.         HF8 {
  639.             @Override
  640.             double position0(double p, int n) {
  641.                 return n * p + (p + 1) / 3 - 1;
  642.             }
  643.         },
  644.         /**
  645.          * Quantile estimates are approximately unbiased for the expected order statistics if
  646.          * \( x \) is normally distributed.
  647.          *
  648.          * <p>\( m = p/4 + 3/8 \). \( p_k = \frac{k - 3/8}{n + 1/4} \).
  649.          */
  650.         HF9 {
  651.             @Override
  652.             double position0(double p, int n) {
  653.                 // np + p/4 + 3/8 - 1
  654.                 return (n + 0.25) * p - 0.625;
  655.             }
  656.         };

  657.         /**
  658.          * Finds the real-valued position for calculation of the quantile.
  659.          *
  660.          * <p>Return {@code i + g} such that the quantile value from sorted data is:
  661.          *
  662.          * <p>value = data[i] + g * (data[i+1] - data[i])
  663.          *
  664.          * <p>Warning: Interpolation should not use {@code data[i+1]} unless {@code g != 0}.
  665.          *
  666.          * <p>Note: In contrast to the definition of Hyndman and Fan in the class header
  667.          * which uses a 1-based position, this is a zero based index. This change is for
  668.          * convenience when addressing array positions.
  669.          *
  670.          * @param p p<sup>th</sup> quantile.
  671.          * @param n Size.
  672.          * @return a real-valued position (0-based) into the range {@code [0, n)}
  673.          */
  674.         abstract double position0(double p, int n);

  675.         /**
  676.          * Finds the index {@code i} and fractional part {@code g} of a real-valued position
  677.          * to interpolate the quantile.
  678.          *
  679.          * <p>Return {@code i + g} such that the quantile value from sorted data is:
  680.          *
  681.          * <p>value = data[i] + g * (data[i+1] - data[i])
  682.          *
  683.          * <p>Note: Interpolation should not use {@code data[i+1]} unless {@code g != 0}.
  684.          *
  685.          * @param p p<sup>th</sup> quantile.
  686.          * @param n Size.
  687.          * @return index (in [0, n-1])
  688.          */
  689.         final double index(double p, int n) {
  690.             final double pos = position0(p, n);
  691.             // Bounds check in [0, n-1]
  692.             if (pos < 0) {
  693.                 return 0;
  694.             }
  695.             if (pos > n - 1.0) {
  696.                 return n - 1.0;
  697.             }
  698.             return pos;
  699.         }
  700.     }
  701. }