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 */ 017package org.apache.commons.statistics.descriptive; 018 019/** 020 * Computes the geometric mean of the available values. Uses the following definition 021 * of the geometric mean: 022 * 023 * <p>\[ \left(\prod_{i=1}^n x_i\right)^\frac{1}{n} \] 024 * 025 * <p>where \( n \) is the number of samples. This implementation uses the log scale: 026 * 027 * <p>\[ \exp{\left( {\frac{1}{n}\sum_{i=1}^n \ln x_i} \right)} \] 028 * 029 * <ul> 030 * <li>The result is {@code NaN} if no values are added. 031 * <li>The result is {@code NaN} if any of the values is {@code NaN}. 032 * <li>The result is {@code NaN} if any of the values is negative. 033 * <li>The result is {@code +infinity} if all values are in the range {@code (0, +infinity]} 034 * and at least one value is {@code +infinity}. 035 * <li>The result is {@code 0} if all values are in the range {@code [0, +infinity)} 036 * and at least one value is zero. 037 * <li>The result is {@code NaN} if all values are in the range {@code [0, +infinity]} 038 * and at least one value is zero, and one value is {@code +infinity}. 039 * </ul> 040 * 041 * <p>Supports up to 2<sup>63</sup> (exclusive) observations. 042 * This implementation does not check for overflow of the count. 043 * 044 * <p>This class is designed to work with (though does not require) 045 * {@linkplain java.util.stream streams}. 046 * 047 * <p><strong>This instance is not thread safe.</strong> 048 * If multiple threads access an instance of this class concurrently, 049 * and at least one of the threads invokes the {@link java.util.function.DoubleConsumer#accept(double) accept} or 050 * {@link StatisticAccumulator#combine(StatisticResult) combine} method, it must be synchronized externally. 051 * 052 * <p>However, it is safe to use {@link java.util.function.DoubleConsumer#accept(double) accept} 053 * and {@link StatisticAccumulator#combine(StatisticResult) combine} 054 * as {@code accumulator} and {@code combiner} functions of 055 * {@link java.util.stream.Collector Collector} on a parallel stream, 056 * because the parallel instance of {@link java.util.stream.Stream#collect Stream.collect()} 057 * provides the necessary partitioning, isolation, and merging of results for 058 * safe and efficient parallel execution. 059 * 060 * @see <a href="https://en.wikipedia.org/wiki/Geometric_mean">Geometric mean (Wikipedia)</a> 061 * @see SumOfLogs 062 * @since 1.1 063 */ 064public final class GeometricMean implements DoubleStatistic, StatisticAccumulator<GeometricMean> { 065 /** Count of values that have been added. */ 066 private long n; 067 068 /** 069 * Sum of logs used to compute the geometric mean. 070 */ 071 private final SumOfLogs sumOfLogs; 072 073 /** 074 * Create an instance. 075 */ 076 private GeometricMean() { 077 this(SumOfLogs.create(), 0); 078 } 079 080 /** 081 * Create an instance. 082 * 083 * @param sumOfLogs Sum of logs. 084 * @param n Count of values. 085 */ 086 private GeometricMean(SumOfLogs sumOfLogs, long n) { 087 this.sumOfLogs = sumOfLogs; 088 this.n = n; 089 } 090 091 /** 092 * Creates an instance. 093 * 094 * <p>The initial result is {@code NaN}. 095 * 096 * @return {@code GeometricMean} instance. 097 */ 098 public static GeometricMean create() { 099 return new GeometricMean(); 100 } 101 102 /** 103 * Returns an instance populated using the input {@code values}. 104 * 105 * <p>When the input is an empty array, the result is {@code NaN}. 106 * 107 * @param values Values. 108 * @return {@code GeometricMean} instance. 109 */ 110 public static GeometricMean of(double... values) { 111 return new GeometricMean(SumOfLogs.of(values), values.length); 112 } 113 114 /** 115 * Returns an instance populated using the specified range of {@code values}. 116 * 117 * <p>When the range is empty, the result is {@code NaN}. 118 * 119 * @param values Values. 120 * @param from Inclusive start of the range. 121 * @param to Exclusive end of the range. 122 * @return {@code GeometricMean} instance. 123 * @throws IndexOutOfBoundsException if the sub-range is out of bounds 124 * @since 1.2 125 */ 126 public static GeometricMean ofRange(double[] values, int from, int to) { 127 // Range checks performed by the sum-of-logs 128 return new GeometricMean(SumOfLogs.ofRange(values, from, to), to - from); 129 } 130 131 /** 132 * Returns an instance populated using the input {@code values}. 133 * 134 * <p>When the input is an empty array, the result is {@code NaN}. 135 * 136 * @param values Values. 137 * @return {@code GeometricMean} instance. 138 */ 139 public static GeometricMean of(int... values) { 140 return new GeometricMean(SumOfLogs.of(values), values.length); 141 } 142 143 /** 144 * Returns an instance populated using the specified range of {@code values}. 145 * 146 * <p>When the range is empty, the result is {@code NaN}. 147 * 148 * @param values Values. 149 * @param from Inclusive start of the range. 150 * @param to Exclusive end of the range. 151 * @return {@code GeometricMean} instance. 152 * @throws IndexOutOfBoundsException if the sub-range is out of bounds 153 * @since 1.2 154 */ 155 public static GeometricMean ofRange(int[] values, int from, int to) { 156 // Range checks performed by the sum-of-logs 157 return new GeometricMean(SumOfLogs.ofRange(values, from, to), to - from); 158 } 159 160 /** 161 * Returns an instance populated using the input {@code values}. 162 * 163 * <p>When the input is an empty array, the result is {@code NaN}. 164 * 165 * @param values Values. 166 * @return {@code GeometricMean} instance. 167 */ 168 public static GeometricMean of(long... values) { 169 return new GeometricMean(SumOfLogs.of(values), values.length); 170 } 171 172 /** 173 * Returns an instance populated using the specified range of {@code values}. 174 * 175 * <p>When the range is empty, the result is {@code NaN}. 176 * 177 * @param values Values. 178 * @param from Inclusive start of the range. 179 * @param to Exclusive end of the range. 180 * @return {@code GeometricMean} instance. 181 * @throws IndexOutOfBoundsException if the sub-range is out of bounds 182 * @since 1.2 183 */ 184 public static GeometricMean ofRange(long[] values, int from, int to) { 185 // Range checks performed by the sum-of-logs 186 return new GeometricMean(SumOfLogs.ofRange(values, from, to), to - from); 187 } 188 189 /** 190 * Updates the state of the statistic to reflect the addition of {@code value}. 191 * 192 * @param value Value. 193 */ 194 @Override 195 public void accept(double value) { 196 n++; 197 sumOfLogs.accept(value); 198 } 199 200 /** 201 * Gets the geometric mean of all input values. 202 * 203 * <p>When no values have been added, the result is {@code NaN}. 204 * 205 * @return geometric mean of all values. 206 */ 207 @Override 208 public double getAsDouble() { 209 return computeGeometricMean(n, sumOfLogs); 210 } 211 212 @Override 213 public GeometricMean combine(GeometricMean other) { 214 n += other.n; 215 sumOfLogs.combine(other.sumOfLogs); 216 return this; 217 } 218 219 /** 220 * Compute the geometric mean. 221 * 222 * @param n Count of values. 223 * @param sumOfLogs Sum of logs. 224 * @return the geometric mean 225 */ 226 static double computeGeometricMean(long n, SumOfLogs sumOfLogs) { 227 return n == 0 ? 228 Double.NaN : 229 Math.exp(sumOfLogs.getAsDouble() / n); 230 } 231}