Repository: mne-tools/mne-python

Issue:
epochs.plot_psd loses axis labels
I would expect this code to work

```py
from mne.viz.tests.test_epochs import _get_epochs
import matplotlib.pyplot as plt
import mne

epochs = _get_epochs()

# subplot axis properly labeled
_, (ax1, ax2) = plt.subplots(nrows=2)
epochs.plot_psd(picks=mne.pick_types(epochs.info, meg='meg'), ax=[ax1, ax2])

# proper axis labels
epochs.plot_psd(picks=mne.pick_types(epochs.info, meg='mag'))

# missing labels
_, (ax1, ax2) = plt.subplots(nrows=2)
epochs.plot_psd(picks=mne.pick_types(epochs.info, meg='mag'), ax=ax1)
```
 
![image](https://user-images.githubusercontent.com/7044835/50742462-c6c12e00-120b-11e9-9e85-5a7c059d1b08.png)



Discussion:
I can replicate the pb

>

The issue is here:

We recover the figure and if the number of axis match, then we set `make_label=True`.

https://github.com/mne-tools/mne-python/blob/feb0bec3acf334ee391711109c9a541697813a30/mne/viz/raw.py#L600-L602

What I'm not sure if this is a bug or a feature.
This was added by @larsoner [here](https://github.com/mne-tools/mne-python/commit/53506d1d5547a5dbed8e30d9927232a58ced7a81#diff-190df768fb5ee305b0a421d3dd6ae9adR600)
I would say it's a bug. It's easier to change the labels afterwards.

It's a heuristic to try not to do bad things with the labels. See later in the code there is a check for when to add the xlabel for example. These things might behave badly when this check is removed. I'm sure I added this because of this sort of thing, but can't remember the exact use case.

I'm fine with changing this, but make sure the change is in the BUG section because it's a backward incompatible change that will break some people's plotting code. (I'm sure it will break some of mine.)
>I'm fine with changing this, but make sure the change is in the BUG section because it's a backward incompatible change that will break some people's plotting code. (I'm sure it will break some of mine.)

That's why I asked. What I could not figure out is when this should be false. My understanding is that you add that so that the first plot is possible. (otherwise, it does not). But If we always return True that would work in both cases (first and last). But I could not figure out when we would like to not write the labels.
Basically if we create the axes internally, we know (at least) the `xlabel` is placed correctly; if the user supplies them, we can't guarantee it
yes that's why it should be displayed by default when you pass one ax

>


