Repository: mne-tools/mne-python

Issue:
Ignoring bads in raw.plot_psd doesn't work
By default, `raw.plot_psd()` excludes bad channels. According to the docs, I can include bad channels by setting `picks="all"`, but this doesn't work:

```
import mne
from mne.datasets import sample


data_path = sample.data_path()
raw_fname = data_path + '/MEG/sample/sample_audvis_raw.fif'
raw = mne.io.read_raw_fif(raw_fname, preload=True)

raw.drop_channels(raw.info["ch_names"][:315])
raw.drop_channels(raw.info["ch_names"][4:])
raw.info["bads"] = ['EEG 003', 'EEG 004']

raw.plot_psd(spatial_colors=False)
raw.plot_psd(spatial_colors=False, picks="all")
```

Both plots only show the traces for the good channels.


Discussion:
pr welcome 🙏 

Creating a PSD with `raw.plot_psd(picks="all")` actually calls `plot_raw_psd(..., picks="all")`, which calls `_set_psd_plot_params(..., picks="all")` at the beginning. Inside this function, `_picks_to_idx(..., picks="all")` gets called, which eventually calls `_picks_str_to_idx(picks="all")`. Finally, this function checks if the argument is equal to `"all"`  (which it is in our case), but even in this case it excludes bad channels (because `exclude` is set to its default value of `"bads"` implicitly). However, it is impossible to change the `exclude` parameter from `raw.plot_psd` (or `plot_raw_psd` or `_set_psd_plot_params`). The first function in this hierarchy to accept an `exclude` argument is `_picks_to_idx`.

How do you want me to handle this? IMO, if I specify `picks="all"`, all channels (including bads) should be picked, but this changes the current behavior. The other string options are `"data"` and `"data_or_ica"` (which also exclude bads by default), so I'm not sure how this is supposed to work.
is it an inconsistent behavior with other functions accepting picks='all'?
if so it can be considered a bug fix

>

There are some functions that accept both `picks` and `exclude` arguments - this *should* make it possible to pass `picks="all", exclude=None` to pick all channels (e.g. `plot_sources`). But I was wondering what `picks="all"` means then? I'd find it intuitive if this picked all channels, irrespective of their type and no matter if they're marked bad or not (this is how I'd interpret the [docstring](https://github.com/mne-tools/mne-python/blob/897c3b2d12d0562f888784c5e18c2da31ed27ceb/mne/utils/docs.py#L52) - BTW the new docstring magic makes reading docstrings in the source code really hard). However, it is possible that I'm missing something and that this actually makes sense, in which case we'd need either another string keyword to pick all channels including bads, or a separate `exclude` parameter everywhere we want to pick all channels including bads.
My suggestion would be:

1. PR1: Document current behavior properly. This probably means:
    a. adding an `%(picks_all_good)s` and updating `raw.plot_psd` and possibly other functions that don't have an `exclude` param but do exclude `'bads'`.
    b. Update `%(picks_all_good)s` and `%(picks_all)s` to say what happens when there is an `exclude` parameter, if relevant.
2. PR2: Add `exclude` anywhere there is picks. I'm not 100% sure we need this. It's convenient and more consistent, the downside being parameter bloat, so I'm on the fence. In the meantime people can always resort to `mne.pick_*` functions to be explicit about what they want. So this one would need a champion to work on it.
`%(picks_all_good)s` is exactly what currently happens if you supply `picks="all"`. Instead of adding a new parameter, we could add a new string `picks="all_good"` (or `picks="goods"` if this is the MNE opposite of "bads") and change the behavior for `picks="all"`.

Of course we could/should go ahead with PR1 to document current behavior. We could even include a sentence on how to include all channels (with bads) using the `mne.pick_*` functions, maybe this also solves the problem and we can completely avoid adding a new parameter or a new string keyword.
Interesting. Using `mne.pick_channels` doesn't even work in this case. `raw.plot_psd(picks=mne.pick_channels(raw.info["ch_names"], include=[]), spatial_colors=False)` still shows only the two good channels (and omitting `spatial_colors=False` only shows the first channel). Or without the pick function, this also generates only two traces: `raw.plot_psd(picks=[0, 1, 2, 3], spatial_colors=False)`.
then I think it's a clear bug that is likely to be specific to plot_psd

>


Resolving patch:
diff --git a/mne/viz/utils.py b/mne/viz/utils.py
--- a/mne/viz/utils.py
+++ b/mne/viz/utils.py
@@ -2884,7 +2884,7 @@ def _set_psd_plot_params(info, proj, picks, ax, area_mode):
     for meg, eeg, seeg, ecog, name in zip(megs, eegs, seegs, ecogs,
                                           _data_types):
         these_picks = pick_types(info, meg=meg, eeg=eeg, seeg=seeg, ecog=ecog,
-                                 ref_meg=False)
+                                 ref_meg=False, exclude=[])
         these_picks = np.intersect1d(these_picks, picks)
         if len(these_picks) > 0:
             picks_list.append(these_picks)


Test patch: