or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

format-conversion.mdindex.mdio-operations.mdplotting.mdsignal-processing.md

plotting.mddocs/

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

```