@@ -15,16 +15,16 @@ class Annotation(object):
15
15
"""
16
16
The class representing WFDB annotations.
17
17
18
- Annotation objects can be created using the initializer, or by reading a
19
- WFDB annotation file with `rdann`.
18
+ Annotation objects can be created using the initializer, or by
19
+ reading a WFDB annotation file with `rdann`.
20
20
21
21
The attributes of the Annotation object give information about the
22
22
annotation as specified by:
23
23
https://www.physionet.org/physiotools/wag/annot-5.htm
24
24
25
- Call `show_ann_labels()` to see the list of standard annotation codes. Any
26
- text used to label annotations that are not one of these codes should go in
27
- the 'aux_note' field rather than the 'sym' field.
25
+ Call `show_ann_labels()` to see the list of standard annotation
26
+ codes. Any text used to label annotations that are not one of these
27
+ codes should go in the 'aux_note' field rather than the 'sym' field.
28
28
29
29
Examples
30
30
--------
@@ -33,11 +33,15 @@ class Annotation(object):
33
33
aux_note=[None, None, 'Serious Vfib'])
34
34
35
35
"""
36
+ # The data fields for each individual annotation
37
+ DATA_FIELDS = ['sample' , 'symbol' , 'subtype' , 'chan' , 'num' ,
38
+ 'aux_note' , 'label_store' , 'description' ]
39
+
36
40
37
41
def __init__ (self , record_name , extension , sample , symbol = None ,
38
- subtype = None , chan = None , num = None , aux_note = None , fs = None ,
39
- label_store = None , description = None , custom_labels = None ,
40
- contained_labels = None ):
42
+ subtype = None , chan = None , num = None , aux_note = None ,
43
+ fs = None , label_store = None , description = None ,
44
+ custom_labels = None ):
41
45
"""
42
46
Parameters
43
47
----------
@@ -47,19 +51,19 @@ def __init__(self, record_name, extension, sample, symbol=None,
47
51
extension : str
48
52
The file extension of the file the annotation is stored in.
49
53
sample : numpy array
50
- A numpy array containing the annotation locations in samples relative to
51
- the beginning of the record.
52
- symbol : list, or numpy array, optional
53
- The symbols used to display the annotation labels. List or numpy array.
54
- If this field is present, `label_store` must not be present.
54
+ A numpy array containing the annotation locations in samples
55
+ relative to the beginning of the record.
56
+ symbol : numpy array, optional
57
+ The symbols used to display the annotation labels.
55
58
subtype : numpy array, optional
56
- A numpy array containing the marked class/category of each annotation.
57
- chan : numpy array, optional
58
- A numpy array containing the signal channel associated with each
59
+ A numpy array containing the marked class/category of each
59
60
annotation.
61
+ chan : numpy array, optional
62
+ A numpy array containing the signal channel associated with
63
+ each annotation.
60
64
num : numpy array, optional
61
- A numpy array containing the labelled annotation number for each
62
- annotation.
65
+ A numpy array containing the labelled annotation number for
66
+ each annotation.
63
67
aux_note : list, optional
64
68
A list containing the auxiliary information string (or None for
65
69
annotations without notes) for each annotation.
@@ -74,9 +78,6 @@ def __init__(self, record_name, extension, sample, symbol=None,
74
78
the relationship between the three label fields. The data type is a
75
79
pandas DataFrame with three columns:
76
80
['label_store', 'symbol', 'description']
77
- contained_labels : pandas dataframe, optional
78
- The unique labels contained in this annotation. Same structure as
79
- `custom_labels`.
80
81
81
82
"""
82
83
self .record_name = record_name
@@ -97,9 +98,6 @@ def __init__(self, record_name, extension, sample, symbol=None,
97
98
self .custom_labels = custom_labels
98
99
self .contained_labels = contained_labels
99
100
100
- self .ann_len = len (self .sample )
101
-
102
- #__label_map__: (storevalue, symbol, description) hidden attribute
103
101
104
102
# Equal comparison operator for objects of this type
105
103
def __eq__ (self , other ):
@@ -150,8 +148,6 @@ def apply_range(self, sampfrom=0, sampto=None):
150
148
151
149
self .aux_note = [self .aux_note [i ] for i in kept_inds ]
152
150
153
- self .ann_len = len (self .sample )
154
-
155
151
def wrann (self , write_fs = False , write_dir = '' ):
156
152
"""
157
153
Write a WFDB annotation file from this object.
@@ -203,17 +199,16 @@ def get_label_fields(self):
203
199
204
200
return present_label_fields
205
201
206
- # Check the set fields of the annotation object
202
+
207
203
def check_fields (self ):
208
- # Check all set fields
204
+ """
205
+ Check the set fields of the annotation object
206
+ """
209
207
for field in ALLOWED_TYPES :
210
- if getattr (self , field ) is not None :
211
- # Check the type of the field's elements
208
+ if hasattr (self , field ):
212
209
self .check_field (field )
213
210
return
214
211
215
-
216
-
217
212
def check_field (self , field ):
218
213
"""
219
214
Check a particular annotation field
@@ -648,9 +643,11 @@ def calc_fs_bytes(self):
648
643
649
644
return np .array (data_bytes ).astype ('u1' )
650
645
651
- # Calculate the bytes written to the annotation file for the
652
- # custom_labels field
653
646
def calc_cl_bytes (self ):
647
+ """
648
+ Calculate the bytes written to the annotation file for the
649
+ custom_labels field
650
+ """
654
651
655
652
if self .custom_labels is None :
656
653
return []
@@ -675,7 +672,7 @@ def calc_cl_bytes(self):
675
672
# custombytes = [customcode2bytes(triplet) for triplet in writecontent]
676
673
# custombytes = [item for sublist in custombytes for item in sublist]
677
674
678
- return np .array (headbytes + custom_bytes + tailbytes ).astype ('u1' )
675
+ return np .array (headbytes + custom_bytes + tailbytes ).astype ('u1' )
679
676
680
677
def calc_core_bytes (self ):
681
678
"""
@@ -687,13 +684,13 @@ def calc_core_bytes(self):
687
684
else :
688
685
sampdiff = np .concatenate (([self .sample [0 ]], np .diff (self .sample )))
689
686
690
- # Create a copy of the annotation object with a
691
- # compact version of fields to write
687
+ # Create a copy of the annotation object with a compact version
688
+ # of fields to write
692
689
compact_annotation = copy .deepcopy (self )
693
- compact_annotation .compact_fields ()
694
-
690
+ compact_annotation ._compact_fields ()
695
691
696
- # The optional fields to be written. Write if they are not None or all empty
692
+ # The optional fields to be written. Write if they are not None
693
+ # or all empty
697
694
extra_write_fields = []
698
695
699
696
for field in ['num' , 'subtype' , 'chan' , 'aux_note' ]:
@@ -721,7 +718,7 @@ def calc_core_bytes(self):
721
718
722
719
# Compact all of the object's fields so that the output
723
720
# writing annotation file writes as few bytes as possible
724
- def compact_fields (self ):
721
+ def _compact_fields (self ):
725
722
726
723
# Number of annotations
727
724
nannots = len (self .sample )
@@ -785,7 +782,7 @@ def sym_to_aux(self):
785
782
def get_contained_labels (self , inplace = True ):
786
783
"""
787
784
Get the set of unique labels contained in this annotation.
788
- Returns a pandas dataframe or sets the __contained__ labels
785
+ Returns a pandas dataframe or sets the contained_labels
789
786
attribute of the object.
790
787
791
788
@@ -876,15 +873,18 @@ def set_label_elements(self, wanted_label_elements):
876
873
unwanted_label_elements = list (set (ANN_LABEL_FIELDS )
877
874
- set (wanted_label_elements ))
878
875
879
- self .rm_attributes (unwanted_label_elements )
876
+ self ._rm_attributes (unwanted_label_elements )
880
877
881
878
return
882
879
883
- def rm_attributes (self , attributes ):
880
+ def _rm_attributes (self , attributes ):
881
+ """
882
+ Remove the specified attributes from the object.
883
+ """
884
884
if isinstance (attributes , str ):
885
885
attributes = [attributes ]
886
886
for a in attributes :
887
- setattr (self , a , None )
887
+ delattr (self , a )
888
888
return
889
889
890
890
def convert_label_attribute (self , source_field , target_field ,
@@ -1187,7 +1187,20 @@ def wrann(record_name, extension, sample, symbol=None, subtype=None, chan=None,
1187
1187
1188
1188
def show_ann_labels ():
1189
1189
"""
1190
- Display the standard wfdb annotation label mapping.
1190
+ Display the standard wfdb annotation label mapping table.
1191
+
1192
+ When writing WFDB annotation files, please adhere to these standards
1193
+ for all annotation definitions present in this table, and define the
1194
+ `custom_labels` field for items that are not present.
1195
+
1196
+ Columns
1197
+ -------
1198
+ label_store :
1199
+ The integer values used to store the labels in the file.
1200
+ symbol :
1201
+ The symbol used to display each label.
1202
+ description :
1203
+ The full description of what each label means.
1191
1204
1192
1205
Examples
1193
1206
--------
@@ -1199,7 +1212,20 @@ def show_ann_labels():
1199
1212
1200
1213
def show_ann_classes ():
1201
1214
"""
1202
- Display the standard wfdb annotation file extensions.
1215
+ Display the standard WFDB annotation file extensions and their
1216
+ meanings.
1217
+
1218
+ When writing WFDB annotation files, please adhere to these
1219
+ standards.
1220
+
1221
+ Columns
1222
+ -------
1223
+ extension :
1224
+ The file extension.
1225
+ description :
1226
+ The description of the annotation content.
1227
+ human_reviewed :
1228
+ Whether the annotations were reviewed by humans.
1203
1229
1204
1230
Examples
1205
1231
--------
@@ -1208,9 +1234,10 @@ def show_ann_classes():
1208
1234
"""
1209
1235
print (ANN_EXTENSIONS )
1210
1236
1237
+ ['sample' , 'symbol' , 'subtype' , 'chan' , 'num' , 'aux_note' , 'label_store' , 'description' ]
1211
1238
1212
1239
def rdann (record_name , extension , sampfrom = 0 , sampto = None , shift_samps = False ,
1213
- pb_dir = None , return_label_elements = [ 'symbol' ] ,
1240
+ pb_dir = None , data_fields = Annotation . DATA_FIELDS ,
1214
1241
summarize_labels = False , return_df = False ):
1215
1242
"""
1216
1243
Read a WFDB annotation file record_name.extension and return an
@@ -1676,7 +1703,7 @@ def rm_last(*args):
1676
1703
ANN_LABEL_FIELDS = ('label_store' , 'symbol' , 'description' )
1677
1704
1678
1705
1679
- # Standard annotation file extensions
1706
+ # Standard WFDB annotation file extensions
1680
1707
ANN_EXTENSIONS = pd .DataFrame (data = [
1681
1708
('atr' , 'Reference ECG annotations' , True ),
1682
1709
@@ -1702,45 +1729,50 @@ def rm_last(*args):
1702
1729
1703
1730
# The standard library annotation label map
1704
1731
ANN_LABELS = pd .DataFrame (data = [
1705
- (0 , ' ' , 'NOTANN' , 'Not an actual annotation' ),
1706
- (1 , 'N' , 'NORMAL' , 'Normal beat' ),
1707
- (2 , 'L' , 'LBBB' , 'Left bundle branch block beat' ),
1708
- (3 , 'R' , 'RBBB' , 'Right bundle branch block beat' ),
1709
- (4 , 'a' , 'ABERR' , 'Aberrated atrial premature beat' ),
1710
- (5 , 'V' , 'PVC' , 'Premature ventricular contraction' ),
1711
- (6 , 'F' , 'FUSION' , 'Fusion of ventricular and normal beat' ),
1712
- (7 , 'J' , 'NPC' , 'Nodal (junctional) premature beat' ),
1713
- (8 , 'A' , 'APC' , 'Atrial premature contraction' ),
1714
- (9 , 'S' , 'SVPB' , 'Premature or ectopic supraventricular beat' ),
1715
- (10 , 'E' , 'VESC' , 'Ventricular escape beat' ),
1716
- (11 , 'j' , 'NESC' , 'Nodal (junctional) escape beat' ),
1717
- (12 , '/' , 'PACE' , 'Paced beat' ),
1718
- (13 , 'Q' , 'UNKNOWN' , 'Unclassifiable beat' ),
1719
- (14 , '~' , 'NOISE' , 'Signal quality change' ),
1720
- (16 , '|' , 'ARFCT' , 'Isolated QRS-like artifact' ),
1721
- (18 , 's' , 'STCH' , 'ST change' ),
1722
- (19 , 'T' , 'TCH' , 'T-wave change' ),
1723
- (20 , '*' , 'SYSTOLE' , 'Systole' ),
1724
- (21 , 'D' , 'DIASTOLE' , 'Diastole' ),
1725
- (22 , '"' , 'NOTE' , 'Comment annotation' ),
1726
- (23 , '=' , 'MEASURE' , 'Measurement annotation' ),
1727
- (24 , 'p' , 'PWAVE' , 'P-wave peak' ),
1728
- (25 , 'B' , 'BBB' , 'Left or right bundle branch block' ),
1729
- (26 , '^' , 'PACESP' , 'Non-conducted pacer spike' ),
1730
- (27 , 't' , 'TWAVE' , 'T-wave peak' ),
1731
- (28 , '+' , 'RHYTHM' , 'Rhythm change' ),
1732
- (29 , 'u' , 'UWAVE' , 'U-wave peak' ),
1733
- (30 , '?' , 'LEARN' , 'Learning' ),
1734
- (31 , '!' , 'FLWAV' , 'Ventricular flutter wave' ),
1735
- (32 , '[' , 'VFON' , 'Start of ventricular flutter/fibrillation' ),
1736
- (33 , ']' , 'VFOFF' , 'End of ventricular flutter/fibrillation' ),
1737
- (34 , 'e' , 'AESC' , 'Atrial escape beat' ),
1738
- (35 , 'n' , 'SVESC' , 'Supraventricular escape beat' ),
1739
- (36 , '@' , 'LINK' , 'Link to external data (aux_note contains URL)' ),
1740
- (37 , 'x' , 'NAPC' , 'Non-conducted P-wave (blocked APB)' ),
1741
- (38 , 'f' , 'PFUS' , 'Fusion of paced and normal beat' ),
1742
- (39 , '(' , 'WFON' , 'Waveform onset' ),
1743
- (40 , ')' , 'WFOFF' , 'Waveform end' ),
1744
- (41 , 'r' , 'RONT' , 'R-on-T premature ventricular contraction' ),
1745
- ], columns = ['label_store' , 'symbol' , 'short_description' , 'description' ]
1732
+ # 0 is used in the file as an indicator flag.
1733
+ # (0, ' ', 'Not an actual annotation'),
1734
+ (1 , 'N' , 'Normal beat' ),
1735
+ (2 , 'L' , 'Left bundle branch block beat' ),
1736
+ (3 , 'R' , 'Right bundle branch block beat' ),
1737
+ (4 , 'a' , 'Aberrated atrial premature beat' ),
1738
+ (5 , 'V' , 'Premature ventricular contraction' ),
1739
+ (6 , 'F' , 'Fusion of ventricular and normal beat' ),
1740
+ (7 , 'J' , 'Nodal (junctional) premature beat' ),
1741
+ (8 , 'A' , 'Atrial premature contraction' ),
1742
+ (9 , 'S' , 'Premature or ectopic supraventricular beat' ),
1743
+ (10 , 'E' , 'Ventricular escape beat' ),
1744
+ (11 , 'j' , 'Nodal (junctional) escape beat' ),
1745
+ (12 , '/' , 'Paced beat' ),
1746
+ (13 , 'Q' , 'Unclassifiable beat' ),
1747
+ (14 , '~' , 'Signal quality change' ),
1748
+ (16 , '|' , 'Isolated QRS-like artifact' ),
1749
+ (18 , 's' , 'ST change' ),
1750
+ (19 , 'T' , 'T-wave change' ),
1751
+ (20 , '*' , 'Systole' ),
1752
+ (21 , 'D' , 'Diastole' ),
1753
+ (22 , '"' , 'Comment annotation' ),
1754
+ (23 , '=' , 'Measurement annotation' ),
1755
+ (24 , 'p' , 'P-wave peak' ),
1756
+ (25 , 'B' , 'Left or right bundle branch block' ),
1757
+ (26 , '^' , 'Non-conducted pacer spike' ),
1758
+ (27 , 't' , 'T-wave peak' ),
1759
+ (28 , '+' , 'Rhythm change' ),
1760
+ (29 , 'u' , 'U-wave peak' ),
1761
+ (30 , '?' , 'Learning' ),
1762
+ (31 , '!' , 'Ventricular flutter wave' ),
1763
+ (32 , '[' , 'Start of ventricular flutter/fibrillation' ),
1764
+ (33 , ']' , 'End of ventricular flutter/fibrillation' ),
1765
+ (34 , 'e' , 'Atrial escape beat' ),
1766
+ (35 , 'n' , 'Supraventricular escape beat' ),
1767
+ (36 , '@' , 'Link to external data (aux_note contains URL)' ),
1768
+ (37 , 'x' , 'Non-conducted P-wave (blocked APB)' ),
1769
+ (38 , 'f' , 'Fusion of paced and normal beat' ),
1770
+ (39 , '(' , 'Waveform onset' ),
1771
+ (40 , ')' , 'Waveform end' ),
1772
+ (41 , 'r' , 'R-on-T premature ventricular contraction' ),
1773
+ ], columns = ['label_store' , 'symbol' , 'description' ]
1746
1774
)
1775
+
1776
+ # The allowed integer range for the label_store value in wfdb
1777
+ # annotations. 0 is used to
1778
+ LABEL_RANGE = (1 , 49 )
0 commit comments