Skip to content

Commit ca20b1a

Browse files
author
Benjamin Moody
committed
rdrecord: smooth frames by invoking smooth_frames().
To simplify the implementation of _rd_segment and _rd_dat_signals, we want to eliminate the smooth_frames argument, so that the return values of these two functions will always have the same type (a list of numpy arrays.) Therefore, if the application requested frame smoothing, then instead of calling _rd_segment with smooth_frames=True, we will call _rd_segment with smooth_frames=False, and post-process the result by calling Record.smooth_frames. Record.smooth_frames (SignalMixin.smooth_frames) will give a result equivalent to what _rd_segment gives with smooth_frames=True, but there are likely differences in performance: - Record.smooth_frames performs the computation by slicing along the "long" axis and storing the intermediate results in an int64 numpy array. _rd_dat_signals slices along the "short" axis and stores the intermediate results in a Python list. Record.smooth_frames should therefore be faster for large inputs. - Record.smooth_frames only operates on the channels present in e_d_signal, whereas _rd_dat_signals smooths all of the signals in the input file. Record.smooth_frames therefore saves memory and time when reading a subset of channels. - Record.smooth_frames always returns an int64 array, whereas _rd_dat_signals returns an array of the same type as the original data. Record.smooth_frames therefore uses more memory in many cases. (Note that rdrecord will post-process the result in any case, making this change invisible to applications; the issue of increased temporary memory usage can be addressed separately.) - If there are multiple channels in a signal file, then calling _rd_dat_signals with smooth_frames=False requires making an extra copy of each signal that has multiple samples per frame (because of the "reshape(-1)".) (This could be addressed in the future by allowing _rd_segment, or at least _rd_dat_signals, to return a list of *two-dimensional* arrays instead.) In order for this to work correctly, Record.smooth_frames must be called after setting both e_d_signal and samps_per_frame. In particular, it must be done after Record._arrange_fields "rearranges" samps_per_frame according to channels. On the other hand, _arrange_fields is expected to set checksum and init_value in different ways depending on whether the result is to be smoothed. (This use of checksum and init_value is somewhat dubious.) Therefore, smooth_frames is now invoked as part of _arrange_fields, after setting channel-specific metadata and before setting checksum and init_value. _arrange_fields should never be invoked other than by rdrecord; it doesn't make any sense to call this function at other times. Change the signature of this function to reflect the fact that it actively transforms the signal array, and make all arguments mandatory.
1 parent 9d77075 commit ca20b1a

File tree

1 file changed

+14
-16
lines changed

1 file changed

+14
-16
lines changed

wfdb/io/record.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ def wrsamp(self, expanded=False, write_dir=''):
668668
self.wr_dats(expanded=expanded, write_dir=write_dir)
669669

670670

671-
def _arrange_fields(self, channels, sampfrom=0, expanded=False):
671+
def _arrange_fields(self, channels, sampfrom, smooth_frames):
672672
"""
673673
Arrange/edit object fields to reflect user channel and/or signal
674674
range input.
@@ -677,10 +677,11 @@ def _arrange_fields(self, channels, sampfrom=0, expanded=False):
677677
----------
678678
channels : list
679679
List of channel numbers specified.
680-
sampfrom : int, optional
680+
sampfrom : int
681681
Starting sample number read.
682-
expanded : bool, optional
683-
Whether the record was read in expanded mode.
682+
smooth_frames : bool
683+
Whether to convert the expanded signal array (e_d_signal) into
684+
a smooth signal array (d_signal).
684685
685686
Returns
686687
-------
@@ -693,18 +694,21 @@ def _arrange_fields(self, channels, sampfrom=0, expanded=False):
693694
setattr(self, field, [item[c] for c in channels])
694695

695696
# Expanded signals - multiple samples per frame.
696-
if expanded:
697+
if not smooth_frames:
697698
# Checksum and init_value to be updated if present
698699
# unless the whole signal length was input
699700
if self.sig_len != int(len(self.e_d_signal[0]) / self.samps_per_frame[0]):
700-
self.checksum = self.calc_checksum(expanded)
701+
self.checksum = self.calc_checksum(True)
701702
self.init_value = [s[0] for s in self.e_d_signal]
702703

703704
self.n_sig = len(channels)
704705
self.sig_len = int(len(self.e_d_signal[0]) / self.samps_per_frame[0])
705706

706707
# MxN numpy array d_signal
707708
else:
709+
self.d_signal = self.smooth_frames('digital')
710+
self.e_d_signal = None
711+
708712
# Checksum and init_value to be updated if present
709713
# unless the whole signal length was input
710714
if self.sig_len != self.d_signal.shape[0]:
@@ -3517,7 +3521,7 @@ def rdrecord(record_name, sampfrom=0, sampto=None, channels=None,
35173521
no_file = False
35183522
sig_data = None
35193523

3520-
signals = _signal._rd_segment(
3524+
record.e_d_signal = _signal._rd_segment(
35213525
file_name=record.file_name,
35223526
dir_name=dir_name,
35233527
pn_dir=pn_dir,
@@ -3531,35 +3535,29 @@ def rdrecord(record_name, sampfrom=0, sampto=None, channels=None,
35313535
sampfrom=sampfrom,
35323536
sampto=sampto,
35333537
channels=channels,
3534-
smooth_frames=smooth_frames,
3538+
smooth_frames=False,
35353539
ignore_skew=ignore_skew,
35363540
no_file=no_file,
35373541
sig_data=sig_data,
35383542
return_res=return_res)
35393543

35403544
# Only 1 sample/frame, or frames are smoothed. Return uniform numpy array
35413545
if smooth_frames:
3542-
# Read signals from the associated dat files that contain
3543-
# wanted channels
3544-
record.d_signal = signals
3545-
35463546
# Arrange/edit the object fields to reflect user channel
35473547
# and/or signal range input
35483548
record._arrange_fields(channels=channels, sampfrom=sampfrom,
3549-
expanded=False)
3549+
smooth_frames=True)
35503550

35513551
if physical:
35523552
# Perform inplace dac to get physical signal
35533553
record.dac(expanded=False, return_res=return_res, inplace=True)
35543554

35553555
# Return each sample of the signals with multiple samples per frame
35563556
else:
3557-
record.e_d_signal = signals
3558-
35593557
# Arrange/edit the object fields to reflect user channel
35603558
# and/or signal range input
35613559
record._arrange_fields(channels=channels, sampfrom=sampfrom,
3562-
expanded=True)
3560+
smooth_frames=False)
35633561

35643562
if physical:
35653563
# Perform dac to get physical signal

0 commit comments

Comments
 (0)