View Javadoc
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.math4.legacy.stat.descriptive.moment;
18  
19  import org.apache.commons.math4.legacy.exception.MathIllegalArgumentException;
20  import org.apache.commons.math4.legacy.exception.MathIllegalStateException;
21  import org.apache.commons.math4.legacy.exception.NullArgumentException;
22  import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
23  import org.apache.commons.math4.legacy.stat.descriptive.AbstractStorelessUnivariateStatistic;
24  import org.apache.commons.math4.legacy.stat.descriptive.StorelessUnivariateStatistic;
25  import org.apache.commons.math4.legacy.stat.descriptive.summary.SumOfLogs;
26  import org.apache.commons.math4.core.jdkmath.JdkMath;
27  
28  /**
29   * Returns the <a href="http://www.xycoon.com/geometric_mean.htm">
30   * geometric mean </a> of the available values.
31   * <p>
32   * Uses a {@link SumOfLogs} instance to compute sum of logs and returns
33   * <code> exp( 1/n  (sum of logs) ).</code>  Therefore, </p>
34   * <ul>
35   * <li>If any of values are {@code < 0}, the result is <code>NaN.</code></li>
36   * <li>If all values are non-negative and less than
37   * <code>Double.POSITIVE_INFINITY</code>,  but at least one value is 0, the
38   * result is <code>0.</code></li>
39   * <li>If both <code>Double.POSITIVE_INFINITY</code> and
40   * <code>Double.NEGATIVE_INFINITY</code> are among the values, the result is
41   * <code>NaN.</code></li>
42   * </ul>
43   * <p>
44   * <strong>Note that this implementation is not synchronized.</strong> If
45   * multiple threads access an instance of this class concurrently, and at least
46   * one of the threads invokes the <code>increment()</code> or
47   * <code>clear()</code> method, it must be synchronized externally.</p>
48   */
49  public class GeometricMean extends AbstractStorelessUnivariateStatistic {
50      /** Wrapped SumOfLogs instance. */
51      private StorelessUnivariateStatistic sumOfLogs;
52  
53      /**
54       * Create a GeometricMean instance.
55       */
56      public GeometricMean() {
57          sumOfLogs = new SumOfLogs();
58      }
59  
60      /**
61       * Copy constructor, creates a new {@code GeometricMean} identical
62       * to the {@code original}.
63       *
64       * @param original the {@code GeometricMean} instance to copy
65       * @throws NullArgumentException if original is null
66       */
67      public GeometricMean(GeometricMean original) throws NullArgumentException {
68          super();
69          copy(original, this);
70      }
71  
72      /**
73       * Create a GeometricMean instance using the given SumOfLogs instance.
74       * @param sumOfLogs sum of logs instance to use for computation.
75       */
76      public GeometricMean(SumOfLogs sumOfLogs) {
77          this.sumOfLogs = sumOfLogs;
78      }
79  
80      /**
81       * {@inheritDoc}
82       */
83      @Override
84      public GeometricMean copy() {
85          GeometricMean result = new GeometricMean();
86          // no try-catch or advertised exception because args guaranteed non-null
87          copy(this, result);
88          return result;
89      }
90  
91      /**
92       * {@inheritDoc}
93       */
94      @Override
95      public void increment(final double d) {
96          sumOfLogs.increment(d);
97      }
98  
99      /**
100      * {@inheritDoc}
101      */
102     @Override
103     public double getResult() {
104         if (sumOfLogs.getN() > 0) {
105             return JdkMath.exp(sumOfLogs.getResult() / sumOfLogs.getN());
106         } else {
107             return Double.NaN;
108         }
109     }
110 
111     /**
112      * {@inheritDoc}
113      */
114     @Override
115     public void clear() {
116         sumOfLogs.clear();
117     }
118 
119     /**
120      * Returns the geometric mean of the entries in the specified portion
121      * of the input array.
122      * <p>
123      * See {@link GeometricMean} for details on the computing algorithm.</p>
124      * <p>
125      * Throws <code>IllegalArgumentException</code> if the array is null.</p>
126      *
127      * @param values input array containing the values
128      * @param begin first array element to include
129      * @param length the number of elements to include
130      * @return the geometric mean or Double.NaN if length = 0 or
131      * any of the values are &lt;= 0.
132      * @throws MathIllegalArgumentException if the input array is null or the array
133      * index parameters are not valid
134      */
135     @Override
136     public double evaluate(final double[] values, final int begin, final int length)
137         throws MathIllegalArgumentException {
138         return JdkMath.exp(sumOfLogs.evaluate(values, begin, length) / length);
139     }
140 
141     /**
142      * {@inheritDoc}
143      */
144     @Override
145     public long getN() {
146         return sumOfLogs.getN();
147     }
148 
149     /**
150      * <p>Sets the implementation for the sum of logs.</p>
151      * <p>This method must be activated before any data has been added - i.e.,
152      * before {@link #increment(double) increment} has been used to add data;
153      * otherwise an IllegalStateException will be thrown.</p>
154      *
155      * @param sumLogImpl the StorelessUnivariateStatistic instance to use
156      * for computing the log sum
157      * @throws MathIllegalStateException if data has already been added
158      *  (i.e if n &gt; 0)
159      */
160     public void setSumLogImpl(StorelessUnivariateStatistic sumLogImpl)
161         throws MathIllegalStateException {
162         checkEmpty();
163         this.sumOfLogs = sumLogImpl;
164     }
165 
166     /**
167      * Returns the currently configured sum of logs implementation.
168      *
169      * @return the StorelessUnivariateStatistic implementing the log sum
170      */
171     public StorelessUnivariateStatistic getSumLogImpl() {
172         return sumOfLogs;
173     }
174 
175     /**
176      * Copies source to dest.
177      * <p>Neither source nor dest can be null.</p>
178      *
179      * @param source GeometricMean to copy
180      * @param dest GeometricMean to copy to
181      * @throws NullArgumentException if either source or dest is null
182      */
183     public static void copy(GeometricMean source, GeometricMean dest)
184         throws NullArgumentException {
185         NullArgumentException.check(source);
186         NullArgumentException.check(dest);
187         dest.sumOfLogs = source.sumOfLogs.copy();
188     }
189 
190     /**
191      * Throws MathIllegalStateException if n > 0.
192      * @throws MathIllegalStateException if data has been added to this statistic
193      */
194     private void checkEmpty() throws MathIllegalStateException {
195         if (getN() > 0) {
196             throw new MathIllegalStateException(
197                     LocalizedFormats.VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC,
198                     getN());
199         }
200     }
201 }