ezmsg-sigproc 2.7.0__py3-none-any.whl → 2.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ezmsg/sigproc/__version__.py +2 -2
- ezmsg/sigproc/affinetransform.py +23 -1
- ezmsg/sigproc/butterworthzerophase.py +1 -1
- ezmsg/sigproc/denormalize.py +3 -4
- ezmsg/sigproc/detrend.py +1 -1
- ezmsg/sigproc/diff.py +3 -4
- ezmsg/sigproc/ewma.py +44 -7
- ezmsg/sigproc/extract_axis.py +1 -2
- ezmsg/sigproc/filter.py +3 -4
- ezmsg/sigproc/fir_hilbert.py +1 -1
- ezmsg/sigproc/linear.py +118 -0
- ezmsg/sigproc/rollingscaler.py +4 -4
- ezmsg/sigproc/scaler.py +52 -3
- {ezmsg_sigproc-2.7.0.dist-info → ezmsg_sigproc-2.8.0.dist-info}/METADATA +1 -1
- {ezmsg_sigproc-2.7.0.dist-info → ezmsg_sigproc-2.8.0.dist-info}/RECORD +17 -16
- {ezmsg_sigproc-2.7.0.dist-info → ezmsg_sigproc-2.8.0.dist-info}/WHEEL +0 -0
- {ezmsg_sigproc-2.7.0.dist-info → ezmsg_sigproc-2.8.0.dist-info}/licenses/LICENSE +0 -0
ezmsg/sigproc/__version__.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.
|
|
32
|
-
__version_tuple__ = version_tuple = (2,
|
|
31
|
+
__version__ = version = '2.8.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 8, 0)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
ezmsg/sigproc/affinetransform.py
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
"""Affine transformations via matrix multiplication: y = Ax or y = Ax + B.
|
|
2
|
+
|
|
3
|
+
For full matrix transformations where channels are mixed (off-diagonal weights),
|
|
4
|
+
use :obj:`AffineTransformTransformer` or the `AffineTransform` unit.
|
|
5
|
+
|
|
6
|
+
For simple per-channel scaling and offset (diagonal weights only), use
|
|
7
|
+
:obj:`LinearTransformTransformer` from :mod:`ezmsg.sigproc.linear` instead,
|
|
8
|
+
which is more efficient as it avoids matrix multiplication.
|
|
9
|
+
"""
|
|
10
|
+
|
|
1
11
|
import os
|
|
2
12
|
from pathlib import Path
|
|
3
13
|
|
|
@@ -17,7 +27,6 @@ from ezmsg.util.messages.util import replace
|
|
|
17
27
|
class AffineTransformSettings(ez.Settings):
|
|
18
28
|
"""
|
|
19
29
|
Settings for :obj:`AffineTransform`.
|
|
20
|
-
See :obj:`affine_transform` for argument details.
|
|
21
30
|
"""
|
|
22
31
|
|
|
23
32
|
weights: np.ndarray | str | Path
|
|
@@ -39,6 +48,19 @@ class AffineTransformState:
|
|
|
39
48
|
class AffineTransformTransformer(
|
|
40
49
|
BaseStatefulTransformer[AffineTransformSettings, AxisArray, AxisArray, AffineTransformState]
|
|
41
50
|
):
|
|
51
|
+
"""Apply affine transformation via matrix multiplication: y = Ax or y = Ax + B.
|
|
52
|
+
|
|
53
|
+
Use this transformer when you need full matrix transformations that mix
|
|
54
|
+
channels (off-diagonal weights), such as spatial filters or projections.
|
|
55
|
+
|
|
56
|
+
For simple per-channel scaling and offset where each output channel depends
|
|
57
|
+
only on its corresponding input channel (diagonal weight matrix), use
|
|
58
|
+
:obj:`LinearTransformTransformer` instead, which is more efficient.
|
|
59
|
+
|
|
60
|
+
The weights matrix can include an offset row (stacked as [A|B]) where the
|
|
61
|
+
input is automatically augmented with a column of ones to compute y = Ax + B.
|
|
62
|
+
"""
|
|
63
|
+
|
|
42
64
|
def __call__(self, message: AxisArray) -> AxisArray:
|
|
43
65
|
# Override __call__ so we can shortcut if weights are None.
|
|
44
66
|
if self.settings.weights is None or (
|
|
@@ -4,10 +4,10 @@ import typing
|
|
|
4
4
|
import ezmsg.core as ez
|
|
5
5
|
import numpy as np
|
|
6
6
|
import scipy.signal
|
|
7
|
+
from ezmsg.baseproc import SettingsType
|
|
7
8
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
8
9
|
from ezmsg.util.messages.util import replace
|
|
9
10
|
|
|
10
|
-
from ezmsg.sigproc.base import SettingsType
|
|
11
11
|
from ezmsg.sigproc.butterworthfilter import ButterworthFilterSettings, butter_design_fun
|
|
12
12
|
from ezmsg.sigproc.filter import (
|
|
13
13
|
BACoeffs,
|
ezmsg/sigproc/denormalize.py
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import ezmsg.core as ez
|
|
2
2
|
import numpy as np
|
|
3
3
|
import numpy.typing as npt
|
|
4
|
-
from ezmsg.
|
|
5
|
-
from ezmsg.util.messages.util import replace
|
|
6
|
-
|
|
7
|
-
from ezmsg.sigproc.base import (
|
|
4
|
+
from ezmsg.baseproc import (
|
|
8
5
|
BaseStatefulTransformer,
|
|
9
6
|
BaseTransformerUnit,
|
|
10
7
|
processor_state,
|
|
11
8
|
)
|
|
9
|
+
from ezmsg.util.messages.axisarray import AxisArray
|
|
10
|
+
from ezmsg.util.messages.util import replace
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class DenormalizeSettings(ez.Settings):
|
ezmsg/sigproc/detrend.py
CHANGED
ezmsg/sigproc/diff.py
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import ezmsg.core as ez
|
|
2
2
|
import numpy as np
|
|
3
3
|
import numpy.typing as npt
|
|
4
|
-
from ezmsg.
|
|
5
|
-
from ezmsg.util.messages.util import replace
|
|
6
|
-
|
|
7
|
-
from ezmsg.sigproc.base import (
|
|
4
|
+
from ezmsg.baseproc import (
|
|
8
5
|
BaseStatefulTransformer,
|
|
9
6
|
BaseTransformerUnit,
|
|
10
7
|
processor_state,
|
|
11
8
|
)
|
|
9
|
+
from ezmsg.util.messages.axisarray import AxisArray, slice_along_axis
|
|
10
|
+
from ezmsg.util.messages.util import replace
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
class DiffSettings(ez.Settings):
|
ezmsg/sigproc/ewma.py
CHANGED
|
@@ -139,8 +139,15 @@ class EWMA_Deprecated:
|
|
|
139
139
|
|
|
140
140
|
class EWMASettings(ez.Settings):
|
|
141
141
|
time_constant: float = 1.0
|
|
142
|
+
"""The amount of time for the smoothed response of a unit step function to reach 1 - 1/e approx-eq 63.2%."""
|
|
143
|
+
|
|
142
144
|
axis: str | None = None
|
|
143
145
|
|
|
146
|
+
accumulate: bool = True
|
|
147
|
+
"""If True, update the EWMA state with each sample. If False, only apply
|
|
148
|
+
the current EWMA estimate without updating state (useful for inference
|
|
149
|
+
periods where you don't want to adapt statistics)."""
|
|
150
|
+
|
|
144
151
|
|
|
145
152
|
@processor_state
|
|
146
153
|
class EWMAState:
|
|
@@ -166,15 +173,45 @@ class EWMATransformer(BaseStatefulTransformer[EWMASettings, AxisArray, AxisArray
|
|
|
166
173
|
return message
|
|
167
174
|
axis = self.settings.axis or message.dims[0]
|
|
168
175
|
axis_idx = message.get_axis_idx(axis)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
+
if self.settings.accumulate:
|
|
177
|
+
# Normal behavior: update state with new samples
|
|
178
|
+
expected, self._state.zi = sps.lfilter(
|
|
179
|
+
[self._state.alpha],
|
|
180
|
+
[1.0, self._state.alpha - 1.0],
|
|
181
|
+
message.data,
|
|
182
|
+
axis=axis_idx,
|
|
183
|
+
zi=self._state.zi,
|
|
184
|
+
)
|
|
185
|
+
else:
|
|
186
|
+
# Process-only: compute output without updating state
|
|
187
|
+
expected, _ = sps.lfilter(
|
|
188
|
+
[self._state.alpha],
|
|
189
|
+
[1.0, self._state.alpha - 1.0],
|
|
190
|
+
message.data,
|
|
191
|
+
axis=axis_idx,
|
|
192
|
+
zi=self._state.zi,
|
|
193
|
+
)
|
|
176
194
|
return replace(message, data=expected)
|
|
177
195
|
|
|
178
196
|
|
|
179
197
|
class EWMAUnit(BaseTransformerUnit[EWMASettings, AxisArray, AxisArray, EWMATransformer]):
|
|
180
198
|
SETTINGS = EWMASettings
|
|
199
|
+
|
|
200
|
+
@ez.subscriber(BaseTransformerUnit.INPUT_SETTINGS)
|
|
201
|
+
async def on_settings(self, msg: EWMASettings) -> None:
|
|
202
|
+
"""
|
|
203
|
+
Handle settings updates with smart reset behavior.
|
|
204
|
+
|
|
205
|
+
Only resets state if `axis` changes (structural change).
|
|
206
|
+
Changes to `time_constant` or `accumulate` are applied without
|
|
207
|
+
resetting accumulated state.
|
|
208
|
+
"""
|
|
209
|
+
old_axis = self.SETTINGS.axis
|
|
210
|
+
self.apply_settings(msg)
|
|
211
|
+
|
|
212
|
+
if msg.axis != old_axis:
|
|
213
|
+
# Axis changed - need full reset
|
|
214
|
+
self.create_processor()
|
|
215
|
+
else:
|
|
216
|
+
# Only accumulate or time_constant changed - keep state
|
|
217
|
+
self.processor.settings = msg
|
ezmsg/sigproc/extract_axis.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import ezmsg.core as ez
|
|
2
2
|
import numpy as np
|
|
3
|
+
from ezmsg.baseproc import BaseTransformer, BaseTransformerUnit
|
|
3
4
|
from ezmsg.util.messages.axisarray import AxisArray, replace
|
|
4
5
|
|
|
5
|
-
from ezmsg.sigproc.base import BaseTransformer, BaseTransformerUnit
|
|
6
|
-
|
|
7
6
|
|
|
8
7
|
class ExtractAxisSettings(ez.Settings):
|
|
9
8
|
axis: str = "freq"
|
ezmsg/sigproc/filter.py
CHANGED
|
@@ -6,10 +6,7 @@ import ezmsg.core as ez
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import numpy.typing as npt
|
|
8
8
|
import scipy.signal
|
|
9
|
-
from ezmsg.
|
|
10
|
-
from ezmsg.util.messages.util import replace
|
|
11
|
-
|
|
12
|
-
from ezmsg.sigproc.base import (
|
|
9
|
+
from ezmsg.baseproc import (
|
|
13
10
|
BaseConsumerUnit,
|
|
14
11
|
BaseStatefulTransformer,
|
|
15
12
|
BaseTransformerUnit,
|
|
@@ -17,6 +14,8 @@ from ezmsg.sigproc.base import (
|
|
|
17
14
|
TransformerType,
|
|
18
15
|
processor_state,
|
|
19
16
|
)
|
|
17
|
+
from ezmsg.util.messages.axisarray import AxisArray
|
|
18
|
+
from ezmsg.util.messages.util import replace
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
@dataclass
|
ezmsg/sigproc/fir_hilbert.py
CHANGED
|
@@ -4,10 +4,10 @@ import typing
|
|
|
4
4
|
import ezmsg.core as ez
|
|
5
5
|
import numpy as np
|
|
6
6
|
import scipy.signal as sps
|
|
7
|
+
from ezmsg.baseproc import BaseStatefulTransformer, processor_state
|
|
7
8
|
from ezmsg.util.messages.axisarray import AxisArray
|
|
8
9
|
from ezmsg.util.messages.util import replace
|
|
9
10
|
|
|
10
|
-
from ezmsg.sigproc.base import BaseStatefulTransformer, processor_state
|
|
11
11
|
from ezmsg.sigproc.filter import (
|
|
12
12
|
BACoeffs,
|
|
13
13
|
BaseFilterByDesignTransformerUnit,
|
ezmsg/sigproc/linear.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Apply a linear transformation: output = scale * input + offset.
|
|
2
|
+
|
|
3
|
+
Supports per-element scale and offset along a specified axis.
|
|
4
|
+
Uses Array API for compatibility with numpy, cupy, torch, etc.
|
|
5
|
+
|
|
6
|
+
For full matrix transformations, use :obj:`AffineTransformTransformer` instead.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import ezmsg.core as ez
|
|
10
|
+
import numpy as np
|
|
11
|
+
import numpy.typing as npt
|
|
12
|
+
from array_api_compat import get_namespace
|
|
13
|
+
from ezmsg.util.messages.axisarray import AxisArray
|
|
14
|
+
from ezmsg.util.messages.util import replace
|
|
15
|
+
|
|
16
|
+
from .base import BaseStatefulTransformer, BaseTransformerUnit, processor_state
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class LinearTransformSettings(ez.Settings):
|
|
20
|
+
scale: float | list[float] | npt.ArrayLike = 1.0
|
|
21
|
+
"""Scale factor(s). Can be a scalar (applied to all elements) or an array
|
|
22
|
+
matching the size of the specified axis for per-element scaling."""
|
|
23
|
+
|
|
24
|
+
offset: float | list[float] | npt.ArrayLike = 0.0
|
|
25
|
+
"""Offset value(s). Can be a scalar (applied to all elements) or an array
|
|
26
|
+
matching the size of the specified axis for per-element offset."""
|
|
27
|
+
|
|
28
|
+
axis: str | None = None
|
|
29
|
+
"""Axis along which to apply per-element scale/offset. If None, scalar
|
|
30
|
+
scale/offset are broadcast to all elements."""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@processor_state
|
|
34
|
+
class LinearTransformState:
|
|
35
|
+
scale: npt.NDArray = None
|
|
36
|
+
"""Prepared scale array for broadcasting."""
|
|
37
|
+
|
|
38
|
+
offset: npt.NDArray = None
|
|
39
|
+
"""Prepared offset array for broadcasting."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class LinearTransformTransformer(
|
|
43
|
+
BaseStatefulTransformer[LinearTransformSettings, AxisArray, AxisArray, LinearTransformState]
|
|
44
|
+
):
|
|
45
|
+
"""Apply linear transformation: output = scale * input + offset.
|
|
46
|
+
|
|
47
|
+
This transformer is optimized for element-wise linear operations with
|
|
48
|
+
optional per-channel (or per-axis) coefficients. For full matrix
|
|
49
|
+
transformations, use :obj:`AffineTransformTransformer` instead.
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
# Uniform scaling and offset
|
|
53
|
+
>>> transformer = LinearTransformTransformer(LinearTransformSettings(scale=2.0, offset=1.0))
|
|
54
|
+
|
|
55
|
+
# Per-channel scaling (e.g., for 3-channel data along "ch" axis)
|
|
56
|
+
>>> transformer = LinearTransformTransformer(LinearTransformSettings(
|
|
57
|
+
... scale=[0.5, 1.0, 2.0],
|
|
58
|
+
... offset=[0.0, 0.1, 0.2],
|
|
59
|
+
... axis="ch"
|
|
60
|
+
... ))
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def _hash_message(self, message: AxisArray) -> int:
|
|
64
|
+
"""Hash based on shape and axis to detect when broadcast shapes need recalculation."""
|
|
65
|
+
axis = self.settings.axis
|
|
66
|
+
if axis is not None:
|
|
67
|
+
axis_idx = message.get_axis_idx(axis)
|
|
68
|
+
return hash((message.data.ndim, axis_idx, message.data.shape[axis_idx]))
|
|
69
|
+
return hash(message.data.ndim)
|
|
70
|
+
|
|
71
|
+
def _reset_state(self, message: AxisArray) -> None:
|
|
72
|
+
"""Prepare scale/offset arrays with proper broadcast shapes."""
|
|
73
|
+
xp = get_namespace(message.data)
|
|
74
|
+
ndim = message.data.ndim
|
|
75
|
+
|
|
76
|
+
scale = self.settings.scale
|
|
77
|
+
offset = self.settings.offset
|
|
78
|
+
|
|
79
|
+
# Convert settings to arrays
|
|
80
|
+
if isinstance(scale, (list, np.ndarray)):
|
|
81
|
+
scale = xp.asarray(scale, dtype=xp.float64)
|
|
82
|
+
else:
|
|
83
|
+
# Scalar: create a 0-d array
|
|
84
|
+
scale = xp.asarray(float(scale), dtype=xp.float64)
|
|
85
|
+
|
|
86
|
+
if isinstance(offset, (list, np.ndarray)):
|
|
87
|
+
offset = xp.asarray(offset, dtype=xp.float64)
|
|
88
|
+
else:
|
|
89
|
+
# Scalar: create a 0-d array
|
|
90
|
+
offset = xp.asarray(float(offset), dtype=xp.float64)
|
|
91
|
+
|
|
92
|
+
# If axis is specified and we have 1-d arrays, reshape for proper broadcasting
|
|
93
|
+
if self.settings.axis is not None and ndim > 0:
|
|
94
|
+
axis_idx = message.get_axis_idx(self.settings.axis)
|
|
95
|
+
|
|
96
|
+
if scale.ndim == 1:
|
|
97
|
+
# Create shape for broadcasting: all 1s except at axis_idx
|
|
98
|
+
broadcast_shape = [1] * ndim
|
|
99
|
+
broadcast_shape[axis_idx] = scale.shape[0]
|
|
100
|
+
scale = xp.reshape(scale, broadcast_shape)
|
|
101
|
+
|
|
102
|
+
if offset.ndim == 1:
|
|
103
|
+
broadcast_shape = [1] * ndim
|
|
104
|
+
broadcast_shape[axis_idx] = offset.shape[0]
|
|
105
|
+
offset = xp.reshape(offset, broadcast_shape)
|
|
106
|
+
|
|
107
|
+
self._state.scale = scale
|
|
108
|
+
self._state.offset = offset
|
|
109
|
+
|
|
110
|
+
def _process(self, message: AxisArray) -> AxisArray:
|
|
111
|
+
result = message.data * self._state.scale + self._state.offset
|
|
112
|
+
return replace(message, data=result)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class LinearTransform(BaseTransformerUnit[LinearTransformSettings, AxisArray, AxisArray, LinearTransformTransformer]):
|
|
116
|
+
"""Unit wrapper for LinearTransformTransformer."""
|
|
117
|
+
|
|
118
|
+
SETTINGS = LinearTransformSettings
|
ezmsg/sigproc/rollingscaler.py
CHANGED
|
@@ -3,14 +3,14 @@ from collections import deque
|
|
|
3
3
|
import ezmsg.core as ez
|
|
4
4
|
import numpy as np
|
|
5
5
|
import numpy.typing as npt
|
|
6
|
-
from ezmsg.
|
|
7
|
-
from ezmsg.util.messages.util import replace
|
|
8
|
-
|
|
9
|
-
from ezmsg.sigproc.base import (
|
|
6
|
+
from ezmsg.baseproc import (
|
|
10
7
|
BaseAdaptiveTransformer,
|
|
11
8
|
BaseAdaptiveTransformerUnit,
|
|
12
9
|
processor_state,
|
|
13
10
|
)
|
|
11
|
+
from ezmsg.util.messages.axisarray import AxisArray
|
|
12
|
+
from ezmsg.util.messages.util import replace
|
|
13
|
+
|
|
14
14
|
from ezmsg.sigproc.sampler import SampleMessage
|
|
15
15
|
|
|
16
16
|
|
ezmsg/sigproc/scaler.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
|
|
3
|
+
import ezmsg.core as ez
|
|
3
4
|
import numpy as np
|
|
4
5
|
from ezmsg.baseproc import (
|
|
5
6
|
BaseStatefulTransformer,
|
|
@@ -82,11 +83,38 @@ class AdaptiveStandardScalerTransformer(
|
|
|
82
83
|
]
|
|
83
84
|
):
|
|
84
85
|
def _reset_state(self, message: AxisArray) -> None:
|
|
85
|
-
self._state.samps_ewma = EWMATransformer(
|
|
86
|
-
|
|
86
|
+
self._state.samps_ewma = EWMATransformer(
|
|
87
|
+
time_constant=self.settings.time_constant,
|
|
88
|
+
axis=self.settings.axis,
|
|
89
|
+
accumulate=self.settings.accumulate,
|
|
90
|
+
)
|
|
91
|
+
self._state.vars_sq_ewma = EWMATransformer(
|
|
92
|
+
time_constant=self.settings.time_constant,
|
|
93
|
+
axis=self.settings.axis,
|
|
94
|
+
accumulate=self.settings.accumulate,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def accumulate(self) -> bool:
|
|
99
|
+
"""Whether to accumulate statistics from incoming samples."""
|
|
100
|
+
return self.settings.accumulate
|
|
101
|
+
|
|
102
|
+
@accumulate.setter
|
|
103
|
+
def accumulate(self, value: bool) -> None:
|
|
104
|
+
"""
|
|
105
|
+
Set the accumulate mode and propagate to child EWMA transformers.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
value: If True, update statistics with each sample.
|
|
109
|
+
If False, only apply current statistics without updating.
|
|
110
|
+
"""
|
|
111
|
+
if self._state.samps_ewma is not None:
|
|
112
|
+
self._state.samps_ewma.settings = replace(self._state.samps_ewma.settings, accumulate=value)
|
|
113
|
+
if self._state.vars_sq_ewma is not None:
|
|
114
|
+
self._state.vars_sq_ewma.settings = replace(self._state.vars_sq_ewma.settings, accumulate=value)
|
|
87
115
|
|
|
88
116
|
def _process(self, message: AxisArray) -> AxisArray:
|
|
89
|
-
# Update step
|
|
117
|
+
# Update step (respects accumulate setting via child EWMAs)
|
|
90
118
|
mean_message = self._state.samps_ewma(message)
|
|
91
119
|
var_sq_message = self._state.vars_sq_ewma(replace(message, data=message.data**2))
|
|
92
120
|
|
|
@@ -108,6 +136,27 @@ class AdaptiveStandardScaler(
|
|
|
108
136
|
):
|
|
109
137
|
SETTINGS = AdaptiveStandardScalerSettings
|
|
110
138
|
|
|
139
|
+
@ez.subscriber(BaseTransformerUnit.INPUT_SETTINGS)
|
|
140
|
+
async def on_settings(self, msg: AdaptiveStandardScalerSettings) -> None:
|
|
141
|
+
"""
|
|
142
|
+
Handle settings updates with smart reset behavior.
|
|
143
|
+
|
|
144
|
+
Only resets state if `axis` changes (structural change).
|
|
145
|
+
Changes to `time_constant` or `accumulate` are applied without
|
|
146
|
+
resetting accumulated statistics.
|
|
147
|
+
"""
|
|
148
|
+
old_axis = self.SETTINGS.axis
|
|
149
|
+
self.apply_settings(msg)
|
|
150
|
+
|
|
151
|
+
if msg.axis != old_axis:
|
|
152
|
+
# Axis changed - need full reset
|
|
153
|
+
self.create_processor()
|
|
154
|
+
else:
|
|
155
|
+
# Update accumulate on processor (propagates to child EWMAs)
|
|
156
|
+
self.processor.accumulate = msg.accumulate
|
|
157
|
+
# Also update own settings reference
|
|
158
|
+
self.processor.settings = msg
|
|
159
|
+
|
|
111
160
|
|
|
112
161
|
# Backwards compatibility...
|
|
113
162
|
def scaler_np(time_constant: float = 1.0, axis: str | None = None) -> AdaptiveStandardScalerTransformer:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ezmsg-sigproc
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.0
|
|
4
4
|
Summary: Timeseries signal processing implementations in ezmsg
|
|
5
5
|
Author-email: Griffin Milsap <griffin.milsap@gmail.com>, Preston Peranich <pperanich@gmail.com>, Chadwick Boulay <chadwick.boulay@gmail.com>, Kyle McGraw <kmcgraw@blackrockneuro.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -1,39 +1,40 @@
|
|
|
1
1
|
ezmsg/sigproc/__init__.py,sha256=8K4IcOA3-pfzadoM6s2Sfg5460KlJUocGgyTJTJl96U,52
|
|
2
|
-
ezmsg/sigproc/__version__.py,sha256=
|
|
2
|
+
ezmsg/sigproc/__version__.py,sha256=9o_P9mg_lm8dZdRjWfkpikVSLuPB26JLjcQeqWLe_GI,704
|
|
3
3
|
ezmsg/sigproc/activation.py,sha256=83vnTa3ZcC4Q3VSWcGfaqhCEqYRNySUOyVpMHZXfz-c,2755
|
|
4
4
|
ezmsg/sigproc/adaptive_lattice_notch.py,sha256=ThUR48mbSHuThkimtD0j4IXNMrOVcpZgGhE7PCYfXhU,8818
|
|
5
|
-
ezmsg/sigproc/affinetransform.py,sha256=
|
|
5
|
+
ezmsg/sigproc/affinetransform.py,sha256=jl7DiSa5Yb0qsmFJbfSiSeGmvK1SGoBgycFC5JU5DVY,9434
|
|
6
6
|
ezmsg/sigproc/aggregate.py,sha256=yc3Hnak0yhucqlTCv9Isg6BKR24s6rMZqbZKpayyBgM,9048
|
|
7
7
|
ezmsg/sigproc/bandpower.py,sha256=dAhH56sUrXNhcRFymTTwjdM_KcU5OxFzrR_sxIPAxyw,2264
|
|
8
8
|
ezmsg/sigproc/base.py,sha256=SJvKEb8gw6mUMwlV5sH0iPG0bXrgS8tvkPwhI-j89MQ,3672
|
|
9
9
|
ezmsg/sigproc/butterworthfilter.py,sha256=NKTGkgjvlmC1Dc9gD2Z6UBzUq12KicfnczrzM5ZTosk,5255
|
|
10
|
-
ezmsg/sigproc/butterworthzerophase.py,sha256=
|
|
10
|
+
ezmsg/sigproc/butterworthzerophase.py,sha256=Df3F1QBBE39FBjNi67wvTsb1bSdTRTSTZXbZiKFlxC4,4105
|
|
11
11
|
ezmsg/sigproc/cheby.py,sha256=B8pGt5_pOBpNZCmaibNl_NKkyuasd8ZEJXeTDCTaino,3711
|
|
12
12
|
ezmsg/sigproc/combfilter.py,sha256=MSxr1I-jBePW_9AuCiv3RQ1HUNxIsNhLk0q1Iu8ikAw,4766
|
|
13
13
|
ezmsg/sigproc/coordinatespaces.py,sha256=NWbQVvizmiU4F3AIwHHhiZ30Kg2IeSW0fRaa-yXkn-c,4610
|
|
14
14
|
ezmsg/sigproc/decimate.py,sha256=DCX9p4ZrcGoQ9di-jmPKqiKERTkvTAtSqLg8chQLp84,2336
|
|
15
|
-
ezmsg/sigproc/denormalize.py,sha256=
|
|
16
|
-
ezmsg/sigproc/detrend.py,sha256=
|
|
17
|
-
ezmsg/sigproc/diff.py,sha256=
|
|
15
|
+
ezmsg/sigproc/denormalize.py,sha256=OACzoINCISNUmZcR2pqWfxi3p8uuBLCz1PMw4XNwOCs,3035
|
|
16
|
+
ezmsg/sigproc/detrend.py,sha256=2C-dhVy-ty4b22aE07kIF3VjqCoFitoZqyrX6hAjXYQ,894
|
|
17
|
+
ezmsg/sigproc/diff.py,sha256=m0g1KQXEooSM3V2oLx_MyYEZqdXm04jI9B4V-HjMUa4,2777
|
|
18
18
|
ezmsg/sigproc/downsample.py,sha256=Jqxt1Va1FrQLH1wUQpP0U79iQARTTHEKklKHy7yGL2o,3679
|
|
19
|
-
ezmsg/sigproc/ewma.py,sha256=
|
|
19
|
+
ezmsg/sigproc/ewma.py,sha256=ecLq1ZlON85NWdW_5cz9chWirxjf4y9zCPMOt3IqQuk,7723
|
|
20
20
|
ezmsg/sigproc/ewmfilter.py,sha256=9AuvVn3TDdf5R4bVolYNM46AtDVHFJa8MLGltY6mYPE,4795
|
|
21
|
-
ezmsg/sigproc/extract_axis.py,sha256=
|
|
21
|
+
ezmsg/sigproc/extract_axis.py,sha256=T2e9zW8N0bwOj4_zlB3I0oT0iwaBijpzF6wrFsCfD24,1593
|
|
22
22
|
ezmsg/sigproc/fbcca.py,sha256=JYFWsMDRJEWwUNujr4EsFL5t1ux-cnBGamNVrCRO_RA,12043
|
|
23
|
-
ezmsg/sigproc/filter.py,sha256=
|
|
23
|
+
ezmsg/sigproc/filter.py,sha256=IWg3J5IGVdv7kfp4zaKRzfjEuRa5xNzcq6fHtq4tPCM,11604
|
|
24
24
|
ezmsg/sigproc/filterbank.py,sha256=tD7fn4dZzEvsmp_sSn16VVJ4WcJ5sN5lsSuNTLDCQZ8,13056
|
|
25
25
|
ezmsg/sigproc/filterbankdesign.py,sha256=vLXQVJwoFEK4V6umqzcr1PJKcwv6pGO29klSWQXk7y0,4712
|
|
26
|
-
ezmsg/sigproc/fir_hilbert.py,sha256=
|
|
26
|
+
ezmsg/sigproc/fir_hilbert.py,sha256=ByZDsDFjbJx-EgLZF85vZCPQbprQaiMEuG2dyvTPiDY,10855
|
|
27
27
|
ezmsg/sigproc/fir_pmc.py,sha256=Ze2pd9K8XrdDhgJZG2E7x-5C2hfd6kIns4bYjTJsBvc,6997
|
|
28
28
|
ezmsg/sigproc/firfilter.py,sha256=7r_I476nYuixJsuwc_hQ0Fbq8WB0gnYBZUKs3zultOQ,3790
|
|
29
29
|
ezmsg/sigproc/gaussiansmoothing.py,sha256=BfXm9YQoOtieM4ABK2KRgxeQz055rd7mqtTVqmjT3Rk,2672
|
|
30
30
|
ezmsg/sigproc/kaiser.py,sha256=dIwgHbLXUHPtdotsGNLE9VG_clhcMgvVnSoFkMVgF9M,3483
|
|
31
|
+
ezmsg/sigproc/linear.py,sha256=o5HNEQMx2e6FrFOJskJeKCJwZmSN1uO-1LgBxERzmEE,4535
|
|
31
32
|
ezmsg/sigproc/messages.py,sha256=KQczHTeifn4BZycChN8ZcpfZoQW3lC_xuFmN72QT97s,925
|
|
32
33
|
ezmsg/sigproc/quantize.py,sha256=uSM2z2xXwL0dgSltyzLEmlKjaJZ2meA3PDWX8_bM0Hs,2195
|
|
33
34
|
ezmsg/sigproc/resample.py,sha256=3mm9pvxryNVhQuTCIMW3ToUkUfbVOCsIgvXUiurit1Y,11389
|
|
34
|
-
ezmsg/sigproc/rollingscaler.py,sha256=
|
|
35
|
+
ezmsg/sigproc/rollingscaler.py,sha256=e-smSKDhmDD2nWIf6I77CtRxQp_7sHS268SGPi7aXp8,8499
|
|
35
36
|
ezmsg/sigproc/sampler.py,sha256=iOk2YoUX22u9iTjFKimzP5V074RDBVcmswgfyxvZRZo,10761
|
|
36
|
-
ezmsg/sigproc/scaler.py,sha256=
|
|
37
|
+
ezmsg/sigproc/scaler.py,sha256=oBZa6uzyftChvk6aqBD5clil6pedx3IF-dptrb74EA0,5888
|
|
37
38
|
ezmsg/sigproc/signalinjector.py,sha256=mB62H2b-ScgPtH1jajEpxgDHqdb-RKekQfgyNncsE8Y,2874
|
|
38
39
|
ezmsg/sigproc/slicer.py,sha256=xLXxWf722V08ytVwvPimYjDKKj0pkC2HjdgCVaoaOvs,5195
|
|
39
40
|
ezmsg/sigproc/spectral.py,sha256=wFzuihS7qJZMQcp0ds_qCG-zCbvh5DyhFRjn2wA9TWQ,322
|
|
@@ -58,7 +59,7 @@ ezmsg/sigproc/util/message.py,sha256=ppN3IYtIAwrxWG9JOvgWFn1wDdIumkEzYFfqpH9VQkY
|
|
|
58
59
|
ezmsg/sigproc/util/profile.py,sha256=eVOo9pXgusrnH1yfRdd2RsM7Dbe2UpyC0LJ9MfGpB08,416
|
|
59
60
|
ezmsg/sigproc/util/sparse.py,sha256=NjbJitCtO0B6CENTlyd9c-lHEJwoCan-T3DIgPyeShw,4834
|
|
60
61
|
ezmsg/sigproc/util/typeresolution.py,sha256=fMFzLi63dqCIclGFLcMdM870OYxJnkeWw6aWKNMk718,362
|
|
61
|
-
ezmsg_sigproc-2.
|
|
62
|
-
ezmsg_sigproc-2.
|
|
63
|
-
ezmsg_sigproc-2.
|
|
64
|
-
ezmsg_sigproc-2.
|
|
62
|
+
ezmsg_sigproc-2.8.0.dist-info/METADATA,sha256=JZfuyxTvO6n3dIztHrFs531fF6agB_uKkPyXRMlW-EU,1908
|
|
63
|
+
ezmsg_sigproc-2.8.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
64
|
+
ezmsg_sigproc-2.8.0.dist-info/licenses/LICENSE,sha256=seu0tKhhAMPCUgc1XpXGGaCxY1YaYvFJwqFuQZAl2go,1100
|
|
65
|
+
ezmsg_sigproc-2.8.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|