@@ -204,24 +204,29 @@ def get_label_fields(self):
204
204
# Check the set fields of the annotation object
205
205
def check_fields (self ):
206
206
# Check all set fields
207
- for field in ann_field_types :
207
+ for field in ALLOWED_TYPES :
208
208
if getattr (self , field ) is not None :
209
209
# Check the type of the field's elements
210
210
self .check_field (field )
211
211
return
212
212
213
213
214
- # Check a particular annotation field
214
+
215
215
def check_field (self , field ):
216
+ """
217
+ Check a particular annotation field
218
+ """
216
219
217
220
item = getattr (self , field )
218
221
219
- if not isinstance (item , ann_field_types [field ]):
220
- raise TypeError ('The ' + field + ' field must be one of the following types:' , ann_field_types [field ])
222
+ if not isinstance (item , ALLOWED_TYPES [field ]):
223
+ raise TypeError ('The ' + field + ' field must be one of the following types:' , ALLOWED_TYPES [field ])
221
224
222
- if field in int_ann_fields :
223
- if not hasattr (field , '__index__' ):
224
- raise TypeError ('The ' + field + ' field must have an integer-based dtype.' )
225
+ # Numerical integer annotation fields: sample, label_store, sub,
226
+ # chan, num
227
+ if ALLOWED_TYPES [field ] == (np .ndarray ):
228
+ record .check_np_array (item = item , field_name = field , ndim = 1 ,
229
+ parent_class = np .integer , channel_num = None )
225
230
226
231
# Field specific checks
227
232
if field == 'record_name' :
@@ -286,13 +291,13 @@ def check_field(self, field):
286
291
if not hasattr (label_store [i ], '__index__' ):
287
292
raise TypeError ('The label_store values of the ' + field + ' field must be integer-like' )
288
293
289
- if not isinstance (symbol [i ], strtypes ) or len (symbol [i ]) not in [1 ,2 ,3 ]:
294
+ if not isinstance (symbol [i ], str_types ) or len (symbol [i ]) not in [1 ,2 ,3 ]:
290
295
raise ValueError ('The symbol values of the ' + field + ' field must be strings of length 1 to 3' )
291
296
292
297
if bool (re .search ('[ \t \n \r \f \v ]' , symbol [i ])):
293
298
raise ValueError ('The symbol values of the ' + field + ' field must not contain whitespace characters' )
294
299
295
- if not isinstance (description [i ], strtypes ):
300
+ if not isinstance (description [i ], str_types ):
296
301
raise TypeError ('The description values of the ' + field + ' field must be strings' )
297
302
298
303
# Would be good to enfore this but existing garbage annotations have tabs and newlines...
@@ -304,7 +309,7 @@ def check_field(self, field):
304
309
uniq_elements = set (item )
305
310
306
311
for e in uniq_elements :
307
- if not isinstance (e , strtypes ):
312
+ if not isinstance (e , str_types ):
308
313
raise TypeError ('Subelements of the ' + field + ' field must be strings' )
309
314
310
315
if field == 'symbol' :
@@ -1580,22 +1585,69 @@ def rm_last(*args):
1580
1585
return [a [:- 1 ] for a in args ]
1581
1586
return
1582
1587
1583
- ## ------------- /Reading Annotations ------------- ##
1588
+ ## ------------- Annotation Field Specifications ------------- ##
1589
+
1590
+ """
1591
+ WFDB field specifications for each field. The indexes are the field
1592
+ names.
1593
+
1594
+ Parameters
1595
+ ----------
1596
+ allowed_types:
1597
+ Data types the field (or its elements) can be.
1598
+ delimiter:
1599
+ The text delimiter that precedes the field in the header file.
1600
+ write_required:
1601
+ Whether the field is required for writing a header (more stringent
1602
+ than origin WFDB library).
1603
+ read_default:
1604
+ The default value for the field when read if any. Most fields do not
1605
+ have a default. The reason for the variation, is that we do not want
1606
+ to imply that some fields are present when they are not, unless the
1607
+ field is essential. See the notes.
1608
+ write_default:
1609
+ The default value for the field to fill in before writing, if any.
1610
+
1611
+ Notes
1612
+ -----
1613
+ In the original WFDB package, certain fields have default values, but
1614
+ not all of them. Some attributes need to be present for core
1615
+ functionality, ie. baseline, whereas others are not essential, yet have
1616
+ defaults, ie. base_time.
1617
+
1618
+ This inconsistency has likely resulted in the generation of incorrect
1619
+ files, and general confusion. This library aims to make explicit,
1620
+ whether certain fields are present in the file, by setting their values
1621
+ to None if they are not written in, unless the fields are essential, in
1622
+ which case an actual default value will be set.
1623
+
1624
+ The read vs write default values are different for 2 reasons:
1625
+ 1. We want to force the user to be explicit with certain important
1626
+ fields when writing WFDB records fields, without affecting
1627
+ existing WFDB headers when reading.
1628
+ 2. Certain unimportant fields may be dependencies of other
1629
+ important fields. When writing, we want to fill in defaults
1630
+ so that the user doesn't need to. But when reading, it should
1631
+ be clear that the fields are missing.
1632
+
1633
+ """
1634
+
1584
1635
1585
1636
# Allowed types of each Annotation object attribute.
1586
- ann_field_types = {'record_name' : (str ), 'extension' : (str ), 'sample' : (np .ndarray ),
1587
- 'symbol' : (list , np .ndarray ), 'subtype' : (np .ndarray ), 'chan' : (np .ndarray ),
1588
- 'num' : (np .ndarray ), 'aux_note' : (list , np .ndarray ), 'fs' : _header .float_types ,
1589
- 'label_store' : (np .ndarray ), 'description' :(list , np .ndarray ), 'custom_labels' : (pd .DataFrame , list , tuple ),
1637
+ ALLOWED_TYPES = {'record_name' : (str ), 'extension' : (str ),
1638
+ 'sample' : (np .ndarray ,), 'symbol' : (list , np .ndarray ),
1639
+ 'subtype' : (np .ndarray ,), 'chan' : (np .ndarray ,),
1640
+ 'num' : (np .ndarray ,), 'aux_note' : (list , np .ndarray ),
1641
+ 'fs' : _header .float_types , 'label_store' : (np .ndarray ,),
1642
+ 'description' :(list , np .ndarray ),
1643
+ 'custom_labels' : (pd .DataFrame , list , tuple ),
1590
1644
'contained_labels' :(pd .DataFrame , list , tuple )}
1591
1645
1592
- strtypes = (str , np .str_ )
1646
+ str_types = (str , np .str_ )
1593
1647
1594
1648
# Elements of the annotation label
1595
1649
ann_label_fields = ('label_store' , 'symbol' , 'description' )
1596
1650
1597
- # Numerical integer annotation fields: sample, label_store, sub, chan, num
1598
- int_ann_fields = [field for field in ann_field_types if ann_field_types [field ] == (np .ndarray )]
1599
1651
1600
1652
class AnnotationClass (object ):
1601
1653
def __init__ (self , extension , description , human_reviewed ):
0 commit comments