ezmsg.sigproc.ewma#

Exponentially weighted moving average (EWMA) utilities and parameter conversion.

Functions

ewma_step(sample, zi, alpha, beta=None)[source]#

Do an exponentially weighted moving average step.

Parameters:
  • sample (ndarray[tuple[Any, ...], dtype[_ScalarT]]) – The new sample.

  • zi (ndarray[tuple[Any, ...], dtype[_ScalarT]]) – The output of the previous step.

  • alpha (float) – Fading factor.

  • beta (float | None) – Persisting factor. If None, it is calculated as 1-alpha.

Returns:

alpha * sample + beta * zi

Classes

class EWMASettings(time_constant: float = 1.0, axis: str | None = None, accumulate: bool = True, passthrough: bool = False, reset_on_resume: bool = False, mlx_metal_chunk_sizes: tuple[int, ...] = (32, 1024))[source]#

Bases: Settings

Parameters:
  • time_constant (float)

  • axis (str | None)

  • accumulate (bool)

  • passthrough (bool)

  • reset_on_resume (bool)

  • mlx_metal_chunk_sizes (tuple[int, ...])

time_constant: float = 1.0#

The amount of time for the smoothed response of a unit step function to reach 1 - 1/e approx-eq 63.2%.

axis: str | None = None#
accumulate: bool = True#

If True, update the EWMA state with each sample. If False, only apply the current EWMA estimate without updating state (useful for inference periods where you don’t want to adapt statistics).

passthrough: bool = False#

If True, return the input unchanged (identity) without touching the EWMA. Unlike a very large time_constant – which still applies a (stale) baseline estimate – passthrough leaves the data untouched. May be toggled at runtime without resetting the filter state; see reset_on_resume for what that means for the first message after the gap.

reset_on_resume: bool = False#

Whether switching passthrough back off discards the filter state.

The filter sees none of the samples that go by during passthrough, so zi describes an exponentially-weighted window that ended when passthrough was switched on – and the state cannot tell a 10 ms blip from a 10 minute outage, since both resume identically. For a scaler that means z-scoring post-gap data against pre-gap statistics.

False (the default) resumes from the preserved state, which is right for a short blip and keeps an estimate that may have taken many time_constants to converge. True rebuilds from the first post-gap message instead: with the bias correction below, that first output is exactly the first sample, and the estimate re-converges over time_constant. Prefer True where passthrough may be left on long enough for the signal to drift, which is the case ezmsg.sigproc.binned_aggregate.BinnedAggregateTransformer always assumes.

Empty chunks are not gaps – they carry no samples past the filter – so they never trigger this.

mlx_metal_chunk_sizes: tuple[int, ...] = (32, 1024)#

Allowable compile-time chunk sizes for EWMA Metal kernels. The smallest size that fits the remaining samples is selected on each launch; otherwise the largest size is repeated. Specializations compile lazily on first use. Values must be in [1, 1024].

__init__(time_constant=1.0, axis=None, accumulate=True, passthrough=False, reset_on_resume=False, mlx_metal_chunk_sizes=(32, 1024))#
Parameters:
  • time_constant (float)

  • axis (str | None)

  • accumulate (bool)

  • passthrough (bool)

  • reset_on_resume (bool)

  • mlx_metal_chunk_sizes (tuple[int, ...])

Return type:

None

class EWMAState[source]#

Bases: object

alpha: float#
zi: ndarray[tuple[Any, ...], dtype[_ScalarT]] | None = None#
n_seen: int = 0#

Cumulative sample count since reset, used to bias-correct the output.

class EWMATransformer(*args, **kwargs)[source]#

Bases: BaseStatefulTransformer[EWMASettings, AxisArray, AxisArray, EWMAState]

NONRESET_SETTINGS_FIELDS: ClassVar[frozenset[str]] = frozenset({'accumulate', 'mlx_metal_chunk_sizes', 'passthrough', 'reset_on_resume'})#
class EWMAUnit(*args, settings=None, **kwargs)[source]#

Bases: BaseTransformerUnit[EWMASettings, AxisArray, AxisArray, EWMATransformer]

Parameters:

settings (Settings | None)

SETTINGS#

alias of EWMASettings

class EWMA_Deprecated(alpha, max_len)[source]#

Bases: object

Grabbed these methods from https://stackoverflow.com/a/70998068 and other answers in that topic, but they ended up being slower than the scipy.signal.lfilter method. Additionally, compute and compute2 suffer from potential errors as the vector length increases and beta**n approaches zero.

Parameters:
__init__(alpha, max_len)[source]#
Parameters:
prev: ndarray[tuple[Any, ...], dtype[_ScalarT]] | None#
compute(arr, out=None)[source]#
Parameters:
Return type:

ndarray[tuple[Any, …], dtype[_ScalarT]]

compute2(arr)[source]#

Compute the Exponentially Weighted Moving Average (EWMA) of the input array.

Parameters:

arr (ndarray[tuple[Any, ...], dtype[_ScalarT]]) – The input array to be smoothed.

Returns:

The smoothed array.

Return type:

ndarray[tuple[Any, …], dtype[_ScalarT]]

compute_sample(new_sample)[source]#
Parameters:

new_sample (ndarray[tuple[Any, ...], dtype[_ScalarT]])

Return type:

ndarray[tuple[Any, …], dtype[_ScalarT]]