Skip to content

Commit a01dbe9

Browse files
committed
changed annotation api
1 parent 7aad3d2 commit a01dbe9

File tree

1 file changed

+48
-38
lines changed

1 file changed

+48
-38
lines changed

wfdb/readwrite/annotations.py

Lines changed: 48 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def wrann(self, writefs=False)
130130

131131
# Calculate the label_store field if necessary
132132
if 'label_store' not in present_label_fields:
133-
convert_label_attribute(source_field=present_label_fields[0], target_field='label_store')
133+
self.convert_label_attribute(source_field=present_label_fields[0], target_field='label_store')
134134

135135
# Write the header file using the specified fields
136136
self.wrannfile(writefs=writefs)
@@ -172,7 +172,7 @@ def checkfield(self, field):
172172

173173
# Field specific checks
174174
if field == 'recordname':
175-
if bool(re.search('[^-\w]', self.recordname))
175+
if bool(re.search('[^-\w]', self.recordname)):
176176
raise ValueError('recordname must only comprise of letters, digits, hyphens, and underscores.')
177177
elif field == 'extension':
178178
if bool(re.search('[^a-zA-Z]', self.extension)):
@@ -193,7 +193,7 @@ def checkfield(self, field):
193193
column_names = list(item)
194194
if 'symbol' in column_names and 'description' in column_names:
195195
if 'label_store' in column_names:
196-
label_store = item['label_store'].values
196+
label_store = list(item['label_store'].values)
197197
else:
198198
label_store = None
199199
symbol = item['symbol'].values
@@ -312,11 +312,12 @@ def check_field_cohesion(self, present_label_fields):
312312
# labels present.
313313
for field in present_label_fields:
314314
defined_values = self.__label_map__[field].values
315-
if set(getattr(self, field)) - defined_values != {}:
316-
raise ValueError('\n'.join('\nThe '+field+' field contains elements not encoded in the stardard WFDB annotation labels or this object\'s custom_labels field',
317-
'To see the standard WFDB annotation labels, call: show_ann_labels()',
318-
'To transfer non-encoded symbol items into the aux_note field, call: self.sym_to_aux()',
319-
'To define custom labels, set the custom_labels field as a list of tuple triplets with format: (label_store, symbol, description)'))
315+
316+
if set(getattr(self, field)) - set(defined_values) != set():
317+
raise ValueError('\n'.join(['\nThe '+field+' field contains elements not encoded in the stardard WFDB annotation labels, or this object\'s custom_labels field',
318+
'- To see the standard WFDB annotation labels, call: show_ann_labels()',
319+
'- To transfer non-encoded symbol items into the aux_note field, call: self.sym_to_aux()',
320+
'- To define custom labels, set the custom_labels field as a list of tuple triplets with format: (label_store, symbol, description)']))
320321

321322
return
322323

@@ -355,20 +356,23 @@ def standardize_custom_labels(self):
355356
# Convert to dataframe if not already
356357
if not isinstance(custom_labels, pd.DataFrame):
357358
if len(self.custom_labels[0]) == 2:
358-
symbol , description = get_custom_label_attribute(['symbol', 'description'])
359+
symbol = self.get_custom_label_attribute('symbol')
360+
description = self.get_custom_label_attribute('description')
359361
custom_labels = pd.DataFrame({'symbol': symbol, 'description': description})
360362
else:
361-
label_store, symbol , description = get_custom_label_attribute(['label_store', 'symbol', 'description'])
363+
label_store = self.get_custom_label_attribute('label_store')
364+
symbol = self.get_custom_label_attribute('symbol')
365+
description = self.get_custom_label_attribute('description')
362366
custom_labels = pd.DataFrame({'label_store':label_store, 'symbol': symbol, 'description': description})
363367

364368
# Assign label_store values to the custom labels if not defined
365369
if 'label_store' not in list(custom_labels):
366-
367370
undefined_label_stores = self.get_undefined_label_stores()
371+
368372
if len(custom_labels) > len(undefined_label_stores):
369-
available_label_stores = self.get_available_label_stores()
370-
else:
371-
available_label_stores = undefined_label_stores
373+
available_label_stores = self.get_available_label_stores()
374+
else:
375+
available_label_stores = undefined_label_stores
372376

373377
n_custom_labels = custom_labels.shape[0]
374378

