modusa 0.3.21__py3-none-any.whl → 0.3.23__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 CHANGED
@@ -1,8 +1,9 @@
1
1
  from modusa.utils import excp, config
2
2
 
3
3
  #=====Giving access to plot functions to plot multiple signals.=====
4
- from modusa.tools import plot1d, plot2d
4
+ from modusa.tools import plot1d, plot2d, plot_dist
5
+ #=====
6
+
5
7
  from modusa.tools import play, convert
6
8
  from modusa.tools import download
7
- from modusa.tools import load
8
- #=====
9
+ from modusa.tools import load, load_ann
@@ -9,17 +9,15 @@ from collections import defaultdict
9
9
  # === Configuration ===
10
10
  BASE_MODULES = [
11
11
  'modusa.tools',
12
- 'modusa.models',
13
- 'modusa.generators',
14
- 'modusa.plugins',
15
- # 'modusa.io'
12
+ # 'modusa.models',
13
+ # 'modusa.generators',
14
+ # 'modusa.plugins',
16
15
  ]
17
16
  OUTPUT_DIRS = [
18
17
  Path('docs/source/tools'),
19
- Path('docs/source/models'),
20
- Path('docs/source/generators'),
21
- Path('docs/source/plugins'),
22
- # Path('docs/source/io')
18
+ # Path('docs/source/models'),
19
+ # Path('docs/source/generators'),
20
+ # Path('docs/source/plugins'),
23
21
  ]
24
22
 
25
23
  # Ensure output directories exist
@@ -89,8 +87,6 @@ def generate_docs_source():
89
87
  write_module_rst_file(module_path, class_list, output_dir)
90
88
 
91
89
  section_name = base_module.split('.')[-1].capitalize()
92
- if section_name == "Io":
93
- section_name = "IO"
94
90
 
95
91
  write_index_rst_file(module_class_map, output_dir, section_name=section_name)
96
92
  print(f"✅ Documentation generated for {base_module} in {output_dir}")
modusa/tools/__init__.py CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env python3
2
2
 
3
- from .plotter import plot1d, plot2d
3
+ from .plotter import plot1d, plot2d, plot_dist
4
4
  from .audio_player import play
5
5
  from .audio_converter import convert
6
6
  from .youtube_downloader import download
7
- from .audio_loader import load
7
+ from .audio_loader import load
8
+ from .ann_loader import load_ann
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env python3
2
+
3
+ #---------------------------------
4
+ # Author: Ankit Anand
5
+ # Date: 12/08/25
6
+ # Email: ankit0.anand0@gmail.com
7
+ #---------------------------------
8
+
9
+ from pathlib import Path
10
+
11
+ def load_ann(path):
12
+ """
13
+ Load annotation from audatity label text file.
14
+
15
+ Parameters
16
+ ----------
17
+ path: str
18
+ label text file path.
19
+
20
+ Returns
21
+ -------
22
+ list[tuple, ...]
23
+ - annotation data structure
24
+ - [(start, end, tag), ...]
25
+ """
26
+
27
+ if isinstance(path, (str, Path)):
28
+ raise ValueError(f"`path` must be one of (str, Path), got {type(path)}")
29
+
30
+ ann = []
31
+ with open(str(path), "r") as f:
32
+ lines = [line.rstrip("\n") for line in f]
33
+ for line in lines:
34
+ start, end, tag = line.split("\t")
35
+ start, end = float(start), float(end)
36
+ ann.append((start, end, tag))
37
+
38
+ return ann
modusa/tools/plotter.py CHANGED
@@ -59,7 +59,7 @@ def plot1d(*args, ann=None, events=None, xlim=None, ylim=None, xlabel=None, ylab
59
59
  plt.Figure
60
60
  Matplolib figure.
61
61
  """
62
-
62
+
63
63
  for arg in args:
64
64
  if len(arg) not in [1, 2]: # 1 if it just provides values, 2 if it provided axis as well
65
65
  raise ValueError(f"1D signal needs to have max 2 arrays (y, x) or simply (y, )")
@@ -115,9 +115,16 @@ def plot1d(*args, ann=None, events=None, xlim=None, ylim=None, xlabel=None, ylab
115
115
  width = end - start
116
116
  rect = Rectangle((start, 0), width, 1, color=color, alpha=0.7)
117
117
  annotation_ax.add_patch(rect)
118
- annotation_ax.text((start + end) / 2, 0.5, tag,
119
- ha='center', va='center',
120
- fontsize=10, color='white', fontweight='bold', zorder=10)
118
+
119
+ text_obj = annotation_ax.text(
120
+ (start + end) / 2, 0.5, tag,
121
+ ha='center', va='center',
122
+ fontsize=10, color='white', fontweight='bold', zorder=10, clip_on=True
123
+ )
124
+
125
+ text_obj.set_clip_path(rect)
126
+
127
+
121
128
  # Add vlines
122
129
  if events is not None:
123
130
  for xpos in events:
@@ -140,7 +147,7 @@ def plot1d(*args, ann=None, events=None, xlim=None, ylim=None, xlabel=None, ylab
140
147
  if ylabel is not None:
141
148
  signal_ax.set_ylabel(ylabel)
142
149
 
143
- # Decorating annotation axis thicker
150
+ # Remove the boundaries and ticks from an axis
144
151
  if ann is not None:
145
152
  annotation_ax.tick_params(left=False, bottom=False, labelleft=False, labelbottom=False)
146
153
  else:
@@ -296,9 +303,14 @@ def plot2d(*args, ann=None, events=None, xlim=None, ylim=None, origin="lower", M
296
303
  width = end - start
297
304
  rect = Rectangle((start, 0), width, 1, color=color, alpha=0.7)
298
305
  annotation_ax.add_patch(rect)
299
- annotation_ax.text((start + end) / 2, 0.5, tag,
300
- ha='center', va='center',
301
- fontsize=10, color='white', fontweight='bold', zorder=10)
306
+ text_obj = annotation_ax.text(
307
+ (start + end) / 2, 0.5, tag,
308
+ ha='center', va='center',
309
+ fontsize=10, color='white', fontweight='bold', zorder=10, clip_on=True
310
+ )
311
+
312
+ text_obj.set_clip_path(rect)
313
+
302
314
  # Add vlines
303
315
  if events is not None:
304
316
  for xpos in events:
@@ -350,4 +362,142 @@ def plot2d(*args, ann=None, events=None, xlim=None, ylim=None, origin="lower", M
350
362
 
351
363
  fig.subplots_adjust(hspace=0.01, wspace=0.05)
352
364
  plt.close()
353
- return fig
365
+ return fig
366
+
367
+ #======== Plot distribution ===========
368
+ def plot_dist(*args, ann=None, xlim=None, ylim=None, ylabel=None, xlabel=None, title=None, legend=None, show_hist=True, npoints=200):
369
+ """
370
+ Plot distribution.
371
+
372
+ .. code-block:: python
373
+
374
+ import modusa as ms
375
+ import numpy as np
376
+ np.random.seed(42)
377
+ data = np.random.normal(loc=1, scale=1, size=1000)
378
+ ms.plot_dist(data, data+5, data-10, ann=[(0, 1, "A")], legend=("D1", "D2", "D3"), ylim=(0, 1), xlabel="X", ylabel="Counts", title="Distribution")
379
+
380
+ Parameters
381
+ ----------
382
+ *args: ndarray
383
+ - Data arrays for which distribution needs to be plotted.
384
+ - Arrays will be flattened.
385
+ ann : list[tuple[Number, Number, str] | None
386
+ - A list of annotations to mark specific points. Each tuple should be of the form (start, end, label).
387
+ - Default: None => No annotation.
388
+ events : list[Number] | None
389
+ - A list of x-values where vertical lines (event markers) will be drawn.
390
+ - Default: None
391
+ xlim : tuple[Number, Number] | None
392
+ - Limits for the x-axis as (xmin, xmax).
393
+ - Default: None
394
+ ylim : tuple[Number, Number] | None
395
+ - Limits for the y-axis as (ymin, ymax).
396
+ - Default: None
397
+ xlabel : str | None
398
+ - Label for the x-axis.
399
+ - - Default: None
400
+ ylabel : str | None
401
+ - Label for the y-axis.
402
+ - Default: None
403
+ title : str | None
404
+ - Title of the plot.
405
+ - Default: None
406
+ legend : list[str] | None
407
+ - List of legend labels corresponding to each signal if plotting multiple distributions.
408
+ - Default: None
409
+ show_hist: bool
410
+ - Want to show histogram as well.
411
+ npoints: int
412
+ - Number of points for which gaussian needs to be computed between min and max.
413
+ - Higher value means more points are evaluated with the fitted gaussian, thereby higher resolution.
414
+
415
+ Returns
416
+ -------
417
+ plt.Figure
418
+ - Matplotlib figure.
419
+ """
420
+ from scipy.stats import gaussian_kde
421
+
422
+ if isinstance(legend, str):
423
+ legend = (legend, )
424
+
425
+ if legend is not None:
426
+ if len(legend) < len(args):
427
+ raise ValueError(f"Legend should be provided for each signal.")
428
+
429
+ # Create figure
430
+ fig = plt.figure(figsize=(16, 4))
431
+ gs = gridspec.GridSpec(2, 1, height_ratios=[0.1, 1])
432
+
433
+ colors = plt.get_cmap('tab10').colors
434
+
435
+ dist_ax = fig.add_subplot(gs[1, 0])
436
+ annotation_ax = fig.add_subplot(gs[0, 0], sharex=dist_ax)
437
+
438
+ # Set limits
439
+ if xlim is not None:
440
+ dist_ax.set_xlim(xlim)
441
+
442
+ if ylim is not None:
443
+ dist_ax.set_ylim(ylim)
444
+
445
+ # Add plot
446
+ for i, data in enumerate(args):
447
+ # Fit gaussian to the data
448
+ kde = gaussian_kde(data)
449
+
450
+ # Create points to evaluate KDE
451
+ x_vals = np.linspace(min(data), max(data), npoints)
452
+ y_vals = kde(x_vals)
453
+
454
+ if legend is not None:
455
+ dist_ax.plot(x_vals, y_vals, color=colors[i], label=legend[i])
456
+ if show_hist is True:
457
+ dist_ax.hist(data, bins=30, density=True, alpha=0.3, facecolor=colors[i], edgecolor='black', label=legend[i])
458
+ else:
459
+ dist_ax.plot(x_vals, y_vals, color=colors[i])
460
+ if show_hist is True:
461
+ dist_ax.hist(data, bins=30, density=True, alpha=0.3, facecolor=colors[i], edgecolor='black', label=legeng[i])
462
+
463
+ # Add annotations
464
+ if ann is not None:
465
+ annotation_ax.set_ylim(0, 1)
466
+ for i, (start, end, tag) in enumerate(ann):
467
+ if xlim is not None:
468
+ if end < xlim[0] or start > xlim[1]:
469
+ continue # Skip out-of-view regions
470
+ # Clip boundaries to xlim
471
+ start = max(start, xlim[0])
472
+ end = min(end, xlim[1])
473
+
474
+ color = colors[i % len(colors)]
475
+ width = end - start
476
+ rect = Rectangle((start, 0), width, 1, color=color, alpha=0.7)
477
+ annotation_ax.add_patch(rect)
478
+
479
+ text_obj = annotation_ax.text((start + end) / 2, 0.5, tag, ha='center', va='center', fontsize=10, color='white', fontweight='bold', zorder=10, clip_on=True)
480
+ text_obj.set_clip_path(rect)
481
+
482
+ # Add legend
483
+ if legend is not None:
484
+ handles, labels = dist_ax.get_legend_handles_labels()
485
+ fig.legend(handles, labels, loc='upper right', bbox_to_anchor=(0.9, 1.1), ncol=len(legend), frameon=True)
486
+
487
+ # Set title, labels
488
+ if title is not None:
489
+ annotation_ax.set_title(title, pad=10, size=11)
490
+ if xlabel is not None:
491
+ dist_ax.set_xlabel(xlabel)
492
+ if ylabel is not None:
493
+ dist_ax.set_ylabel(ylabel)
494
+
495
+ # Remove the boundaries and ticks from annotation axis
496
+ if ann is not None:
497
+ annotation_ax.tick_params(left=False, bottom=False, labelleft=False, labelbottom=False)
498
+ else:
499
+ annotation_ax.axis("off")
500
+
501
+ fig.subplots_adjust(hspace=0.01, wspace=0.05)
502
+ plt.close()
503
+ return fig
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: modusa
3
- Version: 0.3.21
3
+ Version: 0.3.23
4
4
  Summary: A modular signal analysis python library.
5
5
  Author-Email: Ankit Anand <ankit0.anand0@gmail.com>
6
6
  License: MIT
@@ -1,12 +1,12 @@
1
- modusa-0.3.21.dist-info/METADATA,sha256=4eI0lipMc4DDbHET_5se-4HUd6bo4u-fuKBu-YyAi6c,1369
2
- modusa-0.3.21.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
- modusa-0.3.21.dist-info/entry_points.txt,sha256=fmKpleVXj6CdaBVL14WoEy6xx7JQCs85jvzwTi3lePM,73
4
- modusa-0.3.21.dist-info/licenses/LICENSE.md,sha256=JTaXAjx5awk76VArKCx5dUW8vmLEWsL_ZlR7-umaHbA,1078
1
+ modusa-0.3.23.dist-info/METADATA,sha256=4WtEHA0q3OuyzT_zEwzH_c4N7jjfrtD2b2AxqGNh9Eo,1369
2
+ modusa-0.3.23.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
+ modusa-0.3.23.dist-info/entry_points.txt,sha256=fmKpleVXj6CdaBVL14WoEy6xx7JQCs85jvzwTi3lePM,73
4
+ modusa-0.3.23.dist-info/licenses/LICENSE.md,sha256=JTaXAjx5awk76VArKCx5dUW8vmLEWsL_ZlR7-umaHbA,1078
5
5
  modusa/.DS_Store,sha256=_gm6qJREwfMi8dE7n5S89_RG46u5t3xHyD-smNhtNoM,6148
6
- modusa/__init__.py,sha256=AALG_17vibpDZbo0MymvGoQroaLp3uVm_onZaESONQA,257
6
+ modusa/__init__.py,sha256=uq6kORFFAODiCMGmOLWO0shE8-dVFWf5gmV8wxekmnk,280
7
7
  modusa/config.py,sha256=bTqK4t00FZqERVITrxW_q284aDDJAa9aMSfFknfR-oU,280
8
8
  modusa/decorators.py,sha256=8zeNX_wE37O6Vp0ysR4-WCZaEL8mq8dyCF_I5DHOzks,5905
9
- modusa/devtools/generate_docs_source.py,sha256=qqxr3UgTeM4iotarQGRN_q2QXwAzGH7ha9bVjs8D4GQ,3409
9
+ modusa/devtools/generate_docs_source.py,sha256=UDflHsk-Yh9-3YJTVBzKL32y8hcxiRgAlFEBTMiDqwM,3301
10
10
  modusa/devtools/generate_template.py,sha256=0sT_5-SjMLXyvePRDaIxr1RCz_9v6zFH9OoJqpuD2zI,5204
11
11
  modusa/devtools/list_authors.py,sha256=FWBQKOLznVthvMYMASrx2Gw5lqKHEOccQpBisDZ53Dw,24
12
12
  modusa/devtools/list_plugins.py,sha256=g-R5hoaCzHfClY_Sfa788IQ9CAKzQGTfyqmthXDZQqw,1729
@@ -27,7 +27,6 @@ modusa/generators/s2d.py,sha256=kU67dZj4tdIDSUFJeheXm_JKbyHpZZOGmW5jya6w0wo,6874
27
27
  modusa/generators/s_ax.py,sha256=4CTFp_KwYnl4HDno-ATpTXOfKR9WEVOV45nwuk2OoVk,2221
28
28
  modusa/generators/t_ax.py,sha256=X-XOJuGWw99dPAVsCVzNTfBFr81Vw56aZlGDmcl5q3k,1478
29
29
  modusa/generators/tds.py,sha256=eGvzcjXyWaw5NVzM3D098r3xkoMcX8Ma9YoDUdL30Mo,5960
30
- modusa/main.py,sha256=v9oOhnPcbw8ULfkKRoantEfHw7MqOETJwXC-odx3dnA,25
31
30
  modusa/models/__init__.py,sha256=G8sNOnBnTKqPmC4Z46ximBhc_pfBOF_G6AIfxT8DFzw,271
32
31
  modusa/models/__pycache__/signal1D.cpython-312.pyc.4443461152,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
32
  modusa/models/audio.py,sha256=IcNx3h8tb5Jt6KZug_TQKM5iufVk3i7Ug37iKG4gsJ0,2411
@@ -41,14 +40,14 @@ modusa/models/t_ax.py,sha256=ZUhvZPUW1TkdZYuUd6Ucm-vsv0JqtZ9yEe3ab67Ma6w,8022
41
40
  modusa/models/tds.py,sha256=FAGfibjyyE_lkEuQp-vSCuqQnopOjmy_IXqUjRlg9kc,11677
42
41
  modusa/plugins/__init__.py,sha256=r1Bf5mnrVKRIwxboutY1iGzDy4EPQhqpk1kSW7iJj_Q,54
43
42
  modusa/plugins/base.py,sha256=Bh_1Bja7fOymFsCgwhXDbV6ys3D8muNrPwrfDrG_G_A,2382
44
- modusa/tmp.py,sha256=zPwgFVk5Oh6Rp6GUCbWJdkmwo6vYvhSqI35QVhW5xQM,3180
45
- modusa/tools/__init__.py,sha256=jOFL7PJ1IztN6F7aMdrVKNMTid2yUmoSlSST-WFf0Cc,199
43
+ modusa/tools/__init__.py,sha256=ixqujVIJwU5TNX2j64lVajGtZlXTspcxN-30X54GgcM,243
44
+ modusa/tools/ann_loader.py,sha256=hsSpjXmAJOCsgKX4j202t8lPzgmBkAyU0278sSbBriM,776
46
45
  modusa/tools/audio_converter.py,sha256=415qBoPm2sBIuBSI7m1XBKm0AbmVmPydIPPr-uO8D3c,1778
47
46
  modusa/tools/audio_loader.py,sha256=DrCzq0pdiQrUDIG-deLJGcu8EaylO5yRtwT4lr8WSf8,2166
48
47
  modusa/tools/audio_player.py,sha256=GP04TWW4jBwQBjANkfR_cJtEy7cIhvbu8RTwnf9hD6E,2817
49
48
  modusa/tools/base.py,sha256=C0ESJ0mIfjjRlAkRbSetNtMoOfS6IrHBjexRp3l_Mh4,1293
50
49
  modusa/tools/math_ops.py,sha256=ZZ7U4DgqT7cOeE7_Lzi_Qq-48WYfwR9_osbZwTmE9eg,8690
51
- modusa/tools/plotter.py,sha256=lxc6T9H8gKYU3pbFZlQYHXb7p5rGNzJol73ClElCZ_g,10579
50
+ modusa/tools/plotter.py,sha256=U-Xkm8Br7Ba5C60xBuApd1qf9AScJ1llH_ichQksrxY,15295
52
51
  modusa/tools/youtube_downloader.py,sha256=hB_X8-7nOHXOlxg6vv3wyhBLoAsWyomrULP6_uCQL7s,1698
53
52
  modusa/utils/.DS_Store,sha256=nLXMwF7QJNuglLI_Gk74F7vl5Dyus2Wd74Mgowijmdo,6148
54
53
  modusa/utils/__init__.py,sha256=1oLL20yLB1GL9IbFiZD8OReDqiCpFr-yetIR6x1cNkI,23
@@ -57,4 +56,4 @@ modusa/utils/excp.py,sha256=L9vhaGjKpv9viJYdmC9n5ndmk2GVbUBuFyZyhAQZmWY,906
57
56
  modusa/utils/logger.py,sha256=K0rsnObeNKCxlNeSnVnJeRhgfmob6riB2uyU7h3dDmA,571
58
57
  modusa/utils/np_func_cat.py,sha256=TyIFgRc6bARRMDnZxlVURO5Z0I-GWhxRONYyIv-Vwxs,1007
59
58
  modusa/utils/plot.py,sha256=s_vNdxvKfwxEngvJPgrF1PcmxZNnNaaXPViHWjyjJ-c,5335
60
- modusa-0.3.21.dist-info/RECORD,,
59
+ modusa-0.3.23.dist-info/RECORD,,
modusa/main.py DELETED
@@ -1,3 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
-
modusa/tmp.py DELETED
@@ -1,98 +0,0 @@
1
- #def autocorr(self) -> Self:
2
- # """
3
- #
4
- # """
5
- # raise NotImplementedError
6
- # r = np.correlate(self.data, self.data, mode="full")
7
- # r = r[self.data.shape[0] - 1:]
8
- # r_signal = self.__class__(data=r, sr=self.sr, t0=self.t0, title=self.title + " [Autocorr]")
9
- # return r_signal
10
-
11
- # #----------------------------
12
- # # To different signals
13
- # #----------------------------
14
- # def to_audio_signal(self) -> "AudioSignal":
15
- # """
16
- # Moves TimeDomainSignal to AudioSignal
17
- # """
18
- # raise NotImplementedError
19
- # from modusa.signals.audio_signal import AudioSignal
20
- #
21
- # return AudioSignal(data=self.data, sr=self.sr, t0=self.t0, title=self.title)
22
- #
23
- # def to_spectrogram(
24
- # self,
25
- # n_fft: int = 2048,
26
- # hop_length: int = 512,
27
- # win_length: int | None = None,
28
- # window: str = "hann"
29
- # ) -> "Spectrogram":
30
- # """
31
- # Compute the Short-Time Fourier Transform (STFT) and return a Spectrogram object.
32
- #
33
- # Parameters
34
- # ----------
35
- # n_fft : int
36
- # FFT size.
37
- # win_length : int or None
38
- # Window length. Defaults to `n_fft` if None.
39
- # hop_length : int
40
- # Hop length between frames.
41
- # window : str
42
- # Type of window function to use (e.g., 'hann', 'hamming').
43
- #
44
- # Returns
45
- # -------
46
- # Spectrogram
47
- # Spectrogram object containing S (complex STFT), t (time bins), and f (frequency bins).
48
- # """
49
- # raise NotImplementedError
50
- # import warnings
51
- # warnings.filterwarnings("ignore", category=UserWarning, module="librosa.core.intervals")
52
- #
53
- # from modusa.signals.feature_time_domain_signal import FeatureTimeDomainSignal
54
- # import librosa
55
- #
56
- # S = librosa.stft(self.data, n_fft=n_fft, win_length=win_length, hop_length=hop_length, window=window)
57
- # f = librosa.fft_frequencies(sr=self.sr, n_fft=n_fft)
58
- # t = librosa.frames_to_time(np.arange(S.shape[1]), sr=self.sr, hop_length=hop_length)
59
- # frame_rate = self.sr / hop_length
60
- # spec = FeatureTimeDomainSignal(data=S, feature=f, feature_label="Freq (Hz)", frame_rate=frame_rate, t0=self.t0, time_label="Time (sec)", title=self.title)
61
- # if self.title != self._name: # Means title of the audio was reset so we pass that info to spec
62
- # spec = spec.set_meta_info(title=self.title)
63
- #
64
- # return spec
65
- # #=====================================
66
-
67
- #=====================================
68
-
69
- #--------------------------
70
- # Other signal ops
71
- #--------------------------
72
-
73
- # def interpolate(self, to: TimeDomainSignal, kind: str = "linear", fill_value: str | float = "extrapolate") -> TimeDomainSignal:
74
- # """
75
- # Interpolate the current signal to match the time axis of `to`.
76
- #
77
- # Parameters:
78
- # to (TimeDomainSignal): The signal whose time axis will be used.
79
- # kind (str): Interpolation method ('linear', 'nearest', etc.)
80
- # fill_value (str or float): Value used to fill out-of-bounds.
81
- #
82
- # Returns:
83
- # TimeDomainSignal: A new signal with values interpolated at `to.t`.
84
- # """
85
- # assert self.y.ndim == 1, "Only 1D signals supported for interpolation"
86
- #
87
- # interpolator = interp1d(
88
- # self.t,
89
- # self.y,
90
- # kind=kind,
91
- # fill_value=fill_value,
92
- # bounds_error=False,
93
- # assume_sorted=True
94
- # )
95
- #
96
- # y_interp = interpolator(to.y)
97
-
98
- # return self.__class__(y=y_interp, sr=to.sr, t0=to.t0, title=f"{self.title} → interpolated")