MIT-LCP / MIT-LCP/wfdb-python

Handle all-NaN channels in calc_adc_params

Open
#485 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Jupyter Notebook
Stars
853
Forks
322
PR merge metrics
No merged PRs in 30d

Description

If all samples in a channel are NaN, calc_adc_params will fail:

>>> wfdb.wrsamp("xxx", fs=500, units=["mV"], sig_name=["I"], p_signal=numpy.array([[numpy.nan]]), fmt=["16"])
/home/bmoody/work/wfdb-python/wfdb/io/_signal.py:740: RuntimeWarning: All-NaN slice encountered
  minvals = np.nanmin(self.p_signal, axis=0)
/home/bmoody/work/wfdb-python/wfdb/io/_signal.py:741: RuntimeWarning: All-NaN slice encountered
  maxvals = np.nanmax(self.p_signal, axis=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/bmoody/work/wfdb-python/wfdb/io/record.py", line 2943, in wrsamp
    record.set_d_features(do_adc=1)
  File "/home/bmoody/work/wfdb-python/wfdb/io/_signal.py", line 470, in set_d_features
    self.adc_gain, self.baseline = self.calc_adc_params()
  File "/home/bmoody/work/wfdb-python/wfdb/io/_signal.py", line 787, in calc_adc_params
    baseline = int(np.floor(baseline))
ValueError: cannot convert float NaN to integer

A couple things are wrong here:

  1. if pmin == np.nan doesn't do what you think.

  2. nanmin and nanmax will give a RuntimeWarning if all samples in a channel are NaN.

(1) is easy to fix. (2) is a little weirder; have a look at the code of nanmin:

    if type(a) is np.ndarray and a.dtype != np.object_:
        # Fast, but not safe for subclasses of ndarray, or object arrays,
        # which do not implement isnan (gh-9009), or fmin correctly (gh-8975)
        res = np.fmin.reduce(a, axis=axis, out=out, **kwargs)
        if np.isnan(res).any():
            warnings.warn("All-NaN slice encountered", RuntimeWarning,
                          stacklevel=3)

In other words, for ordinary numeric numpy arrays, np.fmin.reduce gives what we want (minimum non-NaN value if there is one, otherwise NaN, and no warning.) It might not work if the array is something more exotic (e.g. a numpy-compatible array class created by some other python package.)

I think I understand the comment about object arrays (https://github.com/numpy/numpy/issues/8975, https://github.com/numpy/numpy/issues/9009), but I don't understand the "subclasses of ndarray" comment. When I try creating a trivial subclass of ndarray, fmin still appears to work as expected. So I don't see why the strict is np.ndarray is needed.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in wfdb/io/_signal.py at calc_adc_params and trace the nanmin/nanmax handling used by wfdb.wrsamp. Reproduce the all-NaN channel example, then verify that all-NaN channels no longer raise a warning or fail while channels containing values retain their existing behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
numpy, python
Domain
data
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.