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 <= 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 > 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 }