You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by ah...@apache.org on 2024/01/05 16:13:39 UTC

(commons-statistics) 01/02: STATISTICS-84: Reuse Sum instance when creating MEAN and SUM

This is an automated email from the ASF dual-hosted git repository.

aherbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-statistics.git

commit c9077da40d52581be9246c356f1f0076c82b5d58
Author: Alex Herbert <ah...@apache.org>
AuthorDate: Fri Jan 5 15:46:59 2024 +0000

    STATISTICS-84: Reuse Sum instance when creating MEAN and SUM
---
 .../statistics/descriptive/DoubleStatistics.java   | 49 +++++++++++++++++-----
 .../statistics/descriptive/FirstMoment.java        | 10 ++++-
 .../apache/commons/statistics/descriptive/Sum.java | 16 +++++--
 .../descriptive/SumOfCubedDeviations.java          | 30 ++++++++++++-
 .../descriptive/SumOfFourthDeviations.java         | 30 ++++++++++++-
 .../descriptive/SumOfSquaredDeviations.java        | 29 ++++++++++++-
 6 files changed, 146 insertions(+), 18 deletions(-)

diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/DoubleStatistics.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/DoubleStatistics.java
index eee279f..44bcb90 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/DoubleStatistics.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/DoubleStatistics.java
@@ -18,6 +18,7 @@ package org.apache.commons.statistics.descriptive;
 
 import java.util.Objects;
 import java.util.Set;
+import java.util.function.BiFunction;
 import java.util.function.DoubleConsumer;
 import java.util.function.Function;
 
