Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 228 additions & 0 deletions lib/node_modules/@stdlib/stats/incr/mkurtosis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
<!--

@license Apache-2.0

Copyright (c) 2026 The Stdlib Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

-->

# mkurtosis

> Compute a moving [corrected sample excess kurtosis][sample-excess-kurtosis] incrementally.

<section class="intro">

The [kurtosis][sample-excess-kurtosis] for a random variable `X` is defined as

<!-- <equation class="equation" label="eq:kurtosis" align="center" raw="\operatorname{Kurtosis}[X] = \mathrm{E}\biggl[ \biggl( \frac{X - \mu}{\sigma} \biggr)^4 \biggr]" alt="Equation for the kurtosis."> -->

```math
\mathop{\mathrm{Kurtosis}}[X] = \mathrm{E}\biggl[ \biggl( \frac{X - \mu}{\sigma} \biggr)^4 \biggr]
```

<!-- </equation> -->

Using a univariate normal distribution as the standard of comparison, the [excess kurtosis][sample-excess-kurtosis] is the kurtosis minus `3`.

For a window of `W` values, the [sample excess kurtosis][sample-excess-kurtosis] is

<!-- <equation class="equation" label="eq:sample_excess_kurtosis" align="center" raw="g_2 = \frac{m_4}{m_2^2} - 3 = \frac{\frac{1}{W} \displaystyle\sum_{i=0}^{W-1} (x_i - \bar{x})^4}{\biggl(\frac{1}{W} \displaystyle\sum_{i=0}^{W-1} (x_i - \bar{x})^2\biggr)^2}" alt="Equation for the sample excess kurtosis."> -->

```math
g_2 = \frac{m_4}{m_2^2} - 3 = \frac{\frac{1}{W} \displaystyle\sum_{i=0}^{W-1} (x_i - \bar{x})^4}{\biggl(\frac{1}{W} \displaystyle\sum_{i=0}^{W-1} (x_i - \bar{x})^2\biggr)^2}
```

<!-- </equation> -->

The corrected (unbiased under normality) sample excess kurtosis is

<!-- <equation class="equation" label="eq:corrected_sample_excess_kurtosis" align="center" raw="G_2 = \frac{(n+1)n}{(n-1)(n-2)(n-3)} \frac{\displaystyle\sum_{i=0}^{n-1} (x_i - \bar{x})^4}{\biggl(\displaystyle\sum_{i=0}^{n-1} (x_i - \bar{x})^2\biggr)^2} - 3 \frac{(n-1)^2}{(n-2)(n-3)}" alt="Equation for the corrected sample excess kurtosis."> -->

```math
G_2 = \frac{(n+1)n}{(n-1)(n-2)(n-3)} \frac{\displaystyle\sum_{i=0}^{n-1} (x_i - \bar{x})^4}{\biggl(\displaystyle\sum_{i=0}^{n-1} (x_i - \bar{x})^2\biggr)^2} - 3 \frac{(n-1)^2}{(n-2)(n-3)}
```

<!-- </equation> -->

where `n` is the number of values currently in the window (grows from 1 to `W` during the fill phase, then stays at `W`).

</section>

<!-- /.intro -->

<section class="usage">

## Usage

```javascript
var mkurtosis = require( '@stdlib/stats/incr/mkurtosis' );
```

#### mkurtosis( W )

Returns an accumulator `function` which incrementally computes a moving [corrected sample excess kurtosis][sample-excess-kurtosis] over a sliding window of size `W`.

```javascript
var accumulator = mkurtosis( 4 );
```

#### accumulator( \[x] )

If provided an input value `x`, the accumulator function returns an updated moving [corrected sample excess kurtosis][sample-excess-kurtosis]. If not provided an input value `x`, the accumulator function returns the current moving [corrected sample excess kurtosis][sample-excess-kurtosis].

```javascript
var accumulator = mkurtosis( 4 );

var kurtosis = accumulator( 2.0 );
// returns null

kurtosis = accumulator( 2.0 );
// returns null

kurtosis = accumulator( -4.0 );
// returns null

kurtosis = accumulator( -4.0 );
// returns -6.0

kurtosis = accumulator( 3.0 );
// returns -5.652200677131425

kurtosis = accumulator();
// returns -5.652200677131425
```

</section>

<!-- /.usage -->

<section class="notes">

## Notes

