Skip to content

Commit 4062725

Browse files
committed
functionality to only write necessary ann file bytes
1 parent 285bdb4 commit 4062725

File tree

1 file changed

+92
-36
lines changed

1 file changed

+92
-36
lines changed

wfdb/readwrite/annotations.py

Lines changed: 92 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import pandas as pd
33
import re
44
import os
5+
import copy
56
from . import _headers
67
from . import downloads
78

@@ -73,7 +74,6 @@ def __eq__(self, other):
7374
return False
7475
else:
7576
if v1 != v2:
76-
print(k)
7777
return False
7878

7979
return True
@@ -246,49 +246,24 @@ def wrannfile(self):
246246
with open(self.recordname+'.'+self.annotator, 'wb') as f:
247247
databytes.tofile(f)
248248

249-
# # Convert all used annotation fields into bytes to write
250-
# def fieldbytes(self):
251-
252-
# # The difference samples to write
253-
# annsampdiff = np.concatenate(([self.annsamp[0]], np.diff(self.annsamp)))
254-
255-
# # All fields to be written. samp and type are together
256-
# extrawritefields = []
257-
258-
# for field in ['num', 'subtype', 'chan', 'aux']:
259-
# if getattr(self, field) is not None:
260-
# extrawritefields.append(field)
261-
262-
# databytes = []
263-
264-
# # Iterate across all fields one index at a time
265-
# for i in range(len(annsampdiff)):
266-
267-
# # Process the annsamp (difference) and anntype items
268-
# databytes.append(field2bytes('samptype', [annsampdiff[i], self.anntype[i]]))
269-
270-
# for field in extrawritefields:
271-
# value = getattr(self, field)[i]
272-
# if value is not None:
273-
# databytes.append(field2bytes(field, value))
274-
275-
# # Flatten and convert to correct format
276-
# databytes = np.array([item for sublist in databytes for item in sublist]).astype('u1')
277-
278-
# return databytes
279-
280-
281249
# Convert all used annotation fields into bytes to write
282250
def fieldbytes(self):
283251

284252
# The difference samples to write
285253
annsampdiff = np.concatenate(([self.annsamp[0]], np.diff(self.annsamp)))
286254

287-
# All fields to be written. samp and type are together
255+
256+
# Create a copy of the annotation object with a
257+
# compact version of fields to write
258+
compact_annotation = copy.deepcopy(self)
259+
compact_annotation.compact_fields()
260+
261+
262+
# The optional fields to be written. Write if they are not None or all empty
288263
extrawritefields = []
289264

290265
for field in ['num', 'subtype', 'chan', 'aux']:
291-
if getattr(self, field) is not None:
266+
if not isblank(getattr(compact_annotation, field)):
292267
extrawritefields.append(field)
293268

294269
databytes = []
@@ -299,8 +274,9 @@ def fieldbytes(self):
299274
# Process the annsamp (difference) and anntype items
300275
databytes.append(field2bytes('samptype', [annsampdiff[i], self.anntype[i]]))
301276

277+
# Process the extra optional fields
302278
for field in extrawritefields:
303-
value = getattr(self, field)[i]
279+
value = getattr(compact_annotation, field)[i]
304280
if value is not None:
305281
databytes.append(field2bytes(field, value))
306282

@@ -309,6 +285,44 @@ def fieldbytes(self):
309285

310286
return databytes
311287

288+
# Compact all of the object's fields so that the output
289+
# writing annotation file writes as few bytes as possible
290+
def compact_fields(self):
291+
292+
# Number of annotations
293+
nannots = len(self.annsamp)
294+
295+
# Chan and num carry over previous fields. Get lists of as few
296+
# elements to write as possible
297+
self.chan = compact_carry_field(self.chan)
298+
self.num = compact_carry_field(self.num)
299+
300+
# Elements of 0 (default) do not need to be written for subtype.
301+
# num and sub are signed in original c package...
302+
if self.subtype is not None:
303+
if type(self.subtype) == list:
304+
for i in range(nannots):
305+
if self.subtype[i] == 0:
306+
self.subtype[i] = None
307+
if np.array_equal(self.subtype, [None]*nannots):
308+
self.subtype = None
309+
else:
310+
zero_inds = np.where(self.subtype==0)[0]
311+
if len(zero_inds) == nannots:
312+
self.subtype = None
313+
else:
314+
self.subtype = list(self.subtype)
315+
for i in zero_inds:
316+
self.subtype[i] = None
317+
318+
# Empty aux strings are not written
319+
if self.aux is not None:
320+
for i in range(nannots):
321+
if self.aux[i] == '':
322+
self.aux[i] = None
323+
if np.array_equal(self.aux, [None]*nannots):
324+
self.aux = None
325+
312326

313327
# Move non-encoded anntype elements into the aux field
314328
def type2aux(self):
@@ -404,7 +418,49 @@ def customcode2bytes(c_triplet):
404418

405419
return annbytes
406420

421+
# Tests whether the item is blank
422+
def isblank(x):
423+
if x is None:
424+
return True
425+
elif type(x) == list:
426+
if np.array_equal(x, [None]*len(x)):
427+
return True
428+
return False
429+
430+
431+
def compact_carry_field(full_field):
432+
"""
433+
Return the compact list version of a list/array of an
434+
annotation field that has previous values carried over
435+
(chan or num)
436+
- The first sample is 0 by default. Only set otherwise
437+
if necessary.
438+
- Only set fields if they are different from their prev
439+
field
440+
"""
441+
442+
# Keep in mind that the field may already be compact or None
443+
444+
if full_field is None:
445+
return None
446+
447+
# List of same length. Place None where element
448+
# does not need to be written
449+
compact_field = [None]*len(full_field)
450+
451+
prev_field = 0
452+
453+
for i in range(len(full_field)):
454+
current_field = full_field[i]
455+
if current_field != prev_field:
456+
compact_field[i] = current_field
457+
prev_field = current_field
458+
459+
# May further simplify
460+
if np.array_equal(compact_field, [None]*len(full_field)):
461+
compact_field = None
407462

463+
return compact_field
408464

409465

410466
# Convert an annotation field into bytes to write

0 commit comments

Comments
 (0)