@@ -32,7 +32,7 @@ class Annotation(object):
32
32
def __init__(self, recordname, annotator, annsamp, anntype, subtype = None,
33
33
chan = None, num = None, aux = None, fs = None, custom_anntypes = None)
34
34
35
- Call 'showanncodes ()' to see the list of standard annotation codes. Any text used to label
35
+ Call 'show_ann_labels ()' to see the list of standard annotation codes. Any text used to label
36
36
annotations that are not one of these codes should go in the 'aux' field rather than the
37
37
'anntype' field.
38
38
@@ -202,9 +202,9 @@ def checkfield(self, field):
202
202
raise ValueError ('WFDB annotation files cannot store sample differences greater than 2**31' )
203
203
elif field == 'anntype' :
204
204
# Ensure all fields lie in standard WFDB annotation codes or custom codes
205
- if set (self .anntype ) - set (annsyms .values () ).union () != set ():
205
+ if set (self .anntype ) - set (ann_label_table [ 'Symbol' ] .values ).union () != set ():
206
206
print ("The 'anntype' field contains items not encoded in the WFDB library, or in this object's custom defined anntypes." )
207
- print ('To see the valid annotation codes call: showanncodes ()' )
207
+ print ('To see the valid annotation codes call: show_ann_labels ()' )
208
208
print ('To transfer non-encoded anntype items into the aux field call: self.type2aux()' )
209
209
print ("To define custom codes, set the custom_anntypes field as a dictionary with format: {custom anntype character:description}" )
210
210
raise Exception ()
@@ -346,7 +346,7 @@ def type2aux(self):
346
346
if type (at ) != str :
347
347
raise TypeError ('anntype elements must all be strings' )
348
348
349
- external_anntypes = set (self .anntype ) - set (annsyms .values () )
349
+ external_anntypes = set (self .anntype ) - set (ann_label_table [ 'Symbol' ] .values )
350
350
351
351
# Nothing to do
352
352
if external_anntypes == set ():
@@ -412,7 +412,7 @@ def ca2bytes(custom_anntypes):
412
412
105 ,116 ,105 ,111 ,110 ,115 ,0 ,0 ,236 ,255 ,255 ,255 ,255 ,1 ,0 ]
413
413
414
414
# Annotation codes range from 0-49.
415
- freenumbers = list (set (range (50 )) - set (annsyms . keys () ))
415
+ freenumbers = list (set (range (50 )) - set (ann_label_table [ 'Store-Value' ]. values ))
416
416
417
417
if len (custom_anntypes ) > len (freenumbers ):
418
418
raise Exception ('There can only be a maximum of ' + len (freenumbers )+ ' custom annotation codes.' )
@@ -494,9 +494,9 @@ def field2bytes(field, value):
494
494
495
495
# annsamp and anntype bytes come together
496
496
if field == 'samptype' :
497
-
498
497
# Numerical value encoding annotation symbol
499
- typecode = revannsyms [value [1 ]]
498
+ typecode = ann_label_table .loc [ann_label_table ['Symbol' ]== value [1 ], 'Store-Value' ].values [0 ]
499
+
500
500
# sample difference
501
501
sd = value [0 ]
502
502
@@ -579,16 +579,23 @@ def wrann(recordname, annotator, annsamp, anntype, subtype = None, chan = None,
579
579
# Perform field checks and write the annotation file
580
580
annotation .wrann (writefs = True )
581
581
582
- # Display the annotation symbols and the codes they represent
583
- def showanncodes ():
582
+
583
+ def show_ann_labels ():
584
584
"""
585
- Display the annotation symbols and the codes they represent according to the
586
- standard WFDB library 10.5.24
585
+ Display the standard wfdb annotation label mapping
587
586
588
587
Usage:
589
- showanncodes ()
588
+ show_ann_labels ()
590
589
"""
591
- print (symcodes )
590
+ print (ann_label_table )
591
+
592
+
593
+ def show_ann_classes ():
594
+ """
595
+ Display the standard wfdb annotation classes
596
+ """
597
+
598
+ pass
592
599
593
600
## ------------- Reading Annotations ------------- ##
594
601
@@ -873,7 +880,6 @@ def carry_fields(ai, cpychan, cpynum, chan, num):
873
880
return chan , num
874
881
875
882
876
-
877
883
# Remove unallocated part of array
878
884
def snip_arrays (annsamp ,anntype ,num ,subtype ,chan ,aux ,ai ):
879
885
annsamp = annsamp [0 :ai ].astype (int )
@@ -943,7 +949,7 @@ def proc_special_types(annsamp,anntype,num,subtype,chan,aux):
943
949
# Custom annotation types
944
950
custom_anntypes = {}
945
951
# The annotation symbol dictionary to modify and use
946
- allannsyms = annsyms . copy ( )
952
+ allannsyms = dict ( zip ( ann_label_table [ 'Store-Value' ]. values , ann_label_table [ 'Symbol' ]. values ) )
947
953
948
954
if special_inds != []:
949
955
# The annotation indices to be removed
@@ -988,125 +994,7 @@ def proc_special_types(annsamp,anntype,num,subtype,chan,aux):
988
994
## ------------- /Reading Annotations ------------- ##
989
995
990
996
991
- # Annotation mnemonic symbols for the 'anntype' field as specified in annot.c
992
- # from wfdb software library 10.5.24. At this point, several values are blank.
993
- # Commented out values are present in original file but have no meaning.
994
- annsyms = {
995
- 0 : ' ' , # not-QRS (not a getann/putann codedict) */
996
- 1 : 'N' , # normal beat */
997
- 2 : 'L' , # left bundle branch block beat */
998
- 3 : 'R' , # right bundle branch block beat */
999
- 4 : 'a' , # aberrated atrial premature beat */
1000
- 5 : 'V' , # premature ventricular contraction */
1001
- 6 : 'F' , # fusion of ventricular and normal beat */
1002
- 7 : 'J' , # nodal (junctional) premature beat */
1003
- 8 : 'A' , # atrial premature contraction */
1004
- 9 : 'S' , # premature or ectopic supraventricular beat */
1005
- 10 : 'E' , # ventricular escape beat */
1006
- 11 : 'j' , # nodal (junctional) escape beat */
1007
- 12 : '/' , # paced beat */
1008
- 13 : 'Q' , # unclassifiable beat */
1009
- 14 : '~' , # signal quality change */
1010
- # 15: '[15]',
1011
- 16 : '|' , # isolated QRS-like artifact */
1012
- # 17: '[17]',
1013
- 18 : 's' , # ST change */
1014
- 19 : 'T' , # T-wave change */
1015
- 20 : '*' , # systole */
1016
- 21 : 'D' , # diastole */
1017
- 22 : '"' , # comment annotation */
1018
- 23 : '=' , # measurement annotation */
1019
- 24 : 'p' , # P-wave peak */
1020
- 25 : 'B' , # left or right bundle branch block */
1021
- 26 : '^' , # non-conducted pacer spike */
1022
- 27 : 't' , # T-wave peak */
1023
- 28 : '+' , # rhythm change */
1024
- 29 : 'u' , # U-wave peak */
1025
- 30 : '?' , # learning */
1026
- 31 : '!' , # ventricular flutter wave */
1027
- 32 : '[' , # start of ventricular flutter/fibrillation */
1028
- 33 : ']' , # end of ventricular flutter/fibrillation */
1029
- 34 : 'e' , # atrial escape beat */
1030
- 35 : 'n' , # supraventricular escape beat */
1031
- 36 : '@' , # link to external data (aux contains URL) */
1032
- 37 : 'x' , # non-conducted P-wave (blocked APB) */
1033
- 38 : 'f' , # fusion of paced and normal beat */
1034
- 39 : '(' , # waveform onset */
1035
- 40 : ')' , # waveform end */
1036
- 41 : 'r' , # R-on-T premature ventricular contraction */
1037
- # 42: '[42]',
1038
- # 43: '[43]',
1039
- # 44: '[44]',
1040
- # 45: '[45]',
1041
- # 46: '[46]',
1042
- # 47: '[47]',
1043
- # 48: '[48]',
1044
- # 49: '[49]',
1045
- }
1046
- # Reverse ann symbols for mapping symbols back to numbers
1047
- revannsyms = {v : k for k , v in annsyms .items ()}
1048
-
1049
- # Annotation codes for 'anntype' field as specified in ecgcodes.h from
1050
- # wfdb software library 10.5.24. Commented out values are present in
1051
- # original file but have no meaning.
1052
- anncodes = {
1053
- 0 : 'NOTQRS' , # not-QRS (not a getann/putann codedict) */
1054
- 1 : 'NORMAL' , # normal beat */
1055
- 2 : 'LBBB' , # left bundle branch block beat */
1056
- 3 : 'RBBB' , # right bundle branch block beat */
1057
- 4 : 'ABERR' , # aberrated atrial premature beat */
1058
- 5 : 'PVC' , # premature ventricular contraction */
1059
- 6 : 'FUSION' , # fusion of ventricular and normal beat */
1060
- 7 : 'NPC' , # nodal (junctional) premature beat */
1061
- 8 : 'APC' , # atrial premature contraction */
1062
- 9 : 'SVPB' , # premature or ectopic supraventricular beat */
1063
- 10 : 'VESC' , # ventricular escape beat */
1064
- 11 : 'NESC' , # nodal (junctional) escape beat */
1065
- 12 : 'PACE' , # paced beat */
1066
- 13 : 'UNKNOWN' , # unclassifiable beat */
1067
- 14 : 'NOISE' , # signal quality change */
1068
- # 15: '',
1069
- 16 : 'ARFCT' , # isolated QRS-like artifact */
1070
- # 17: '',
1071
- 18 : 'STCH' , # ST change */
1072
- 19 : 'TCH' , # T-wave change */
1073
- 20 : 'SYSTOLE' , # systole */
1074
- 21 : 'DIASTOLE' , # diastole */
1075
- 22 : 'NOTE' , # comment annotation */
1076
- 23 : 'MEASURE' , # measurement annotation */
1077
- 24 : 'PWAVE' , # P-wave peak */
1078
- 25 : 'BBB' , # left or right bundle branch block */
1079
- 26 : 'PACESP' , # non-conducted pacer spike */
1080
- 27 : 'TWAVE' , # T-wave peak */
1081
- 28 : 'RHYTHM' , # rhythm change */
1082
- 29 : 'UWAVE' , # U-wave peak */
1083
- 30 : 'LEARN' , # learning */
1084
- 31 : 'FLWAV' , # ventricular flutter wave */
1085
- 32 : 'VFON' , # start of ventricular flutter/fibrillation */
1086
- 33 : 'VFOFF' , # end of ventricular flutter/fibrillation */
1087
- 34 : 'AESC' , # atrial escape beat */
1088
- 35 : 'SVESC' , # supraventricular escape beat */
1089
- 36 : 'LINK' , # link to external data (aux contains URL) */
1090
- 37 : 'NAPC' , # non-conducted P-wave (blocked APB) */
1091
- 38 : 'PFUS' , # fusion of paced and normal beat */
1092
- 39 : 'WFON' , # waveform onset */
1093
- 40 : 'WFOFF' , # waveform end */
1094
- 41 : 'RONT' , # R-on-T premature ventricular contraction */
1095
- # 42: '',
1096
- # 43: '',
1097
- # 44: '',
1098
- # 45: '',
1099
- # 46: '',
1100
- # 47: '',
1101
- # 48: '',
1102
- # 49: ''
1103
- }
1104
-
1105
-
1106
- # Mapping annotation symbols to the annotation codes
1107
- # For printing/user guidance
1108
- symcodes = pd .DataFrame ({'Ann Symbol' : list (annsyms .values ()), 'Ann Code Meaning' : list (anncodes .values ())})
1109
- symcodes = symcodes .set_index ('Ann Symbol' , list (annsyms .values ()))
997
+
1110
998
1111
999
# All annotation fields. Note: custom_anntypes placed first to check field before anntype
1112
1000
annfields = ['recordname' , 'annotator' , 'custom_anntypes' , 'annsamp' , 'anntype' , 'num' , 'subtype' , 'chan' , 'aux' , 'fs' ]
@@ -1121,30 +1009,107 @@ def proc_special_types(annsamp,anntype,num,subtype,chan,aux):
1121
1009
1122
1010
1123
1011
1012
+
1124
1013
# Classes = extensions
1125
1014
class AnnotationClass (object ):
1126
- def __init__ (self , extension , description , isreference ):
1015
+ def __init__ (self , extension , description , human_reviewed ):
1016
+
1127
1017
self .extension = extension
1128
1018
self .description = description
1129
- self .isreference = isreference
1019
+ self .human_reviewed = human_reviewed
1020
+
1130
1021
1131
- annclasses = [
1022
+ ann_classes = [
1132
1023
AnnotationClass ('atr' , 'Reference ECG annotations' , True ),
1133
- AnnotationClass ('apn' , 'Reference apnea annotations' , True ),
1134
- AnnotationClass ('alarm' , 'Machine alarm annotations' , False ),
1024
+
1025
+ AnnotationClass ('blh' , 'Human reviewed beat labels' , True ),
1026
+ AnnotationClass ('blm' , 'Machine beat labels' , False ),
1027
+
1028
+ AnnotationClass ('alh' , 'Human reviewed alarms' , True ),
1029
+ AnnotationClass ('alm' , 'Machine alarms' , False ),
1030
+
1031
+ AnnotationClass ('qrsc' , 'Human reviewed qrs detections' , True ),
1032
+ AnnotationClass ('qrs' , 'Machine QRS detections' , False ),
1033
+
1034
+ AnnotationClass ('bph' , 'Human reviewed BP beat detections' , True ),
1035
+ AnnotationClass ('bpm' , 'Machine BP beat detections' , False ),
1036
+
1037
+ #AnnotationClass('alh', 'Human reviewed BP alarms', True),
1038
+ #AnnotationClass('alm', 'Machine BP alarms', False),
1039
+ # separate ecg and other signal category alarms?
1040
+ # Can we use signum to determine the channel it was triggered off?
1041
+
1042
+ #ppg alarms?
1043
+ #eeg alarms
1135
1044
]
1136
1045
1137
1046
# Individual annotation labels
1138
1047
class AnnotationLabel (object ):
1139
- def __init__ (self , storevalue , symbol , description ):
1140
- self .storevalue = storevalue
1048
+ def __init__ (self , store_value , symbol , short_description , description ):
1049
+ self .store_value = store_value
1141
1050
self .symbol = symbol
1051
+ self .short_description = short_description
1142
1052
self .description = description
1143
1053
1144
- annlabels = [
1145
- AnnotationLabel (0 , ' ' , 'Not an actual annotation' ),
1146
- AnnotationLabel (1 , 'N' , 'Normal beat' ),
1147
- AnnotationLabel (2 , 'L' , 'Left bundle branch block beat' ),
1148
- AnnotationLabel (3 , 'R' , 'Right bundle branch block beat' ),
1149
- AnnotationLabel (4 , 'a' , 'Aberrated atrial premature beat' ),
1054
+ def __str__ (self ):
1055
+ return str (self .store_value )+ ', ' + str (self .symbol )+ ', ' + str (self .short_description )+ ', ' + str (self .description )
1056
+
1057
+ ann_labels = [
1058
+ AnnotationLabel (0 , " " , 'NOTQRS' , 'Not an actual annotation' ),
1059
+ AnnotationLabel (1 , "N" , 'NORMAL' , 'Normal beat' ),
1060
+ AnnotationLabel (2 , "L" , 'LBBB' , 'Left bundle branch block beat' ),
1061
+ AnnotationLabel (3 , "R" , 'RBBB' , 'Right bundle branch block beat' ),
1062
+ AnnotationLabel (4 , "a" , 'ABERR' , 'Aberrated atrial premature beat' ),
1063
+ AnnotationLabel (5 , "V" , 'PVC' , 'Premature ventricular contraction' ),
1064
+ AnnotationLabel (6 , "F" , 'FUSION' , 'Fusion of ventricular and normal beat' ),
1065
+ AnnotationLabel (7 , "J" , 'NPC' , 'Nodal (junctional) premature beat' ),
1066
+ AnnotationLabel (8 , "A" , 'APC' , 'Atrial premature contraction' ),
1067
+ AnnotationLabel (9 , "S" , 'SVPB' , 'Premature or ectopic supraventricular beat' ),
1068
+ AnnotationLabel (10 , "E" , 'VESC' , 'Ventricular escape beat' ),
1069
+ AnnotationLabel (11 , "j" , 'NESC' , 'Nodal (junctional) escape beat' ),
1070
+ AnnotationLabel (12 , "/" , 'PACE' , 'Paced beat' ),
1071
+ AnnotationLabel (13 , "Q" , 'UNKNOWN' , 'Unclassifiable beat' ),
1072
+ AnnotationLabel (14 , "~" , 'NOISE' , 'Signal quality change' ),
1073
+ # AnnotationLabel(15, None, None, None),
1074
+ AnnotationLabel (16 , "|" , 'ARFCT' , 'Isolated QRS-like artifact' ),
1075
+ # AnnotationLabel(17, None, None, None),
1076
+ AnnotationLabel (18 , "s" , 'STCH' , 'ST change' ),
1077
+ AnnotationLabel (19 , "T" , 'TCH' , 'T-wave change' ),
1078
+ AnnotationLabel (20 , "*" , 'SYSTOLE' , 'Systole' ),
1079
+ AnnotationLabel (21 , "D" , 'DIASTOLE' , 'Diastole' ),
1080
+ AnnotationLabel (22 , '"' , 'NOTE' , 'Comment annotation' ),
1081
+ AnnotationLabel (23 , "=" , 'MEASURE' , 'Measurement annotation' ),
1082
+ AnnotationLabel (24 , "p" , 'PWAVE' , 'P-wave peak' ),
1083
+ AnnotationLabel (25 , "B" , 'BBB' , 'Left or right bundle branch block' ),
1084
+ AnnotationLabel (26 , "^" , 'PACESP' , 'Non-conducted pacer spike' ),
1085
+ AnnotationLabel (27 , "t" , 'TWAVE' , 'T-wave peak' ),
1086
+ AnnotationLabel (28 , "+" , 'RHYTHM' , 'Rhythm change' ),
1087
+ AnnotationLabel (29 , "u" , 'UWAVE' , 'U-wave peak' ),
1088
+ AnnotationLabel (30 , "?" , 'LEARN' , 'Learning' ),
1089
+ AnnotationLabel (31 , "!" , 'FLWAV' , 'Ventricular flutter wave' ),
1090
+ AnnotationLabel (32 , "[" , 'VFON' , 'Start of ventricular flutter/fibrillation' ),
1091
+ AnnotationLabel (33 , "]" , 'VFOFF' , 'End of ventricular flutter/fibrillation' ),
1092
+ AnnotationLabel (34 , "e" , 'AESC' , 'Atrial escape beat' ),
1093
+ AnnotationLabel (35 , "n" , 'SVESC' , 'Supraventricular escape beat' ),
1094
+ AnnotationLabel (36 , "@" , 'LINK' , 'Link to external data (aux contains URL)' ),
1095
+ AnnotationLabel (37 , "x" , 'NAPC' , 'Non-conducted P-wave (blocked APB)' ),
1096
+ AnnotationLabel (38 , "f" , 'PFUS' , 'Fusion of paced and normal beat' ),
1097
+ AnnotationLabel (39 , "(" , 'WFON' , 'Waveform onset' ),
1098
+ AnnotationLabel (40 , ")" , 'WFOFF' , 'Waveform end' ),
1099
+ AnnotationLabel (41 , "r" , 'RONT' , 'R-on-T premature ventricular contraction' ),
1100
+ # AnnotationLabel(42, None, None, None),
1101
+ # AnnotationLabel(43, None, None, None),
1102
+ # AnnotationLabel(44, None, None, None),
1103
+ # AnnotationLabel(45, None, None, None),
1104
+ # AnnotationLabel(46, None, None, None),
1105
+ # AnnotationLabel(47, None, None, None),
1106
+ # AnnotationLabel(48, None, None, None),
1107
+ # AnnotationLabel(49, None, None, None),
1150
1108
]
1109
+
1110
+
1111
+ ann_label_table = pd .DataFrame ({'Store-Value' :[al .store_value for al in ann_labels ], 'Symbol' :[al .symbol for al in ann_labels ],
1112
+ 'Short-Description' :[al .short_description for al in ann_labels ], 'Description' :[al .description for al in ann_labels ]})
1113
+ ann_label_table .set_index ('Store-Value' , list (ann_label_table ['Store-Value' ].values ))
1114
+ ann_label_table = ann_label_table [['Store-Value' ,'Symbol' ,'Short-Description' ,'Description' ]]
1115
+
0 commit comments