@@ -130,7 +130,7 @@ def wrann(self, writefs=False)
130
130
131
131
# Calculate the label_store field if necessary
132
132
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' )
134
134
135
135
# Write the header file using the specified fields
136
136
self .wrannfile (writefs = writefs )
@@ -172,7 +172,7 @@ def checkfield(self, field):
172
172
173
173
# Field specific checks
174
174
if field == 'recordname' :
175
- if bool (re .search ('[^-\w]' , self .recordname ))
175
+ if bool (re .search ('[^-\w]' , self .recordname )):
176
176
raise ValueError ('recordname must only comprise of letters, digits, hyphens, and underscores.' )
177
177
elif field == 'extension' :
178
178
if bool (re .search ('[^a-zA-Z]' , self .extension )):
@@ -193,7 +193,7 @@ def checkfield(self, field):
193
193
column_names = list (item )
194
194
if 'symbol' in column_names and 'description' in column_names :
195
195
if 'label_store' in column_names :
196
- label_store = item ['label_store' ].values
196
+ label_store = list ( item ['label_store' ].values )
197
197
else :
198
198
label_store = None
199
199
symbol = item ['symbol' ].values
@@ -312,11 +312,12 @@ def check_field_cohesion(self, present_label_fields):
312
312
# labels present.
313
313
for field in present_label_fields :
314
314
defined_values = self .__label_map__ [field ].values
315
- if set (getattr (self , field )) - defined_values != {}:
316
- raise ValueError ('\n ' .join ('\n The ' + 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 (['\n The ' + 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)' ]))
320
321
321
322
return
322
323
@@ -355,20 +356,23 @@ def standardize_custom_labels(self):
355
356
# Convert to dataframe if not already
356
357
if not isinstance (custom_labels , pd .DataFrame ):
357
358
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' )
359
361
custom_labels = pd .DataFrame ({'symbol' : symbol , 'description' : description })
360
362
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' )
362
366
custom_labels = pd .DataFrame ({'label_store' :label_store , 'symbol' : symbol , 'description' : description })
363
367
364
368
# Assign label_store values to the custom labels if not defined
365
369
if 'label_store' not in list (custom_labels ):
366
-
367
370
undefined_label_stores = self .get_undefined_label_stores ()
371
+
368
372
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
372
376
373
377
n_custom_labels = custom_labels .shape [0 ]
374
378
@@ -378,6 +382,8 @@ def standardize_custom_labels(self):
378
382
custom_labels ['label_store' ] = available_label_stores [:n_custom_labels ]
379
383
380
384
custom_labels .set_index (custom_labels ['label_store' ].values , inplace = True )
385
+ custom_labels = custom_labels [list (ann_label_fields )]
386
+
381
387
self .custom_labels = custom_labels
382
388
383
389
return
@@ -439,14 +445,14 @@ def get_available_label_stores(self, usefield = 'tryall'):
439
445
# Get the standard wfdb label_store values overwritten by the
440
446
# custom_labels if any
441
447
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 ))
443
449
if usefield == 'label_store' :
444
450
overwritten_label_stores = set (custom_field ).intersection (set (ann_label_table ['label_store' ]))
445
451
else :
446
452
overwritten_fields = set (custom_field ).intersection (set (ann_label_table [usefield ]))
447
453
overwritten_label_stores = ann_label_table .loc [ann_label_table [usefield ] in overwritten_fields , 'label_store' ].values
448
454
else :
449
- overwritten_label_stores = {}
455
+ overwritten_label_stores = set ()
450
456
451
457
452
458
# The undefined values in the standard wfdb labels
@@ -462,13 +468,9 @@ def get_custom_label_attribute(self, attribute):
462
468
Get a list of the custom_labels attribute.
463
469
ie. label_store, symbol, or description.
464
470
465
- attribute can also be a list of attributes.
466
-
467
471
The custom_labels variable could be in
468
472
a number of formats
469
473
"""
470
- if type (attribute ) != 'string' :
471
- return [get_custom_label_attribute [a ] for a in attribute ]
472
474
473
475
if attribute not in ann_label_fields :
474
476
raise ValueError ('Invalid attribute specified' )
@@ -524,16 +526,25 @@ def wrannfile(self, writefs):
524
526
"""
525
527
526
528
# 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 = []
528
533
# Calculate the custom_labels bytes to write if present
529
534
cl_bytes = self .calc_cl_bytes ()
530
535
# Calculate the core field bytes to write
531
536
core_bytes = self .calc_core_bytes ()
532
537
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
+
533
544
# Write the file
534
545
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 )
537
548
538
549
return
539
550
@@ -564,9 +575,6 @@ def calc_fs_bytes(self):
564
575
if ndigits % 2 :
565
576
data_bytes .append (0 )
566
577
567
- # Add the extra -1 0 notqrs filler
568
- data_bytes = data_bytes + [0 , 236 , 255 , 255 , 255 , 255 , 1 , 0 ]
569
-
570
578
return np .array (data_bytes ).astype ('u1' )
571
579
572
580
# Calculate the bytes written to the annotation file for the custom_labels field
@@ -581,11 +589,12 @@ def calc_cl_bytes(self):
581
589
582
590
# The end wrapper: '0 NOTE length aux_note ## end of definitions' followed by SKIP -1, +1
583
591
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 ]
585
593
586
594
custom_bytes = []
595
+
587
596
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 ) ]))
589
598
590
599
# writecontent = []
591
600
# for i in range(len(self.custom_labels)):
@@ -622,7 +631,7 @@ def calc_core_bytes(self):
622
631
for i in range (len (sampdiff )):
623
632
624
633
# 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 ]]))
626
635
627
636
# Process the extra optional fields
628
637
for field in extra_write_fields :
@@ -640,7 +649,7 @@ def calc_core_bytes(self):
640
649
def compact_fields (self ):
641
650
642
651
# Number of annotations
643
- nannots = len (self .samp )
652
+ nannots = len (self .sample )
644
653
645
654
# Chan and num carry over previous fields. Get lists of as few
646
655
# elements to write as possible
@@ -686,7 +695,7 @@ def sym_to_aux(self):
686
695
return
687
696
688
697
if self .aux_note is None :
689
- self .aux_note = ['' ]* len (self .samp )
698
+ self .aux_note = ['' ]* len (self .sample )
690
699
691
700
for ext in external_syms :
692
701
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
815
824
if getattr (self , target_field ) is not None :
816
825
return
817
826
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 )
820
829
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
822
831
823
- if source_field != 'label_store' :
832
+ if target_field != 'label_store' :
824
833
# Should already be int64 dtype if target is label_store
825
834
target_item = list (target_item )
826
835
@@ -854,7 +863,6 @@ def custom_triplet_bytes(custom_triplet):
854
863
Convert triplet of [label_store, symbol, description] into bytes
855
864
for defining custom labels in the annotation file
856
865
"""
857
-
858
866
# Structure: 0, NOTE, len(aux_note), aux_note, codenumber, space, codesymbol, space, description, (0 null if necessary)
859
867
# Remember, aux_note string includes 'number(s)<space><symbol><space><description>''
860
868
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):
871
879
if x is None :
872
880
return True
873
881
elif isinstance (x , list ):
874
- set (x ) == set ([None ]):
882
+ if set (x ) == set ([None ]):
875
883
return True
876
884
return False
877
885
@@ -1143,6 +1151,8 @@ def rdann(recordname, extension, sampfrom=0, sampto=None, shiftsamps=False,
1143
1151
subtype = subtype , chan = chan , num = num , aux_note = aux_note , fs = fs ,
1144
1152
custom_labels = custom_labels )
1145
1153
1154
+
1155
+
1146
1156
# Get the set of unique label definitions contained in this annotation
1147
1157
if summarize_labels :
1148
1158
annotation .get_contained_labels (inplace = True )
@@ -1427,7 +1437,7 @@ def lists_to_arrays(*args):
1427
1437
# Allowed types of each Annotation object attribute.
1428
1438
ann_field_types = {'recordname' : (str ), 'extension' : (str ), 'sample' : (np .ndarray ),
1429
1439
'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 ) ,
1431
1441
'label_store' : (np .ndarray ), 'description' :(list , np .ndarray ), 'custom_labels' : (pd .DataFrame , list , tuple ),
1432
1442
'contained_labels' :(pd .DataFrame , list , tuple )}
1433
1443
0 commit comments