Repository: mne-tools/mne-python

Issue:
Vertical placement of channel traces seems off in raw butterfly plot
MWE:
```python
# %%
from pathlib import Path
import mne
import numpy as np


sample_dir = Path(mne.datasets.sample.data_path())
sample_fname = sample_dir / 'MEG' / 'sample' / 'sample_audvis_raw.fif'
raw = mne.io.read_raw_fif(sample_fname, preload=True)

# %%

fig = (
    raw.copy()
       .pick_types(eeg=True, eog=True, stim=True)
       .plot(butterfly=True, show_scrollbars=False, start=0,
             duration=1, show=False)
)
```
![output](https://user-images.githubusercontent.com/2046265/144600353-1897e818-d2a0-430e-b8ed-5b84dd8fa5de.png)

It appears as if the stim channel has a lot of signals, and the EOG channel were flat.

However, the following code reveals that this ain't so:
```python
# %%
fig = (
    raw.copy()
       .pick_types(stim=True)
       .plot(butterfly=True, show_scrollbars=False, start=0,
             duration=1, show=False)
)
```
![output](https://user-images.githubusercontent.com/2046265/144600515-3ed5fb7e-329e-498f-99ad-14c0cd0dc0b3.png)



```python
# %%
fig = (
    raw.copy()
       .pick_types(eog=True)
       .plot(butterfly=True, show_scrollbars=False, start=0,
             duration=1, show=False)
)
```
![output](https://user-images.githubusercontent.com/2046265/144600552-aad4aa96-2fe2-46ad-a8cf-11b06d5325bc.png)


cc @drammock 


Discussion:
> It appears as if the stim channel has a lot of signals, and the EOG channel were flat.

How do you know which is supposed to be which? To me I see three sets of signals, which I'd expect. But I wouldn't expect it to be in the order you gave in `pick_types` because that function does not reorder channels, or set the plotting order.

If you want specifically ordered channels, you need to pass `order=...`, probably in your case something like:
```
order = np.concatenate([
    mne.pick_types(raw.info, eeg=True),
    mne.pick_types(raw.info, eog=True),
    mne.pick_types(raw.info, stim=True)])
raw.plot(order=order)
```
gets you closer to what you want. However, I'm not sure if butterfly will respect this, it might just be non-butterfly mode.

If this is the case, I would just update the doc for the `order` param to say that it isn't used in butterfly mode, because butterfly mode has to group by channel type. (I guess we could go based on the order we see each channel type *first in the instance*, but I think this has disadvantages, too, so probably shouldn't be the default.)
> How do you know which is supposed to be which?

Based on the ordinate labels. The respective butterfly traces should oscillate around the respective channel type label, or be clipped if they're exceeding a certain amplitude.

It's misleading if, like in the first picture, the `eog` label is perfectly aligned with a flat line, when in fact this line represents the `stim` channels.

Similarly, the labelling seems to indicate there's many traces in the stim channels, while actually these belong to `eeg`.

And the EOG trace is centered around the `eeg` label.
@larsoner Will you be around at today's dev meeting? We can discuss there.
Oh wow I didn't even see the labels because of image transparency and GitHub dark mode. Agreed those labels are wrong :)
No I can't, but it seems like a straightforward bug to me. The fix is hopefully similarly straightforward, but with viz, who knows!
> No I can't,

Bummer!



> The fix is hopefully similarly straightforward, but with viz, who knows!

Yeah I have no idea honestly :) Maybe @drammock has an immediate intuition?

Resolving patch:
diff --git a/mne/viz/_mpl_figure.py b/mne/viz/_mpl_figure.py
--- a/mne/viz/_mpl_figure.py
+++ b/mne/viz/_mpl_figure.py
@@ -1717,6 +1717,8 @@ def _draw_traces(self):
         picks = self.mne.picks
         ch_names = self.mne.ch_names[picks]
         ch_types = self.mne.ch_types[picks]
+        offset_ixs = picks if self.mne.butterfly else slice(None)
+        offsets = self.mne.trace_offsets[offset_ixs]
         bad_bool = np.in1d(ch_names, self.mne.info['bads'])
         # colors
         good_ch_colors = [self.mne.ch_color_dict[_type] for _type in ch_types]
@@ -1785,7 +1787,7 @@ def _draw_traces(self):
         for ii, line in enumerate(self.mne.traces):
             this_name = ch_names[ii]
             this_type = ch_types[ii]
-            this_offset = self.mne.trace_offsets[ii]
+            this_offset = offsets[ii]
             this_times = decim_times[decim[ii]]
             this_data = this_offset - self.mne.data[ii] * self.mne.scale_factor
             this_data = this_data[..., ::decim[ii]]


Test patch: