GeometricMean.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.  *      https://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. /*
  18.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.stat.descriptive.moment;

  22. import java.io.Serializable;

  23. import org.hipparchus.exception.MathIllegalArgumentException;
  24. import org.hipparchus.exception.NullArgumentException;
  25. import org.hipparchus.stat.descriptive.AbstractStorelessUnivariateStatistic;
  26. import org.hipparchus.stat.descriptive.AggregatableStatistic;
  27. import org.hipparchus.stat.descriptive.summary.SumOfLogs;
  28. import org.hipparchus.util.FastMath;
  29. import org.hipparchus.util.MathUtils;

  30. /**
  31.  * Returns the <a href="http://www.xycoon.com/geometric_mean.htm">
  32.  * geometric mean </a> of the available values.
  33.  * <p>
  34.  * Uses a {@link SumOfLogs} instance to compute sum of logs and returns
  35.  * <code> exp( 1/n  (sum of logs) ).</code>  Therefore,
  36.  * <ul>
  37.  * <li>If any of values are &lt; 0, the result is <code>NaN.</code></li>
  38.  * <li>If all values are non-negative and less than
  39.  * <code>Double.POSITIVE_INFINITY</code>,  but at least one value is 0, the
  40.  * result is <code>0.</code></li>
  41.  * <li>If both <code>Double.POSITIVE_INFINITY</code> and
  42.  * <code>Double.NEGATIVE_INFINITY</code> are among the values, the result is
  43.  * <code>NaN.</code></li>
  44.  * </ul>
  45.  * <p>
  46.  * <strong>Note that this implementation is not synchronized.</strong> If
  47.  * multiple threads access an instance of this class concurrently, and at least
  48.  * one of the threads invokes the <code>increment()</code> or
  49.  * <code>clear()</code> method, it must be synchronized externally.
  50.  */
  51. public class GeometricMean extends AbstractStorelessUnivariateStatistic
  52.     implements AggregatableStatistic<GeometricMean>, Serializable {

  53.     /** Serializable version identifier */
  54.     private static final long serialVersionUID = 20150412L;

  55.     /** Wrapped SumOfLogs instance */
  56.     private final SumOfLogs sumOfLogs;

  57.     /**
  58.      * Determines whether or not this statistic can be incremented or cleared.
  59.      * <p>
  60.      * Statistics based on (constructed from) external statistics cannot
  61.      * be incremented or cleared.
  62.      */
  63.     private final boolean incSumOfLogs;

  64.     /**
  65.      * Create a GeometricMean instance.
  66.      */
  67.     public GeometricMean() {
  68.         sumOfLogs = new SumOfLogs();
  69.         incSumOfLogs = true;
  70.     }

  71.     /**
  72.      * Create a GeometricMean instance using the given SumOfLogs instance.
  73.      * @param sumOfLogs sum of logs instance to use for computation.
  74.      */
  75.     public GeometricMean(SumOfLogs sumOfLogs) {
  76.         this.sumOfLogs = sumOfLogs;
  77.         incSumOfLogs = false;
  78.     }

  79.     /**
  80.      * Copy constructor, creates a new {@code GeometricMean} identical
  81.      * to the {@code original}.
  82.      *
  83.      * @param original the {@code GeometricMean} instance to copy
  84.      * @throws NullArgumentException if original is null
  85.      */
  86.     public GeometricMean(GeometricMean original) throws NullArgumentException {
  87.         MathUtils.checkNotNull(original);
  88.         this.sumOfLogs    = original.sumOfLogs.copy();
  89.         this.incSumOfLogs = original.incSumOfLogs;
  90.     }

  91.     /** {@inheritDoc} */
  92.     @Override
  93.     public GeometricMean copy() {
  94.         return new GeometricMean(this);
  95.     }

  96.     /** {@inheritDoc} */
  97.     @Override
  98.     public void increment(final double d) {
  99.         if (incSumOfLogs) {
  100.             sumOfLogs.increment(d);
  101.         }
  102.     }

  103.     /** {@inheritDoc} */
  104.     @Override
  105.     public double getResult() {
  106.         if (sumOfLogs.getN() > 0) {
  107.             return FastMath.exp(sumOfLogs.getResult() / sumOfLogs.getN());
  108.         } else {
  109.             return Double.NaN;
  110.         }
  111.     }

  112.     /** {@inheritDoc} */
  113.     @Override
  114.     public void clear() {
  115.         if (incSumOfLogs) {
  116.             sumOfLogs.clear();
  117.         }
  118.     }

  119.     /** {@inheritDoc} */
  120.     @Override
  121.     public void aggregate(GeometricMean other) {
  122.         MathUtils.checkNotNull(other);
  123.         if (incSumOfLogs) {
  124.             this.sumOfLogs.aggregate(other.sumOfLogs);
  125.         }
  126.     }

  127.     /**
  128.      * Returns the geometric mean of the entries in the specified portion
  129.      * of the input array.
  130.      * <p>
  131.      * See {@link GeometricMean} for details on the computing algorithm.
  132.      *
  133.      * @param values input array containing the values
  134.      * @param begin first array element to include
  135.      * @param length the number of elements to include
  136.      * @return the geometric mean or Double.NaN if length = 0 or
  137.      * any of the values are &lt;= 0.
  138.      * @throws MathIllegalArgumentException if the input array is null or the array
  139.      * index parameters are not valid
  140.      */
  141.     @Override
  142.     public double evaluate(final double[] values, final int begin, final int length)
  143.         throws MathIllegalArgumentException {
  144.         return FastMath.exp(sumOfLogs.evaluate(values, begin, length) / length);
  145.     }

  146.     /** {@inheritDoc} */
  147.     @Override
  148.     public long getN() {
  149.         return sumOfLogs.getN();
  150.     }

  151. }