Repository: mne-tools/mne-python

Issue:
Setting custom_ref_applied for ECoG data
#### Setting average reference for ECoG data
When using the `raw.set_eeg_reference(ref_channels='average')` to perform a common average reference on ECoG data, the `custom_ref_applied` bool flag in raw.info remains set to `False`. I looked at `io/reference.py` and in the function definition for `set_eeg_reference` it appears that when `_apply_reference` is called, it looks only for channels of type 'eeg' is 'custom_ref_applied' set to `True`. 

Also, in the comments for `_apply_reference`, it appears to say that `info['custom_ref_applied']=True` refers to a "non-CAR" reference, but the test is whether the reference includes EEG electrodes (which it would for an average reference, right?)

My general question then, is, for consistency, should `custom_ref_applied=True` after applying an average reference to ECoG data, or am I misunderstanding the use of this flag?  It seems like a nice marker for whether this operation has happened or not. Happy to help fix if this makes sense.  I see this is similar to this issue: https://github.com/mne-tools/mne-python/issues/6454, but not exactly.


Discussion:
what type of reference would you apply on ECoG data? CAR? The usecase with
ECoG or sEEG has been
really explored that why it's called set_eeg_reference and not
set_reference(ch_type='eeg')

I guess we 'just' need the use case to adjust MNE to behave properly.

For ECoG/sEEG, a CAR is common, but not always done. Sometimes a single reference channel is used (a subgaleal reference, for example, or even a white matter reference). Bipolar reference is also often used clinically. Basically it would make sense for `set_reference` or `set_eeg_reference` to work exactly as it does, but have the ability to set the channel type, as you suggested above. 
ok you suggest we need a set_reference(ch_type) and make set_eeg_reference
call set_reference(ch_type='eeg') before deprecating set_eeg_reference()

any objection anyone? Can you give it a shot?

>

Rather than force a deprecation cycle, I would just add a `ch_type` argument to `set_eeg_reference`. It leaves it as a misnomer but since `set_eeg_reference` is mandatory for inverse modeling, it's going to be all over the place for users so the pain of deprecating this function is quite high.
> Rather than force a deprecation cycle, I would just add a `ch_type` argument to `set_eeg_reference`. It leaves it as a misnomer but since `set_eeg_reference` is mandatory for inverse modeling, it's going to be all over the place for users so the pain of deprecating this function is quite high.

In that case, I'd prefer @agramfort's proposal sans the deprecation… i.e., add `set_reference()` and simply make `set_eeg_reference()` call `set_reference(ch_type='eeg')`
> Rather than force a deprecation cycle, I would just add a `ch_type` argument to `set_eeg_reference`. It leaves it as a misnomer [...]

Could we compromise by setting a longer deprecation cycle? (2 releases? Or even 4?)
FWIW, I don't feel strongly about it being called `set_reference` instead -- intracranial EEG is a subset of EEG, I guess, so semantically it doesn't bother me if that's also the issue. 
> Could we compromise by setting a longer deprecation cycle? (2 releases? Or even 4?)

To me it's not about the duration, it's about the cost (amount of code changes people will have to make at some point or another) relative to the gain we (maintenance) and users (usability) get from the change. Here what I see is no maintenance benefit, a tiny usability/discoverability benefit, and a high cost of changing (i.e., any user scripts that involve EEG inverse modeling, or even most that process EEG in general, since these often set references with or without projections).
I anticipate people will want to do it for sEEG too as some point...

>

I am fine keeping set_eeg_reference to avoid breaking too much code

>

Ok I can take a stab at this later!
@libertyh just a heads up that we're looking to release 0.20 in the next couple of weeks so if you want this fix to make it into that release we'll have to iterate somewhat quickly. No rush from our end, though

Resolving patch:
diff --git a/mne/io/reference.py b/mne/io/reference.py
--- a/mne/io/reference.py
+++ b/mne/io/reference.py
@@ -47,12 +47,15 @@ def _copy_channel(inst, ch_name, new_ch_name):
     return inst
 
 
-def _apply_reference(inst, ref_from, ref_to=None, forward=None):
+def _apply_reference(inst, ref_from, ref_to=None, forward=None,
+                     ch_type='auto'):
     """Apply a custom EEG referencing scheme."""
     # Check to see that data is preloaded
     _check_preload(inst, "Applying a reference")
 
