Skip to content

Commit 0ce255a

Browse files
committed
continue refactor
1 parent 1df7e91 commit 0ce255a

File tree

4 files changed

+106
-78
lines changed

4 files changed

+106
-78
lines changed

demo.ipynb

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
},
2121
{
2222
"cell_type": "code",
23-
"execution_count": null,
23+
"execution_count": 1,
2424
"metadata": {},
2525
"outputs": [],
2626
"source": [
@@ -43,9 +43,24 @@
4343
},
4444
{
4545
"cell_type": "code",
46-
"execution_count": null,
47-
"metadata": {},
48-
"outputs": [],
46+
"execution_count": 2,
47+
"metadata": {},
48+
"outputs": [
49+
{
50+
"ename": "KeyError",
51+
"evalue": "'allowed_types'",
52+
"output_type": "error",
53+
"traceback": [
54+
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
55+
"\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)",
56+
"\u001b[0;32m<ipython-input-2-dcc3a4ec7266>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# Demo 1 - Read a wfdb record using the 'rdrecord' function into a wfdb.Record object.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 2\u001b[0m \u001b[0;31m# Plot the signals, and show the data.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mrecord\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mwfdb\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrdrecord\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'sample-data/a103l'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 4\u001b[0m \u001b[0mwfdb\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mplot_wfdb\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrecord\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mrecord\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtitle\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'Record a103l from Physionet Challenge 2015'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mdisplay\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrecord\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__dict__\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
57+
"\u001b[0;32m~/Projects/wfdb-python/wfdb/io/record.py\u001b[0m in \u001b[0;36mrdrecord\u001b[0;34m(record_name, sampfrom, sampto, channels, physical, pb_dir, m2s, smooth_frames, ignore_skew, return_res)\u001b[0m\n\u001b[1;32m 1026\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1027\u001b[0m \u001b[0;31m# Read the header fields\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1028\u001b[0;31m \u001b[0mrecord\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mrdheader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mrecord_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpb_dir\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mpb_dir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrd_segments\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mFalse\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1029\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1030\u001b[0m \u001b[0;31m# Set defaults for sampto and channels input variables\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
58+
"\u001b[0;32m~/Projects/wfdb-python/wfdb/io/record.py\u001b[0m in \u001b[0;36mrdheader\u001b[0;34m(record_name, pb_dir, rd_segments)\u001b[0m\n\u001b[1;32m 883\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 884\u001b[0m \u001b[0;31m# Get fields from record line\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 885\u001b[0;31m \u001b[0mrecord_fields\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0m_header\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_read_record_line\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mheader_lines\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 886\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 887\u001b[0m \u001b[0;31m# Single segment header - Process signal specification lines\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
59+
"\u001b[0;32m~/Projects/wfdb-python/wfdb/io/_header.py\u001b[0m in \u001b[0;36m_read_record_line\u001b[0;34m(record_line)\u001b[0m\n\u001b[1;32m 634\u001b[0m \u001b[0;31m# Replace empty strings with their read defaults (which are\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 635\u001b[0m \u001b[0;31m# mostly None)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 636\u001b[0;31m \u001b[0;32mif\u001b[0m \u001b[0mrecord_fields\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mfield\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m''\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 637\u001b[0m \u001b[0mrecord_fields\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mfield\u001b[0m\u001b[0;34m]\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mRECORD_SPECS\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mfield\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_default\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 638\u001b[0m \u001b[0;31m# Typecast non-empty strings for numerical and date/time fields\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
60+
"\u001b[0;31mKeyError\u001b[0m: 'allowed_types'"
61+
]
62+
}
63+
],
4964
"source": [
5065
"# Demo 1 - Read a wfdb record using the 'rdrecord' function into a wfdb.Record object.\n",
5166
"# Plot the signals, and show the data.\n",

wfdb/io/_header.py

Lines changed: 76 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,8 @@ def get_write_subset(self, spec_type):
146146
"""
147147
Get a set of fields used to write the header; either 'record'
148148
or 'signal' specification fields. Helper function for
149-
`get_write_fields`.
149+
`get_write_fields`. Gets the default required fields, the user
150+
defined fields, and their dependencies.
150151
151152
Parameters
152153
----------
@@ -159,27 +160,31 @@ def get_write_subset(self, spec_type):
159160
write_fields : list or dict
160161
For record fields, returns a list of all fields needed. For
161162
signal fields, it returns a dictionary of all fields needed,
162-
with keys = field and value = list of 1 or 0 indicating
163-
channel for the field
163+
with keys = field and value = list of channels that must be
164+
present for the field.
164165
165166
"""
166167
if spec_type == 'record':
167168
write_fields = []
168-
fieldspecs = OrderedDict(reversed(list(rec_field_specs.items())))
169-
# Remove this requirement for single segs
169+
record_specs = RECORD_SPECS.copy()
170+
171+
# Remove the n_seg requirement for single segment items
170172
if not hasattr(self, 'n_seg'):
171-
del(fieldspecs['n_seg'])
173+
del(record_specs['n_seg'])
172174

173-
for f in fieldspecs:
174-
if f in write_fields:
175+
for field in record_specs.index[-1::-1]:
176+
# Continue if the field has already been included
177+
if field in write_fields:
175178
continue
176-
# If the field is required by default or has been defined by the user
177-
if fieldspecs[f].write_req or getattr(self, f) is not None:
178-
rf = f
179+
# If the field is required by default or has been
180+
# defined by the user
181+
if (record_specs.loc[field, 'write_required']
182+
or getattr(self, field) is not None):
183+
req_field = field
179184
# Add the field and its recursive dependencies
180-
while rf is not None:
181-
write_fields.append(rf)
182-
rf = fieldspecs[rf].dependency
185+
while req_field is not None:
186+
write_fields.append(req_field)
187+
req_field = record_specs.loc[req_field, 'dependency']
183188
# Add comments if any
184189
if getattr(self, 'comments') is not None:
185190
write_fields.append('comments')
@@ -188,41 +193,40 @@ def get_write_subset(self, spec_type):
188193
elif spec_type == 'signal':
189194
# List of lists for each channel
190195
write_fields = []
191-
192-
allwrite_fields = []
193-
fieldspecs = OrderedDict(reversed(list(SIGNAL_FIELDS.items())))
196+
signal_specs = SIGNAL_SPECS.copy()
194197

195198
for ch in range(self.n_sig):
196199
# The fields needed for this channel
197-
write_fieldsch = []
198-
for f in fieldspecs:
199-
if f in write_fieldsch:
200+
write_fields_ch = []
201+
for field in signal_specs[-1::-1]:
202+
if field in write_fields_ch:
200203
continue
201204

202-
fielditem = getattr(self, f)
205+
item = getattr(self, field)
203206
# If the field is required by default or has been defined by the user
204-
if fieldspecs[f].write_req or (fielditem is not None and fielditem[ch] is not None):
205-
rf=f
207+
if signal_specs.loc[field, 'write_req'] or (item is not None and item[ch] is not None):
208+
req_field = field
206209
# Add the field and its recursive dependencies
207-
while rf is not None:
208-
write_fieldsch.append(rf)
209-
rf = fieldspecs[rf].dependency
210+
while req_field is not None:
211+
write_fields_ch.append(req_field)
212+
req_field = signal_specs.loc[req_field, 'dependency']
210213

211-
write_fields.append(write_fieldsch)
214+
write_fields.append(write_fields_ch)
212215

213216
# Convert the list of lists to a single dictionary.
214-
# keys = field and value = list of 1 or 0 indicating channel for the field
215-
dictwrite_fields = {}
217+
# keys = field and value = list of channels in which the
218+
# field is required.
219+
dict_write_fields = {}
216220

217221
# For fields present in any channel:
218-
for f in set([i for wsub in write_fields for i in wsub]):
219-
dictwrite_fields[f] = [0]*self.n_sig
222+
for field in set([i for write_fields_ch in write_fields for i in write_fields_ch]):
223+
dict_write_fields[field] = []
220224

221225
for ch in range(self.n_sig):
222-
if f in write_fields[ch]:
223-
dictwrite_fields[f][ch] = 1
226+
if field in write_fields[ch]:
227+
dict_write_fields[field].append(ch)
224228

225-
write_fields = dictwrite_fields
229+
write_fields = dict_write_fields
226230

227231
return write_fields
228232

@@ -322,21 +326,26 @@ def get_write_fields(self):
322326

323327
return rec_write_fields, sig_write_fields
324328

325-
# Set the object's attribute to its default value if it is missing
326-
# and there is a default. Not responsible for initializing the
327-
# attribute. That is done by the constructor.
329+
328330
def set_default(self, field):
331+
"""
332+
Set the object's attribute to its default value if it is missing
333+
and there is a default.
334+
335+
Not responsible for initializing the
336+
attribute. That is done by the constructor.
337+
"""
329338

330339
# Record specification fields
331-
if field in rec_field_specs:
340+
if field in RECORD_SPECS.index:
332341
# Return if no default to set, or if the field is already present.
333-
if rec_field_specs[field].write_def is None or getattr(self, field) is not None:
342+
if RECORD_SPECS.loc[field, 'write_default'] is None or getattr(self, field) is not None:
334343
return
335-
setattr(self, field, rec_field_specs[field].write_def)
344+
setattr(self, field, RECORD_SPECS.loc[field, 'write_default'])
336345

337346
# Signal specification fields
338347
# Setting entire list default, not filling in blanks in lists.
339-
elif field in SIGNAL_FIELDS:
348+
elif field in SIGNAL_FIELDS.index:
340349

341350
# Specific dynamic case
342351
if field == 'file_name' and self.file_name is None:
@@ -346,19 +355,23 @@ def set_default(self, field):
346355
item = getattr(self, field)
347356

348357
# Return if no default to set, or if the field is already present.
349-
if SIGNAL_FIELDS[field].write_def is None or item is not None:
358+
if SIGNAL_SPECS.loc[field, 'write_default'] is None or item is not None:
350359
return
351360

352361
# Set more specific defaults if possible
353362
if field == 'adc_res' and self.fmt is not None:
354363
self.adc_res=_signal.wfdbfmtres(self.fmt)
355364
return
356365

357-
setattr(self, field, [SIGNAL_FIELDS[field].write_def]*self.n_sig)
366+
setattr(self, field,
367+
[SIGNAL_SPECS.loc[field, 'write_default']] * self.n_sig)
368+
358369

359-
# Check the cohesion of fields used to write the header
360370
def check_field_cohesion(self, rec_write_fields, sig_write_fields):
371+
"""
372+
Check the cohesion of fields used to write the header
361373
374+
"""
362375
# If there are no signal specification fields, there is nothing to check.
363376
if self.n_sig>0:
364377

@@ -393,21 +406,21 @@ def check_field_cohesion(self, rec_write_fields, sig_write_fields):
393406

394407
def wr_header_file(self, rec_write_fields, sig_write_fields, write_dir):
395408
# Write a header file using the specified fields
396-
header_lines=[]
409+
header_lines = []
397410

398411
# Create record specification line
399-
recordline = ''
412+
record_line = ''
400413
# Traverse the ordered dictionary
401-
for field in rec_field_specs:
414+
for field in RECORD_SPECS:
402415
# If the field is being used, add it with its delimiter
403416
if field in rec_write_fields:
404417
stringfield = str(getattr(self, field))
405418
# If fs is float, check whether it as an integer
406419
if field == 'fs' and isinstance(self.fs, float):
407420
if round(self.fs, 8) == float(int(self.fs)):
408421
stringfield = str(int(self.fs))
409-
recordline = recordline + rec_field_specs[field].delimiter + stringfield
410-
header_lines.append(recordline)
422+
record_line = record_line + RECORD_SPECS[field].delimiter + stringfield
423+
header_lines.append(record_line)
411424

412425
# Create signal specification lines (if any) one channel at a time
413426
if self.n_sig>0:
@@ -489,11 +502,11 @@ def get_write_fields(self):
489502
def set_default(self, field):
490503

491504
# Record specification fields
492-
if field in rec_field_specs:
505+
if field in RECORD_SPECS:
493506
# Return if no default to set, or if the field is already present.
494-
if rec_field_specs[field].write_def is None or getattr(self, field) is not None:
507+
if RECORD_SPECS[field].write_def is None or getattr(self, field) is not None:
495508
return
496-
setattr(self, field, rec_field_specs[field].write_def)
509+
setattr(self, field, RECORD_SPECS[field].write_def)
497510

498511

499512

@@ -516,20 +529,20 @@ def wr_header_file(self, write_fields, write_dir):
516529
header_lines=[]
517530

518531
# Create record specification line
519-
recordline = ''
532+
record_line = ''
520533
# Traverse the ordered dictionary
521-
for field in rec_field_specs:
534+
for field in RECORD_SPECS:
522535
# If the field is being used, add it with its delimiter
523536
if field in write_fields:
524-
recordline = recordline + rec_field_specs[field].delimiter + str(getattr(self, field))
525-
header_lines.append(recordline)
537+
record_line = record_line + RECORD_SPECS[field].delimiter + str(getattr(self, field))
538+
header_lines.append(record_line)
526539

527540
# Create segment specification lines
528541
segmentlines = self.n_seg*['']
529542
# For both fields, add each of its elements with the delimiter to the appropriate line
530543
for field in ['seg_name', 'seg_name']:
531544
for segnum in range(0, self.n_seg):
532-
segmentlines[segnum] = segmentlines[segnum] + seg_field_specs[field].delimiter + str(getattr(self, field)[segnum])
545+
segmentlines[segnum] = segmentlines[segnum] + SEGMENT_SPECS[field].delimiter + str(getattr(self, field)[segnum])
533546

534547
header_lines = header_lines + segmentlines
535548

@@ -630,14 +643,14 @@ def _read_record_line(record_line):
630643
record_fields['sig_len'], record_fields['base_time'],
631644
record_fields['base_date']) = re.findall(_rx_record, record_line)[0]
632645

633-
for field in rec_field_specs:
646+
for field in RECORD_SPECS.index:
634647
# Replace empty strings with their read defaults (which are
635648
# mostly None)
636649
if record_fields[field] == '':
637-
record_fields[field] = rec_field_specs[field].read_default
650+
record_fields[field] = RECORD_SPECS.loc[field, 'read_default']
638651
# Typecast non-empty strings for numerical and date/time fields
639652
else:
640-
if rec_field_specs[field].allowed_types is int_types:
653+
if RECORD_SPECS.loc[field, 'allowed_types'] is int_types:
641654
record_fields[field] = int(record_fields[field])
642655
# fs may be read as float or int
643656
elif field == 'fs':
@@ -701,17 +714,17 @@ def _read_segment_lines(segment_lines):
701714
segment_fields = {}
702715

703716
# Each dictionary field is a list
704-
for field in seg_field_specs:
717+
for field in SEGMENT_SPECS:
705718
segment_fields[field] = [None]*len(segment_lines)
706719

707720
# Read string fields from signal line
708721
for i in range(0, len(segment_lines)):
709722
(segment_fields['seg_name'][i], segment_fields['seg_len'][i]) = _rx_segment.findall(segment_lines[i])[0]
710723

711-
for field in seg_field_specs:
724+
for field in SEGMENT_SPECS:
712725
# Replace empty strings with their read defaults (which are mostly None)
713726
if segment_fields[field][i] == '':
714-
segment_fields[field][i] = seg_field_specs[field].read_default
727+
segment_fields[field][i] = SEGMENT_SPECS[field].read_default
715728
# Typecast non-empty strings for numerical field
716729
else:
717730
if field == 'seg_len':

0 commit comments

Comments
 (0)