Skip to content

Commit 0d42dfb

Browse files
authored
Move conversion functions to their own modules and remove cyclic imports (#370)
* Move wav, edf, mat, and csv conversion functions to their own modules * Move content to remove cyclic dependencies * Move get_version for a PhysioNet database, to download.py * Remove rdrecord's ability to read edf and wav files * Update documentation
1 parent efca603 commit 0d42dfb

25 files changed

+3191
-3202
lines changed

docs/convert.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
Format Conversions
2+
==================
3+
4+
The wfdb.io.convert subpackage contains functions to read and write
5+
non-WFDB files commonly used for waveforms.
6+
7+
CSV
8+
---
9+
10+
.. automodule:: wfdb.io.convert.csv
11+
:members: csv_to_wfdb
12+
13+
EDF
14+
---
15+
16+
.. automodule:: wfdb.io.convert.edf
17+
:members:
18+
19+
Matlab
20+
------
21+
22+
.. automodule:: wfdb.io.convert.matlab
23+
:members:
24+
25+
TFF
26+
---
27+
28+
.. automodule:: wfdb.io.convert.tff
29+
:members:
30+
31+
WAV
32+
---
33+
34+
.. automodule:: wfdb.io.convert.wav
35+
:members:

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Other Content
5353

5454
installation
5555
wfdb-specifications
56+
convert
5657

5758

5859
Indices and tables

docs/io.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ WFDB Records
99
---------------
1010

1111
.. automodule:: wfdb.io
12-
:members: rdrecord, rdheader, rdsamp, wrsamp, edf2mit, mit2edf, wav2mit, mit2wav, csv2mit
12+
:members: rdrecord, rdheader, rdsamp, wrsamp
1313

1414
.. autoclass:: wfdb.io.Record
1515
:members: wrsamp, adc, dac
@@ -32,4 +32,4 @@ Downloading
3232
-----------
3333

3434
.. automodule:: wfdb.io
35-
:members: get_dbs, get_record_list, dl_database, dl_files, set_db_index_url
35+
:members: dl_files, dl_database, get_dbs, get_record_list, set_db_index_url

docs/processing.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Heart Rate
1717
----------
1818

1919
.. automodule:: wfdb.processing
20-
:members: compute_hr
20+
:members: compute_hr, calc_rr, calc_mean_hr, ann2rr, rr2ann
2121

2222

2323
Peaks
@@ -26,6 +26,11 @@ Peaks
2626
.. automodule:: wfdb.processing
2727
:members: find_peaks, find_local_peaks, correct_peaks
2828

29+
Filters
30+
-------
31+
32+
.. automodule:: wfdb.processing
33+
:members: sigavg
2934

3035
QRS Detectors
3136
-------------

docs/wfdb.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ WFDB Records
99
---------------
1010

1111
.. automodule:: wfdb
12-
:members: rdrecord, rdheader, rdsamp, wrsamp, edf2mit, mit2edf, wav2mit, mit2wav, csv2mit
12+
:members: rdrecord, rdheader, rdsamp, wrsamp
1313

1414
.. autoclass:: wfdb.Record
1515
:members: wrsamp, adc, dac
@@ -32,7 +32,7 @@ Downloading
3232
-----------
3333

3434
.. automodule:: wfdb
35-
:members: get_dbs, get_record_list, dl_database, dl_files, set_db_index_url
35+
:members: dl_files, dl_database, get_dbs, get_record_list, set_db_index_url
3636

3737

3838
Plotting

tests/io/test_convert.py

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import numpy as np
2+
3+
from wfdb.io.record import rdrecord
4+
from wfdb.io.convert.edf import read_edf
5+
6+
7+
class TestConvert:
8+
def test_edf_uniform(self):
9+
"""
10+
EDF format conversion to MIT for uniform sample rates.
11+
12+
"""
13+
# Uniform sample rates
14+
record_MIT = rdrecord("sample-data/n16").__dict__
15+
record_EDF = read_edf("sample-data/n16.edf").__dict__
16+
17+
fields = list(record_MIT.keys())
18+
# Original MIT format method of checksum is outdated, sometimes
19+
# the same value though
20+
fields.remove("checksum")
21+
# Original MIT format units are less comprehensive since they
22+
# default to mV if unknown.. therefore added more default labels
23+
fields.remove("units")
24+
25+
test_results = []
26+
for field in fields:
27+
# Signal value will be slightly off due to C to Python type conversion
28+
if field == "p_signal":
29+
true_array = np.array(record_MIT[field]).flatten()
30+
pred_array = np.array(record_EDF[field]).flatten()
31+
# Prevent divide by zero warning
32+
for i, v in enumerate(true_array):
33+
if v == 0:
34+
true_array[i] = 1
35+
pred_array[i] = 1
36+
sig_diff = np.abs((pred_array - true_array) / true_array)
37+
sig_diff[sig_diff == -np.inf] = 0
38+
sig_diff[sig_diff == np.inf] = 0
39+
sig_diff = np.nanmean(sig_diff, 0)
40+
# 5% tolerance
41+
if np.max(sig_diff) <= 5:
42+
test_results.append(True)
43+
else:
44+
test_results.append(False)
45+
elif field == "init_value":
46+
signal_diff = [
47+
abs(record_MIT[field][i] - record_EDF[field][i])
48+
for i in range(len(record_MIT[field]))
49+
]
50+
if abs(max(min(signal_diff), max(signal_diff), key=abs)) <= 2:
51+
test_results.append(True)
52+
else:
53+
test_results.append(False)
54+
else:
55+
test_results.append(record_MIT[field] == record_MIT[field])
56+
57+
target_results = len(fields) * [True]
58+
assert np.array_equal(test_results, target_results)
59+
60+
def test_edf_non_uniform(self):
61+
"""
62+
EDF format conversion to MIT for non-uniform sample rates.
63+
64+
"""
65+
# Non-uniform sample rates
66+
record_MIT = rdrecord("sample-data/wave_4").__dict__
67+
record_EDF = read_edf("sample-data/wave_4.edf").__dict__
68+
69+
fields = list(record_MIT.keys())
70+
# Original MIT format method of checksum is outdated, sometimes
71+
# the same value though
72+
fields.remove("checksum")
73+
# Original MIT format units are less comprehensive since they
74+
# default to mV if unknown.. therefore added more default labels
75+
fields.remove("units")
76+
77+
test_results = []
78+
for field in fields:
79+
# Signal value will be slightly off due to C to Python type conversion
80+
if field == "p_signal":
81+
true_array = np.array(record_MIT[field]).flatten()
82+
pred_array = np.array(record_EDF[field]).flatten()
83+
# Prevent divide by zero warning
84+
for i, v in enumerate(true_array):
85+
if v == 0:
86+
true_array[i] = 1
87+
pred_array[i] = 1
88+
sig_diff = np.abs((pred_array - true_array) / true_array)
89+
sig_diff[sig_diff == -np.inf] = 0
90+
sig_diff[sig_diff == np.inf] = 0
91+
sig_diff = np.nanmean(sig_diff, 0)
92+
# 5% tolerance
93+
if np.max(sig_diff) <= 5:
94+
test_results.append(True)
95+
else:
96+
test_results.append(False)
97+
elif field == "init_value":
98+
signal_diff = [
99+
abs(record_MIT[field][i] - record_EDF[field][i])
100+
for i in range(len(record_MIT[field]))
101+
]
102+
if abs(max(min(signal_diff), max(signal_diff), key=abs)) <= 2:
103+
test_results.append(True)
104+
else:
105+
test_results.append(False)
106+
else:
107+
test_results.append(record_MIT[field] == record_MIT[field])
108+
109+
target_results = len(fields) * [True]
110+
assert np.array_equal(test_results, target_results)

tests/test_record.py

Lines changed: 1 addition & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import os
2-
import pdb
32
import shutil
43
import unittest
54

65
import numpy as np
6+
77
import wfdb
88

99

@@ -339,110 +339,6 @@ def test_2e(self):
339339
sig_target = sig_target.reshape([977, 1])
340340
assert np.array_equal(sig, sig_target)
341341

342-
def test_2f(self):
343-
"""
344-
EDF format conversion to MIT for uniform sample rates.
345-
346-
"""
347-
# Uniform sample rates
348-
record_MIT = wfdb.rdrecord("sample-data/n16").__dict__
349-
record_EDF = wfdb.edf2mit("sample-data/n16.edf").__dict__
350-
351-
fields = list(record_MIT.keys())
352-
# Original MIT format method of checksum is outdated, sometimes
353-
# the same value though
354-
fields.remove("checksum")
355-
# Original MIT format units are less comprehensive since they
356-
# default to mV if unknown.. therefore added more default labels
357-
fields.remove("units")
358-
359-
test_results = []
360-
for field in fields:
361-
# Signal value will be slightly off due to C to Python type conversion
362-
if field == "p_signal":
363-
true_array = np.array(record_MIT[field]).flatten()
364-
pred_array = np.array(record_EDF[field]).flatten()
365-
# Prevent divide by zero warning
366-
for i, v in enumerate(true_array):
367-
if v == 0:
368-
true_array[i] = 1
369-
pred_array[i] = 1
370-
sig_diff = np.abs((pred_array - true_array) / true_array)
371-
sig_diff[sig_diff == -np.inf] = 0
372-
sig_diff[sig_diff == np.inf] = 0
373-
sig_diff = np.nanmean(sig_diff, 0)
374-
# 5% tolerance
375-
if np.max(sig_diff) <= 5:
376-
test_results.append(True)
377-
else:
378-
test_results.append(False)
379-
elif field == "init_value":
380-
signal_diff = [
381-
abs(record_MIT[field][i] - record_EDF[field][i])
382-
for i in range(len(record_MIT[field]))
383-
]
384-
if abs(max(min(signal_diff), max(signal_diff), key=abs)) <= 2:
385-
test_results.append(True)
386-
else:
387-
test_results.append(False)
388-
else:
389-
test_results.append(record_MIT[field] == record_MIT[field])
390-
391-
target_results = len(fields) * [True]
392-
assert np.array_equal(test_results, target_results)
393-
394-
def test_2g(self):
395-
"""
396-
EDF format conversion to MIT for non-uniform sample rates.
397-
398-
"""
399-
# Non-uniform sample rates
400-
record_MIT = wfdb.rdrecord("sample-data/wave_4").__dict__
401-
record_EDF = wfdb.edf2mit("sample-data/wave_4.edf").__dict__
402-
403-
fields = list(record_MIT.keys())
404-
# Original MIT format method of checksum is outdated, sometimes
405-
# the same value though
406-
fields.remove("checksum")
407-
# Original MIT format units are less comprehensive since they
408-
# default to mV if unknown.. therefore added more default labels
409-
fields.remove("units")
410-
411-
test_results = []
412-
for field in fields:
413-
# Signal value will be slightly off due to C to Python type conversion
414-
if field == "p_signal":
415-
true_array = np.array(record_MIT[field]).flatten()
416-
pred_array = np.array(record_EDF[field]).flatten()
417-
# Prevent divide by zero warning
418-
for i, v in enumerate(true_array):
419-
if v == 0:
420-
true_array[i] = 1
421-
pred_array[i] = 1
422-
sig_diff = np.abs((pred_array - true_array) / true_array)
423-
sig_diff[sig_diff == -np.inf] = 0
424-
sig_diff[sig_diff == np.inf] = 0
425-
sig_diff = np.nanmean(sig_diff, 0)
426-
# 5% tolerance
427-
if np.max(sig_diff) <= 5:
428-
test_results.append(True)
429-
else:
430-
test_results.append(False)
431-
elif field == "init_value":
432-
signal_diff = [
433-
abs(record_MIT[field][i] - record_EDF[field][i])
434-
for i in range(len(record_MIT[field]))
435-
]
436-
if abs(max(min(signal_diff), max(signal_diff), key=abs)) <= 2:
437-
test_results.append(True)
438-
else:
439-
test_results.append(False)
440-
else:
441-
test_results.append(record_MIT[field] == record_MIT[field])
442-
443-
target_results = len(fields) * [True]
444-
assert np.array_equal(test_results, target_results)
445-
446342
# --------------------- 3. Multi-dat records --------------------- #
447343

448344
def test_3a(self):

wfdb/__init__.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,23 @@
66
rdsamp,
77
wrsamp,
88
dl_database,
9-
edf2mit,
10-
mit2edf,
11-
wav2mit,
12-
mit2wav,
13-
wfdb2mat,
14-
csv2mit,
159
sampfreq,
1610
signame,
1711
wfdbdesc,
1812
wfdbtime,
19-
sigavg,
2013
)
2114
from wfdb.io.annotation import (
2215
Annotation,
2316
rdann,
2417
wrann,
2518
show_ann_labels,
2619
show_ann_classes,
27-
ann2rr,
28-
rr2ann,
29-
csv2ann,
30-
rdedfann,
3120
mrgann,
3221
)
3322
from wfdb.io.download import (
23+
dl_files,
3424
get_dbs,
3525
get_record_list,
36-
dl_files,
3726
set_db_index_url,
3827
)
3928
from wfdb.plot.plot import plot_items, plot_wfdb, plot_all_records

0 commit comments

Comments
 (0)