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