-    eeg_idx = pick_types(inst.info, eeg=True, meg=False, ref_meg=False)
+    ch_type = _get_ch_type(inst, ch_type)
+    ch_dict = {ch_type: True, 'meg': False, 'ref_meg': False}
+    eeg_idx = pick_types(inst.info, **ch_dict)
 
     if ref_to is None:
         ref_to = [inst.ch_names[i] for i in eeg_idx]
@@ -109,8 +112,8 @@ def _apply_reference(inst, ref_from, ref_to=None, forward=None):
         data[..., ref_to, :] -= ref_data
         ref_data = ref_data[..., 0, :]
 
-        # If the reference touches EEG electrodes, note in the info that a
-        # non-CAR has been applied.
+        # If the reference touches EEG/ECoG/sEEG electrodes, note in the info
+        # that a non-CAR has been applied.
         if len(np.intersect1d(ref_to, eeg_idx)) > 0:
             inst.info['custom_ref_applied'] = FIFF.FIFFV_MNE_CUSTOM_REF_ON
         # REST
@@ -323,25 +326,7 @@ def set_eeg_reference(inst, ref_channels='average', copy=True,
     del projection  # not used anymore
 
     inst = inst.copy() if copy else inst
-
-    _check_option('ch_type', ch_type, ('auto', 'eeg', 'ecog', 'seeg'))
-    # if ch_type is 'auto', search through list to find first reasonable
-    # reference-able channel type.
-    possible_types = ['eeg', 'ecog', 'seeg']
-    if ch_type == 'auto':
-        for type_ in possible_types:
-            if type_ in inst:
-                ch_type = type_
-                logger.info('%s channel type selected for '
-                            're-referencing' % DEFAULTS['titles'][type_])
-                break
-        # if auto comes up empty, or the user specifies a bad ch_type.
-        else:
-            raise ValueError('No EEG, ECoG or sEEG channels found '
-                             'to rereference.')
-    else:
-        type_ = ch_type
-
+    ch_type = _get_ch_type(inst, ch_type)
     ch_dict = {ch_type: True, 'meg': False, 'ref_meg': False}
     eeg_idx = pick_types(inst.info, **ch_dict)
     ch_sel = [inst.ch_names[i] for i in eeg_idx]
@@ -359,9 +344,29 @@ def set_eeg_reference(inst, ref_channels='average', copy=True,
         logger.info('EEG data marked as already having the desired reference.')
     else:
         logger.info('Applying a custom %s '
-                    'reference.' % DEFAULTS['titles'][type_])
+                    'reference.' % DEFAULTS['titles'][ch_type])
+
+    return _apply_reference(inst, ref_channels, ch_sel, forward,
+                            ch_type=ch_type)
 
-    return _apply_reference(inst, ref_channels, ch_sel, forward)
+
+def _get_ch_type(inst, ch_type):
+    _validate_type(ch_type, str, 'ch_type')
+    _check_option('ch_type', ch_type, ('auto', 'eeg', 'ecog', 'seeg'))
+    # if ch_type is 'auto', search through list to find first reasonable
+    # reference-able channel type.
+    if ch_type == 'auto':
+        for type_ in ['eeg', 'ecog', 'seeg']:
+            if type_ in inst:
+                ch_type = type_
+                logger.info('%s channel type selected for '
+                            're-referencing' % DEFAULTS['titles'][type_])
+                break
+        # if auto comes up empty, or the user specifies a bad ch_type.
+        else:
+            raise ValueError('No EEG, ECoG or sEEG channels found '
+                             'to rereference.')
+    return ch_type
 
 
 @verbose


Test patch:
diff --git a/mne/io/tests/test_reference.py b/mne/io/tests/test_reference.py
--- a/mne/io/tests/test_reference.py
+++ b/mne/io/tests/test_reference.py
@@ -237,6 +237,7 @@ def test_set_eeg_reference():
     data = rng.randn(3, 1000)
     raw = RawArray(data, create_info(3, 1000., ['ecog'] * 2 + ['misc']))
     reref, ref_data = set_eeg_reference(raw.copy())
+    assert reref.info['custom_ref_applied']  # gh-7350
     _test_reference(raw, reref, ref_data, ['0', '1'])