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.math.stat.descriptive.moment;
018
019 import java.io.Serializable;
020
021 import org.apache.commons.math.exception.MathIllegalStateException;
022 import org.apache.commons.math.exception.NullArgumentException;
023 import org.apache.commons.math.exception.util.LocalizedFormats;
024 import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
025 import org.apache.commons.math.stat.descriptive.StorelessUnivariateStatistic;
026 import org.apache.commons.math.stat.descriptive.summary.SumOfLogs;
027 import org.apache.commons.math.util.FastMath;
028 import org.apache.commons.math.util.MathUtils;
029
030 /**
031 * Returns the <a href="http://www.xycoon.com/geometric_mean.htm">
032 * geometric mean </a> of the available values.
033 * <p>
034 * Uses a {@link SumOfLogs} instance to compute sum of logs and returns
035 * <code> exp( 1/n (sum of logs) ).</code> Therefore, </p>
036 * <ul>
037 * <li>If any of values are < 0, the result is <code>NaN.</code></li>
038 * <li>If all values are non-negative and less than
039 * <code>Double.POSITIVE_INFINITY</code>, but at least one value is 0, the
040 * result is <code>0.</code></li>
041 * <li>If both <code>Double.POSITIVE_INFINITY</code> and
042 * <code>Double.NEGATIVE_INFINITY</code> are among the values, the result is
043 * <code>NaN.</code></li>
044 * </ul> </p>
045 * <p>
046 * <strong>Note that this implementation is not synchronized.</strong> If
047 * multiple threads access an instance of this class concurrently, and at least
048 * one of the threads invokes the <code>increment()</code> or
049 * <code>clear()</code> method, it must be synchronized externally.</p>
050 *
051 *
052 * @version $Id: GeometricMean.java 1178202 2011-10-02 16:20:04Z psteitz $
053 */
054 public class GeometricMean extends AbstractStorelessUnivariateStatistic implements Serializable {
055
056 /** Serializable version identifier */
057 private static final long serialVersionUID = -8178734905303459453L;
058
059 /** Wrapped SumOfLogs instance */
060 private StorelessUnivariateStatistic sumOfLogs;
061
062 /**
063 * Create a GeometricMean instance
064 */
065 public GeometricMean() {
066 sumOfLogs = new SumOfLogs();
067 }
068
069 /**
070 * Copy constructor, creates a new {@code GeometricMean} identical
071 * to the {@code original}
072 *
073 * @param original the {@code GeometricMean} instance to copy
074 */
075 public GeometricMean(GeometricMean original) {
076 super();
077 copy(original, this);
078 }
079
080 /**
081 * Create a GeometricMean instance using the given SumOfLogs instance
082 * @param sumOfLogs sum of logs instance to use for computation
083 */
084 public GeometricMean(SumOfLogs sumOfLogs) {
085 this.sumOfLogs = sumOfLogs;
086 }
087
088 /**
089 * {@inheritDoc}
090 */
091 @Override
092 public GeometricMean copy() {
093 GeometricMean result = new GeometricMean();
094 copy(this, result);
095 return result;
096 }
097
098 /**
099 * {@inheritDoc}
100 */
101 @Override
102 public void increment(final double d) {
103 sumOfLogs.increment(d);
104 }
105
106 /**
107 * {@inheritDoc}
108 */
109 @Override
110 public double getResult() {
111 if (sumOfLogs.getN() > 0) {
112 return FastMath.exp(sumOfLogs.getResult() / sumOfLogs.getN());
113 } else {
114 return Double.NaN;
115 }
116 }
117
118 /**
119 * {@inheritDoc}
120 */
121 @Override
122 public void clear() {
123 sumOfLogs.clear();
124 }
125
126 /**
127 * Returns the geometric mean of the entries in the specified portion
128 * of the input array.
129 * <p>
130 * See {@link GeometricMean} for details on the computing algorithm.</p>
131 * <p>
132 * Throws <code>IllegalArgumentException</code> if the array is null.</p>
133 *
134 * @param values input array containing the values
135 * @param begin first array element to include
136 * @param length the number of elements to include
137 * @return the geometric mean or Double.NaN if length = 0 or
138 * any of the values are <= 0.
139 * @throws IllegalArgumentException if the input array is null or the array
140 * index parameters are not valid
141 */
142 @Override
143 public double evaluate(
144 final double[] values, final int begin, final int length) {
145 return FastMath.exp(
146 sumOfLogs.evaluate(values, begin, length) / length);
147 }
148
149 /**
150 * {@inheritDoc}
151 */
152 public long getN() {
153 return sumOfLogs.getN();
154 }
155
156 /**
157 * <p>Sets the implementation for the sum of logs.</p>
158 * <p>This method must be activated before any data has been added - i.e.,
159 * before {@link #increment(double) increment} has been used to add data;
160 * otherwise an IllegalStateException will be thrown.</p>
161 *
162 * @param sumLogImpl the StorelessUnivariateStatistic instance to use
163 * for computing the log sum
164 * @throws IllegalStateException if data has already been added
165 * (i.e if n > 0)
166 */
167 public void setSumLogImpl(
168 StorelessUnivariateStatistic sumLogImpl) {
169 checkEmpty();
170 this.sumOfLogs = sumLogImpl;
171 }
172
173 /**
174 * Returns the currently configured sum of logs implementation
175 *
176 * @return the StorelessUnivariateStatistic implementing the log sum
177 */
178 public StorelessUnivariateStatistic getSumLogImpl() {
179 return sumOfLogs;
180 }
181
182 /**
183 * Copies source to dest.
184 * <p>Neither source nor dest can be null.</p>
185 *
186 * @param source GeometricMean to copy
187 * @param dest GeometricMean to copy to
188 * @throws NullArgumentException if either source or dest is null
189 */
190 public static void copy(GeometricMean source, GeometricMean dest)
191 throws NullArgumentException {
192 MathUtils.checkNotNull(source);
193 MathUtils.checkNotNull(dest);
194 dest.setData(source.getDataRef());
195 dest.sumOfLogs = source.sumOfLogs.copy();
196 }
197
198
199 /**
200 * Throws IllegalStateException if n > 0.
201 */
202 private void checkEmpty() {
203 if (getN() > 0) {
204 throw new MathIllegalStateException(
205 LocalizedFormats.VALUES_ADDED_BEFORE_CONFIGURING_STATISTIC,
206 getN());
207 }
208 }
209
210 }