Skip to content

Commit 7f6d760

Browse files
authored
Merge pull request #387 from MIT-LCP/plot-test
Test cases for plot_wfdb
2 parents 41ebfb5 + dcce4da commit 7f6d760

File tree

1 file changed

+173
-2
lines changed

1 file changed

+173
-2
lines changed

tests/test_plot.py

Lines changed: 173 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,176 @@
1+
import unittest
2+
3+
import matplotlib.pyplot as plt
4+
import numpy as np
5+
16
import wfdb
27
from wfdb.plot import plot
3-
import unittest
48

59

6-
class TestPlot(unittest.TestCase):
10+
class TestPlotWfdb(unittest.TestCase):
11+
"""
12+
Tests for the wfdb.plot_wfdb function
13+
"""
14+
15+
def assertAxesMatchSignal(self, axes, signal, t_divisor=1):
16+
"""
17+
Check that axis limits are reasonable for plotting a signal array.
18+
19+
Parameters
20+
----------
21+
axes : matplotlib.axes.Axes
22+
An Axes object.
23+
signal : numpy.ndarray
24+
A one-dimensional array of sample values.
25+
t_divisor : float, optional
26+
The intended plotting resolution (number of samples of `signal`
27+
per unit of the X axis.)
28+
29+
"""
30+
xmin, xmax = axes.get_xlim()
31+
tmin = 0
32+
tmax = (len(signal) - 1) / t_divisor
33+
# The range from tmin to tmax should fit within the plot.
34+
self.assertLessEqual(
35+
xmin,
36+
tmin,
37+
msg=f"X range is [{xmin}, {xmax}]; expected [{tmin}, {tmax}]",
38+
)
39+
self.assertGreaterEqual(
40+
xmax,
41+
tmax,
42+
msg=f"X range is [{xmin}, {xmax}]; expected [{tmin}, {tmax}]",
43+
)
44+
# The padding on left and right sides should be approximately equal.
45+
self.assertAlmostEqual(
46+
xmin - tmin,
47+
tmax - xmax,
48+
delta=(tmax - tmin) / 10 + 1 / t_divisor,
49+
msg=f"X range is [{xmin}, {xmax}]; expected [{tmin}, {tmax}]",
50+
)
51+
52+
ymin, ymax = axes.get_ylim()
53+
vmin = np.nanmin(signal)
54+
vmax = np.nanmax(signal)
55+
# The range from vmin to vmax should fit within the plot.
56+
self.assertLessEqual(
57+
ymin,
58+
vmin,
59+
msg=f"Y range is [{ymin}, {ymax}]; expected [{vmin}, {vmax}]",
60+
)
61+
self.assertGreaterEqual(
62+
ymax,
63+
vmax,
64+
msg=f"Y range is [{ymin}, {ymax}]; expected [{vmin}, {vmax}]",
65+
)
66+
# The padding on top and bottom should be approximately equal.
67+
self.assertAlmostEqual(
68+
ymin - vmin,
69+
vmax - ymax,
70+
delta=(vmax - vmin) / 10,
71+
msg=f"Y range is [{ymin}, {ymax}]; expected [{vmin}, {vmax}]",
72+
)
73+
74+
def test_physical_smooth(self):
75+
"""
76+
Plot a record with physical, single-frequency data
77+
"""
78+
record = wfdb.rdrecord(
79+
"sample-data/100",
80+
sampto=1000,
81+
physical=True,
82+
smooth_frames=True,
83+
)
84+
self.assertIsNotNone(record.p_signal)
85+
86+
annotation = wfdb.rdann("sample-data/100", "atr", sampto=1000)
87+
88+
fig = wfdb.plot_wfdb(
89+
record,
90+
annotation,
91+
time_units="samples",
92+
ecg_grids="all",
93+
return_fig=True,
94+
)
95+
plt.close(fig)
96+
97+
self.assertEqual(len(fig.axes), record.n_sig)
98+
for ch in range(record.n_sig):
99+
self.assertAxesMatchSignal(fig.axes[ch], record.p_signal[:, ch])
100+
101+
def test_digital_smooth(self):
102+
"""
103+
Plot a record with digital, single-frequency data
104+
"""
105+
record = wfdb.rdrecord(
106+
"sample-data/drive02",
107+
sampto=1000,
108+
physical=False,
109+
smooth_frames=True,
110+
)
111+
self.assertIsNotNone(record.d_signal)
112+
113+
fig = wfdb.plot_wfdb(record, time_units="seconds", return_fig=True)
114+
plt.close(fig)
115+
116+
self.assertEqual(len(fig.axes), record.n_sig)
117+
for ch in range(record.n_sig):
118+
self.assertAxesMatchSignal(
119+
fig.axes[ch], record.d_signal[:, ch], record.fs
120+
)
121+
122+
def test_physical_multifrequency(self):
123+
"""
124+
Plot a record with physical, multi-frequency data
125+
"""
126+
record = wfdb.rdrecord(
127+
"sample-data/wave_4",
128+
sampto=10,
129+
physical=True,
130+
smooth_frames=False,
131+
)
132+
self.assertIsNotNone(record.e_p_signal)
133+
134+
fig = wfdb.plot_wfdb(record, time_units="seconds", return_fig=True)
135+
plt.close(fig)
136+
137+
self.assertEqual(len(fig.axes), record.n_sig)
138+
for ch in range(record.n_sig):
139+
self.assertAxesMatchSignal(
140+
fig.axes[ch],
141+
record.e_p_signal[ch],
142+
record.fs * record.samps_per_frame[ch],
143+
)
144+
145+
def test_digital_multifrequency(self):
146+
"""
147+
Plot a record with digital, multi-frequency data
148+
"""
149+
record = wfdb.rdrecord(
150+
"sample-data/multi-segment/041s/041s",
151+
sampto=1000,
152+
physical=False,
153+
smooth_frames=False,
154+
)
155+
self.assertIsNotNone(record.e_d_signal)
156+
157+
fig = wfdb.plot_wfdb(record, time_units="seconds", return_fig=True)
158+
plt.close(fig)
159+
160+
self.assertEqual(len(fig.axes), record.n_sig)
161+
for ch in range(record.n_sig):
162+
self.assertAxesMatchSignal(
163+
fig.axes[ch],
164+
record.e_d_signal[ch],
165+
record.fs * record.samps_per_frame[ch],
166+
)
167+
168+
169+
class TestPlotInternal(unittest.TestCase):
170+
"""
171+
Unit tests for internal wfdb.plot.plot functions
172+
"""
173+
7174
def test_get_plot_dims(self):
8175
sampfrom = 0
9176
sampto = 3000
@@ -39,3 +206,7 @@ def test_create_figure_multiple_subplots(self):
39206
assert fig is not None
40207
assert axes is not None
41208
assert len(axes) == n_subplots
209+
210+
211+
if __name__ == "__main__":
212+
unittest.main()

0 commit comments

Comments
 (0)