modusa 0.4.29__py3-none-any.whl → 0.4.31__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.
- modusa/__init__.py +12 -8
- modusa/tools/__init__.py +11 -3
- modusa/tools/ann_saver.py +30 -0
- modusa/tools/audio_recorder.py +0 -1
- modusa/tools/audio_stft.py +72 -0
- modusa/tools/youtube_downloader.py +1 -4
- {modusa-0.4.29.dist-info → modusa-0.4.31.dist-info}/METADATA +2 -2
- modusa-0.4.31.dist-info/RECORD +22 -0
- pyproject.toml +2 -2
- modusa/config.py +0 -18
- modusa/decorators.py +0 -176
- modusa/devtools/generate_docs_source.py +0 -92
- modusa/devtools/generate_template.py +0 -144
- modusa/devtools/list_authors.py +0 -2
- modusa/devtools/list_plugins.py +0 -60
- modusa/devtools/main.py +0 -45
- modusa/devtools/templates/generator.py +0 -24
- modusa/devtools/templates/io.py +0 -24
- modusa/devtools/templates/model.py +0 -47
- modusa/devtools/templates/plugin.py +0 -41
- modusa/devtools/templates/test.py +0 -10
- modusa/devtools/templates/tool.py +0 -24
- modusa/generators/__init__.py +0 -13
- modusa/generators/audio.py +0 -188
- modusa/generators/audio_waveforms.py +0 -236
- modusa/generators/base.py +0 -29
- modusa/generators/ftds.py +0 -298
- modusa/generators/s1d.py +0 -270
- modusa/generators/s2d.py +0 -300
- modusa/generators/s_ax.py +0 -102
- modusa/generators/t_ax.py +0 -64
- modusa/generators/tds.py +0 -267
- modusa/models/__init__.py +0 -14
- modusa/models/audio.py +0 -90
- modusa/models/base.py +0 -70
- modusa/models/data.py +0 -457
- modusa/models/ftds.py +0 -584
- modusa/models/s1d.py +0 -578
- modusa/models/s2d.py +0 -619
- modusa/models/s_ax.py +0 -448
- modusa/models/t_ax.py +0 -335
- modusa/models/tds.py +0 -465
- modusa/plugins/__init__.py +0 -3
- modusa/plugins/base.py +0 -100
- modusa/tools/_plotter_old.py +0 -629
- modusa/tools/audio_saver.py +0 -30
- modusa/tools/base.py +0 -43
- modusa/tools/math_ops.py +0 -335
- modusa/utils/__init__.py +0 -1
- modusa/utils/config.py +0 -25
- modusa/utils/excp.py +0 -49
- modusa/utils/logger.py +0 -18
- modusa/utils/np_func_cat.py +0 -44
- modusa/utils/plot.py +0 -142
- modusa-0.4.29.dist-info/RECORD +0 -65
- {modusa-0.4.29.dist-info → modusa-0.4.31.dist-info}/WHEEL +0 -0
- {modusa-0.4.29.dist-info → modusa-0.4.31.dist-info}/entry_points.txt +0 -0
- {modusa-0.4.29.dist-info → modusa-0.4.31.dist-info}/licenses/LICENSE.md +0 -0
modusa/models/s_ax.py
DELETED
@@ -1,448 +0,0 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
|
3
|
-
|
4
|
-
from modusa import excp
|
5
|
-
from modusa.decorators import immutable_property, validate_args_type
|
6
|
-
from .base import ModusaSignalAxis
|
7
|
-
from .data import Data
|
8
|
-
from modusa.tools.math_ops import MathOps
|
9
|
-
from typing import Self, Any, Callable
|
10
|
-
import numpy as np
|
11
|
-
import matplotlib.pyplot as plt
|
12
|
-
|
13
|
-
class SAx(ModusaSignalAxis):
|
14
|
-
"""
|
15
|
-
Space to represent any signal's axis.
|
16
|
-
|
17
|
-
Parameters
|
18
|
-
----------
|
19
|
-
values: array-like
|
20
|
-
- Any array-like object on which np.asarray can be applied.
|
21
|
-
label: str
|
22
|
-
- Label for the axis.
|
23
|
-
- Default: None => ''
|
24
|
-
|
25
|
-
Note
|
26
|
-
----
|
27
|
-
- Use :class:`~modusa.generators.s_ax.SAxGen` API to instantiate this class.
|
28
|
-
- It can be uniform/non-uniform.
|
29
|
-
- It is numpy compatible, so you can use numpy methods directly on this class object.
|
30
|
-
- Since the object of this class represents an axis, any mathematical operations on it will result in another object of :class:`~modusa.models.s1d.S1D` class with `y` being the result of the operation and `x` being the axis itself.
|
31
|
-
"""
|
32
|
-
|
33
|
-
#--------Meta Information----------
|
34
|
-
_name = "Signal Axis"
|
35
|
-
_nickname = "axis" # This is to be used in repr/str methods
|
36
|
-
_description = "Axis for different types of signals."
|
37
|
-
_author_name = "Ankit Anand"
|
38
|
-
_author_email = "ankit0.anand0@gmail.com"
|
39
|
-
_created_at = "2025-07-20"
|
40
|
-
#----------------------------------
|
41
|
-
|
42
|
-
@validate_args_type()
|
43
|
-
def __init__(self, values, label = None):
|
44
|
-
|
45
|
-
super().__init__() # Instantiating `ModusaSignalAxis` class
|
46
|
-
|
47
|
-
values = np.asarray(values)
|
48
|
-
if values.ndim == 0:
|
49
|
-
values = np.asarray([values])
|
50
|
-
|
51
|
-
assert values.ndim == 1
|
52
|
-
|
53
|
-
self._values = values
|
54
|
-
self._label = label or ""
|
55
|
-
|
56
|
-
#-----------------------------------
|
57
|
-
# Properties (User Facing)
|
58
|
-
#-----------------------------------
|
59
|
-
|
60
|
-
@property
|
61
|
-
def values(self) -> np.ndarray:
|
62
|
-
return self._values
|
63
|
-
|
64
|
-
@property
|
65
|
-
def label(self) -> str:
|
66
|
-
return self._label
|
67
|
-
|
68
|
-
@property
|
69
|
-
def shape(self) -> tuple:
|
70
|
-
return self.values.shape
|
71
|
-
|
72
|
-
@property
|
73
|
-
def ndim(self) -> int:
|
74
|
-
return self.values.ndim # Should be 1
|
75
|
-
|
76
|
-
@property
|
77
|
-
def size(self) -> int:
|
78
|
-
return self.values.size
|
79
|
-
|
80
|
-
#====================================
|
81
|
-
|
82
|
-
#------------------------------------
|
83
|
-
# Utility methods
|
84
|
-
#------------------------------------
|
85
|
-
|
86
|
-
def is_same_as(self, other) -> bool:
|
87
|
-
"""
|
88
|
-
Compare it with another SAx object.
|
89
|
-
|
90
|
-
Parameters
|
91
|
-
----------
|
92
|
-
other: SAx
|
93
|
-
Another object to compare with.
|
94
|
-
|
95
|
-
Returns
|
96
|
-
-------
|
97
|
-
bool
|
98
|
-
True if same ow False
|
99
|
-
|
100
|
-
Note
|
101
|
-
----
|
102
|
-
- We check the shape and all the values.
|
103
|
-
- We are not checking the labels for now.
|
104
|
-
"""
|
105
|
-
|
106
|
-
if other.size == 1: # Meaning it is scalar
|
107
|
-
return True
|
108
|
-
|
109
|
-
axis1_arr = np.asarray(self)
|
110
|
-
axis2_arr = np.asarray(other)
|
111
|
-
|
112
|
-
if not isinstance(axis2_arr, type(axis1_arr)):
|
113
|
-
return False
|
114
|
-
if axis1_arr.shape != axis2_arr.shape:
|
115
|
-
return False
|
116
|
-
if not np.allclose(axis1_arr, axis2_arr):
|
117
|
-
return False
|
118
|
-
|
119
|
-
return True
|
120
|
-
|
121
|
-
def copy(self) -> Self:
|
122
|
-
"""
|
123
|
-
Return a new copy of SAx object.
|
124
|
-
|
125
|
-
Returns
|
126
|
-
-------
|
127
|
-
SAx
|
128
|
-
A new copy of the SAx object.
|
129
|
-
"""
|
130
|
-
copied_values = np.asarray(self).copy()
|
131
|
-
copied_label = self.label
|
132
|
-
|
133
|
-
return self.__class__(values=copied_values, label=copied_label)
|
134
|
-
|
135
|
-
def set_meta_info(self, label):
|
136
|
-
"""
|
137
|
-
Set meta info for the axis.
|
138
|
-
|
139
|
-
.. code-block:: python
|
140
|
-
|
141
|
-
import modusa as ms
|
142
|
-
x = ms.sax.linear(100, 10)
|
143
|
-
print(x)
|
144
|
-
x = x.set_meta_info("My Axis (unit)")
|
145
|
-
print(x)
|
146
|
-
|
147
|
-
# I personally prefer setting it inline
|
148
|
-
x = ms.sax.linear(100, 10).set_meta_info("My Axis (unit)")
|
149
|
-
print(x)
|
150
|
-
|
151
|
-
Parameters
|
152
|
-
----------
|
153
|
-
label: str
|
154
|
-
Label for the axis (e.g. "Time (sec)").
|
155
|
-
Returns
|
156
|
-
-------
|
157
|
-
Self
|
158
|
-
A new Self instance with new label.
|
159
|
-
"""
|
160
|
-
|
161
|
-
if label is None:
|
162
|
-
return self
|
163
|
-
else:
|
164
|
-
return self.__class__(values=self.values.copy(), label=label)
|
165
|
-
|
166
|
-
|
167
|
-
def index_of(self, value) -> int:
|
168
|
-
"""
|
169
|
-
Return the index whose value is closest
|
170
|
-
to `value`.
|
171
|
-
|
172
|
-
Parameters
|
173
|
-
----------
|
174
|
-
value: float
|
175
|
-
value to find the index of.
|
176
|
-
|
177
|
-
Returns
|
178
|
-
-------
|
179
|
-
int
|
180
|
-
Index with value closest to the `value`
|
181
|
-
"""
|
182
|
-
from .data import Data
|
183
|
-
|
184
|
-
idx = np.argmin(np.abs(self.values - value))
|
185
|
-
|
186
|
-
return Data(values=idx, label=None)
|
187
|
-
|
188
|
-
#====================================
|
189
|
-
|
190
|
-
#-------------------------------
|
191
|
-
# NumPy Protocol
|
192
|
-
#-------------------------------
|
193
|
-
|
194
|
-
def __array__(self, dtype=None) -> np.ndarray:
|
195
|
-
return np.asarray(self.values, dtype=dtype)
|
196
|
-
|
197
|
-
def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
|
198
|
-
"""
|
199
|
-
Provides operation support for the universal functions
|
200
|
-
on the Data object.
|
201
|
-
"""
|
202
|
-
from .s1d import S1D
|
203
|
-
from .data import Data
|
204
|
-
|
205
|
-
raw_inputs = [x.values if isinstance(x, type(self)) else x for x in inputs]
|
206
|
-
|
207
|
-
# Call the actual ufunc
|
208
|
-
result = getattr(ufunc, method)(*raw_inputs, **kwargs)
|
209
|
-
|
210
|
-
if isinstance(result, (np.ndarray, np.generic)):
|
211
|
-
y = Data(values=result, label=None)
|
212
|
-
x = self
|
213
|
-
return S1D(y=y, x=x, title=None)
|
214
|
-
else:
|
215
|
-
return result
|
216
|
-
|
217
|
-
def __array_function__(self, func, types, args, kwargs):
|
218
|
-
"""
|
219
|
-
Additional numpy function support.
|
220
|
-
"""
|
221
|
-
from modusa.utils import np_func_cat as nfc
|
222
|
-
from .data import Data
|
223
|
-
|
224
|
-
if not all(issubclass(t, type(self)) for t in types):
|
225
|
-
return NotImplemented
|
226
|
-
|
227
|
-
# Not supporting concatenate like operations as axis any random axis can't be concatenated
|
228
|
-
if func in nfc.CONCAT_FUNCS:
|
229
|
-
raise NotImplementedError(f"`{func.__name__}` is not yet tested on modusa signal, please create a GitHub issue.")
|
230
|
-
|
231
|
-
# Single signal input expected
|
232
|
-
x = args[0]
|
233
|
-
x_arr = np.asarray(x)
|
234
|
-
result = func(x_arr, **kwargs)
|
235
|
-
|
236
|
-
if func in nfc.REDUCTION_FUNCS:
|
237
|
-
# If the number of dimensions is reduced
|
238
|
-
if result.ndim == 0:
|
239
|
-
return Data(values=result, label=None)
|
240
|
-
else:
|
241
|
-
raise RuntimeError(f"Unexpected result: `result` has more than 0 dimensions, {result.ndim}")
|
242
|
-
|
243
|
-
elif func in nfc.X_NEEDS_ADJUSTMENT_FUNCS:
|
244
|
-
# You must define logic for adjusting x
|
245
|
-
raise NotImplementedError(f"{func.__name__} requires x-axis adjustment logic.")
|
246
|
-
|
247
|
-
else:
|
248
|
-
raise NotImplementedError(f"`{func.__name__}` is not yet tested on modusa signal, please create a GitHub issue.")
|
249
|
-
|
250
|
-
#================================
|
251
|
-
|
252
|
-
|
253
|
-
#------------------------------------
|
254
|
-
# Visualisation
|
255
|
-
#------------------------------------
|
256
|
-
|
257
|
-
def plot(self, ax = None, fmt = "b-", show_stem=False):
|
258
|
-
"""
|
259
|
-
Plot the axis values. This is useful to analyse
|
260
|
-
the axis if it is linear or follows some other
|
261
|
-
trend.
|
262
|
-
|
263
|
-
.. code-block:: python
|
264
|
-
|
265
|
-
import modusa as ms
|
266
|
-
x = ms.sax.linear(100, 10)
|
267
|
-
x.plot()
|
268
|
-
|
269
|
-
Parameters
|
270
|
-
----------
|
271
|
-
ax: plt.Axes | None
|
272
|
-
- Incase, you want to plot it on your defined matplot ax.
|
273
|
-
- If not provided, We create a new figure and return that.
|
274
|
-
fmt: str
|
275
|
-
- Matplotlib fmt for setting different colors and styles for the plot.
|
276
|
-
- General fmt is [color][marker][line], e.g. "bo:"
|
277
|
-
- Useful while plotting multiple SAx instances on the same plot.
|
278
|
-
|
279
|
-
Returns
|
280
|
-
-------
|
281
|
-
plt.Figure | None
|
282
|
-
- Figure if ax is None.
|
283
|
-
- None is ax is not None.
|
284
|
-
"""
|
285
|
-
from modusa.tools.plotter import Plotter
|
286
|
-
|
287
|
-
fig: plt.Figure | None = Plotter.plot_signal(y=self.values, x=np.arange(len(self)), ax=ax, fmt=fmt, title=self.label, y_label=self.label, x_label="Index", show_stem=show_stem)
|
288
|
-
|
289
|
-
return fig
|
290
|
-
#====================================
|
291
|
-
|
292
|
-
#-----------------------------------
|
293
|
-
# Indexing
|
294
|
-
#-----------------------------------
|
295
|
-
|
296
|
-
def __getitem__(self, key) -> Self:
|
297
|
-
"""
|
298
|
-
Defining how to index SAx instance.
|
299
|
-
|
300
|
-
.. code-block:: python
|
301
|
-
|
302
|
-
import modusa as ms
|
303
|
-
x = ms.sax.linear(100, 10)
|
304
|
-
print(x)
|
305
|
-
print(x[10:20])
|
306
|
-
|
307
|
-
Parameters
|
308
|
-
----------
|
309
|
-
key: int | slice
|
310
|
-
What can go inside the square bracket [] for indexing.
|
311
|
-
|
312
|
-
Returns
|
313
|
-
-------
|
314
|
-
SAx:
|
315
|
-
Sliced instance of the axis.
|
316
|
-
"""
|
317
|
-
|
318
|
-
from .s1d import S1D
|
319
|
-
|
320
|
-
if not isinstance(key, (int, slice, tuple)):
|
321
|
-
raise TypeError(f"Invalid key type {type(key)}")
|
322
|
-
|
323
|
-
sliced_values = self.values[key]
|
324
|
-
|
325
|
-
# If the number of dimensions is reduced, add back singleton dims
|
326
|
-
while np.ndim(sliced_values) < self.ndim:
|
327
|
-
sliced_values = np.expand_dims(sliced_values, axis=0)
|
328
|
-
|
329
|
-
return self.__class__(values=sliced_values, label=self.label)
|
330
|
-
|
331
|
-
|
332
|
-
def __setitem__(self, key, value):
|
333
|
-
"""
|
334
|
-
Raises error if trying to set values
|
335
|
-
of an axis.
|
336
|
-
|
337
|
-
Meaningful axis are not meant to be altered.
|
338
|
-
"""
|
339
|
-
raise TypeError("Axis do not support item assignment.")
|
340
|
-
|
341
|
-
#===============================
|
342
|
-
|
343
|
-
#-------------------------------
|
344
|
-
# Basic arithmetic operations
|
345
|
-
#-------------------------------
|
346
|
-
def __add__(self, other):
|
347
|
-
return np.add(self, other)
|
348
|
-
|
349
|
-
def __radd__(self, other):
|
350
|
-
return np.add(other, self)
|
351
|
-
|
352
|
-
def __sub__(self, other):
|
353
|
-
return np.subtract(self, other)
|
354
|
-
|
355
|
-
def __rsub__(self, other):
|
356
|
-
return np.subtract(other, self)
|
357
|
-
|
358
|
-
def __mul__(self, other):
|
359
|
-
return np.multiply(self, other)
|
360
|
-
|
361
|
-
def __rmul__(self, other):
|
362
|
-
return np.multiply(other, self)
|
363
|
-
|
364
|
-
def __truediv__(self, other):
|
365
|
-
return np.divide(self, other)
|
366
|
-
|
367
|
-
def __rtruediv__(self, other):
|
368
|
-
return np.divide(other, self)
|
369
|
-
|
370
|
-
def __floordiv__(self, other):
|
371
|
-
return np.floor_divide(self, other)
|
372
|
-
|
373
|
-
def __rfloordiv__(self, other):
|
374
|
-
return np.floor_divide(other, self)
|
375
|
-
|
376
|
-
def __pow__(self, other):
|
377
|
-
return np.power(self, other)
|
378
|
-
|
379
|
-
def __rpow__(self, other):
|
380
|
-
return np.power(other, self)
|
381
|
-
|
382
|
-
#===============================
|
383
|
-
|
384
|
-
|
385
|
-
#-------------------------------
|
386
|
-
# Basic comparison operations
|
387
|
-
#-------------------------------
|
388
|
-
def __eq__(self, other):
|
389
|
-
return np.equal(self, other)
|
390
|
-
|
391
|
-
def __ne__(self, other):
|
392
|
-
return np.not_equal(self, other)
|
393
|
-
|
394
|
-
def __lt__(self, other):
|
395
|
-
return np.less(self, other)
|
396
|
-
|
397
|
-
def __le__(self, other):
|
398
|
-
return np.less_equal(self, other)
|
399
|
-
|
400
|
-
def __gt__(self, other):
|
401
|
-
return np.greater(self, other)
|
402
|
-
|
403
|
-
def __ge__(self, other):
|
404
|
-
return np.greater_equal(self, other)
|
405
|
-
|
406
|
-
#===============================
|
407
|
-
|
408
|
-
#----------------------------------
|
409
|
-
# Information
|
410
|
-
#----------------------------------
|
411
|
-
|
412
|
-
def print_info(self) -> None:
|
413
|
-
"""
|
414
|
-
Prints info about the SAx instance.
|
415
|
-
|
416
|
-
.. code-block:: python
|
417
|
-
|
418
|
-
import modusa as ms
|
419
|
-
# For SAx
|
420
|
-
x = ms.sax.linear(100, 10)
|
421
|
-
x.print_info()
|
422
|
-
# For TAx
|
423
|
-
x = ms.tax.linear(100, 10)
|
424
|
-
x.print_info()
|
425
|
-
|
426
|
-
Returns
|
427
|
-
-------
|
428
|
-
None
|
429
|
-
"""
|
430
|
-
print("-" * 50)
|
431
|
-
print("Axis Info")
|
432
|
-
print("-" * 50)
|
433
|
-
print(f"{'Label':<20}: {self.label}")
|
434
|
-
print(f"{'Shape':<20}: {self.shape}")
|
435
|
-
# Inheritance chain
|
436
|
-
cls_chain = " → ".join(cls.__name__ for cls in reversed(self.__class__.__mro__[:-1]))
|
437
|
-
print(f"{'Inheritance':<20}: {cls_chain}")
|
438
|
-
print("=" * 50)
|
439
|
-
|
440
|
-
def __str__(self):
|
441
|
-
arr_str = np.array2string(self.values, separator=", ", threshold=30, edgeitems=3, max_line_width=120)
|
442
|
-
return f"{self._nickname}({arr_str})"
|
443
|
-
|
444
|
-
def __repr__(self):
|
445
|
-
arr_str = np.array2string(self.values, separator=", ", threshold=30, edgeitems=3, max_line_width=120)
|
446
|
-
return f"{self._nickname}({arr_str})"
|
447
|
-
#===================================
|
448
|
-
|