@@ -378,6 +382,8 @@ def standardize_custom_labels(self):
378382
custom_labels['label_store'] = available_label_stores[:n_custom_labels]
379383

380384
custom_labels.set_index(custom_labels['label_store'].values, inplace=True)
385+
custom_labels = custom_labels[list(ann_label_fields)]
386+
381387
self.custom_labels = custom_labels
382388

383389
return
@@ -439,14 +445,14 @@ def get_available_label_stores(self, usefield = 'tryall'):
439445
# Get the standard wfdb label_store values overwritten by the
440446
# custom_labels if any
441447
if self.custom_symbols is not None:
442-
custom_field = set(get_custom_label_attribute(usefield))
448+
custom_field = set(self.get_custom_label_attribute(usefield))
443449
if usefield == 'label_store':
444450
overwritten_label_stores = set(custom_field).intersection(set(ann_label_table['label_store']))
445451
else:
446452
overwritten_fields = set(custom_field).intersection(set(ann_label_table[usefield]))
447453
overwritten_label_stores = ann_label_table.loc[ann_label_table[usefield] in overwritten_fields, 'label_store'].values
448454
else:
449-
overwritten_label_stores = {}
455+
overwritten_label_stores = set()
450456

451457

452458
# The undefined values in the standard wfdb labels
@@ -462,13 +468,9 @@ def get_custom_label_attribute(self, attribute):
462468
Get a list of the custom_labels attribute.
463469
ie. label_store, symbol, or description.
464470
465-
attribute can also be a list of attributes.
466-
467471
The custom_labels variable could be in
468472
a number of formats
469473
"""
470-
if type(attribute) != 'string':
471-
return [get_custom_label_attribute[a] for a in attribute]
472474

473475
if attribute not in ann_label_fields:
474476
raise ValueError('Invalid attribute specified')
@@ -524,16 +526,25 @@ def wrannfile(self, writefs):
524526
"""
525527

526528
# Calculate the fs bytes to write if present and desired to write
527-
fs_bytes = self.calc_fs_bytes()
529+
if writefs:
530+
fs_bytes = self.calc_fs_bytes()
531+
else:
532+
fs_bytes = []
528533
# Calculate the custom_labels bytes to write if present
529534
cl_bytes = self.calc_cl_bytes()
530535
# Calculate the core field bytes to write
531536
core_bytes = self.calc_core_bytes()
532537

538+
# Mark the end of the special annotation types if needed
539+
if fs_bytes == [] and cl_bytes == []:
540+
end_special_bytes = []
541+
else:
542+
end_special_bytes = [0, 236, 255, 255, 255, 255, 1, 0]
543+
533544
# Write the file
534545
with open(self.recordname+'.'+self.extension, 'wb') as f:
535-
# Combine all bytes to write: fs (if any), custom annotations (inf any), main content, file terminator
536-
np.concatenate((fsbytes, core_bytes, data_bytes, np.array([0,0]))).astype('u1').tofile(f)
546+
# Combine all bytes to write: fs (if any), custom annotations (if any), main content, file terminator
547+
np.concatenate((fs_bytes, cl_bytes, end_special_bytes, core_bytes, np.array([0,0]))).astype('u1').tofile(f)
537548

538549
return
539550

@@ -564,9 +575,6 @@ def calc_fs_bytes(self):
564575
if ndigits % 2:
565576
data_bytes.append(0)
566577

567-
# Add the extra -1 0 notqrs filler
568-
data_bytes = data_bytes+[0, 236, 255, 255, 255, 255, 1, 0]
569-
570578
return np.array(data_bytes).astype('u1')
571579

572580
# Calculate the bytes written to the annotation file for the custom_labels field
@@ -581,11 +589,12 @@ def calc_cl_bytes(self):
581589

582590
# The end wrapper: '0 NOTE length aux_note ## end of definitions' followed by SKIP -1, +1
583591
tailbytes = [0,88,21,252,35,35,32,101,110,100,32,111,102,32,100,101,102,105,110,
584-
105,116,105,111,110,115,0,0,236,255,255,255,255,1,0]
592+
105,116,105,111,110,115,0]
585593

586594
custom_bytes = []
595+
587596
for i in self.custom_labels.index:
588-
custom_bytes += custom_triplet_bytes(list(self.custom_labels.loc[i,:]))
597+
custom_bytes += custom_triplet_bytes(list(self.custom_labels.loc[i, list(ann_label_fields)]))
589598

