code: def _plot_evoked(
      evoked,
      picks=None,
      exclude="bads",
      unit=True,
      show=True,
      ylim=None,
      proj=False,
      xlim="tight",
      hline=None,
      units=None,
      scalings=None,
      titles=None,
      axes=None,
      plot_type="butterfly",
      cmap=None,
      gfp=False,
      window_title=None,
      spatial_colors=False,
      selectable=True,
      zorder="unsorted",
      noise_cov=None,
      colorbar=True,
      mask=None,
      mask_style=None,
      mask_cmap=None,
      mask_alpha=0.25,
      time_unit="s",
      show_names=False,
      group_by=None,
      sphere=None,
      *,
      highlight=None,
      draw=True,
  ):
      """Aux function for plot_evoked and plot_evoked_image (cf. docstrings).
  
      Extra params are:
  
      plot_type : str, value ('butterfly' | 'image')
          The type of graph to plot: 'butterfly' plots each channel as a line
          (x axis: time, y axis: amplitude). 'image' plots a 2D image where
          color depicts the amplitude of each channel at a given time point
          (x axis: time, y axis: channel). In 'image' mode, the plot is not
          interactive.
      draw : bool
          If True, draw at the end.
      """
      import matplotlib.pyplot as plt
  
      _check_option(
          "spatial_colors", spatial_colors, [True, False, "auto"]
      )
      # For evoked.plot_image ...
      # First input checks for group_by and axes if any of them is not None.
      # Either both must be dicts, or neither.
      # If the former, the two dicts provide picks and axes to plot them to.
      # Then, we call this function recursively for each entry in `group_by`.
      if plot_type == "image" and isinstance(group_by, dict):
          if axes is None:
              axes = dict()
              for sel in group_by:
                  plt.figure(layout="constrained")
                  axes[sel] = plt.axes()
          if not isinstance(axes, dict):
              raise ValueError(
                  "If `group_by` is a dict, `axes` must be "
                  "a dict of axes or None."
              )
          _validate_if_list_of_axes(list(axes.values()))
          remove_xlabels = any(
              [_is_last_row(ax) for ax in axes.values()]
          )
          for sel in group_by:  # ... we loop over selections
              if sel not in axes:
                  raise ValueError(
                      sel + " present in `group_by`, but not "
                      "found in `axes`"
                  )
              ax = axes[sel]
              # the unwieldy dict comp below defaults the title to the sel
              title = (
                  {
                      channel_type(evoked.info, idx): sel
                      for idx in group_by[sel]
                  }
                  if titles is None
                  else titles
              )
              _plot_evoked(
                  evoked,
                  group_by[sel],
                  exclude,
                  unit,
                  show,
                  ylim,
                  proj,
                  xlim,
                  hline,
                  units,
                  scalings,
                  title,
                  ax,
                  plot_type,
                  cmap=cmap,
                  gfp=gfp,
                  window_title=window_title,
                  selectable=selectable,
                  noise_cov=noise_cov,
                  colorbar=colorbar,
                  mask=mask,
                  mask_style=mask_style,
                  mask_cmap=mask_cmap,
                  mask_alpha=mask_alpha,
                  time_unit=time_unit,
                  show_names=show_names,
                  sphere=sphere,
                  draw=False,
                  spatial_colors=spatial_colors,
              )
              if remove_xlabels and not _is_last_row(ax):
                  ax.set_xticklabels([])
                  ax.set_xlabel("")
          ims = [ax.images[0] for ax in axes.values()]
          clims = np.array([im.get_clim() for im in ims])
          min, max = clims.min(), clims.max()
          for im in ims:
              im.set_clim(min, max)
          figs = [ax.get_figure() for ax in axes.values()]
          if len(set(figs)) == 1:
              return figs[0]
          else:
              return figs
      elif isinstance(axes, dict):
          raise ValueError(
              "If `group_by` is not a dict, "
              "`axes` must not be a dict either."
          )
  
      time_unit, times = _check_time_unit(time_unit, evoked.times)
      evoked = evoked.copy()  # we modify info
      info = evoked.info
      if axes is not None and proj == "interactive":
          raise RuntimeError(
              "Currently only single axis figures are supported"
              " for interactive SSP selection."
          )
  
      _check_option("gfp", gfp, [True, False, "only"])
  
      if highlight is not None:
          highlight = np.array(highlight, dtype=float)
          highlight = np.atleast_2d(highlight)
          if highlight.shape[1] != 2:
              raise ValueError(
                  f'"highlight" must be reshapable into a 2D array with shape '
                  f"(n, 2). Got {highlight.shape}."
              )
  
      scalings = _handle_default("scalings", scalings)
      titles = _handle_default("titles", titles)
      units = _handle_default("units", units)
  
      if plot_type == "image":
          if ylim is not None and not isinstance(ylim, dict):
              # The user called Evoked.plot_image() or plot_evoked_image(), the
              # clim parameters of those functions end up to be the ylim here.
              raise ValueError(
                  "`clim` must be a dict. "
                  "E.g. clim = dict(eeg=[-20, 20])"
              )
  
      picks = _picks_to_idx(info, picks, none="all", exclude=())
      if len(picks) != len(set(picks)):
          raise ValueError(
              "`picks` are not unique. Please remove duplicates."
          )
  
      bad_ch_idx = [
          info["ch_names"].index(ch)
          for ch in info["bads"]
          if ch in info["ch_names"]
      ]
      if len(exclude) > 0:
          if isinstance(exclude, str) and exclude == "bads":
              exclude = bad_ch_idx
          elif isinstance(exclude, list) and all(
              isinstance(ch, str) for ch in exclude
          ):
              exclude = [info["ch_names"].index(ch) for ch in exclude]
          else:
              raise ValueError(
                  'exclude has to be a list of channel names or "bads"'
              )
  
          picks = np.array(
              [pick for pick in picks if pick not in exclude]
          )
  
      types = np.array(info.get_channel_types(picks), str)
      ch_types_used = list()
      for this_type in _VALID_CHANNEL_TYPES:
          if this_type in types:
              ch_types_used.append(this_type)
  
      fig = None
      if axes is None:
          fig, axes = plt.subplots(
              len(ch_types_used), 1, layout="constrained"
          )
          if isinstance(axes, plt.Axes):
              axes = [axes]
          fig.set_size_inches(6.4, 2 + len(axes))
  
      if isinstance(axes, plt.Axes):
          axes = [axes]
      elif isinstance(axes, np.ndarray):
          axes = list(axes)
  
      if fig is None:
          fig = axes[0].get_figure()
  
      if window_title is not None:
          _set_window_title(fig, window_title)
  
      if len(axes) != len(ch_types_used):
          raise ValueError(
              "Number of axes (%g) must match number of channel "
              "types (%d: %s)"
              % (len(axes), len(ch_types_used), sorted(ch_types_used))
          )
      _check_option(
          "proj", proj, (True, False, "interactive", "reconstruct")
      )
      noise_cov = _check_cov(noise_cov, info)
      if proj == "reconstruct" and noise_cov is not None:
          raise ValueError(
              'Cannot use proj="reconstruct" when noise_cov is not '
              "None"
          )
      projector, whitened_ch_names = _setup_plot_projector(
          info, noise_cov, proj=proj is True, nave=evoked.nave
      )
      if len(whitened_ch_names) > 0:
          unit = False
      if projector is not None:
          evoked.data[:] = np.dot(projector, evoked.data)
      if proj == "reconstruct":
          evoked = evoked._reconstruct_proj()
  
      if plot_type == "butterfly":
          _plot_lines(
              evoked.data,
              info,
              picks,
              fig,
              axes,
              spatial_colors,
              unit,
              units,
              scalings,
              hline,
              gfp,
              types,
              zorder,
              xlim,
              ylim,
              times,
              bad_ch_idx,
              titles,
              ch_types_used,
              selectable,
              False,
              line_alpha=1.0,
              nave=evoked.nave,
              time_unit=time_unit,
              sphere=sphere,
              highlight=highlight,
          )
          plt.setp(axes, xlabel=f"Time ({time_unit})")
  
      elif plot_type == "image":
          for ai, (ax, this_type) in enumerate(
              zip(axes, ch_types_used)
          ):
              use_nave = evoked.nave if ai == 0 else None
              this_picks = list(picks[types == this_type])
              _plot_image(
                  evoked.data,
                  ax,
                  this_type,
                  this_picks,
                  cmap,
                  unit,
                  units,
                  scalings,
                  times,
                  xlim,
                  ylim,
                  titles,
                  colorbar=colorbar,
                  mask=mask,
                  mask_style=mask_style,
                  mask_cmap=mask_cmap,
                  mask_alpha=mask_alpha,
                  nave=use_nave,
                  time_unit=time_unit,
                  show_names=show_names,
                  ch_names=evoked.ch_names,
              )
      if proj == "interactive":
          _check_delayed_ssp(evoked)
          params = dict(
              evoked=evoked,
              fig=fig,
              projs=info["projs"],
              axes=axes,
              types=types,
              units=units,
              scalings=scalings,
              unit=unit,
              ch_types_used=ch_types_used,
              picks=picks,
              plot_update_proj_callback=_plot_update_evoked,
              plot_type=plot_type,
          )
          _draw_proj_checkbox(None, params)
  
      plt.setp(fig.axes[: len(ch_types_used) - 1], xlabel="")
      if draw:
          fig.canvas.draw()  # for axes plots update axes.
      plt_show(show)
      return fig
  
