Statistics.java
- /*
- * 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.function.DoubleConsumer;
- import java.util.function.IntConsumer;
- import java.util.function.LongConsumer;
- /**
- * Utility methods for statistics.
- *
- * @since 1.1
- */
- final class Statistics {
- /** A no-operation double consumer. This is exposed for testing. */
- static final DoubleConsumer DOUBLE_NOOP = new DoubleConsumer() {
- @Override
- public void accept(double value) {
- // Do nothing
- }
- @Override
- public DoubleConsumer andThen(DoubleConsumer after) {
- // Delegate to the after consumer
- return after;
- }
- };
- /** A no-operation int consumer. This is exposed for testing. */
- static final IntConsumer INT_NOOP = new IntConsumer() {
- @Override
- public void accept(int value) {
- // Do nothing
- }
- @Override
- public IntConsumer andThen(IntConsumer after) {
- // Delegate to the after consumer
- return after;
- }
- };
- /** A no-operation long consumer. This is exposed for testing. */
- static final LongConsumer LONG_NOOP = new LongConsumer() {
- @Override
- public void accept(long value) {
- // Do nothing
- }
- @Override
- public LongConsumer andThen(LongConsumer after) {
- // Delegate to the after consumer
- return after;
- }
- };
- /** Error message for an incompatible statistics. */
- private static final String INCOMPATIBLE_STATISTICS = "Incompatible statistics";
- /** No instances. */
- private Statistics() {}
- /**
- * Add all the {@code values} to the {@code statistic}.
- *
- * @param <T> Type of the statistic
- * @param statistic Statistic.
- * @param values Values.
- * @return the statistic
- */
- static <T extends DoubleConsumer> T add(T statistic, double[] values) {
- for (final double x : values) {
- statistic.accept(x);
- }
- return statistic;
- }
- /**
- * Add all the {@code values} to the {@code statistic}.
- *
- * @param <T> Type of the statistic
- * @param statistic Statistic.
- * @param values Values.
- * @return the statistic
- */
- static <T extends DoubleConsumer> T add(T statistic, int[] values) {
- for (final double x : values) {
- statistic.accept(x);
- }
- return statistic;
- }
- /**
- * Add all the {@code values} to the {@code statistic}.
- *
- * @param <T> Type of the statistic
- * @param statistic Statistic.
- * @param values Values.
- * @return the statistic
- */
- static <T extends DoubleConsumer> T add(T statistic, long[] values) {
- for (final double x : values) {
- statistic.accept(x);
- }
- return statistic;
- }
- /**
- * Add all the {@code values} to the {@code statistic}.
- *
- * @param <T> Type of the statistic
- * @param statistic Statistic.
- * @param values Values.
- * @return the statistic
- */
- static <T extends IntConsumer> T add(T statistic, int[] values) {
- for (final int x : values) {
- statistic.accept(x);
- }
- return statistic;
- }
- /**
- * Add all the {@code values} to the {@code statistic}.
- *
- * @param <T> Type of the statistic
- * @param statistic Statistic.
- * @param values Values.
- * @return the statistic
- */
- static <T extends LongConsumer> T add(T statistic, long[] values) {
- for (final long x : values) {
- statistic.accept(x);
- }
- return statistic;
- }
- /**
- * Returns {@code true} if the second central moment {@code m2} is effectively
- * zero given the magnitude of the first raw moment {@code m1}.
- *
- * <p>This method shares the logic for detecting a zero variance among implementations
- * that divide by the variance (e.g. skewness, kurtosis).
- *
- * @param m1 First raw moment (mean).
- * @param m2 Second central moment (biased variance).
- * @return true if the variance is zero
- */
- static boolean zeroVariance(double m1, double m2) {
- // Note: Commons Math checks the variance is < 1e-19.
- // The absolute threshold does not account for the magnitude of the sample.
- // This checks the average squared deviation from the mean (m2)
- // is smaller than the squared precision of the mean (m1).
- // Precision is set to 15 decimal digits
- // (1e-15 ~ 4.5 eps where eps = 2^-52).
- final double meanPrecision = 1e-15 * m1;
- return m2 <= meanPrecision * meanPrecision;
- }
- /**
- * Chain the {@code consumers} into a single composite consumer. Ignore any {@code null}
- * consumer. Returns {@code null} if all arguments are {@code null}.
- *
- * @param consumers Consumers.
- * @return a composed consumer (or null)
- */
- static DoubleConsumer compose(DoubleConsumer... consumers) {
- DoubleConsumer action = DOUBLE_NOOP;
- for (final DoubleConsumer consumer : consumers) {
- if (consumer != null) {
- action = action.andThen(consumer);
- }
- }
- return action == DOUBLE_NOOP ? null : action;
- }
- /**
- * Chain the {@code consumers} into a single composite consumer. Ignore any {@code null}
- * consumer. Returns {@code null} if all arguments are {@code null}.
- *
- * @param consumers Consumers.
- * @return a composed consumer (or null)
- */
- static IntConsumer compose(IntConsumer... consumers) {
- IntConsumer action = INT_NOOP;
- for (final IntConsumer consumer : consumers) {
- if (consumer != null) {
- action = action.andThen(consumer);
- }
- }
- return action == INT_NOOP ? null : action;
- }
- /**
- * Chain the {@code consumers} into a single composite consumer. Ignore any {@code null}
- * consumer. Returns {@code null} if all arguments are {@code null}.
- *
- * @param consumers Consumers.
- * @return a composed consumer (or null)
- */
- static LongConsumer compose(LongConsumer... consumers) {
- LongConsumer action = LONG_NOOP;
- for (final LongConsumer consumer : consumers) {
- if (consumer != null) {
- action = action.andThen(consumer);
- }
- }
- return action == LONG_NOOP ? null : action;
- }
- /**
- * Gets the statistic result using the {@code int} value.
- * Return {@code null} is the statistic is {@code null}.
- *
- * @param s Statistic.
- * @return the result or null
- */
- static StatisticResult getResultAsIntOrNull(StatisticResult s) {
- if (s != null) {
- return (IntStatisticResult) s::getAsInt;
- }
- return null;
- }
- /**
- * Gets the statistic result using the {@code long} value.
- * Return {@code null} is the statistic is {@code null}.
- *
- * @param s Statistic.
- * @return the result or null
- */
- static StatisticResult getResultAsLongOrNull(StatisticResult s) {
- if (s != null) {
- return (LongStatisticResult) s::getAsLong;
- }
- return null;
- }
- /**
- * Gets the statistic result using the {@code double} value.
- * Return {@code null} is the statistic is {@code null}.
- *
- * @param s Statistic.
- * @return the result or null
- */
- static StatisticResult getResultAsDoubleOrNull(StatisticResult s) {
- if (s != null) {
- return s::getAsDouble;
- }
- return null;
- }
- /**
- * Gets the statistic result using the {@code BigInteger} value.
- * Return {@code null} is the statistic is {@code null}.
- *
- * @param s Statistic.
- * @return the result or null
- */
- static StatisticResult getResultAsBigIntegerOrNull(StatisticResult s) {
- if (s != null) {
- return (BigIntegerStatisticResult) s::getAsBigInteger;
- }
- return null;
- }
- /**
- * Check left-hand side argument {@code a} is {@code null} or else the right-hand side
- * argument {@code b} must also be non-{@code null} so the statistics can be combined.
- *
- * @param <T> {@link StatisticResult} being accumulated.
- * @param a LHS.
- * @param b RHS.
- * @throws IllegalArgumentException if the objects cannot be combined
- */
- static <T extends StatisticResult & StatisticAccumulator<T>> void checkCombineCompatible(T a, T b) {
- if (a != null && b == null) {
- throw new IllegalArgumentException(INCOMPATIBLE_STATISTICS);
- }
- }
- /**
- * Check left-hand side argument {@code a} is {@code null} or else the right-hand side
- * argument {@code b} must be run-time assignable to the same class as {@code a}
- * so the statistics can be combined.
- *
- * @param a LHS.
- * @param b RHS.
- * @throws IllegalArgumentException if the objects cannot be combined
- */
- static void checkCombineAssignable(FirstMoment a, FirstMoment b) {
- if (a != null && (b == null || !a.getClass().isAssignableFrom(b.getClass()))) {
- throw new IllegalArgumentException(INCOMPATIBLE_STATISTICS);
- }
- }
- /**
- * If the left-hand side argument {@code a} is non-{@code null}, combine it with the
- * right-hand side argument {@code b}.
- *
- * @param <T> {@link StatisticResult} being accumulated.
- * @param a LHS.
- * @param b RHS.
- */
- static <T extends StatisticResult & StatisticAccumulator<T>> void combine(T a, T b) {
- if (a != null) {
- a.combine(b);
- }
- }
- /**
- * If the left-hand side argument {@code a} is non-{@code null}, combine it with the
- * right-hand side argument {@code b}. Assumes that the RHS is run-time assignable
- * to the same class as LHS.
- *
- * @param a LHS.
- * @param b RHS.
- * @see #checkCombineAssignable(FirstMoment, FirstMoment)
- */
- static void combineMoment(FirstMoment a, FirstMoment b) {
- // Avoid reflection and use the simpler instanceof
- if (a instanceof SumOfFourthDeviations) {
- ((SumOfFourthDeviations) a).combine((SumOfFourthDeviations) b);
- } else if (a instanceof SumOfCubedDeviations) {
- ((SumOfCubedDeviations) a).combine((SumOfCubedDeviations) b);
- } else if (a instanceof SumOfSquaredDeviations) {
- ((SumOfSquaredDeviations) a).combine((SumOfSquaredDeviations) b);
- } else if (a != null) {
- a.combine(b);
- }
- }
- }