001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.commons.math3.stat.descriptive.moment;
018
019 import java.io.Serializable;
020
021 import org.apache.commons.math3.exception.MathIllegalArgumentException;
022 import org.apache.commons.math3.exception.MathIllegalStateException;
023 import org.apache.commons.math3.exception.NullArgumentException;
024 import org.apache.commons.math3.exception.util.LocalizedFormats;
025 import org.apache.commons.math3.stat.descriptive.AbstractStorelessUnivariateStatistic;
026 import org.apache.commons.math3.stat.descriptive.StorelessUnivariateStatistic;
027 import org.apache.commons.math3.stat.descriptive.summary.SumOfLogs;
028 import org.apache.commons.math3.util.FastMath;
029 import org.apache.commons.math3.util.MathUtils;
030
031 /**
032 * Returns the <a href="http://www.xycoon.com/geometric_mean.htm">
033 * geometric mean </a> of the available values.
034 * <p>
035 * Uses a {@link SumOfLogs} instance to compute sum of logs and returns
036 * <code> exp( 1/n (sum of logs) ).</code> Therefore, </p>
037 * <ul>
038 * <li>If any of values are < 0, the result is <code>NaN.</code></li>
039 * <li>If all values are non-negative and less than
040 * <code>Double.POSITIVE_INFINITY</code>, but at least one value is 0, the
041 * result is <code>0.</code></li>
042 * <li>If both <code>Double.POSITIVE_INFINITY</code> and
043 * <code>Double.NEGATIVE_INFINITY</code> are among the values, the result is
044 * <code>NaN.</code></li>
045 * </ul> </p>
046 * <p>
047 * <strong>Note that this implementation is not synchronized.</strong> If
048 * multiple threads access an instance of this class concurrently, and at least
049 * one of the threads invokes the <code>increment()</code> or
050 * <code>clear()</code> method, it must be synchronized externally.</p>
051 *
052 *
053 * @version $Id: GeometricMean.java 1416643 2012-12-03 19:37:14Z tn $
054 */
055 public class GeometricMean extends AbstractStorelessUnivariateStatistic implements Serializable {
056
057 /** Serializable version identifier */
058 private static final long serialVersionUID = -8178734905303459453L;
059
060 /** Wrapped SumOfLogs instance */
061 private StorelessUnivariateStatistic sumOfLogs;
062
063 /**
064 * Create a GeometricMean instance
065 */
066 public GeometricMean() {
067 sumOfLogs = new SumOfLogs();
068 }
069
070 /**
071 * Copy constructor, creates a new {@code GeometricMean} identical
072 * to the {@code original}
073 *
074 * @param original the {@code GeometricMean} instance to copy
075 * @throws NullArgumentException if original is null
076 */
077 public GeometricMean(GeometricMean original) throws NullArgumentException {
078 super();
079 copy(original, this);
080 }
081
082 /**
083 * Create a GeometricMean instance using the given SumOfLogs instance
084 * @param sumOfLogs sum of logs instance to use for computation
085 */
086 public GeometricMean(SumOfLogs sumOfLogs) {
087 this.sumOfLogs = sumOfLogs;
088 }
089
090 /**
091 * {@inheritDoc}
092 */
093 @Override
094 public GeometricMean copy() {
095 GeometricMean result = new GeometricMean();
096 // no try-catch or advertised exception because args guaranteed non-null
097 copy(this, result);
098 return result;
099 }
100
101 /**
102 * {@inheritDoc}
103 */
104 @Override
105 public void increment(final double d) {
106 sumOfLogs.increment(d);
107 }
108
109 /**
110 * {@inheritDoc}
111 */
112 @Override
113 public double getResult() {
114 if (sumOfLogs.getN() > 0) {
115 return FastMath.exp(sumOfLogs.getResult() / sumOfLogs.getN());
116 } else {
117 return Double.NaN;
118 }
119 }
120
121 /**
122 * {@inheritDoc}
123 */
124 @Override
125 public void clear() {
126 sumOfLogs.clear();
127 }
128
129 /**
130 * Returns the geometric mean of the entries in the specified portion
131 * of the input array.
132 * <p>
133 * See {@link GeometricMean} for details on the computing algorithm.</p>
134 * <p>
135 * Throws <code>IllegalArgumentException</code> if the array is null.</p>
136 *
137 * @param values input array containing the values
138 * @param begin first array element to include
139 * @param length the number of elements to include
140 * @return the geometric mean or Double.NaN if length = 0 or
141 * any of the values are <= 0.
142 * @throws MathIllegalArgumentException if the input array is null or the array
143 * index parameters are not valid
144 */
145 @Override
146 public double evaluate(
147 final double[] values, final int begin, final int length)
148 throws MathIllegalArgumentException {
149 return FastMath.exp(
150 sumOfLogs.evaluate(values, begin, length) / length);
151 }
152
153 /**
154 * {@inheritDoc}
155 */
156 public long getN() {
157 return sumOfLogs.getN();
158 }
159
160 /**
161 * <p>Sets the implementation for the sum of logs.</p>
162 * <p>This method must be activated before any data has been added - i.e.,
163 * before {@link #increment(double) increment} has been used to add data;
164 * otherwise an IllegalStateException will be thrown.</p>
165 *
166 * @param sumLogImpl the StorelessUnivariateStatistic instance to use
167 * for computing the log sum
168 * @throws MathIllegalStateException if data has already been added
169 * (i.e if n > 0)
170 */
171 public void setSumLogImpl(StorelessUnivariateStatistic sumLogImpl)
172 throws MathIllegalStateException {
173 checkEmpty();
174 this.sumOfLogs = sumLogImpl;
175 }
176
177 /**
178 * Returns the currently configured sum of logs implementation
179 *
180 * @return the StorelessUnivariateStatistic implementing the log sum
181 */
182 public StorelessUnivariateStatistic getSumLogImpl() {
183 return sumOfLogs;
184 }
185
186 /**
187 * Copies source to dest.
188 * <p>Neither source nor dest can be null.</p>
189 *
190 * @param source GeometricMean to copy
191 * @param dest GeometricMean to copy to
192 * @throws NullArgumentException if either source or dest is null
193 */
194 public static void copy(GeometricMean source, GeometricMean dest)
195 throws NullArgumentException {
196 MathUtils.checkNotNull(source);
197 MathUtils.checkNotNull(dest);
198 dest.setData(source.getDataRef());
199 dest.sumOfLogs = source.sumOfLogs.copy();
200 }
201
202
203 /**
204 * Throws MathIllegalStateException if n > 0.
205 * @throws MathIllegalStateException if data has been added to this statistic
206 */
207 private void checkEmpty() throws MathIllegalStateException {
208 if (getN() > 0) {
209 throw new MathIllegalStateException(
210 LocalizedFormats.VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC,
211 getN());
212 }
213 }
214
215 }