Skip to content

Commit 21a7b52

Browse files
authored
Optimization of wfdb.io.annotation.field2bytes function (#406)
Authored-by: Nicolas Beaudoin-Gagnon <nicolas.beaudoin-gagnon@icentia.com>
1 parent 3c30345 commit 21a7b52

File tree

5 files changed

+43
-28
lines changed

5 files changed

+43
-28
lines changed

tests/test_annotation.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -260,15 +260,23 @@ def test_5(self):
260260
ann_idx = np.array([1, 1000, 2000, 3000])
261261
ann_chan = np.array([3, 1, 2, 3])
262262
# write custom labels
263-
ann_label_store = np.array([ 4, 2, 1, 3])
264-
ann_custom_labels = {'label_store': [1, 2, 3, 4],
265-
'symbol': ['v','l','r','z'],
266-
'description':['pvc','lbbb','rbbb','pac']}
263+
ann_label_store = np.array([4, 2, 1, 3])
264+
ann_custom_labels = {
265+
"label_store": [1, 2, 3, 4],
266+
"symbol": ["v", "l", "r", "z"],
267+
"description": ["pvc", "lbbb", "rbbb", "pac"],
268+
}
267269
ann_custom_labels = pd.DataFrame(data=ann_custom_labels)
268-
wfdb.wrann('CustomLabel', 'atr', ann_idx, chan=ann_chan,
269-
custom_labels=ann_custom_labels, label_store=ann_label_store)
270-
ann = wfdb.rdann('CustomLabel', 'atr')
271-
self.assertEqual(ann.symbol, ['z', 'l', 'v', 'r'])
270+
wfdb.wrann(
271+
"CustomLabel",
272+
"atr",
273+
ann_idx,
274+
chan=ann_chan,
275+
custom_labels=ann_custom_labels,
276+
label_store=ann_label_store,
277+
)
278+
ann = wfdb.rdann("CustomLabel", "atr")
279+
self.assertEqual(ann.symbol, ["z", "l", "v", "r"])
272280

273281
@classmethod
274282
def tearDownClass(cls):
@@ -277,7 +285,7 @@ def tearDownClass(cls):
277285
"1003.atr",
278286
"12726.anI",
279287
"huge.qrs",
280-
"CustomLabel.atr"
288+
"CustomLabel.atr",
281289
]
282290
for file in writefiles:
283291
if os.path.isfile(file):

tests/test_record.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,10 @@ def test_to_dataframe(self):
589589
self.assertEqual(record.sig_name, list(df.columns))
590590
self.assertEqual(len(df), record.sig_len)
591591
self.assertEqual(df.index[0], pd.Timedelta(0))
592-
self.assertEqual(df.index[-1], pd.Timedelta(seconds=1 / record.fs * (record.sig_len - 1)))
592+
self.assertEqual(
593+
df.index[-1],
594+
pd.Timedelta(seconds=1 / record.fs * (record.sig_len - 1)),
595+
)
593596
assert np.array_equal(record.p_signal, df.values)
594597

595598
def test_header_with_non_utf8(self):

wfdb/io/_signal.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2505,4 +2505,3 @@ def _infer_sig_len(
25052505
sig_len = int(data_size / (BYTES_PER_SAMPLE[fmt] * tsamps_per_frame))
25062506

25072507
return sig_len
2508-

wfdb/io/annotation.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1165,19 +1165,34 @@ def calc_core_bytes(self):
11651165

11661166
data_bytes = []
11671167

1168+
# Allow use of custom labels
1169+
label_table = ann_label_table
1170+
if self.custom_labels is not None:
1171+
label_table = pd.concat(
1172+
[label_table, self.custom_labels], ignore_index=True
1173+
)
1174+
1175+
# Generate typecodes from annotation label table
1176+
typecodes = {
1177+
label_table.iloc[i]["symbol"]: label_table.iloc[i]["label_store"]
1178+
for i in range(len(label_table))
1179+
}
1180+
11681181
# Iterate across all fields one index at a time
11691182
for i in range(len(sampdiff)):
11701183

11711184
# Process the samp (difference) and sym items
11721185
data_bytes.append(
1173-
field2bytes("samptype", [sampdiff[i], self.symbol[i]], self.custom_labels)
1186+
field2bytes(
1187+
"samptype", [sampdiff[i], self.symbol[i]], typecodes
1188+
)
11741189
)
11751190

11761191
# Process the extra optional fields
11771192
for field in extra_write_fields:
11781193
value = getattr(compact_annotation, field)[i]
11791194
if value is not None:
1180-
data_bytes.append(field2bytes(field, value, self.custom_labels))
1195+
data_bytes.append(field2bytes(field, value, typecodes))
11811196

11821197
# Flatten and convert to correct format
11831198
data_bytes = np.array(
@@ -1600,7 +1615,7 @@ def compact_carry_field(full_field):
16001615
return compact_field
16011616

16021617

1603-
def field2bytes(field, value, custom_labels=None):
1618+
def field2bytes(field, value, typecodes):
16041619
"""
16051620
Convert an annotation field into bytes to write.
16061621
@@ -1610,6 +1625,8 @@ def field2bytes(field, value, custom_labels=None):
16101625
The annotation field of the value to be converted to bytes.
16111626
value : list
16121627
The value to be converted to bytes.
1628+
typecodes : dict
1629+
The mapping between each annotation label an its corresponding typecode.
16131630
16141631
Returns
16151632
-------
@@ -1619,18 +1636,10 @@ def field2bytes(field, value, custom_labels=None):
16191636
"""
16201637
data_bytes = []
16211638

1622-
# allow use of custom labels
1623-
label_table = ann_label_table
1624-
if custom_labels is not None:
1625-
label_table = pd.concat([label_table, custom_labels], ignore_index=True)
1626-
16271639
# samp and sym bytes come together
16281640
if field == "samptype":
16291641
# Numerical value encoding annotation symbol
1630-
typecode = label_table.loc[
1631-
label_table["symbol"] == value[1], "label_store"
1632-
].values[0]
1633-
1642+
typecode = typecodes[value[1]]
16341643
# sample difference
16351644
sd = value[0]
16361645

wfdb/io/record.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1021,11 +1021,7 @@ def to_dataframe(self) -> pd.DataFrame:
10211021
else:
10221022
raise ValueError("No signal in record.")
10231023

1024-
return pd.DataFrame(
1025-
data=data,
1026-
index=index,
1027-
columns=self.sig_name
1028-
)
1024+
return pd.DataFrame(data=data, index=index, columns=self.sig_name)
10291025

10301026

10311027
class MultiRecord(BaseRecord, _header.MultiHeaderMixin):

0 commit comments

Comments
 (0)