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.statistics.descriptive; 18 19 import java.math.BigInteger; 20 21 /** 22 * Computes the variance of the available values. The default implementation uses the 23 * following definition of the <em>sample variance</em>: 24 * 25 * <p>\[ \tfrac{1}{n-1} \sum_{i=1}^n (x_i-\overline{x})^2 \] 26 * 27 * <p>where \( \overline{x} \) is the sample mean, and \( n \) is the number of samples. 28 * 29 * <ul> 30 * <li>The result is {@code NaN} if no values are added. 31 * <li>The result is zero if there is one value in the data set. 32 * </ul> 33 * 34 * <p>The use of the term \( n − 1 \) is called Bessel's correction. This is an unbiased 35 * estimator of the variance of a hypothetical infinite population. If the 36 * {@link #setBiased(boolean) biased} option is enabled the normalisation factor is 37 * changed to \( \frac{1}{n} \) for a biased estimator of the <em>sample variance</em>. 38 * 39 * <p>The implementation uses an exact integer sum to compute the scaled (by \( n \)) 40 * sum of squared deviations from the mean; this is normalised by the scaled correction factor. 41 * 42 * <p>\[ \frac {n \times \sum_{i=1}^n x_i^2 - (\sum_{i=1}^n x_i)^2}{n \times (n - 1)} \] 43 * 44 * <p>Supports up to 2<sup>63</sup> (exclusive) observations. 45 * This implementation does not check for overflow of the count. 46 * 47 * <p>This class is designed to work with (though does not require) 48 * {@linkplain java.util.stream streams}. 49 * 50 * <p><strong>This implementation is not thread safe.</strong> 51 * If multiple threads access an instance of this class concurrently, 52 * and at least one of the threads invokes the {@link java.util.function.IntConsumer#accept(int) accept} or 53 * {@link StatisticAccumulator#combine(StatisticResult) combine} method, it must be synchronized externally. 54 * 55 * <p>However, it is safe to use {@link java.util.function.IntConsumer#accept(int) accept} 56 * and {@link StatisticAccumulator#combine(StatisticResult) combine} 57 * as {@code accumulator} and {@code combiner} functions of 58 * {@link java.util.stream.Collector Collector} on a parallel stream, 59 * because the parallel implementation of {@link java.util.stream.Stream#collect Stream.collect()} 60 * provides the necessary partitioning, isolation, and merging of results for 61 * safe and efficient parallel execution. 62 * 63 * @see <a href="https://en.wikipedia.org/wiki/variance">variance (Wikipedia)</a> 64 * @see <a href="https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance"> 65 * Algorithms for computing the variance (Wikipedia)</a> 66 * @see <a href="https://en.wikipedia.org/wiki/Bessel%27s_correction">Bessel's correction</a> 67 * @since 1.1 68 */ 69 public final class IntVariance implements IntStatistic, StatisticAccumulator<IntVariance> { 70 /** Small array sample size. 71 * Used to avoid computing with UInt96 then converting to UInt128. */ 72 static final int SMALL_SAMPLE = 10; 73 74 /** Sum of the squared values. */ 75 private final UInt128 sumSq; 76 /** Sum of the values. */ 77 private final Int128 sum; 78 /** Count of values that have been added. */ 79 private long n; 80 81 /** Flag to control if the statistic is biased, or should use a bias correction. */ 82 private boolean biased; 83 84 /** 85 * Create an instance. 86 */ 87 private IntVariance() { 88 this(UInt128.create(), Int128.create(), 0); 89 } 90 91 /** 92 * Create an instance. 93 * 94 * @param sumSq Sum of the squared values. 95 * @param sum Sum of the values. 96 * @param n Count of values that have been added. 97 */ 98 private IntVariance(UInt128 sumSq, Int128 sum, int n) { 99 this.sumSq = sumSq; 100 this.sum = sum; 101 this.n = n; 102 } 103 104 /** 105 * Creates an instance. 106 * 107 * <p>The initial result is {@code NaN}. 108 * 109 * @return {@code IntVariance} instance. 110 */ 111 public static IntVariance create() { 112 return new IntVariance(); 113 } 114 115 /** 116 * Returns an instance populated using the input {@code values}. 117 * 118 * @param values Values. 119 * @return {@code IntVariance} instance. 120 */ 121 public static IntVariance of(int... values) { 122 // Small arrays can be processed using the object 123 if (values.length < SMALL_SAMPLE) { 124 final IntVariance stat = new IntVariance(); 125 for (final int x : values) { 126 stat.accept(x); 127 } 128 return stat; 129 } 130 131 // Arrays can be processed using specialised counts knowing the maximum limit 132 // for an array is 2^31 values. 133 long s = 0; 134 final UInt96 ss = UInt96.create(); 135 // Process pairs as we know two maximum value int^2 will not overflow 136 // an unsigned long. 137 final int end = values.length & ~0x1; 138 for (int i = 0; i < end; i += 2) { 139 final long x = values[i]; 140 final long y = values[i + 1]; 141 s += x + y; 142 ss.addPositive(x * x + y * y); 143 } 144 if (end < values.length) { 145 final long x = values[end]; 146 s += x; 147 ss.addPositive(x * x); 148 } 149 150 // Convert 151 return new IntVariance(UInt128.of(ss), Int128.of(s), values.length); 152 } 153 154 /** 155 * Updates the state of the statistic to reflect the addition of {@code value}. 156 * 157 * @param value Value. 158 */ 159 @Override 160 public void accept(int value) { 161 sumSq.addPositive((long) value * value); 162 sum.add(value); 163 n++; 164 } 165 166 /** 167 * Gets the variance of all input values. 168 * 169 * <p>When no values have been added, the result is {@code NaN}. 170 * 171 * @return variance of all values. 172 */ 173 @Override 174 public double getAsDouble() { 175 return computeVarianceOrStd(sumSq, sum, n, biased, false); 176 } 177 178 /** 179 * Compute the variance (or standard deviation). 180 * 181 * <p>The {@code std} flag controls if the result is returned as the standard deviation 182 * using the {@link Math#sqrt(double) square root} function. 183 * 184 * @param sumSq Sum of the squared values. 185 * @param sum Sum of the values. 186 * @param n Count of values that have been added. 187 * @param biased Flag to control if the statistic is biased, or should use a bias correction. 188 * @param std Flag to control if the statistic is the standard deviation. 189 * @return the variance (or standard deviation) 190 */ 191 static double computeVarianceOrStd(UInt128 sumSq, Int128 sum, long n, boolean biased, boolean std) { 192 if (n == 0) { 193 return Double.NaN; 194 } 195 // Avoid a divide by zero 196 if (n == 1) { 197 return 0; 198 } 199 // Sum-of-squared deviations: sum(x^2) - sum(x)^2 / n 200 // Sum-of-squared deviations precursor: n * sum(x^2) - sum(x)^2 201 // The precursor is computed in integer precision. 202 // The divide uses double precision. 203 // This ensures we avoid cancellation in the difference and use a fast divide. 204 // The result is limited to by the rounding in the double computation. 205 final double diff = computeSSDevN(sumSq, sum, n); 206 final long n0 = biased ? n : n - 1; 207 final double v = diff / IntMath.unsignedMultiplyToDouble(n, n0); 208 if (std) { 209 return Math.sqrt(v); 210 } 211 return v; 212 } 213 214 /** 215 * Compute the sum-of-squared deviations multiplied by the count of values: 216 * {@code n * sum(x^2) - sum(x)^2}. 217 * 218 * @param sumSq Sum of the squared values. 219 * @param sum Sum of the values. 220 * @param n Count of values that have been added. 221 * @return the sum-of-squared deviations precursor 222 */ 223 private static double computeSSDevN(UInt128 sumSq, Int128 sum, long n) { 224 // Compute the term if possible using fast integer arithmetic. 225 // 128-bit sum(x^2) * n will be OK when the upper 32-bits are zero. 226 // 128-bit sum(x)^2 will be OK when the upper 64-bits are zero. 227 // Both are safe when n < 2^32. 228 if ((n >>> Integer.SIZE) == 0) { 229 return sumSq.unsignedMultiply((int) n).subtract(sum.squareLow()).toDouble(); 230 } else { 231 return sumSq.toBigInteger().multiply(BigInteger.valueOf(n)) 232 .subtract(square(sum.toBigInteger())).doubleValue(); 233 } 234 } 235 236 /** 237 * Compute the sum of the squared deviations from the mean. 238 * 239 * <p>This is a helper method used in higher order moments. 240 * 241 * @return the sum of the squared deviations 242 */ 243 double computeSumOfSquaredDeviations() { 244 return computeSSDevN(sumSq, sum, n) / n; 245 } 246 247 /** 248 * Compute the mean. 249 * 250 * <p>This is a helper method used in higher order moments. 251 * 252 * @return the mean 253 */ 254 double computeMean() { 255 return IntMean.computeMean(sum, n); 256 } 257 258 /** 259 * Convenience method to square a BigInteger. 260 * 261 * @param x Value 262 * @return x^2 263 */ 264 private static BigInteger square(BigInteger x) { 265 return x.multiply(x); 266 } 267 268 @Override 269 public IntVariance combine(IntVariance other) { 270 sumSq.add(other.sumSq); 271 sum.add(other.sum); 272 n += other.n; 273 return this; 274 } 275 276 /** 277 * Sets the value of the biased flag. The default value is {@code false}. 278 * 279 * <p>If {@code false} the sum of squared deviations from the sample mean is normalised by 280 * {@code n - 1} where {@code n} is the number of samples. This is Bessel's correction 281 * for an unbiased estimator of the variance of a hypothetical infinite population. 282 * 283 * <p>If {@code true} the sum of squared deviations is normalised by the number of samples 284 * {@code n}. 285 * 286 * <p>Note: This option only applies when {@code n > 1}. The variance of {@code n = 1} is 287 * always 0. 288 * 289 * <p>This flag only controls the final computation of the statistic. The value of this flag 290 * will not affect compatibility between instances during a {@link #combine(IntVariance) combine} 291 * operation. 292 * 293 * @param v Value. 294 * @return {@code this} instance 295 */ 296 public IntVariance setBiased(boolean v) { 297 biased = v; 298 return this; 299 } 300 }