Repository: mne-tools/mne-python

Issue:
Add add_reference_channels() method to Raw, Epochs, and Evoked
Currently there's `mne.add_reference_channels()`, which takes an instance of `Raw`, `Epochs`, or `Evoked`, and adds reference channels.

It would be great to have this functionality exposed as a **method** too, i.e., `Raw.add_reference_channels()`, `Epochs.add_reference_channels()`, and `Evoked.add_reference_channels()`, just as is the case e.g. with `set_eeg_reference()`

The respective `Raw`/`Epochs`/`Evoked` instance should be modified in-place, and the modified instance should be returned to allow for method chaining.

This would need to be added to `UpdateChannelsMixin` in `mne/channels/channels.py`

Overlapping parts of the method and  `mne.add_reference_channels()` docstrings should be added to the `docdict` to avoid duplication.


Discussion:
no objection

>

I'll take this one :)

Resolving patch:
diff --git a/mne/channels/channels.py b/mne/channels/channels.py
--- a/mne/channels/channels.py
+++ b/mne/channels/channels.py
@@ -1072,6 +1072,27 @@ def add_channels(self, add_list, force_update_info=False):
             assert all(len(r) == self.info['nchan'] for r in self._read_picks)
         return self
 
+    @fill_doc
+    def add_reference_channels(self, ref_channels):
+        """Add reference channels to data that consists of all zeros.
+
+        Adds reference channels to data that were not included during
+        recording. This is useful when you need to re-reference your data
+        to different channels. These added channels will consist of all zeros.
+
+        Parameters
+        ----------
+        %(ref_channels)s
+
+        Returns
+        -------
+        inst : instance of Raw | Epochs | Evoked
+               The modified instance.
+        """
+        from ..io.reference import add_reference_channels
+
+        return add_reference_channels(self, ref_channels, copy=False)
+
 
 class InterpolationMixin(object):
     """Mixin class for Raw, Evoked, Epochs."""
diff --git a/mne/io/reference.py b/mne/io/reference.py
--- a/mne/io/reference.py
+++ b/mne/io/reference.py
@@ -16,7 +16,7 @@
 from ..evoked import Evoked
 from ..epochs import BaseEpochs
 from ..utils import (logger, warn, verbose, _validate_type, _check_preload,
-                     _check_option)
+                     _check_option, fill_doc)
 from ..defaults import DEFAULTS
 
 
@@ -139,6 +139,7 @@ def _apply_reference(inst, ref_from, ref_to=None, forward=None,
     return inst, ref_data
 
 
+@fill_doc
 def add_reference_channels(inst, ref_channels, copy=True):
     """Add reference channels to data that consists of all zeros.
 
@@ -150,10 +151,7 @@ def add_reference_channels(inst, ref_channels, copy=True):
     ----------
     inst : instance of Raw | Epochs | Evoked
         Instance of Raw or Epochs with EEG channels and reference channel(s).
-    ref_channels : str | list of str
-        Name of the electrode(s) which served as the reference in the
-        recording. If a name is provided, a corresponding channel is added
-        and its data is set to 0. This is useful for later re-referencing.
+    %(ref_channels)s
     copy : bool
         Specifies whether the data will be copied (True) or modified in-place
         (False). Defaults to True.
diff --git a/mne/utils/docs.py b/mne/utils/docs.py
--- a/mne/utils/docs.py
+++ b/mne/utils/docs.py
@@ -2134,6 +2134,13 @@
     exists.
 """
 
+docdict['ref_channels'] = """
+ref_channels : str | list of str
+    Name of the electrode(s) which served as the reference in the
+    recording. If a name is provided, a corresponding channel is added
+    and its data is set to 0. This is useful for later re-referencing.
+"""
+
 docdict_indented = {}
 
 


Test patch:
diff --git a/mne/channels/tests/test_channels.py b/mne/channels/tests/test_channels.py
--- a/mne/channels/tests/test_channels.py
+++ b/mne/channels/tests/test_channels.py
@@ -366,6 +366,31 @@ def test_drop_channels():
     pytest.raises(ValueError, raw.drop_channels, 5)  # must be list or str
 
 
+def test_add_reference_channels():
+    """Test if there is a new reference channel that consist of all zeros."""
+    raw = read_raw_fif(raw_fname, preload=True)
+    n_raw_original_channels = len(raw.ch_names)
+    epochs = Epochs(raw, read_events(eve_fname))
+    epochs.load_data()
+    epochs_original_shape = epochs._data.shape[1]
+    evoked = epochs.average()
+    n_evoked_original_channels = len(evoked.ch_names)
+
+    # Raw object
+    raw.add_reference_channels(['REF 123'])
+    assert len(raw.ch_names) == n_raw_original_channels + 1
+    assert np.all(raw.get_data()[-1] == 0)
+
+    # Epochs object
+    epochs.add_reference_channels(['REF 123'])
+    assert epochs._data.shape[1] == epochs_original_shape + 1
+
+    # Evoked object
+    evoked.add_reference_channels(['REF 123'])
+    assert len(evoked.ch_names) == n_evoked_original_channels + 1
+    assert np.all(evoked._data[-1] == 0)
+
+
 def test_equalize_channels():
     """Test equalizing channels and their ordering."""
     # This function only tests the generic functionality of equalize_channels.