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 /* 19 * This is not the original file distributed by the Apache Software Foundation 20 * It has been modified by the Hipparchus project 21 */ 22 package org.hipparchus.stat.descriptive.moment; 23 24 import java.io.Serializable; 25 26 import org.hipparchus.exception.MathIllegalArgumentException; 27 import org.hipparchus.exception.NullArgumentException; 28 import org.hipparchus.stat.descriptive.AbstractStorelessUnivariateStatistic; 29 import org.hipparchus.stat.descriptive.AggregatableStatistic; 30 import org.hipparchus.stat.descriptive.summary.SumOfLogs; 31 import org.hipparchus.util.FastMath; 32 import org.hipparchus.util.MathUtils; 33 34 /** 35 * Returns the <a href="http://www.xycoon.com/geometric_mean.htm"> 36 * geometric mean </a> of the available values. 37 * <p> 38 * Uses a {@link SumOfLogs} instance to compute sum of logs and returns 39 * <code> exp( 1/n (sum of logs) ).</code> Therefore, 40 * <ul> 41 * <li>If any of values are < 0, the result is <code>NaN.</code></li> 42 * <li>If all values are non-negative and less than 43 * <code>Double.POSITIVE_INFINITY</code>, but at least one value is 0, the 44 * result is <code>0.</code></li> 45 * <li>If both <code>Double.POSITIVE_INFINITY</code> and 46 * <code>Double.NEGATIVE_INFINITY</code> are among the values, the result is 47 * <code>NaN.</code></li> 48 * </ul> 49 * <p> 50 * <strong>Note that this implementation is not synchronized.</strong> If 51 * multiple threads access an instance of this class concurrently, and at least 52 * one of the threads invokes the <code>increment()</code> or 53 * <code>clear()</code> method, it must be synchronized externally. 54 */ 55 public class GeometricMean extends AbstractStorelessUnivariateStatistic 56 implements AggregatableStatistic<GeometricMean>, Serializable { 57 58 /** Serializable version identifier */ 59 private static final long serialVersionUID = 20150412L; 60 61 /** Wrapped SumOfLogs instance */ 62 private final SumOfLogs sumOfLogs; 63 64 /** 65 * Determines whether or not this statistic can be incremented or cleared. 66 * <p> 67 * Statistics based on (constructed from) external statistics cannot 68 * be incremented or cleared. 69 */ 70 private final boolean incSumOfLogs; 71 72 /** 73 * Create a GeometricMean instance. 74 */ 75 public GeometricMean() { 76 sumOfLogs = new SumOfLogs(); 77 incSumOfLogs = true; 78 } 79 80 /** 81 * Create a GeometricMean instance using the given SumOfLogs instance. 82 * @param sumOfLogs sum of logs instance to use for computation. 83 */ 84 public GeometricMean(SumOfLogs sumOfLogs) { 85 this.sumOfLogs = sumOfLogs; 86 incSumOfLogs = false; 87 } 88 89 /** 90 * Copy constructor, creates a new {@code GeometricMean} identical 91 * to the {@code original}. 92 * 93 * @param original the {@code GeometricMean} instance to copy 94 * @throws NullArgumentException if original is null 95 */ 96 public GeometricMean(GeometricMean original) throws NullArgumentException { 97 MathUtils.checkNotNull(original); 98 this.sumOfLogs = original.sumOfLogs.copy(); 99 this.incSumOfLogs = original.incSumOfLogs; 100 } 101 102 /** {@inheritDoc} */ 103 @Override 104 public GeometricMean copy() { 105 return new GeometricMean(this); 106 } 107 108 /** {@inheritDoc} */ 109 @Override 110 public void increment(final double d) { 111 if (incSumOfLogs) { 112 sumOfLogs.increment(d); 113 } 114 } 115 116 /** {@inheritDoc} */ 117 @Override 118 public double getResult() { 119 if (sumOfLogs.getN() > 0) { 120 return FastMath.exp(sumOfLogs.getResult() / sumOfLogs.getN()); 121 } else { 122 return Double.NaN; 123 } 124 } 125 126 /** {@inheritDoc} */ 127 @Override 128 public void clear() { 129 if (incSumOfLogs) { 130 sumOfLogs.clear(); 131 } 132 } 133 134 /** {@inheritDoc} */ 135 @Override 136 public void aggregate(GeometricMean other) { 137 MathUtils.checkNotNull(other); 138 if (incSumOfLogs) { 139 this.sumOfLogs.aggregate(other.sumOfLogs); 140 } 141 } 142 143 /** 144 * Returns the geometric mean of the entries in the specified portion 145 * of the input array. 146 * <p> 147 * See {@link GeometricMean} for details on the computing algorithm. 148 * 149 * @param values input array containing the values 150 * @param begin first array element to include 151 * @param length the number of elements to include 152 * @return the geometric mean or Double.NaN if length = 0 or 153 * any of the values are <= 0. 154 * @throws MathIllegalArgumentException if the input array is null or the array 155 * index parameters are not valid 156 */ 157 @Override 158 public double evaluate(final double[] values, final int begin, final int length) 159 throws MathIllegalArgumentException { 160 return FastMath.exp(sumOfLogs.evaluate(values, begin, length) / length); 161 } 162 163 /** {@inheritDoc} */ 164 @Override 165 public long getN() { 166 return sumOfLogs.getN(); 167 } 168 169 }