590599
# writecontent = []
591600
# for i in range(len(self.custom_labels)):
@@ -622,7 +631,7 @@ def calc_core_bytes(self):
622631
for i in range(len(sampdiff)):
623632

624633
# Process the samp (difference) and sym items
625-
data_bytes.append(field2bytes('samptype', [sampdiff[i], self.sym[i]]))
634+
data_bytes.append(field2bytes('samptype', [sampdiff[i], self.symbol[i]]))
626635

627636
# Process the extra optional fields
628637
for field in extra_write_fields:
@@ -640,7 +649,7 @@ def calc_core_bytes(self):
640649
def compact_fields(self):
641650

642651
# Number of annotations
643-
nannots = len(self.samp)
652+
nannots = len(self.sample)
644653

645654
# Chan and num carry over previous fields. Get lists of as few
646655
# elements to write as possible
@@ -686,7 +695,7 @@ def sym_to_aux(self):
686695
return
687696

688697
if self.aux_note is None:
689-
self.aux_note = ['']*len(self.samp)
698+
self.aux_note = ['']*len(self.sample)
690699

691700
for ext in external_syms:
692701
for i in [i for i,x in enumerate(self.symbol) if x == ext]:
@@ -815,12 +824,12 @@ def convert_label_attribute(self, source_field, target_field, inplace=True, over
815824
if getattr(self, target_field) is not None:
816825
return
817826

818-
label_map_table = self.create_label_map()
819-
label_map_table.set_index(label_map_table[source_field].values, inplace=True)
827+
label_map = self.create_label_map(inplace=False)
828+
label_map.set_index(label_map[source_field].values, inplace=True)
820829

821-
target_item = label_map_table.loc[getattr(self, source_field), target_field].values
830+
target_item = label_map.loc[getattr(self, source_field), target_field].values
822831

823-
if source_field != 'label_store':
832+
if target_field != 'label_store':
824833
# Should already be int64 dtype if target is label_store
825834
target_item = list(target_item)
826835

@@ -854,7 +863,6 @@ def custom_triplet_bytes(custom_triplet):
854863
Convert triplet of [label_store, symbol, description] into bytes
855864
for defining custom labels in the annotation file
856865
"""
857-
858866
# Structure: 0, NOTE, len(aux_note), aux_note, codenumber, space, codesymbol, space, description, (0 null if necessary)
859867
# Remember, aux_note string includes 'number(s)<space><symbol><space><description>''
860868
annbytes = [0, 88, len(custom_triplet[2]) + 3 + len(str(custom_triplet[0])), 252] + [ord(c) for c in str(custom_triplet[0])] \
@@ -871,7 +879,7 @@ def isblank(x):
871879
if x is None:
872880
return True
873881
elif isinstance(x, list):
874-
set(x) == set([None]):
882+
if set(x) == set([None]):
875883
return True
876884
return False
877885

@@ -1143,6 +1151,8 @@ def rdann(recordname, extension, sampfrom=0, sampto=None, shiftsamps=False,
11431151
subtype=subtype, chan=chan, num=num, aux_note=aux_note, fs=fs,
11441152
custom_labels=custom_labels)
11451153

1154+
1155+
11461156
# Get the set of unique label definitions contained in this annotation
11471157
if summarize_labels:
11481158
annotation.get_contained_labels(inplace=True)
@@ -1427,7 +1437,7 @@ def lists_to_arrays(*args):
14271437
# Allowed types of each Annotation object attribute.
14281438
ann_field_types = {'recordname': (str), 'extension': (str), 'sample': (np.ndarray),
14291439
'symbol': (list, np.ndarray), 'subtype': (np.ndarray), 'chan': (np.ndarray),
1430-
'num': (np.ndarray), 'aux_note': (list, np.ndarray), 'fs': _headers.floattypes,
1440+
'num': (np.ndarray), 'aux_note': (list, np.ndarray), 'fs': tuple(_headers.floattypes),
14311441
'label_store': (np.ndarray), 'description':(list, np.ndarray), 'custom_labels': (pd.DataFrame, list, tuple),
14321442
'contained_labels':(pd.DataFrame, list, tuple)}
14331443

0 commit comments

Comments
 (0)