- The `W` parameter defines the **window size**: only the `W` most recently provided values are used when computing the kurtosis. During the initial fill phase (fewer than `W` values have been provided), the kurtosis is computed over however many values are available.

- The corrected sample excess kurtosis is only defined for **4 or more** values. If fewer than 4 values have been provided, the accumulator returns `null`.

- **Algorithm** — The implementation avoids a full O(W) recomputation at each step by maintaining running central moment accumulators `M2`, `M3`, and `M4` (à la Welford). When the window is full, adding a new value `x` and discarding the oldest value `xo` is handled in two O(1) operations:

1. **Downdate** — algebraically reverse the Welford update that originally incorporated `xo`, recovering the moments as though `xo` were never in the window.
2. **Update** — apply the standard Welford update for `x`.

The downdate formulas, derived by inverting the Welford recurrence, are:

```text
mean_new = (W·mean - xo) / (W - 1)
delta = xo - mean_new
deltaN = delta / W
term1 = delta · deltaN · (W - 1)
M2_new = M2 - term1
M3_new = M3 - term1·deltaN·(W-2) + 3·deltaN·M2_new
M4_new = M4 - term1·deltaN²·(W²-3W+3) - 6·deltaN²·M2_new + 4·deltaN·M3_new
```

- **NaN handling** — Input values are **not** type-checked. If provided `NaN` or a value which, when used in computations, results in `NaN`, the accumulated value is `NaN` for, at most, `W` future invocations. Once the `NaN` value leaves the sliding window, the moments are **recomputed from scratch** from the circular buffer and the accumulator recovers. This differs from `@stdlib/stats/incr/kurtosis`, where a `NaN` permanently corrupts all future results.

</section>

<!-- /.notes -->

<section class="examples">

## Examples

<!-- eslint no-undef: "error" -->

```javascript
var randu = require( '@stdlib/random/base/randu' );
var mkurtosis = require( '@stdlib/stats/incr/mkurtosis' );

var accumulator;
var v;
var i;

// Initialize a moving kurtosis accumulator with a window size of 20:
accumulator = mkurtosis( 20 );

// For each simulated datum, update the moving corrected sample excess kurtosis:
for ( i = 0; i < 100; i++ ) {
v = randu() * 100.0;
accumulator( v );
}
console.log( accumulator() );
```

</section>

<!-- /.examples -->

* * *

<section class="references">

## References

- Joanes, D. N., and C. A. Gill. 1998. "Comparing measures of sample skewness and kurtosis." _Journal of the Royal Statistical Society: Series D (The Statistician)_ 47 (1). Blackwell Publishers Ltd: 183–89. doi:[10.1111/1467-9884.00122][@joanes:1998].
- Pébay, Philippe. 2008. "Formulas for Robust, One-Pass Parallel Computation of Covariances and Arbitrary-Order Statistical Moments." _Technical Report_ SAND2008-6212. Sandia National Laboratories. <https://prod-ng.sandia.gov/techlib-noauth/access-control.cgi/2008/086212.pdf>.
- Welford, B. P. 1962. "Note on a Method for Calculating Corrected Sums of Squares and Products." _Technometrics_ 4 (3): 419–20. doi:[10.1080/00401706.1962.10490022][@welford:1962].

[@joanes:1998]: http://dx.doi.org/10.1111/1467-9884.00122
[@welford:1962]: https://doi.org/10.1080/00401706.1962.10490022

</section>

<!-- /.references -->

<!-- Section for related `stdlib` packages. Do not manually edit this section, as it is automatically populated. -->

<section class="related">

* * *

## See Also

- <span class="package-name">[`@stdlib/stats/incr/kurtosis`][@stdlib/stats/incr/kurtosis]</span><span class="delimiter">: </span><span class="description">compute a corrected sample excess kurtosis incrementally (all data, no window).</span>
- <span class="package-name">[`@stdlib/stats/incr/mskewness`][@stdlib/stats/incr/mskewness]</span><span class="delimiter">: </span><span class="description">compute a moving corrected sample skewness incrementally.</span>
- <span class="package-name">[`@stdlib/stats/incr/mmean`][@stdlib/stats/incr/mmean]</span><span class="delimiter">: </span><span class="description">compute a moving arithmetic mean incrementally.</span>
- <span class="package-name">[`@stdlib/stats/incr/mvariance`][@stdlib/stats/incr/mvariance]</span><span class="delimiter">: </span><span class="description">compute a moving unbiased sample variance incrementally.</span>
- <span class="package-name">[`@stdlib/stats/incr/msummary`][@stdlib/stats/incr/msummary]</span><span class="delimiter">: </span><span class="description">compute a moving statistical summary incrementally.</span>

