0
# Plotting and Visualization
1
2
Tools for plotting physiological signals and annotations with customizable styling, multiple display options, and publication-quality output. This module provides matplotlib-based visualization specifically designed for biomedical signal data and WFDB records.
3
4
## Capabilities
5
6
### Record and Annotation Plotting
7
8
High-level functions for plotting WFDB records and annotations with automatic formatting and styling.
9
10
```python { .api }
11
def plot_wfdb(record: Record = None, annotation: Annotation = None,
12
plot_sym: bool = False, time_units: str = 'seconds',
13
title: str = None, sig_style: List[str] = [''], ann_style: List[str] = ['r*'],
14
ecg_grids: List[int] = [], figsize: Tuple[float, float] = None,
15
return_fig: bool = False, sharex: str = "auto") -> Union[None, matplotlib.figure.Figure]:
16
"""
17
Plot WFDB record and annotation objects with automatic formatting.
18
19
Parameters:
20
- record: Record, WFDB record object to plot
21
- annotation: Annotation, annotation object to overlay
22
- plot_sym: bool, whether to plot annotation symbols (default: False)
23
- time_units: str, x-axis units ('samples', 'seconds', 'minutes', 'hours')
24
- title: str, plot title
25
- sig_style: list of str, signal line styles for each channel
26
- ann_style: list of str, annotation marker styles
27
- ecg_grids: list of int, channels to apply ECG grid styling
28
- figsize: tuple, figure size (width, height) in inches
29
- return_fig: bool, return figure object
30
- sharex: str, x-axis sharing mode ("auto", "all", "none")
31
32
Returns:
33
None or Figure object based on return_fig parameter
34
"""
35
36
def plot_items(signal: Union[np.ndarray, List[np.ndarray]] = None,
37
ann_samp: List[Union[int, np.ndarray]] = None,
38
ann_sym: List[List[str]] = None, fs: float = None,
39
time_units: str = "samples", sig_name: List[str] = None,
40
sig_units: List[str] = None, xlabel: str = None, ylabel: str = None,
41
title: str = None, sig_style: List[str] = [""],
42
ann_style: List[str] = ["r*"], ecg_grids: List[int] = [],
43
figsize: Tuple[float, float] = None, sharex: bool = False,
44
sharey: bool = False, return_fig: bool = False,
45
return_fig_axes: bool = False, sampling_freq: float = None,
46
ann_freq: List[float] = None) -> Union[None, matplotlib.figure.Figure, Tuple[matplotlib.figure.Figure, List[matplotlib.axes.Axes]]]:
47
"""
48
Plot signals and annotations with detailed customization options.
49
50
Parameters:
51
- signal: ndarray or list of ndarray, signals to plot
52
- ann_samp: list of array-like, annotation sample locations for each subplot
53
- ann_sym: list of list of str, annotation symbols for each subplot
54
- fs: float, sampling frequency for time conversion
55
- time_units: str, x-axis time units ('samples', 'seconds', 'minutes', 'hours')
56
- sig_name: list of str, signal names for subplot titles
57
- sig_units: list of str, signal units for y-axis labels
58
- xlabel: str, x-axis label (overrides time_units)
59
- ylabel: str, y-axis label for single subplot
60
- title: str, main plot title
61
- sig_style: list of str, line styles for each signal
62
- ann_style: list of str, marker styles for annotations
63
- ecg_grids: list of int, subplot indices for ECG grid styling
64
- figsize: tuple, figure size (width, height) in inches
65
- sharex: bool, share x-axis across subplots
66
- sharey: bool, share y-axis across subplots
67
- return_fig: bool, return figure object
68
- return_fig_axes: bool, return both figure and axes objects
69
- sampling_freq: float, deprecated, use fs parameter
70
- ann_freq: list of float, sampling frequencies for annotations
71
72
Returns:
73
None, Figure object, or (Figure, Axes) tuple based on return parameters
74
"""
75
76
def plot_all_records(directory: str = "") -> None:
77
"""
78
Plot all WFDB records in a directory.
79
80
Parameters:
81
- directory: str, directory path containing WFDB records (default: current)
82
"""
83
```
84
85
## Usage Examples
86
87
### Basic Record Plotting
88
89
```python
90
import wfdb
91
import matplotlib.pyplot as plt
92
93
# Read record and annotations
94
record = wfdb.rdrecord('100', pn_dir='mitdb')
95
annotation = wfdb.rdann('100', 'atr', pn_dir='mitdb')
96
97
# Simple plot with default settings
98
wfdb.plot.plot_wfdb(record=record, annotation=annotation)
99
plt.show()
100
101
# Plot with time units in seconds
102
wfdb.plot.plot_wfdb(record=record, annotation=annotation,
103
time_units='seconds', title='MIT-BIH Record 100')
104
plt.show()
105
106
# Plot subset of data (first 10 seconds)
107
record_subset = wfdb.rdrecord('100', pn_dir='mitdb',
108
sampfrom=0, sampto=3600) # 10 sec at 360 Hz
109
wfdb.plot.plot_wfdb(record=record_subset,
110
time_units='seconds', title='First 10 seconds')
111
plt.show()
112
```
113
114
### Advanced Plotting with Custom Styling
115
116
```python
117
import wfdb
118
import matplotlib.pyplot as plt
119
import numpy as np
120
121
# Read multi-channel record
122
record = wfdb.rdrecord('100', pn_dir='mitdb')
123
annotation = wfdb.rdann('100', 'atr', pn_dir='mitdb')
124
125
# Custom styling
126
fig, axes = wfdb.plot.plot_wfdb(
127
record=record,
128
annotation=annotation,
129
time_units='minutes',
130
title='ECG with Custom Styling',
131
sig_style='b-', # Blue solid line
132
ann_style=['ro', 'g^'], # Red circles, green triangles
133
ecg_grids=[0, 1], # Apply ECG grid to both channels
134
figsize=(12, 8),
135
return_fig_axes=True
136
)
137
138
# Further customize the plot
139
for ax in axes:
140
ax.grid(True, alpha=0.3)
141
ax.set_xlim(0, 5) # First 5 minutes
142
143
plt.tight_layout()
144
plt.show()
145
```
146
147
### Plotting Individual Signals
148
149
```python
150
import wfdb
151
import numpy as np
152
import matplotlib.pyplot as plt
153
154
# Read signals only
155
signals, fields = wfdb.rdsamp('100', pn_dir='mitdb', channels=[0, 1])
156
fs = fields['fs']
157
158
# Create sample annotations (e.g., detected peaks)
159
qrs_inds = wfdb.processing.xqrs_detect(signals[:, 0], fs=fs)
160
161
# Plot using plot_items for more control
162
wfdb.plot.plot_items(
163
signal=signals.T, # Transpose for subplot per signal
164
ann_samp=[qrs_inds, []], # Annotations only on first channel
165
ann_sym=[['R'] * len(qrs_inds), []], # Symbol for each annotation
166
fs=fs,
167
time_units='seconds',
168
sig_name=['MLI', 'MLII'],
169
sig_units=['mV', 'mV'],
170
title='ECG Signals with QRS Detection',
171
sig_style=['b-', 'r-'],
172
ann_style=['ko'],
173
figsize=(15, 6)
174
)
175
plt.show()
176
```
177
178
### Plotting Multiple Records
179
180
```python
181
import wfdb
182
import matplotlib.pyplot as plt
183
184
# Plot multiple records for comparison
185
records = ['100', '101', '102']
186
fig, axes = plt.subplots(len(records), 1, figsize=(12, 10), sharex=True)
187
188
for i, record_name in enumerate(records):
189
record = wfdb.rdrecord(record_name, pn_dir='mitdb',
190
sampfrom=0, sampto=1800) # 5 seconds
191
192
# Plot each on separate subplot
193
plt.sca(axes[i])
194
wfdb.plot.plot_wfdb(record=record, time_units='seconds',
195
title=f'Record {record_name}')
196
197
plt.tight_layout()
198
plt.show()
199
```
200
201
### ECG Grid Styling
202
203
```python
204
import wfdb
205
import matplotlib.pyplot as plt
206
207
# Read ECG record
208
record = wfdb.rdrecord('100', pn_dir='mitdb', channels=[0])
209
210
# Plot with ECG-specific grid styling
211
wfdb.plot.plot_wfdb(
212
record=record,
213
time_units='seconds',
214
title='ECG with Standard Grid',
215
ecg_grids=[0], # Apply ECG grid to channel 0
216
figsize=(15, 6)
217
)
218
plt.show()
219
220
# Alternative: manual ECG grid setup
221
fig, ax = plt.subplots(figsize=(15, 6))
222
time_vec = np.arange(record.sig_len) / record.fs
223
ax.plot(time_vec, record.p_signal[:, 0], 'b-', linewidth=0.8)
224
225
# Standard ECG grid (0.04s major, 0.008s minor)
226
ax.grid(True, which='major', alpha=0.5, linestyle='-', linewidth=0.8)
227
ax.grid(True, which='minor', alpha=0.3, linestyle='-', linewidth=0.4)
228
ax.set_xlabel('Time (seconds)')
229
ax.set_ylabel('Amplitude (mV)')
230
ax.set_title('ECG with Manual Grid')
231
plt.show()
232
```
233
234
### Plotting Large Datasets
235
236
```python
237
import wfdb
238
import matplotlib.pyplot as plt
239
240
# Read long record (will be automatically chunked)
241
record = wfdb.rdrecord('100', pn_dir='mitdb') # Full 30-minute record
242
243
# Automatic chunking for large datasets
244
wfdb.plot.plot_wfdb(
245
record=record,
246
time_units='minutes',
247
title='Full 30-minute ECG Record',
248
auto_chunk=True,
249
chunk_size=50000, # Adjust chunk size as needed
250
figsize=(20, 8)
251
)
252
plt.show()
253
254
# Plot specific time windows
255
for start_min in range(0, 30, 5): # 5-minute windows
256
start_samp = int(start_min * 60 * record.fs)
257
end_samp = int((start_min + 5) * 60 * record.fs)
258
259
record_chunk = wfdb.rdrecord('100', pn_dir='mitdb',
260
sampfrom=start_samp, sampto=end_samp)
261
262
wfdb.plot.plot_wfdb(record=record_chunk, time_units='seconds',
263
title=f'Minutes {start_min}-{start_min+5}')
264
plt.show()
265
```
266
267
### Annotation Visualization
268
269
```python
270
import wfdb
271
import matplotlib.pyplot as plt
272
273
# Read record with multiple annotation types
274
record = wfdb.rdrecord('100', pn_dir='mitdb', channels=[0])
275
annotations = wfdb.rdann('100', 'atr', pn_dir='mitdb')
276
277
# Plot with symbol display
278
wfdb.plot.plot_wfdb(
279
record=record,
280
annotation=annotations,
281
plot_sym=True, # Show annotation symbols
282
time_units='seconds',
283
ann_style=['r*'], # Red asterisks
284
title='ECG with Beat Annotations'
285
)
286
plt.show()
287
288
# Custom annotation display
289
normal_beats = annotations.sample[annotations.symbol == 'N']
290
pvc_beats = annotations.sample[annotations.symbol == 'V']
291
292
wfdb.plot.plot_items(
293
signal=[record.p_signal[:, 0]],
294
ann_samp=[normal_beats, pvc_beats],
295
ann_sym=[['N'] * len(normal_beats), ['V'] * len(pvc_beats)],
296
fs=record.fs,
297
time_units='seconds',
298
sig_name=['ECG'],
299
ann_style=['go', 'r^'], # Green circles for N, red triangles for V
300
title='Normal vs PVC Beats',
301
figsize=(15, 6)
302
)
303
plt.show()
304
```
305
306
### Saving Plots
307
308
```python
309
import wfdb
310
import matplotlib.pyplot as plt
311
312
# Create plot
313
record = wfdb.rdrecord('100', pn_dir='mitdb')
314
fig = wfdb.plot.plot_wfdb(record=record, time_units='seconds',
315
title='ECG Record 100', return_fig=True)
316
317
# Save in different formats
318
fig.savefig('ecg_record_100.png', dpi=300, bbox_inches='tight')
319
fig.savefig('ecg_record_100.pdf', bbox_inches='tight')
320
fig.savefig('ecg_record_100.svg', bbox_inches='tight')
321
322
plt.close(fig) # Clean up memory
323
```