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

org.apache.commons.statistics.descriptive.Median Maven / Gradle / Ivy

/*
 * 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.commons.statistics.descriptive;

import java.util.Objects;
import org.apache.commons.numbers.arrays.Selection;

/**
 * Returns the median of the available values.
 *
 * 

For values of length {@code n}, let {@code k = n / 2}: *

    *
  • The result is {@code NaN} if {@code n = 0}. *
  • The result is {@code values[k]} if {@code n} is odd. *
  • The result is {@code (values[k - 1] + values[k]) / 2} if {@code n} is even. *
* *

This implementation respects the ordering imposed by * {@link Double#compare(double, double)} for {@code NaN} values. If a {@code NaN} occurs * in the selected positions in the fully sorted values then the result is {@code NaN}. * *

The {@link NaNPolicy} can be used to change the behaviour on {@code NaN} values. * *

Instances of this class are immutable and thread-safe. * * @see #with(NaNPolicy) * @see Median (Wikipedia) * @since 1.1 */ public final class Median { /** Default instance. */ private static final Median DEFAULT = new Median(false, NaNPolicy.INCLUDE); /** Flag to indicate if the data should be copied. */ private final boolean copy; /** NaN policy for floating point data. */ private final NaNPolicy nanPolicy; /** Transformer for NaN data. */ private final NaNTransformer nanTransformer; /** * @param copy Flag to indicate if the data should be copied. * @param nanPolicy NaN policy. */ private Median(boolean copy, NaNPolicy nanPolicy) { this.copy = copy; this.nanPolicy = nanPolicy; nanTransformer = NaNTransformers.createNaNTransformer(nanPolicy, copy); } /** * Return a new instance with the default options. * *

    *
  • {@linkplain #withCopy(boolean) Copy = false} *
  • {@linkplain #with(NaNPolicy) NaN policy = include} *
* *

Note: The default options configure for processing in-place and including * {@code NaN} values in the data. This is the most efficient mode and has the * smallest memory consumption. * * @return the median implementation * @see #withCopy(boolean) * @see #with(NaNPolicy) */ public static Median withDefaults() { return DEFAULT; } /** * Return an instance with the configured copy behaviour. If {@code false} then * the input array will be modified by the call to evaluate the median; otherwise * the computation uses a copy of the data. * * @param v Value. * @return an instance */ public Median withCopy(boolean v) { return new Median(v, nanPolicy); } /** * Return an instance with the configured {@link NaNPolicy}. * *

Note: This implementation respects the ordering imposed by * {@link Double#compare(double, double)} for {@code NaN} values: {@code NaN} is * considered greater than all other values, and all {@code NaN} values are equal. The * {@link NaNPolicy} changes the computation of the statistic in the presence of * {@code NaN} values. * *

    *
  • {@link NaNPolicy#INCLUDE}: {@code NaN} values are moved to the end of the data; * the size of the data includes the {@code NaN} values and the median will be * {@code NaN} if any value used for median interpolation is {@code NaN}. *
  • {@link NaNPolicy#EXCLUDE}: {@code NaN} values are moved to the end of the data; * the size of the data excludes the {@code NaN} values and the median will * never be {@code NaN} for non-zero size. If all data are {@code NaN} then the size is zero * and the result is {@code NaN}. *
  • {@link NaNPolicy#ERROR}: An exception is raised if the data contains {@code NaN} * values. *
* *

Note that the result is identical for all policies if no {@code NaN} values are present. * * @param v Value. * @return an instance */ public Median with(NaNPolicy v) { return new Median(copy, Objects.requireNonNull(v)); } /** * Evaluate the median. * *

Note: This method may partially sort the input values if not configured to * {@link #withCopy(boolean) copy} the input data. * * @param values Values. * @return the median */ public double evaluate(double[] values) { // Floating-point data handling final int[] bounds = new int[1]; final double[] x = nanTransformer.apply(values, bounds); final int n = bounds[0]; // Special cases if (n <= 2) { switch (n) { case 2: // Sorting the array matches the behaviour of Quantile for n==2 // Handle NaN and signed zeros if (Double.compare(x[1], x[0]) < 0) { final double t = x[0]; x[0] = x[1]; x[1] = t; } return Interpolation.mean(x[0], x[1]); case 1: return x[0]; default: return Double.NaN; } } // Median index final int m = n >>> 1; // Odd if ((n & 0x1) == 1) { Selection.select(x, 0, n, m); return x[m]; } // Even: require (m-1, m) Selection.select(x, 0, n, new int[] {m - 1, m}); return Interpolation.mean(x[m - 1], x[m]); } /** * Evaluate the median. * *

Note: This method may partially sort the input values if not configured to * {@link #withCopy(boolean) copy} the input data. * * @param values Values. * @return the median */ public double evaluate(int[] values) { final int[] x = copy ? values.clone() : values; final int n = values.length; // Special cases if (n <= 2) { switch (n) { case 2: // Sorting the array matches the behaviour of Quantile for n==2 if (x[1] < x[0]) { final int t = x[0]; x[0] = x[1]; x[1] = t; } return Interpolation.mean(x[0], x[1]); case 1: return x[0]; default: return Double.NaN; } } // Median index final int m = n >>> 1; // Odd if ((n & 0x1) == 1) { Selection.select(x, 0, n, m); return x[m]; } // Even: require (m-1, m) Selection.select(x, 0, n, new int[] {m - 1, m}); return Interpolation.mean(x[m - 1], x[m]); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy