Repository: mne-tools/mne-python

Issue:
EDF files now contain lowpass=0
With 0.19.2, it seems like the `lowpass` info entry for EDF files was set to half the sampling frequency if the value in the file was 0 Hz. Using [this EDF file](https://archive.physionet.org/pn4/eegmmidb/S001/S001R04.edf), the behavior of 0.19.2 is:

```python
import mne
raw = mne.io.read_raw_edf("S001R04.edf", preload=True)
raw.info["sfreq"]  # 160.0
raw.info["lowpass"]  # 80.0
```

With the current master this changed:

```python
import mne
raw = mne.io.read_raw_edf("S001R04.edf", preload=True)
raw.info["sfreq"]  # 160.0
raw.info["lowpass"]  # 0.0
```

This leads to a `ZeroDivisionError` e.g. when plotting the `raw` object via `raw.plot()`:

```
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
<ipython-input-25-f60196099aa7> in <module>
----> 1 raw.plot()

~/Repositories/mne-python/mne/io/base.py in plot(self, events, duration, start, n_channels, bgcolor, color, bad_color, event_color, scalings, remove_dc, order, show_options, title, show, block, highpass, lowpass, filtorder, clipping, show_first_samp, proj, group_by, butterfly, decim, noise_cov, event_id, show_scrollbars, show_scalebars, verbose)
   1372              butterfly=False, decim='auto', noise_cov=None, event_id=None,
   1373              show_scrollbars=True, show_scalebars=True, verbose=None):
-> 1374         return plot_raw(self, events, duration, start, n_channels, bgcolor,
   1375                         color, bad_color, event_color, scalings, remove_dc,
   1376                         order, show_options, title, show, block, highpass,

</Users/clemens/Repositories/mne-python/mne/externals/decorator.py:decorator-gen-139> in plot_raw(raw, events, duration, start, n_channels, bgcolor, color, bad_color, event_color, scalings, remove_dc, order, show_options, title, show, block, highpass, lowpass, filtorder, clipping, show_first_samp, proj, group_by, butterfly, decim, noise_cov, event_id, show_scrollbars, show_scalebars, verbose)

~/Repositories/mne-python/mne/utils/_logging.py in wrapper(*args, **kwargs)
     88             with use_log_level(verbose_level):
     89                 return function(*args, **kwargs)
---> 90         return function(*args, **kwargs)
     91     return FunctionMaker.create(
     92         function, 'return decfunc(%(signature)s)',

~/Repositories/mne-python/mne/viz/raw.py in plot_raw(raw, events, duration, start, n_channels, bgcolor, color, bad_color, event_color, scalings, remove_dc, order, show_options, title, show, block, highpass, lowpass, filtorder, clipping, show_first_samp, proj, group_by, butterfly, decim, noise_cov, event_id, show_scrollbars, show_scalebars, verbose)
    382             raise KeyError('only key <= 0 allowed is -1 (cannot use %s)'
    383                            % key)
--> 384     decim, data_picks = _handle_decim(info, decim, lowpass)
    385     noise_cov = _check_cov(noise_cov, info)
    386 

~/Repositories/mne-python/mne/viz/utils.py in _handle_decim(info, decim, lowpass)
   2599         lp = min(lp, info['sfreq'] if lowpass is None else lowpass)
   2600         info['lowpass'] = lp
-> 2601         decim = max(int(info['sfreq'] / (lp * 3) + 1e-6), 1)
   2602     decim = _ensure_int(decim, 'decim', must_be='an int or "auto"')
   2603     if decim <= 0:

ZeroDivisionError: float division by zero
```

I'll try to find out which change introduced this behavior. Also, this should definitely go into 0.20.


Discussion:
It was introduced in 6601b7ce5cd2b8caa8dda9ac913ffadc62fda5bb - now trying to find the offending change.

yes it's a bug... thanks for looking into it

>


Resolving patch:
diff --git a/mne/io/edf/edf.py b/mne/io/edf/edf.py
--- a/mne/io/edf/edf.py
+++ b/mne/io/edf/edf.py
@@ -485,7 +485,7 @@ def _get_info(fname, stim_channel, eog, misc, exclude, preload):
     if lowpass.size == 0:
         pass  # Placeholder for future use. Lowpass set in _empty_info.
     elif all(lowpass):
-        if lowpass[0] == 'NaN':
+        if lowpass[0] in ('NaN', '0', '0.0'):
             pass  # Placeholder for future use. Lowpass set in _empty_info.
         else:
             info['lowpass'] = float(lowpass[0])


Test patch:
diff --git a/mne/io/edf/tests/test_edf.py b/mne/io/edf/tests/test_edf.py
--- a/mne/io/edf/tests/test_edf.py
+++ b/mne/io/edf/tests/test_edf.py
@@ -13,7 +13,7 @@
 
 import numpy as np
 from numpy.testing import (assert_array_almost_equal, assert_array_equal,
-                           assert_equal)
+                           assert_equal, assert_allclose)
 from scipy.io import loadmat
 
 import pytest
@@ -361,3 +361,11 @@ def test_bdf_multiple_annotation_channels():
 
 
 run_tests_if_main()
+
+
+@testing.requires_testing_data
+def test_edf_lowpass_zero():
+    """Test if a lowpass filter of 0Hz is mapped to the Nyquist frequency."""
+    with pytest.warns(RuntimeWarning, match='too long.*truncated'):
+        raw = read_raw_edf(edf_stim_resamp_path)
+    assert_allclose(raw.info["lowpass"], raw.info["sfreq"] / 2)