time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

meanstdev.js (6370B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2018 The Stdlib Authors.
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *    http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 */
     18 
     19 'use strict';
     20 
     21 // MODULES //
     22 
     23 var isnan = require( '@stdlib/math/base/assert/is-nan' );
     24 var sqrt = require( '@stdlib/math/base/special/sqrt' );
     25 
     26 
     27 // MAIN //
     28 
     29 /**
     30 * Returns an accumulator function which incrementally computes a moving arithmetic mean and corrected sample standard deviation.
     31 *
     32 * ## Method
     33 *
     34 * -   Let \\(W\\) be a window of \\(N\\) elements over which we want to compute a corrected sample standard deviation.
     35 *
     36 * -   We first recognize that the corrected sample standard deviation is defined as the square root of the unbiased sample variance. Accordingly, in order to derive an update equation for the corrected sample standard deviation, deriving an update equation for the unbiased sample variance is sufficient.
     37 *
     38 * -   The difference between the unbiased sample variance in a window \\(W_i\\) and the unbiased sample variance in a window \\(W_{i+1})\\) is given by
     39 *
     40 *     ```tex
     41 *     \Delta s^2 = s_{i+1}^2 - s_{i}^2
     42 *     ```
     43 *
     44 * -   If we multiply both sides by \\(N-1\\),
     45 *
     46 *     ```tex
     47 *     (N-1)(\Delta s^2) = (N-1)s_{i+1}^2 - (N-1)s_{i}^2
     48 *     ```
     49 *
     50 * -   If we substitute the definition of the unbiased sample variance having the form
     51 *
     52 *     ```tex
     53 *     \begin{align*}
     54 *     s^2 &= \frac{1}{N-1} \biggl( \sum_{i=1}^{N} (x_i - \bar{x})^2 \biggr) \\
     55 *         &= \frac{1}{N-1} \biggl( \sum_{i=1}^{N} (x_i^2 - 2\bar{x}x_i + \bar{x}^2) \biggr) \\
     56 *         &= \frac{1}{N-1} \biggl( \sum_{i=1}^{N} x_i^2 - 2\bar{x} \sum_{i=1}^{N} x_i + \sum_{i=1}^{N} \bar{x}^2) \biggr) \\
     57 *         &= \frac{1}{N-1} \biggl( \sum_{i=1}^{N} x_i^2 - \frac{2N\bar{x}\sum_{i=1}^{N} x_i}{N} + N\bar{x}^2 \biggr) \\
     58 *         &= \frac{1}{N-1} \biggl( \sum_{i=1}^{N} x_i^2 - 2N\bar{x}^2 + N\bar{x}^2 \biggr) \\
     59 *         &= \frac{1}{N-1} \biggl( \sum_{i=1}^{N} x_i^2 - N\bar{x}^2 \biggr)
     60 *     \end{align*}
     61 *     ```
     62 *
     63 *     we return
     64 *
     65 *     ```tex
     66 *     (N-1)(\Delta s^2) = \biggl(\sum_{k=1}^N x_k^2 - N\bar{x}_{i+1}^2 \biggr) - \biggl(\sum_{k=0}^{N-1} x_k^2 - N\bar{x}_{i}^2 \biggr)
     67 *     ```
     68 *
     69 * -   This can be further simplified by recognizing that subtracting the sums reduces to \\(x_N^2 - x_0^2\\); in which case,
     70 *
     71 *     ```tex
     72 *     \begin{align*}
     73 *     (N-1)(\Delta s^2) &= x_N^2 - x_0^2 - N\bar{x}_{i+1}^2 + N\bar{x}_{i}^2 \\
     74 *     &= x_N^2 - x_0^2 - N(\bar{x}_{i+1}^2 - \bar{x}_{i}^2) \\
     75 *     &= x_N^2 - x_0^2 - N(\bar{x}_{i+1} - \bar{x}_{i})(\bar{x}_{i+1} + \bar{x}_{i})
     76 *     \end{align*}
     77 *     ```
     78 *
     79 * -   Recognizing that the difference of means can be expressed
     80 *
     81 *     ```tex
     82 *     \bar{x}_{i+1} - \bar{x}_i = \frac{1}{N} \biggl( \sum_{k=1}^N x_k - \sum_{k=0}^{N-1} x_k \biggr) = \frac{x_N - x_0}{N}
     83 *     ```
     84 *
     85 *     and substituting into the equation above
     86 *
     87 *     ```tex
     88 *     (N-1)(\Delta s^2) = x_N^2 - x_0^2 - (x_N - x_0)(\bar{x}_{i+1} + \bar{x}_{i})
     89 *     ```
     90 *
     91 * -   Rearranging terms gives us the update equation
     92 *
     93 *     ```tex
     94 *     \begin{align*}
     95 *     (N-1)(\Delta s^2) &= (x_N - x_0)(x_N + x_0) - (x_N - x_0)(\bar{x}_{i+1} + \bar{x}_{i})
     96 *     &= (x_N - x_0)(x_N + x_0 - \bar{x}_{i+1} - \bar{x}_{i}) \\
     97 *     &= (x_N - x_0)(x_N - \bar{x}_{i+1} + x_0 - \bar{x}_{i})
     98 *     \end{align*}
     99 *     ```
    100 *
    101 * @private
    102 * @param {Collection} out - output array
    103 * @param {PositiveInteger} W - window size
    104 * @param {Collection} buf - data buffer
    105 * @returns {Function} accumulator function
    106 *
    107 * @example
    108 * var buf = [ 0.0, 0.0, 0.0 ];
    109 * var accumulator = incrmmeanstdev( [ 0.0, 0.0 ], 3, buf );
    110 *
    111 * var v = accumulator( 2.0, 0 );
    112 * // returns [ 2.0, 0.0 ]
    113 *
    114 * buf[ 0 ] = 2.0;
    115 *
    116 * v = accumulator( -5.0, 1 );
    117 * // returns [ -1.5, ~4.95 ]
    118 *
    119 * buf[ 1 ] = -5.0;
    120 *
    121 * v = accumulator( 3.0, 2 );
    122 * // returns [ 0.0, ~4.36 ]
    123 *
    124 * buf[ 2 ] = 3.0;
    125 *
    126 * v = accumulator( 5.0, 0 );
    127 * // returns [ 1.0, ~5.29 ]
    128 *
    129 * buf[ 0 ] = 5.0;
    130 */
    131 function incrmmeanstdev( out, W, buf ) {
    132 	var delta;
    133 	var tmp;
    134 	var M2;
    135 	var mu;
    136 	var d1;
    137 	var d2;
    138 	var N;
    139 	var n;
    140 
    141 	n = W - 1;
    142 	M2 = 0.0;
    143 	mu = 0.0;
    144 	N = 0;
    145 
    146 	return accumulator;
    147 
    148 	/**
    149 	* Updates accumulator state.
    150 	*
    151 	* @private
    152 	* @param {number} x - input value
    153 	* @param {NonNegativeInteger} i - buffer index
    154 	* @returns {ArrayLike} output array
    155 	*/
    156 	function accumulator( x, i ) {
    157 		var k;
    158 		var v;
    159 
    160 		// Case: incoming value is NaN, the sliding second moment is automatically NaN...
    161 		if ( isnan( x ) ) {
    162 			N = W; // explicitly set to avoid `N < W` branch
    163 			mu = NaN;
    164 			M2 = NaN;
    165 		}
    166 		// Case: initial window...
    167 		else if ( N < W ) {
    168 			N += 1;
    169 			delta = x - mu;
    170 			mu += delta / N;
    171 			M2 += delta * (x - mu);
    172 
    173 			out[ 0 ] = mu;
    174 			if ( N === 1 ) {
    175 				out[ 1 ] = 0.0;
    176 			} else {
    177 				out[ 1 ] = sqrt( M2/(N-1) );
    178 			}
    179 			return out;
    180 		}
    181 		// Case: N = W = 1
    182 		else if ( N === 1 ) {
    183 			mu = x;
    184 			M2 = 0.0;
    185 			out[ 0 ] = x;
    186 			out[ 1 ] = 0.0;
    187 			return out;
    188 		}
    189 		// Case: outgoing value is NaN, and, thus, we need to compute the accumulated values...
    190 		else if ( isnan( buf[ i ] ) ) {
    191 			N = 1;
    192 			mu = x;
    193 			M2 = 0.0;
    194 			for ( k = 0; k < W; k++ ) {
    195 				if ( k !== i ) {
    196 					v = buf[ k ];
    197 					if ( isnan( v ) ) {
    198 						N = W; // explicitly set to avoid `N < W` branch
    199 						mu = NaN;
    200 						M2 = NaN;
    201 						break; // second moment is automatically NaN, so no need to continue
    202 					}
    203 					N += 1;
    204 					delta = v - mu;
    205 					mu += delta / N;
    206 					M2 += delta * (v - mu);
    207 				}
    208 			}
    209 		}
    210 		// Case: neither the current second moment nor the incoming value are NaN, so we need to update the accumulated values...
    211 		else if ( isnan( M2 ) === false ) {
    212 			tmp = buf[ i ];
    213 			delta = x - tmp;
    214 			d1 = tmp - mu;
    215 			mu += delta / W;
    216 			d2 = x - mu;
    217 			M2 += delta * (d1 + d2);
    218 		}
    219 		// Case: the current second moment is NaN, so nothing to do until the buffer no longer contains NaN values...
    220 
    221 		out[ 0 ] = mu;
    222 		out[ 1 ] = sqrt( M2/n );
    223 		return out;
    224 	}
    225 }
    226 
    227 
    228 // EXPORTS //
    229 
    230 module.exports = incrmmeanstdev;