Resolving patch:
diff --git a/mne/viz/epochs.py b/mne/viz/epochs.py
--- a/mne/viz/epochs.py
+++ b/mne/viz/epochs.py
@@ -937,8 +937,8 @@ def plot_epochs_psd(epochs, fmin=0, fmax=np.inf, tmin=None, tmax=None,
     """
     from .utils import _set_psd_plot_params, _plot_psd
     fig, picks_list, titles_list, units_list, scalings_list, ax_list, \
-        make_label = _set_psd_plot_params(epochs.info, proj, picks, ax,
-                                          area_mode)
+        make_label, xlabels_list = \
+        _set_psd_plot_params(epochs.info, proj, picks, ax, area_mode)
     _check_psd_fmax(epochs, fmax)
     del ax
     psd_list = list()
@@ -956,7 +956,7 @@ def plot_epochs_psd(epochs, fmin=0, fmax=np.inf, tmin=None, tmax=None,
     fig = _plot_psd(epochs, fig, freqs, psd_list, picks_list, titles_list,
                     units_list, scalings_list, ax_list, make_label, color,
                     area_mode, area_alpha, dB, estimate, average,
-                    spatial_colors, xscale, line_alpha, sphere)
+                    spatial_colors, xscale, line_alpha, sphere, xlabels_list)
     plt_show(show)
     return fig
 
diff --git a/mne/viz/raw.py b/mne/viz/raw.py
--- a/mne/viz/raw.py
+++ b/mne/viz/raw.py
@@ -619,7 +619,7 @@ def plot_raw_psd(raw, fmin=0, fmax=np.inf, tmin=None, tmax=None, proj=False,
     """
     from .utils import _set_psd_plot_params, _plot_psd
     fig, picks_list, titles_list, units_list, scalings_list, ax_list, \
-        make_label = _set_psd_plot_params(
+        make_label, xlabels_list = _set_psd_plot_params(
             raw.info, proj, picks, ax, area_mode)
     _check_psd_fmax(raw, fmax)
     del ax
@@ -638,7 +638,7 @@ def plot_raw_psd(raw, fmin=0, fmax=np.inf, tmin=None, tmax=None, proj=False,
     fig = _plot_psd(raw, fig, freqs, psd_list, picks_list, titles_list,
                     units_list, scalings_list, ax_list, make_label, color,
                     area_mode, area_alpha, dB, estimate, average,
-                    spatial_colors, xscale, line_alpha, sphere)
+                    spatial_colors, xscale, line_alpha, sphere, xlabels_list)
     plt_show(show)
     return fig
 
diff --git a/mne/viz/utils.py b/mne/viz/utils.py
--- a/mne/viz/utils.py
+++ b/mne/viz/utils.py
@@ -3028,13 +3028,18 @@ def _set_psd_plot_params(info, proj, picks, ax, area_mode):
         fig, ax_list = plt.subplots(len(picks_list), 1, sharex=True,
                                     squeeze=False)
         ax_list = list(ax_list[:, 0])
-        make_label = True
     else:
         fig = ax_list[0].get_figure()
-        make_label = len(ax_list) == len(fig.axes)
+
+    # make_label decides if ylabel and titles are displayed
+    make_label = len(ax_list) == len(fig.axes)
+
+    # Plot Frequency [Hz] xlabel on the last axis
+    xlabels_list = [False] * len(picks_list)
+    xlabels_list[-1] = True
 
     return (fig, picks_list, titles_list, units_list, scalings_list,
-            ax_list, make_label)
+            ax_list, make_label, xlabels_list)
 
 
 def _convert_psds(psds, dB, estimate, scaling, unit, ch_names=None,
@@ -3109,7 +3114,7 @@ def _check_psd_fmax(inst, fmax):
 def _plot_psd(inst, fig, freqs, psd_list, picks_list, titles_list,
               units_list, scalings_list, ax_list, make_label, color, area_mode,
               area_alpha, dB, estimate, average, spatial_colors, xscale,
-              line_alpha, sphere):
+              line_alpha, sphere, xlabels_list):
     # helper function for plot_raw_psd and plot_epochs_psd
     from matplotlib.ticker import ScalarFormatter
     from .evoked import _plot_lines
@@ -3183,7 +3188,8 @@ def _plot_psd(inst, fig, freqs, psd_list, picks_list, titles_list,
                     ch_types_used=ch_types_used, selectable=True, psd=True,
                     line_alpha=line_alpha, nave=None, time_unit='ms',
                     sphere=sphere)
-    for ii, ax in enumerate(ax_list):
+
+    for ii, (ax, xlabel) in enumerate(zip(ax_list, xlabels_list)):
         ax.grid(True, linestyle=':')
         if xscale == 'log':
             ax.set(xscale='log')
@@ -3192,9 +3198,10 @@ def _plot_psd(inst, fig, freqs, psd_list, picks_list, titles_list,
         else:  # xscale == 'linear'
             ax.set(xlim=(freqs[0], freqs[-1]))
         if make_label:
-            if ii == len(picks_list) - 1:
-                ax.set_xlabel('Frequency (Hz)')
             ax.set(ylabel=ylabels[ii], title=titles_list[ii])
+            if xlabel:
+                ax.set_xlabel('Frequency (Hz)')
+
     if make_label:
         fig.subplots_adjust(left=.1, bottom=.1, right=.9, top=.9, wspace=0.3,
                             hspace=0.5)


Test patch: