modusa 0.3.27__py3-none-any.whl → 0.3.29__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/tools/plotter.py CHANGED
@@ -7,8 +7,27 @@ import matplotlib.gridspec as gridspec
7
7
  from matplotlib.patches import Rectangle
8
8
  from mpl_toolkits.axes_grid1.inset_locator import inset_axes
9
9
 
10
+ # Helper for 2D plot
11
+ def _calculate_extent(x, y):
12
+ # Handle spacing safely
13
+ if len(x) > 1:
14
+ dx = x[1] - x[0]
15
+ else:
16
+ dx = 1 # Default spacing for single value
17
+ if len(y) > 1:
18
+ dy = y[1] - y[0]
19
+ else:
20
+ dy = 1 # Default spacing for single value
21
+
22
+ return [
23
+ x[0] - dx / 2,
24
+ x[-1] + dx / 2,
25
+ y[0] - dy / 2,
26
+ y[-1] + dy / 2
27
+ ]
28
+
10
29
  #======== 1D ===========
11
- def plot1d(*args, ann=None, events=None, xlim=None, ylim=None, xlabel=None, ylabel=None, title=None, legend=None):
30
+ def plot1d(*args, ann=None, events=None, xlim=None, ylim=None, xlabel=None, ylabel=None, title=None, legend=None, show_grid=False):
12
31
  """
13
32
  Plots a 1D signal using matplotlib.
14
33
 
@@ -53,6 +72,9 @@ def plot1d(*args, ann=None, events=None, xlim=None, ylim=None, xlabel=None, ylab
53
72
  legend : list[str] | None
54
73
  - List of legend labels corresponding to each signal if plotting multiple lines.
55
74
  - Default: None
75
+ show_grid: bool
76
+ - If you want to show the grid.
77
+ - Default: False
56
78
 
57
79
  Returns
58
80
  -------
@@ -63,6 +85,7 @@ def plot1d(*args, ann=None, events=None, xlim=None, ylim=None, xlabel=None, ylab
63
85
  for arg in args:
64
86
  if len(arg) not in [1, 2]: # 1 if it just provides values, 2 if it provided axis as well
65
87
  raise ValueError(f"1D signal needs to have max 2 arrays (y, x) or simply (y, )")
88
+
66
89
  if isinstance(legend, str): legend = (legend, )
67
90
 
68
91
  if legend is not None:
@@ -89,10 +112,11 @@ def plot1d(*args, ann=None, events=None, xlim=None, ylim=None, xlabel=None, ylab
89
112
  for i, signal in enumerate(args):
90
113
  if len(signal) == 1:
91
114
  y = signal[0]
115
+ x = np.arange(y.size)
92
116
  if legend is not None:
93
- signal_ax.plot(y, label=legend[i])
117
+ signal_ax.plot(x, y, label=legend[i])
94
118
  else:
95
- signal_ax.plot(y)
119
+ signal_ax.plot(x, y)
96
120
  elif len(signal) == 2:
97
121
  y, x = signal[0], signal[1]
98
122
  if legend is not None:
@@ -102,14 +126,19 @@ def plot1d(*args, ann=None, events=None, xlim=None, ylim=None, xlabel=None, ylab
102
126
 
103
127
  # Add annotations
104
128
  if ann is not None:
105
- annotation_ax.set_ylim(0, 1)
129
+ annotation_ax.set_ylim(0, 1) # For consistent layout
130
+ # Determine visible x-range
131
+ x_view_min = xlim[0] if xlim is not None else np.min(x)
132
+ x_view_max = xlim[1] if xlim is not None else np.max(x)
133
+
106
134
  for i, (start, end, tag) in enumerate(ann):
107
- if xlim is not None:
108
- if end < xlim[0] or start > xlim[1]:
109
- continue # Skip out-of-view regions
110
- # Clip boundaries to xlim
111
- start = max(start, xlim[0])
112
- end = min(end, xlim[1])
135
+ # We make sure that we only plot annotation that are within the x range of the current view
136
+ if start >= x_view_max or end <= x_view_min:
137
+ continue
138
+
139
+ # Clip boundaries to xlim
140
+ start = max(start, x_view_min)
141
+ end = min(end, x_view_max)
113
142
 
114
143
  color = colors[i % len(colors)]
115
144
  width = end - start
@@ -146,6 +175,10 @@ def plot1d(*args, ann=None, events=None, xlim=None, ylim=None, xlabel=None, ylab
146
175
  signal_ax.set_xlabel(xlabel)
147
176
  if ylabel is not None:
148
177
  signal_ax.set_ylabel(ylabel)
178
+
179
+ # Add grid to the plot
180
+ if show_grid is True:
181
+ signal_ax.grid(True, linestyle=':', linewidth=0.7, color='gray', alpha=0.7)
149
182
 
150
183
  # Remove the boundaries and ticks from an axis
151
184
  if ann is not None:
@@ -159,7 +192,7 @@ def plot1d(*args, ann=None, events=None, xlim=None, ylim=None, xlabel=None, ylab
159
192
  return fig
160
193
 
161
194
  #======== 2D ===========
162
- def plot2d(*args, ann=None, events=None, xlim=None, ylim=None, origin="lower", Mlabel=None, xlabel=None, ylabel=None, title=None, legend=None, lm=False):
195
+ def plot2d(*args, ann=None, events=None, xlim=None, ylim=None, origin="lower", Mlabel=None, xlabel=None, ylabel=None, title=None, legend=None, lm=False, show_grid=False):
163
196
  """
164
197
  Plots a 2D matrix (e.g., spectrogram or heatmap) with optional annotations and events.
165
198
 
@@ -213,6 +246,9 @@ def plot2d(*args, ann=None, events=None, xlim=None, ylim=None, origin="lower", M
213
246
  - Adds a circular marker for the line.
214
247
  - Default: False
215
248
  - Useful to show the data points.
249
+ show_grid: bool
250
+ - If you want to show the grid.
251
+ - Default: False
216
252
 
217
253
  Returns
218
254
  -------
@@ -276,28 +312,29 @@ def plot2d(*args, ann=None, events=None, xlim=None, ylim=None, origin="lower", M
276
312
  if len(signal) == 1: # It means that the axes were not passed
277
313
  y = np.arange(M.shape[0])
278
314
  x = np.arange(M.shape[1])
279
- dx = x[1] - x[0]
280
- dy = y[1] - y[0]
281
- extent=[x[0] - dx/2, x[-1] + dx/2, y[0] - dy/2, y[-1] + dy/2]
315
+ extent = _calculate_extent(x, y)
282
316
  im = signal_ax.imshow(M, aspect="auto", origin=origin, cmap="gray_r", extent=extent)
283
317
 
284
318
  elif len(signal) == 3: # It means that the axes were passed
285
319
  M, y, x = signal[0], signal[1], signal[2]
286
- dx = x[1] - x[0]
287
- dy = y[1] - y[0]
288
- extent=[x[0] - dx/2, x[-1] + dx/2, y[0] - dy/2, y[-1] + dy/2]
320
+ extent = _calculate_extent(x, y)
289
321
  im = signal_ax.imshow(M, aspect="auto", origin=origin, cmap="gray_r", extent=extent)
290
322
 
291
323
  # Add annotations
292
324
  if ann is not None:
293
- annotation_ax.set_ylim(0, 1)
325
+ annotation_ax.set_ylim(0, 1) # For consistent layout
326
+ # Determine visible x-range
327
+ x_view_min = xlim[0] if xlim is not None else np.min(x)
328
+ x_view_max = xlim[1] if xlim is not None else np.max(x)
329
+
294
330
  for i, (start, end, tag) in enumerate(ann):
295
- if xlim is not None:
296
- if end < xlim[0] or start > xlim[1]:
297
- continue # Skip out-of-view regions
298
- # Clip boundaries to xlim
299
- start = max(start, xlim[0])
300
- end = min(end, xlim[1])
331
+ # We make sure that we only plot annotation that are within the x range of the current view
332
+ if start >= x_view_max or end <= x_view_min:
333
+ continue
334
+
335
+ # Clip boundaries to xlim
336
+ start = max(start, x_view_min)
337
+ end = min(end, x_view_max)
301
338
 
302
339
  color = colors[i % len(colors)]
303
340
  width = end - start
@@ -352,7 +389,10 @@ def plot2d(*args, ann=None, events=None, xlim=None, ylim=None, origin="lower", M
352
389
  signal_ax.set_xlabel(xlabel)
353
390
  if ylabel is not None:
354
391
  signal_ax.set_ylabel(ylabel)
355
-
392
+
393
+ # Add grid to the plot
394
+ if show_grid is True:
395
+ signal_ax.grid(True, linestyle=':', linewidth=0.7, color='gray', alpha=0.7)
356
396
 
357
397
  # Making annotation axis spines thicker
358
398
  if ann is not None:
@@ -451,36 +491,40 @@ def plot_dist(*args, ann=None, xlim=None, ylim=None, ylabel=None, xlabel=None, t
451
491
  kde = gaussian_kde(data)
452
492
 
453
493
  # Create points to evaluate KDE
454
- x_vals = np.linspace(min(data), max(data), npoints)
455
- y_vals = kde(x_vals)
494
+ x = np.linspace(min(data), max(data), npoints)
495
+ y = kde(x)
456
496
 
457
497
  if legend is not None:
458
- dist_ax.plot(x_vals, y_vals, color=colors[i], label=legend[i])
498
+ dist_ax.plot(x, y, color=colors[i], label=legend[i])
459
499
  if show_hist is True:
460
500
  dist_ax.hist(data, bins=bins, density=True, alpha=0.3, facecolor=colors[i], edgecolor='black', label=legend[i])
461
501
  else:
462
- dist_ax.plot(x_vals, y_vals, color=colors[i])
502
+ dist_ax.plot(x, y, color=colors[i])
463
503
  if show_hist is True:
464
504
  dist_ax.hist(data, bins=bins, density=True, alpha=0.3, facecolor=colors[i], edgecolor='black')
465
505
 
466
506
  # Add annotations
467
507
  if ann is not None:
468
- annotation_ax.set_ylim(0, 1)
469
- for i, (start, end, tag) in enumerate(ann):
470
- if xlim is not None:
471
- if end < xlim[0] or start > xlim[1]:
472
- continue # Skip out-of-view regions
473
- # Clip boundaries to xlim
474
- start = max(start, xlim[0])
475
- end = min(end, xlim[1])
476
-
477
- color = colors[i % len(colors)]
478
- width = end - start
479
- rect = Rectangle((start, 0), width, 1, color=color, alpha=0.7)
480
- annotation_ax.add_patch(rect)
508
+ annotation_ax.set_ylim(0, 1) # For consistent layout
509
+ # Determine visible x-range
510
+ x_view_min = xlim[0] if xlim is not None else np.min(x)
511
+ x_view_max = xlim[1] if xlim is not None else np.max(x)
512
+ for i, (start, end, tag) in enumerate(ann):
513
+ # We make sure that we only plot annotation that are within the x range of the current view
514
+ if start >= x_view_max or end <= x_view_min:
515
+ continue
516
+
517
+ # Clip boundaries to xlim
518
+ start = max(start, x_view_min)
519
+ end = min(end, x_view_max)
481
520
 
482
- 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)
483
- text_obj.set_clip_path(rect)
521
+ color = colors[i % len(colors)]
522
+ width = end - start
523
+ rect = Rectangle((start, 0), width, 1, color=color, alpha=0.7)
524
+ annotation_ax.add_patch(rect)
525
+
526
+ 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)
527
+ text_obj.set_clip_path(rect)
484
528
 
485
529
  # Add legend
486
530
  if legend is not None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: modusa
3
- Version: 0.3.27
3
+ Version: 0.3.29
4
4
  Summary: A modular signal analysis python library.
5
5
  Author-Email: Ankit Anand <ankit0.anand0@gmail.com>
6
6
  License: MIT
@@ -1,7 +1,7 @@
1
- modusa-0.3.27.dist-info/METADATA,sha256=YE2Sxy3pyXXbzkIjB5wqMme9ENn6-ZkqsbRiOn8Tfbw,1369
2
- modusa-0.3.27.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
- modusa-0.3.27.dist-info/entry_points.txt,sha256=fmKpleVXj6CdaBVL14WoEy6xx7JQCs85jvzwTi3lePM,73
4
- modusa-0.3.27.dist-info/licenses/LICENSE.md,sha256=JTaXAjx5awk76VArKCx5dUW8vmLEWsL_ZlR7-umaHbA,1078
1
+ modusa-0.3.29.dist-info/METADATA,sha256=vQcmNOAAZljrLUb7gHJoqvkTgSNsdzhzxxu0vcM5YJo,1369
2
+ modusa-0.3.29.dist-info/WHEEL,sha256=9P2ygRxDrTJz3gsagc0Z96ukrxjr-LFBGOgv3AuKlCA,90
3
+ modusa-0.3.29.dist-info/entry_points.txt,sha256=fmKpleVXj6CdaBVL14WoEy6xx7JQCs85jvzwTi3lePM,73
4
+ modusa-0.3.29.dist-info/licenses/LICENSE.md,sha256=JTaXAjx5awk76VArKCx5dUW8vmLEWsL_ZlR7-umaHbA,1078
5
5
  modusa/.DS_Store,sha256=_gm6qJREwfMi8dE7n5S89_RG46u5t3xHyD-smNhtNoM,6148
6
6
  modusa/__init__.py,sha256=uq6kORFFAODiCMGmOLWO0shE8-dVFWf5gmV8wxekmnk,280
7
7
  modusa/config.py,sha256=bTqK4t00FZqERVITrxW_q284aDDJAa9aMSfFknfR-oU,280
@@ -47,7 +47,7 @@ modusa/tools/audio_loader.py,sha256=DrCzq0pdiQrUDIG-deLJGcu8EaylO5yRtwT4lr8WSf8,
47
47
  modusa/tools/audio_player.py,sha256=GP04TWW4jBwQBjANkfR_cJtEy7cIhvbu8RTwnf9hD6E,2817
48
48
  modusa/tools/base.py,sha256=C0ESJ0mIfjjRlAkRbSetNtMoOfS6IrHBjexRp3l_Mh4,1293
49
49
  modusa/tools/math_ops.py,sha256=ZZ7U4DgqT7cOeE7_Lzi_Qq-48WYfwR9_osbZwTmE9eg,8690
50
- modusa/tools/plotter.py,sha256=Aacg-ezlMFdx_BK8yTOdsBBRQIGSwmWBmTwANr5TtHs,15388
50
+ modusa/tools/plotter.py,sha256=A559FRQcBCVfVKYBq90b-YyZjwGayTCf5Gsn1Zxc1k8,16674
51
51
  modusa/tools/youtube_downloader.py,sha256=hB_X8-7nOHXOlxg6vv3wyhBLoAsWyomrULP6_uCQL7s,1698
52
52
  modusa/utils/.DS_Store,sha256=nLXMwF7QJNuglLI_Gk74F7vl5Dyus2Wd74Mgowijmdo,6148
53
53
  modusa/utils/__init__.py,sha256=1oLL20yLB1GL9IbFiZD8OReDqiCpFr-yetIR6x1cNkI,23
@@ -56,4 +56,4 @@ modusa/utils/excp.py,sha256=L9vhaGjKpv9viJYdmC9n5ndmk2GVbUBuFyZyhAQZmWY,906
56
56
  modusa/utils/logger.py,sha256=K0rsnObeNKCxlNeSnVnJeRhgfmob6riB2uyU7h3dDmA,571
57
57
  modusa/utils/np_func_cat.py,sha256=TyIFgRc6bARRMDnZxlVURO5Z0I-GWhxRONYyIv-Vwxs,1007
58
58
  modusa/utils/plot.py,sha256=s_vNdxvKfwxEngvJPgrF1PcmxZNnNaaXPViHWjyjJ-c,5335
59
- modusa-0.3.27.dist-info/RECORD,,
59
+ modusa-0.3.29.dist-info/RECORD,,