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 }