skdh.utility.math.moving_mean#

skdh.utility.math.moving_mean(a, w_len, skip, req_points=1.0, trim=True, axis=-1)#

Compute the moving mean.

Parameters:
aarray-like

Signal to compute moving mean for.

w_lenint

Window length in number of samples.

skipint

Window start location skip in number of samples.

req_pointsfloat, optional

Minimum fraction of points required to compute the mean. Default is 1.0. If a float, is a proportion of the window length. If an int, is the number of points.

trimbool, optional

Trim the ends of the result, where a value cannot be calculated. If False, these values will be set to NaN. Default is True.

axisint, optional

Axis to compute the moving mean along. Default is -1.

Returns:
mmeannumpy.ndarray

Moving mean. Note that if the moving axis is not the last axis, then the result will not be c-contiguous.

Warning

Catastropic cancellation is a concern when skip is less than wlen due to the cumulative sum-type algorithm being used, when input values are very very large, or very very small. With typical IMU data values this should not be an issue, even for very long data series (multiple days worth of data)

Notes

On the moving axis if trim=True, the output length can be computed as follows:

\[\frac{n - w_{len}}{skip} + 1\]

where n is the length of the moving axis. For cases where skip != 1 and trim=False, the length of the return on the moving axis can be calculated as:

\[\frac{n}{skip}\]

Most efficient computations are for skip values that are either factors of wlen, or greater or equal to wlen.

Examples

Compute the with non-overlapping windows:

>>> import numpy as np
>>> x = np.arange(10)
>>> moving_mean(x, 3, 3)
array([1., 4., 7.])

Compute with overlapping windows:

>>> moving_mean(x, 3, 1)
array([1., 2., 3., 4., 5., 6., 7., 8.])

Compute without trimming the result

>>> moving_mean(x, 3, 1, trim=False)
array([1., 2., 3., 4., 5., 6., 7., 8., nan, nan])

Compute on a nd-array to see output shape. On the moving axis, the output should be equal to \((n - w_{len}) / skip + 1\).

>>> n = 500
>>> window_length = 100
>>> window_skip = 50
>>> shape = (3, n, 5, 10)
>>> y = np.random.random(shape)
>>> res = moving_mean(y, window_length, window_skip, axis=1)
>>> print(res.shape)
(3, 9, 5, 10)

Check flags for different axis output

>>> z = np.random.random((10, 10, 10))
>>> moving_mean(z, 3, 3, axis=0).flags['C_CONTIGUOUS']
False
>>> moving_mean(z, 3, 3, axis=1).flags['C_CONTIGUOUS']
False
>>> moving_mean(z, 3, 3, axis=2).flags['C_CONTIGUOUS']
True