    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     */
    017    package org.apache.commons.math.stat.descriptive.moment;
    018
    019    import java.io.Serializable;
    020
    021    import org.apache.commons.math.stat.descriptive.AbstractStorelessUnivariateStatistic;
    022    import org.apache.commons.math.stat.descriptive.WeightedEvaluation;
    023    import org.apache.commons.math.stat.descriptive.summary.Sum;
    024
    025    /**
    026     * <p>Computes the arithmetic mean of a set of values. Uses the definitional
    027     * formula:</p>
    028     * <p>
    029     * mean = sum(x_i) / n
    030     * </p>
    031     * <p>where <code>n</code> is the number of observations.
    032     * </p>
    033     * <p>When {@link #increment(double)} is used to add data incrementally from a
    034     * stream of (unstored) values, the value of the statistic that
    035     * {@link #getResult()} returns is computed using the following recursive
    036     * updating algorithm: </p>
    037     * <ol>
    038     * <li>Initialize <code>m = </code> the first value</li>
    039     * <li>For each additional value, update using <br>
    040     *   <code>m = m + (new value - m) / (number of observations)</code></li>
    041     * </ol>
    042     * <p> If {@link #evaluate(double[])} is used to compute the mean of an array
    043     * of stored values, a two-pass, corrected algorithm is used, starting with
    044     * the definitional formula computed using the array of stored values and then
    045     * correcting this by adding the mean deviation of the data values from the
    046     * arithmetic mean. See, e.g. "Comparison of Several Algorithms for Computing
    047     * Sample Means and Variances," Robert F. Ling, Journal of the American
    048     * Statistical Association, Vol. 69, No. 348 (Dec., 1974), pp. 859-866. </p>
    049     * <p>
    050     *  Returns <code>Double.NaN</code> if the dataset is empty.
    051     * </p>
    052     * <strong>Note that this implementation is not synchronized.</strong> If
    053     * multiple threads access an instance of this class concurrently, and at least
    054     * one of the threads invokes the <code>increment()</code> or
    055     * <code>clear()</code> method, it must be synchronized externally.
    056     *
    057     * @version $Revision: 1006299 $ $Date: 2010-10-10 16:47:17 +0200 (dim. 10 oct. 2010) $
    058     */
    059    public class Mean extends AbstractStorelessUnivariateStatistic
    060        implements Serializable, WeightedEvaluation {
    061
    062        /** Serializable version identifier */
    063        private static final long serialVersionUID = -1296043746617791564L;
    064
    065        /** First moment on which this statistic is based. */
    066        protected FirstMoment moment;
    067
    068        /**
    069         * Determines whether or not this statistic can be incremented or cleared.
    070         * <p>
    071         * Statistics based on (constructed from) external moments cannot
    072         * be incremented or cleared.</p>
    073         */
    074        protected boolean incMoment;
    075
    076        /** Constructs a Mean. */
    077        public Mean() {
    078            incMoment = true;
    079            moment = new FirstMoment();
    080        }
    081
    082        /**
    083         * Constructs a Mean with an External Moment.
    084         *
    085         * @param m1 the moment
    086         */
    087        public Mean(final FirstMoment m1) {
    088            this.moment = m1;
    089            incMoment = false;
    090        }
    091
    092        /**
    093         * Copy constructor, creates a new {@code Mean} identical
    094         * to the {@code original}
    095         *
    096         * @param original the {@code Mean} instance to copy
    097         */
    098        public Mean(Mean original) {
    099            copy(original, this);
    100        }
    101
    102        /**
    103         * {@inheritDoc}
    104         */
    105        @Override
    106        public void increment(final double d) {
    107            if (incMoment) {
    108                moment.increment(d);
    109            }
    110        }
    111
    112        /**
    113         * {@inheritDoc}
    114         */
    115        @Override
    116        public void clear() {
    117            if (incMoment) {
    118                moment.clear();
    119            }
    120        }
    121
    122        /**
    123         * {@inheritDoc}
    124         */
    125        @Override
    126        public double getResult() {
    127            return moment.m1;
    128        }
    129
    130        /**
    131         * {@inheritDoc}
    132         */
    133        public long getN() {
    134            return moment.getN();
    135        }
    136
    137        /**
    138         * Returns the arithmetic mean of the entries in the specified portion of
    139         * the input array, or <code>Double.NaN</code> if the designated subarray
    140         * is empty.
    141         * <p>
    142         * Throws <code>IllegalArgumentException</code> if the array is null.</p>
    143         * <p>
    144         * See {@link Mean} for details on the computing algorithm.</p>
    145         *
    146         * @param values the input array
    147         * @param begin index of the first array element to include
    148         * @param length the number of elements to include
    149         * @return the mean of the values or Double.NaN if length = 0
    150         * @throws IllegalArgumentException if the array is null or the array index
    151         *  parameters are not valid
    152         */
    153        @Override
    154        public double evaluate(final double[] values,final int begin, final int length) {
    155            if (test(values, begin, length)) {
    156                Sum sum = new Sum();
    157                double sampleSize = length;
    158
    159                // Compute initial estimate using definitional formula
    160                double xbar = sum.evaluate(values, begin, length) / sampleSize;
    161
    162                // Compute correction factor in second pass
    163                double correction = 0;
    164                for (int i = begin; i < begin + length; i++) {
    165                    correction += values[i] - xbar;
    166                }
    167                return xbar + (correction/sampleSize);
    168            }
    169            return Double.NaN;
    170        }
    171
    172        /**
    173         * Returns the weighted arithmetic mean of the entries in the specified portion of
    174         * the input array, or <code>Double.NaN</code> if the designated subarray
    175         * is empty.
    176         * <p>
    177         * Throws <code>IllegalArgumentException</code> if either array is null.</p>
    178         * <p>
    179         * See {@link Mean} for details on the computing algorithm. The two-pass algorithm
    180         * described above is used here, with weights applied in computing both the original
    181         * estimate and the correction factor.</p>
    182         * <p>
    183         * Throws <code>IllegalArgumentException</code> if any of the following are true:
    184         * <ul><li>the values array is null</li>
    185         *     <li>the weights array is null</li>
    186         *     <li>the weights array does not have the same length as the values array</li>
    187         *     <li>the weights array contains one or more infinite values</li>
    188         *     <li>the weights array contains one or more NaN values</li>
    189         *     <li>the weights array contains negative values</li>
    190         *     <li>the start and length arguments do not determine a valid array</li>
    191         * </ul></p>
    192         *
    193         * @param values the input array
    194         * @param weights the weights array
    195         * @param begin index of the first array element to include
    196         * @param length the number of elements to include
    197         * @return the mean of the values or Double.NaN if length = 0
    198         * @throws IllegalArgumentException if the parameters are not valid
    199         * @since 2.1
    200         */
    201        public double evaluate(final double[] values, final double[] weights,
    202                               final int begin, final int length) {
    203            if (test(values, weights, begin, length)) {
    204                Sum sum = new Sum();
    205
    206                // Compute initial estimate using definitional formula
    207                double sumw = sum.evaluate(weights,begin,length);
    208                double xbarw = sum.evaluate(values, weights, begin, length) / sumw;
    209
    210                // Compute correction factor in second pass
    211                double correction = 0;
    212                for (int i = begin; i < begin + length; i++) {
    213                    correction += weights[i] * (values[i] - xbarw);
    214                }
    215                return xbarw + (correction/sumw);
    216            }
    217            return Double.NaN;
    218        }
    219
    220        /**
    221         * Returns the weighted arithmetic mean of the entries in the input array.
    222         * <p>
    223         * Throws <code>IllegalArgumentException</code> if either array is null.</p>
    224         * <p>
    225         * See {@link Mean} for details on the computing algorithm. The two-pass algorithm
    226         * described above is used here, with weights applied in computing both the original
    227         * estimate and the correction factor.</p>
    228         * <p>
    229         * Throws <code>IllegalArgumentException</code> if any of the following are true:
    230         * <ul><li>the values array is null</li>
    231         *     <li>the weights array is null</li>
    232         *     <li>the weights array does not have the same length as the values array</li>
    233         *     <li>the weights array contains one or more infinite values</li>
    234         *     <li>the weights array contains one or more NaN values</li>
    235         *     <li>the weights array contains negative values</li>
    236         * </ul></p>
    237         *
    238         * @param values the input array
    239         * @param weights the weights array
    240         * @return the mean of the values or Double.NaN if length = 0
    241         * @throws IllegalArgumentException if the parameters are not valid
    242         * @since 2.1
    243         */
    244        public double evaluate(final double[] values, final double[] weights) {
    245            return evaluate(values, weights, 0, values.length);
    246        }
    247
    248        /**
    249         * {@inheritDoc}
    250         */
    251        @Override
    252        public Mean copy() {
    253            Mean result = new Mean();
    254            copy(this, result);
    255            return result;
    256        }
    257
    258
    259        /**
    260         * Copies source to dest.
    261         * <p>Neither source nor dest can be null.</p>
    262         *
    263         * @param source Mean to copy
    264         * @param dest Mean to copy to
    265         * @throws NullPointerException if either source or dest is null
    266         */
    267        public static void copy(Mean source, Mean dest) {
    268            dest.setData(source.getDataRef());
    269            dest.incMoment = source.incMoment;
    270            dest.moment = source.moment.copy();
    271        }
    272    }