</section>

<!-- /.related -->

<!-- Section for all links. Make sure to keep an empty line after the `section` element and another before the `/section` close. -->

<section class="links">

[sample-excess-kurtosis]: https://en.wikipedia.org/wiki/Kurtosis

<!-- <related-links> -->

[@stdlib/stats/incr/kurtosis]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/stats/incr/kurtosis

[@stdlib/stats/incr/mskewness]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/stats/incr/mskewness

[@stdlib/stats/incr/mmean]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/stats/incr/mmean

[@stdlib/stats/incr/mvariance]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/stats/incr/mvariance

[@stdlib/stats/incr/msummary]: https://github.com/stdlib-js/stdlib/tree/develop/lib/node_modules/%40stdlib/stats/incr/msummary

<!-- </related-links> -->

</section>

<!-- /.links -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @license Apache-2.0
*
* Copyright (c) 2026 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

// MODULES //

var bench = require( '@stdlib/bench' );
var randu = require( '@stdlib/random/base/randu' );
var format = require( '@stdlib/string/format' );
var pkg = require( './../package.json' ).name;
var mkurtosis = require( './../lib' );


// MAIN //

bench( pkg, function benchmark( b ) {
var f;
var i;
b.tic();
for ( i = 0; i < b.iterations; i++ ) {
f = mkurtosis( 20 );
if ( typeof f !== 'function' ) {
b.fail( 'should return a function' );
}
}
b.toc();
if ( typeof f !== 'function' ) {
b.fail( 'should return a function' );
}
b.pass( 'benchmark finished' );
b.end();
});

bench( format( '%s::accumulator,window=20', pkg ), function benchmark( b ) {
var acc;
var v;
var i;

acc = mkurtosis( 20 );

b.tic();
for ( i = 0; i < b.iterations; i++ ) {
v = acc( randu() );
if ( v !== v && v !== null ) {
b.fail( 'unexpected NaN' );
}
}
b.toc();
if ( v !== v && v !== null ) {
b.fail( 'unexpected NaN' );
}
b.pass( 'benchmark finished' );
b.end();
});

bench( format( '%s::accumulator,window=100', pkg ), function benchmark( b ) {
var acc;
var v;
var i;

acc = mkurtosis( 100 );

b.tic();
for ( i = 0; i < b.iterations; i++ ) {
v = acc( randu() );
if ( v !== v && v !== null ) {
b.fail( 'unexpected NaN' );
}
}
b.toc();
if ( v !== v && v !== null ) {
b.fail( 'unexpected NaN' );
}
b.pass( 'benchmark finished' );
b.end();
});
50 changes: 50 additions & 0 deletions lib/node_modules/@stdlib/stats/incr/mkurtosis/docs/repl.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@

{{alias}}( W )
Returns an accumulator function which incrementally computes a moving
corrected sample excess kurtosis.

The `W` parameter defines the number of values over which to compute the
moving corrected sample excess kurtosis.

If provided a value, the accumulator function returns an updated moving
corrected sample excess kurtosis. If not provided a value, the accumulator
function returns the current moving corrected sample excess kurtosis.

As `W` values are needed to fill the window buffer, the first `W-1`
returned values are calculated from smaller sample sizes. Until the window
is full, each returned value is the kurtosis of all values provided thus
far.

For fewer than 4 values, the accumulator returns `null`.

If provided `NaN` or a value which, when used in computations, results in
`NaN`, the accumulated value is `NaN` for, at most, `W` future invocations.
Once the `NaN` value leaves the sliding window, the accumulator recovers.

Parameters
----------
W: integer
Window size. Must be a positive integer.

Returns
-------
acc: Function
Accumulator function.

Examples
--------
> var accumulator = {{alias}}( 4 );
> var v = accumulator( 2.0 )
null
> v = accumulator( 2.0 )
null
> v = accumulator( -4.0 )
null
> v = accumulator( -4.0 )
-6.0
> v = accumulator( 3.0 )
-5.652200677131425

See Also
--------

Loading