imports: from copy import deepcopy
  from functools import partial
  from itertools import cycle
  from numbers import Integral
  import numpy
  from .._fiff.pick import _DATA_CH_TYPES_SPLIT, _PICK_TYPES_DATA_DICT, _VALID_CHANNEL_TYPES, _picks_to_idx, channel_indices_by_type, channel_type, pick_info
  from ..defaults import _handle_default
  from ..fixes import _is_last_row
  from ..utils import _check_ch_locs, _check_if_nan, _clean_names, _is_numeric, _pl, _to_rgb, _validate_type, fill_doc, logger, verbose, warn
  from .topo import _plot_evoked_topo
  from .topomap import _check_sphere, _draw_outlines, _get_pos_outlines, _make_head_outlines, _prepare_topomap, _prepare_topomap_plot, _set_contour_locator, plot_topomap
  from .utils import DraggableColorbar, _check_cov, _check_delayed_ssp, _check_option, _check_time_unit, _draw_proj_checkbox, _get_cmap, _get_color_list, _make_combine_callable, _plot_masked_image, _prepare_joint_axes, _process_times, _prop_kw, _set_title_multiple_electrodes, _set_window_title, _setup_ax_spines, _setup_cmap, _setup_plot_projector, _setup_vmin_vmax, _triage_rank_sss, _trim_ticks, _validate_if_list_of_axes, plt_show
label_desc: Use image node_id 8 for calling a global function with example usage: _plot_evoked(evoked, picks, exclude, unit, show, ylim, proj, xlim, hline, units, scalings, titles, axes, plot_type, cmap, gfp, window_title, spatial_colors, selectable, zorder, noise_cov, colorbar, mask, mask_style, mask_cmap, mask_alpha, time_unit, show_names, group_by, sphere) and returns: fig, figs, figs