@@ -71,9 +72,9 @@ public final class DoubleStatistics implements DoubleConsumer {
         /** The {@link Max} constructor. */
         private Function<double[], Max> max;
         /** The moment constructor. May return any instance of {@link FirstMoment}. */
-        private Function<double[], FirstMoment> moment;
+        private BiFunction<org.apache.commons.numbers.core.Sum, double[], FirstMoment> moment;
         /** The {@link Sum} constructor. */
-        private Function<double[], Sum> sum;
+        private Function<org.apache.commons.numbers.core.Sum, Sum> sum;
         /** The {@link Product} constructor. */
         private Function<double[], Product> product;
         /** The {@link SumOfSquares} constructor. */
@@ -129,7 +130,7 @@ public final class DoubleStatistics implements DoubleConsumer {
                 createMoment(2);
                 break;
             case SUM:
-                sum = Sum::of;
+                sum = Sum::new;
                 break;
             case SUM_OF_SQUARES:
                 sumOfSquares = SumOfSquares::of;
@@ -150,14 +151,14 @@ public final class DoubleStatistics implements DoubleConsumer {
             if (order > momentOrder) {
                 momentOrder = order;
                 if (order == 4) {
-                    moment = SumOfFourthDeviations::of;
+                    moment = SumOfFourthDeviations::create;
                 } else if (order == 3) {
-                    moment = SumOfCubedDeviations::of;
+                    moment = SumOfCubedDeviations::create;
                 } else if (order == 2) {
-                    moment = SumOfSquaredDeviations::of;
+                    moment = SumOfSquaredDeviations::create;
                 } else {
                     // Assume order == 1
-                    moment = FirstMoment::of;
+                    moment = FirstMoment::create;
                 }
             }
         }
@@ -195,12 +196,21 @@ public final class DoubleStatistics implements DoubleConsumer {
          */
         public DoubleStatistics build(double... values) {
             Objects.requireNonNull(values, "values");
+            // Create related statistics
+            FirstMoment m = null;
+            Sum sumStat = null;
+            if (moment != null || sum != null) {
+                final org.apache.commons.numbers.core.Sum s =
+                    org.apache.commons.numbers.core.Sum.of(values);
+                m = create(moment, s, values);
+                sumStat = create(sum, s);
+            }
             return new DoubleStatistics(
                 values.length,
                 create(min, values),
                 create(max, values),
-                create(moment, values),
-                create(sum, values),
+                m,
+                sumStat,
                 create(product, values),
                 create(sumOfSquares, values),
                 create(sumOfLogs, values),
@@ -210,17 +220,36 @@ public final class DoubleStatistics implements DoubleConsumer {
         /**
          * Creates the object from the {@code values}.
          *
+         * @param <S> value type
          * @param <T> object type
          * @param constructor Constructor.
          * @param values Values
          * @return the instance
          */
-        private static <T> T create(Function<double[], T> constructor, double[] values) {
+        private static <S, T> T create(Function<S, T> constructor, S values) {
             if (constructor != null) {
                 return constructor.apply(values);
             }
             return null;
         }
+
+        /**
+         * Creates the object from the values {@code r} and {@code s}.
+         *
+         * @param <R> value type
+         * @param <S> value type
+         * @param <T> object type
+         * @param constructor Constructor.
+         * @param r Value.
+         * @param s Value.
+         * @return the instance
+         */
+        private static <R, S, T> T create(BiFunction<R, S, T> constructor, R r, S s) {
+            if (constructor != null) {
+                return constructor.apply(r, s);
+            }
+            return null;
+        }
     }
 
     /**
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/FirstMoment.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/FirstMoment.java
index 2ef8ad8..5466ff6 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/FirstMoment.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/FirstMoment.java
@@ -160,11 +160,19 @@ class FirstMoment implements DoubleConsumer {
      * <p>Uses the provided {@code sum} if finite; otherwise reverts to using the rolling moment
      * to protect from overflow and adds a second pass correction term.
      *
+     * <p>This method is used by {@link DoubleStatistics} using a sum that can be reused
+     * for the {@link Sum} statistic.
+     *
      * @param sum Sum of the values.
      * @param values Values.
      * @return {@code FirstMoment} instance.
      */
-    private static FirstMoment create(org.apache.commons.numbers.core.Sum sum, double... values) {
+    static FirstMoment create(org.apache.commons.numbers.core.Sum sum, double[] values) {
+        // Protect against empty values
+        if (values.length == 0) {
+            return new FirstMoment();
+        }
+
         final double s = sum.getAsDouble();
         if (Double.isFinite(s)) {
             return new FirstMoment(s / values.length, values.length);
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Sum.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Sum.java
index 4b66236..9c666fe 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Sum.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/Sum.java
@@ -52,14 +52,22 @@ package org.apache.commons.statistics.descriptive;
 public final class Sum implements DoubleStatistic, StatisticAccumulator<Sum> {
 
     /** {@link org.apache.commons.numbers.core.Sum Sum} used to compute the sum. */
-    private final org.apache.commons.numbers.core.Sum delegate =
-            org.apache.commons.numbers.core.Sum.create();
+    private final org.apache.commons.numbers.core.Sum delegate;
 
     /**
      * Create an instance.
      */
     private Sum() {
-        // No-op
+        this(org.apache.commons.numbers.core.Sum.create());
+    }
+
+    /**
+     * Create an instance using the specified {@code sum}.
+     *
+     * @param sum Sum.
+     */
+    Sum(org.apache.commons.numbers.core.Sum sum) {
+        delegate = sum;
     }
 
     /**
@@ -85,7 +93,7 @@ public final class Sum implements DoubleStatistic, StatisticAccumulator<Sum> {
      * @return {@code Sum} instance.
      */
     public static Sum of(double... values) {
-        return Statistics.add(new Sum(), values);
+        return new Sum(org.apache.commons.numbers.core.Sum.of(values));
     }
 
     /**
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfCubedDeviations.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfCubedDeviations.java
index a0741eb..df96614 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfCubedDeviations.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfCubedDeviations.java
@@ -130,7 +130,35 @@ class SumOfCubedDeviations extends SumOfSquaredDeviations {
         if (values.length == 0) {
             return new SumOfCubedDeviations();
         }
-        final SumOfSquaredDeviations ss = SumOfSquaredDeviations.of(values);
+        return create(SumOfSquaredDeviations.of(values), values);
+    }
+
+    /**
+     * Creates the sum of cubed deviations.
+     *
+     * <p>Uses the provided {@code sum} to create the first moment.
+     * This method is used by {@link DoubleStatistics} using a sum that can be reused
+     * for the {@link Sum} statistic.
+     *
+     * @param sum Sum of the values.
+     * @param values Values.
+     * @return {@code SumOfCubedDeviations} instance.
+     */
+    static SumOfCubedDeviations create(org.apache.commons.numbers.core.Sum sum, double[] values) {
+        if (values.length == 0) {
+            return new SumOfCubedDeviations();
+        }
+        return create(SumOfSquaredDeviations.create(sum, values), values);
+    }
+
+    /**
+     * Creates the sum of cubed deviations.
+     *
+     * @param ss Sum of squared deviations.
+     * @param values Values.
+     * @return {@code SumOfCubedDeviations} instance.
+     */
+    private static SumOfCubedDeviations create(SumOfSquaredDeviations ss, double[] values) {
         // Edge cases
         final double xbar = ss.getFirstMoment();
         if (!Double.isFinite(xbar)) {
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfFourthDeviations.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfFourthDeviations.java
index 57eb543..e3e97ba 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfFourthDeviations.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfFourthDeviations.java
@@ -122,7 +122,35 @@ class SumOfFourthDeviations extends SumOfCubedDeviations {
         if (values.length == 0) {
             return new SumOfFourthDeviations();
         }
-        final SumOfCubedDeviations sc = SumOfCubedDeviations.of(values);
+        return create(SumOfCubedDeviations.of(values), values);
+    }
+
+    /**
+     * Creates the sum of fourth deviations.
+     *
+     * <p>Uses the provided {@code sum} to create the first moment.
+     * This method is used by {@link DoubleStatistics} using a sum that can be reused
+     * for the {@link Sum} statistic.
+     *
+     * @param sum Sum of the values.
+     * @param values Values.
+     * @return {@code SumOfFourthDeviations} instance.
+     */
+    static SumOfFourthDeviations create(org.apache.commons.numbers.core.Sum sum, double[] values) {
+        if (values.length == 0) {
+            return new SumOfFourthDeviations();
+        }
+        return create(SumOfCubedDeviations.create(sum, values), values);
+    }
+
+    /**
+     * Creates the sum of fourth deviations.
+     *
+     * @param sc Sum of cubed deviations.
+     * @param values Values.
+     * @return {@code SumOfFourthDeviations} instance.
+     */
+    private static SumOfFourthDeviations create(SumOfCubedDeviations sc, double[] values) {
         // Edge cases
         final double xbar = sc.getFirstMoment();
         if (!Double.isFinite(xbar) ||
diff --git a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfSquaredDeviations.java b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfSquaredDeviations.java
index 4a9860a..624b03f 100644
--- a/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfSquaredDeviations.java
+++ b/commons-statistics-descriptive/src/main/java/org/apache/commons/statistics/descriptive/SumOfSquaredDeviations.java
@@ -113,11 +113,38 @@ class SumOfSquaredDeviations extends FirstMoment {
         if (values.length == 0) {
             return new SumOfSquaredDeviations();
         }
+        return create(FirstMoment.of(values), values);
+    }
+
+    /**
+     * Creates the sum of squared deviations.
+     *
+     * <p>Uses the provided {@code sum} to create the first moment.
+     * This method is used by {@link DoubleStatistics} using a sum that can be reused
+     * for the {@link Sum} statistic.
+     *
+     * @param sum Sum of the values.
+     * @param values Values.
+     * @return {@code SumOfSquaredDeviations} instance.
+     */
+    static SumOfSquaredDeviations create(org.apache.commons.numbers.core.Sum sum, double[] values) {
+        if (values.length == 0) {
+            return new SumOfSquaredDeviations();
+        }
+        return create(FirstMoment.create(sum, values), values);
+    }
 
+    /**
+     * Creates the sum of squared deviations.
+     *
+     * @param m1 First moment.
+     * @param values Values.
+     * @return {@code SumOfSquaredDeviations} instance.
+     */
+    private static SumOfSquaredDeviations create(FirstMoment m1, double[] values) {
         // "Corrected two-pass algorithm"
         // See: Chan et al (1983) Equation 1.7
 
-        final FirstMoment m1 = FirstMoment.of(values);
         final double xbar = m1.getFirstMoment();
         if (!Double.isFinite(xbar)) {
             return new SumOfSquaredDeviations(Double.NaN, m1);