code: def _plot_lines(
      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,
      psd,
      line_alpha,
      nave,
      time_unit,
      sphere,
      *,
      highlight,
  ):
      """Plot data as butterfly plot."""
      from matplotlib import patheffects
      from matplotlib import pyplot as plt
      from matplotlib.widgets import SpanSelector
  
      assert len(axes) == len(ch_types_used)
      texts = list()
      idxs = list()
      lines = list()
      sphere = _check_sphere(sphere, info)
      path_effects = [
          patheffects.withStroke(
              linewidth=2, foreground="w", alpha=0.75
          )
      ]
      gfp_path_effects = [
          patheffects.withStroke(
              linewidth=5, foreground="w", alpha=0.75
          )
      ]
      if selectable:
          selectables = np.ones(len(ch_types_used), dtype=bool)
          for type_idx, this_type in enumerate(ch_types_used):
              idx = picks[types == this_type]
              if len(idx) < 2 or (this_type == "grad" and len(idx) < 4):
                  # prevent unnecessary warnings for e.g. EOG
                  if this_type in _DATA_CH_TYPES_SPLIT:
                      logger.info(
                          "Need more than one channel to make "
                          "topography for %s. Disabling interactivity."
                          % (this_type,)
                      )
                  selectables[type_idx] = False
  
      if selectable:
          # Parameters for butterfly interactive plots
          params = dict(
              axes=axes,
              texts=texts,
              lines=lines,
              ch_names=info["ch_names"],
              idxs=idxs,
              need_draw=False,
              path_effects=path_effects,
          )
          fig.canvas.mpl_connect(
              "pick_event", partial(_butterfly_onpick, params=params)
          )
          fig.canvas.mpl_connect(
              "button_press_event",
              partial(_butterfly_on_button_press, params=params),
          )
      for ai, (ax, this_type) in enumerate(zip(axes, ch_types_used)):
          line_list = (
              list()
          )  # 'line_list' contains the lines for this axes
          if unit is False:
              this_scaling = 1.0
              ch_unit = "NA"  # no unit
          else:
              this_scaling = (
                  1.0 if scalings is None else scalings[this_type]
              )
              ch_unit = units[this_type]
          idx = list(picks[types == this_type])
          idxs.append(idx)
  
          if len(idx) > 0:
              # Set amplitude scaling
              D = this_scaling * data[idx, :]
              _check_if_nan(D)
              gfp_only = gfp == "only"
              if not gfp_only:
                  chs = [info["chs"][i] for i in idx]
                  locs3d = np.array([ch["loc"][:3] for ch in chs])
                  # _plot_psd can pass spatial_colors=color (e.g., "black") so
                  # we need to use "is True" here
                  _spat_col = _check_spatial_colors(
                      info, idx, spatial_colors
                  )
                  if _spat_col is True and not _check_ch_locs(
                      info=info, picks=idx
                  ):
                      warn(
                          "Channel locations not available. Disabling spatial "
                          "colors."
                      )
                      _spat_col = selectable = False
                  if _spat_col is True and len(idx) != 1:
                      x, y, z = locs3d.T
                      colors = _rgb(x, y, z)
                      _handle_spatial_colors(
                          colors, info, idx, this_type, psd, ax, sphere
                      )
                      bad_color = (0.5, 0.5, 0.5)
                  else:
                      if isinstance(_spat_col, (tuple, str)):
                          col = [_spat_col]
                      else:
                          col = ["k"]
                      bad_color = "r"
                      colors = col * len(idx)
                  for i in bad_ch_idx:
                      if i in idx:
                          colors[idx.index(i)] = bad_color
  
                  if zorder == "std":
                      # find the channels with the least activity
                      # to map them in front of the more active ones
                      z_ord = D.std(axis=1).argsort()
                  elif zorder == "unsorted":
                      z_ord = list(range(D.shape[0]))
                  elif not callable(zorder):
                      error = (
                          '`zorder` must be a function, "std" '
                          'or "unsorted", not {0}.'
                      )
                      raise TypeError(error.format(type(zorder)))
                  else:
                      z_ord = zorder(D)
  
                  # plot channels
                  for ch_idx, z in enumerate(z_ord):
                      line_list.append(
                          ax.plot(
                              times,
                              D[ch_idx],
                              picker=True,
                              zorder=z + 1 if _spat_col else 1,
                              color=colors[ch_idx],
                              alpha=line_alpha,
                              linewidth=0.5,
                          )[0]
                      )
                      line_list[-1].set_pickradius(3.0)
  
              # Plot GFP / RMS
              if gfp:
                  if gfp in [True, "only"]:
                      if this_type == "eeg":
                          this_gfp = D.std(axis=0, ddof=0)
                          label = "GFP"
                      else:
                          this_gfp = np.linalg.norm(
                              D, axis=0
                          ) / np.sqrt(len(D))
                          label = "RMS"
  
                  gfp_color = (
                      3 * (0.0,)
                      if spatial_colors is True
                      else (0.0, 1.0, 0.0)
                  )
                  this_ylim = (
                      ax.get_ylim()
                      if (ylim is None or this_type not in ylim.keys())
                      else ylim[this_type]
                  )
                  if gfp_only:
                      y_offset = 0.0
                  else:
                      y_offset = this_ylim[0]
                  this_gfp += y_offset
                  ax.autoscale(False)
                  ax.fill_between(
                      times,
                      y_offset,
                      this_gfp,
                      color="none",
                      facecolor=gfp_color,
                      zorder=1,
                      alpha=0.2,
                  )
                  line_list.append(
                      ax.plot(
                          times,
                          this_gfp,
                          color=gfp_color,
                          zorder=3,
                          alpha=line_alpha,
                      )[0]
                  )
                  ax.text(
                      times[0] + 0.01 * (times[-1] - times[0]),
                      this_gfp[0] + 0.05 * np.diff(ax.get_ylim())[0],
                      label,
                      zorder=4,
                      color=gfp_color,
                      path_effects=gfp_path_effects,
                  )
              for ii, line in zip(idx, line_list):
                  if ii in bad_ch_idx:
                      line.set_zorder(2)
                      if spatial_colors is True:
                          line.set_linestyle("--")
              ax.set_ylabel(ch_unit)
              texts.append(
                  ax.text(
                      0,
                      0,
                      "",
                      zorder=3,
                      verticalalignment="baseline",
                      horizontalalignment="left",
                      fontweight="bold",
                      alpha=0,
                      clip_on=True,
                  )
              )
  
              if xlim is not None:
                  if xlim == "tight":
                      xlim = (times[0], times[-1])
                  ax.set_xlim(xlim)
              if ylim is not None and this_type in ylim:
                  ax.set_ylim(ylim[this_type])
              ax.set(
                  title=r"%s (%d channel%s)"
                  % (titles[this_type], len(D), _pl(len(D)))
              )
              if ai == 0:
                  _add_nave(ax, nave)
              if hline is not None:
                  for h in hline:
                      c = "grey" if spatial_colors is True else "r"
                      ax.axhline(
                          h, linestyle="--", linewidth=2, color=c
                      )
  
              # Plot highlights
              if highlight is not None:
                  this_ylim = (
                      ax.get_ylim()
                      if (ylim is None or this_type not in ylim.keys())
                      else ylim[this_type]
                  )
                  for this_highlight in highlight:
                      ax.fill_betweenx(
                          this_ylim,
                          this_highlight[0],
                          this_highlight[1],
                          facecolor="orange",
                          alpha=0.15,
                          zorder=99,
                      )
                  # Put back the y limits as fill_betweenx messes them up
                  ax.set_ylim(this_ylim)
  
          lines.append(line_list)
  
      if selectable:
          for ax in np.array(axes)[selectables]:
              if len(ax.lines) == 1:
                  continue
              text = ax.annotate(
                  "Loading...",
                  xy=(0.01, 0.1),
                  xycoords="axes fraction",
                  fontsize=20,
                  color="green",
                  zorder=3,
              )
              text.set_visible(False)
              callback_onselect = partial(
                  _line_plot_onselect,
                  ch_types=ch_types_used,
                  info=info,
                  data=data,
                  times=times,
                  text=text,
                  psd=psd,
                  time_unit=time_unit,
                  sphere=sphere,
              )
              blit = False if plt.get_backend() == "MacOSX" else True
              minspan = 0 if len(times) < 2 else times[1] - times[0]
              rect_kw = _prop_kw(
                  "rect", dict(alpha=0.5, facecolor="red")
              )
              ax._span_selector = SpanSelector(
                  ax,
                  callback_onselect,
                  "horizontal",
                  minspan=minspan,
                  useblit=blit,
                  **rect_kw,
              )
  
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 9 for calling a global function with example usage: _plot_lines(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, psd, line_alpha, nave, time_unit, sphere) without return types