View Javadoc
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.StatUtils;
29  import org.hipparchus.stat.descriptive.AbstractStorelessUnivariateStatistic;
30  import org.hipparchus.stat.descriptive.AggregatableStatistic;
31  import org.hipparchus.stat.descriptive.WeightedEvaluation;
32  import org.hipparchus.stat.descriptive.summary.Sum;
33  import org.hipparchus.util.MathArrays;
34  import org.hipparchus.util.MathUtils;
35  
36  /**
37   * Computes the arithmetic mean of a set of values. Uses the definitional
38   * formula:
39   * <p>
40   * mean = sum(x_i) / n
41   * <p>
42   * where <code>n</code> is the number of observations.
43   * <p>
44   * When {@link #increment(double)} is used to add data incrementally from a
45   * stream of (unstored) values, the value of the statistic that
46   * {@link #getResult()} returns is computed using the following recursive
47   * updating algorithm:
48   * <ol>
49   * <li>Initialize <code>m = </code> the first value</li>
50   * <li>For each additional value, update using <br>
51   *   <code>m = m + (new value - m) / (number of observations)</code></li>
52   * </ol>
53   * <p>
54   * If {@link #evaluate(double[])} is used to compute the mean of an array
55   * of stored values, a two-pass, corrected algorithm is used, starting with
56   * the definitional formula computed using the array of stored values and then
57   * correcting this by adding the mean deviation of the data values from the
58   * arithmetic mean. See, e.g. "Comparison of Several Algorithms for Computing
59   * Sample Means and Variances," Robert F. Ling, Journal of the American
60   * Statistical Association, Vol. 69, No. 348 (Dec., 1974), pp. 859-866.
61   * <p>
62   * Returns <code>Double.NaN</code> if the dataset is empty. Note that
63   * Double.NaN may also be returned if the input includes NaN and / or infinite
64   * values.
65   * <p>
66   * <strong>Note that this implementation is not synchronized.</strong> If
67   * multiple threads access an instance of this class concurrently, and at least
68   * one of the threads invokes the <code>increment()</code> or
69   * <code>clear()</code> method, it must be synchronized externally.
70   */
71  public class Mean extends AbstractStorelessUnivariateStatistic
72      implements AggregatableStatistic<Mean>, WeightedEvaluation, Serializable {
73  
74      /** Serializable version identifier */
75      private static final long serialVersionUID = 20150412L;
76  
77      /** First moment on which this statistic is based. */
78      protected final FirstMoment moment;
79  
80      /**
81       * Determines whether or not this statistic can be incremented or cleared.
82       * <p>
83       * Statistics based on (constructed from) external moments cannot
84       * be incremented or cleared.
85       */
86      protected final boolean incMoment;
87  
88      /** Constructs a Mean. */
89      public Mean() {
90          moment = new FirstMoment();
91          incMoment = true;
92      }
93  
94      /**
95       * Constructs a Mean with an External Moment.
96       *
97       * @param m1 the moment
98       */
99      public Mean(final FirstMoment m1) {
100         this.moment = m1;
101         incMoment = false;
102     }
103 
104     /**
105      * Copy constructor, creates a new {@code Mean} identical
106      * to the {@code original}.
107      *
108      * @param original the {@code Mean} instance to copy
109      * @throws NullArgumentException if original is null
110      */
111     public Mean(Mean original) throws NullArgumentException {
112         MathUtils.checkNotNull(original);
113         this.moment    = original.moment.copy();
114         this.incMoment = original.incMoment;
115     }
116 
117     /**
118      * {@inheritDoc}
119      * <p>
120      * Note that when {@link #Mean(FirstMoment)} is used to
121      * create a Mean, this method does nothing. In that case, the
122      * FirstMoment should be incremented directly.
123      */
124     @Override
125     public void increment(final double d) {
126         if (incMoment) {
127             moment.increment(d);
128         }
129     }
130 
131     /** {@inheritDoc} */
132     @Override
133     public void clear() {
134         if (incMoment) {
135             moment.clear();
136         }
137     }
138 
139     /** {@inheritDoc} */
140     @Override
141     public double getResult() {
142         return moment.m1;
143     }
144 
145     /** {@inheritDoc} */
146     @Override
147     public long getN() {
148         return moment.getN();
149     }
150 
151     /** {@inheritDoc} */
152     @Override
153     public void aggregate(Mean other) {
154         MathUtils.checkNotNull(other);
155         if (incMoment) {
156             this.moment.aggregate(other.moment);
157         }
158     }
159 
160     /**
161      * Returns the arithmetic mean of the entries in the specified portion of
162      * the input array, or <code>Double.NaN</code> if the designated subarray
163      * is empty.
164      *
165      * @param values the input array
166      * @param begin index of the first array element to include
167      * @param length the number of elements to include
168      * @return the mean of the values or Double.NaN if length = 0
169      * @throws MathIllegalArgumentException if the array is null or the array index
170      *  parameters are not valid
171      */
172     @Override
173     public double evaluate(final double[] values, final int begin, final int length)
174         throws MathIllegalArgumentException {
175 
176         if (MathArrays.verifyValues(values, begin, length)) {
177             double sampleSize = length;
178 
179             // Compute initial estimate using definitional formula
180             double xbar = StatUtils.sum(values, begin, length) / sampleSize;
181 
182             // Compute correction factor in second pass
183             double correction = 0;
184             for (int i = begin; i < begin + length; i++) {
185                 correction += values[i] - xbar;
186             }
187             return xbar + (correction / sampleSize);
188         }
189         return Double.NaN;
190     }
191 
192     /**
193      * Returns the weighted arithmetic mean of the entries in the specified portion of
194      * the input array, or <code>Double.NaN</code> if the designated subarray
195      * is empty.
196      * <p>
197      * Throws <code>IllegalArgumentException</code> if either array is null.
198      * <p>
199      * See {@link Mean} for details on the computing algorithm. The two-pass algorithm
200      * described above is used here, with weights applied in computing both the original
201      * estimate and the correction factor.
202      * <p>
203      * Throws <code>IllegalArgumentException</code> if any of the following are true:
204      * <ul><li>the values array is null</li>
205      *     <li>the weights array is null</li>
206      *     <li>the weights array does not have the same length as the values array</li>
207      *     <li>the weights array contains one or more infinite values</li>
208      *     <li>the weights array contains one or more NaN values</li>
209      *     <li>the weights array contains negative values</li>
210      *     <li>the start and length arguments do not determine a valid array</li>
211      * </ul>
212      *
213      * @param values the input array
214      * @param weights the weights array
215      * @param begin index of the first array element to include
216      * @param length the number of elements to include
217      * @return the mean of the values or Double.NaN if length = 0
218      * @throws MathIllegalArgumentException if the parameters are not valid
219      */
220     @Override
221     public double evaluate(final double[] values, final double[] weights,
222                            final int begin, final int length)
223         throws MathIllegalArgumentException {
224 
225         if (MathArrays.verifyValues(values, weights, begin, length)) {
226             Sum sum = new Sum();
227 
228             // Compute initial estimate using definitional formula
229             double sumw = sum.evaluate(weights,begin,length);
230             double xbarw = sum.evaluate(values, weights, begin, length) / sumw;
231 
232             // Compute correction factor in second pass
233             double correction = 0;
234             for (int i = begin; i < begin + length; i++) {
235                 correction += weights[i] * (values[i] - xbarw);
236             }
237             return xbarw + (correction/sumw);
238         }
239         return Double.NaN;
240     }
241 
242     /** {@inheritDoc} */
243     @Override
244     public Mean copy() {
245         return new Mean(this);
246     }
247 
248 }