Repository: mne-tools/mne-python

Issue:
BUG/DOC: confusing parameter mapping in _BaseRaw.filter
l_freq : high pass filter cuttoff

h_freq : low pass filter cuttoff

l_trans_bandwidth : low pass filter trans bandwidth

h_trans_bandwidth : high pass filter trans bandwidth

cc @kevinjos @agramfort @Eric89GXL 



Discussion:
Is that actually how it works in the code? I assumed `l_trans_bandwidth` would be paired with `l_freq` meaning "lower" freq as opposed to "lowpass" freq... it is indeed confusing if later in the code we combine `l_freq - l_trans-bandwidth` and `h_freq + h_trans_bandwidth` to find the stop bands.

> Is that actually how it works in the code?

https://github.com/mne-tools/mne-python/blob/master/mne/io/base.py#L377

l_trans_width is mapped to low_pass, but the low pass cuttoff frequency is h_freq

This expresses a certain logic but can be confusing (I got user feedback)

Yeah, that is indeed confusing...

yeah, I have found this difficult as well

We should change this and swap the parameter semantics. The good part: no deprecation neede, defaults remain the same, also I'm not sure how many people actually change defaults here. The bad part: we need to announce it, it's a bad thing when the meaning of parameters becomes the opposite.

oups... to me it's indeed a bug fix more than an API change... Let's just document this change. I doubt anybody touched it manually.

@agramfort @Eric89GXL @mluessi what sense does it make to do these kinds of checks if an order based design is used? 

https://github.com/mne-tools/mne-python/blob/master/mne/filter.py#L723

It's confusing I think. We already have a check for the filter coefs returned from construct_iir_parameters.
I would put a conditional here and don't do this tests if method = 'iir'.

WDYT?

indeed it's checked twice but is it a big deal? I am +0 to avoid those
checks

I'm +0 as well, if you have hit a use case where these checks get in the way of IIR use, then sure add a `method` check as part of any relevant conditionals.


Resolving patch:
diff --git a/mne/filter.py b/mne/filter.py
--- a/mne/filter.py
+++ b/mne/filter.py
@@ -610,8 +610,8 @@ def band_pass_filter(x, Fs, Fp1, Fp2, filter_length='10s',
     Fs = float(Fs)
     Fp1 = float(Fp1)
     Fp2 = float(Fp2)
-    Fs1 = Fp1 - l_trans_bandwidth
-    Fs2 = Fp2 + h_trans_bandwidth
+    Fs1 = Fp1 - l_trans_bandwidth if method == 'fft' else Fp1
+    Fs2 = Fp2 + h_trans_bandwidth if method == 'fft' else Fp2
     if Fs2 > Fs / 2:
         raise ValueError('Effective band-stop frequency (%s) is too high '
                          '(maximum based on Nyquist is %s)' % (Fs2, Fs / 2.))
@@ -720,8 +720,8 @@ def band_stop_filter(x, Fs, Fp1, Fp2, filter_length='10s',
     Fs = float(Fs)
     Fp1 = Fp1.astype(float)
     Fp2 = Fp2.astype(float)
-    Fs1 = Fp1 + l_trans_bandwidth
-    Fs2 = Fp2 - h_trans_bandwidth
+    Fs1 = Fp1 + l_trans_bandwidth if method == 'fft' else Fp1
+    Fs2 = Fp2 - h_trans_bandwidth if method == 'fft' else Fp2
 
     if np.any(Fs1 <= 0):
         raise ValueError('Filter specification invalid: Lower stop frequency '
@@ -816,7 +816,7 @@ def low_pass_filter(x, Fs, Fp, filter_length='10s', trans_bandwidth=0.5,
     iir_params = _check_method(method, iir_params, [])
     Fs = float(Fs)
     Fp = float(Fp)
-    Fstop = Fp + trans_bandwidth
+    Fstop = Fp + trans_bandwidth if method == 'fft' else Fp
     if Fstop > Fs / 2.:
         raise ValueError('Effective stop frequency (%s) is too high '
                          '(maximum based on Nyquist is %s)' % (Fstop, Fs / 2.))
@@ -903,7 +903,7 @@ def high_pass_filter(x, Fs, Fp, filter_length='10s', trans_bandwidth=0.5,
     Fs = float(Fs)
     Fp = float(Fp)
 
-    Fstop = Fp - trans_bandwidth
+    Fstop = Fp - trans_bandwidth if method == 'fft' else Fp
     if Fstop <= 0:
         raise ValueError('Filter specification invalid: Stop frequency too low'
                          '(%0.1fHz). Increase Fp or reduce transition '


Test patch:
diff --git a/mne/tests/test_filter.py b/mne/tests/test_filter.py
--- a/mne/tests/test_filter.py
+++ b/mne/tests/test_filter.py
@@ -38,6 +38,9 @@ def test_iir_stability():
     assert_raises(ValueError, high_pass_filter, sig, fs, 0.1,
                   method='fir', iir_params='blah')
 
+    # should pass because dafault trans_bandwidth is not relevant
+    high_pass_filter(sig, 250, 0.5, method='iir',
+                     iir_params=dict(ftype='butter', order=6))
 
 def test_notch_filters():
     """Test notch filters