code: def plot_filter(
      h,
      sfreq,
      freq=None,
      gain=None,
      title=None,
      color="#1f77b4",
      flim=None,
      fscale="log",
      alim=_DEFAULT_ALIM,
      show=True,
      compensate=False,
      plot=("time", "magnitude", "delay"),
      axes=None,
      *,
      dlim=None,
  ):
      """Plot properties of a filter.
  
      Parameters
      ----------
      h : dict or ndarray
          An IIR dict or 1D ndarray of coefficients (for FIR filter).
      sfreq : float
          Sample rate of the data (Hz).
      freq : array-like or None
          The ideal response frequencies to plot (must be in ascending order).
          If None (default), do not plot the ideal response.
      gain : array-like or None
          The ideal response gains to plot.
          If None (default), do not plot the ideal response.
      title : str | None
          The title to use. If None (default), determine the title based
          on the type of the system.
      color : color object
          The color to use (default '#1f77b4').
      flim : tuple or None
          If not None, the x-axis frequency limits (Hz) to use.
          If None, freq will be used. If None (default) and freq is None,
          ``(0.1, sfreq / 2.)`` will be used.
      fscale : str
          Frequency scaling to use, can be "log" (default) or "linear".
      alim : tuple
          The y-axis amplitude limits (dB) to use (default: (-60, 10)).
      show : bool
          Show figure if True (default).
      compensate : bool
          If True, compensate for the filter delay (phase will not be shown).
  
          - For linear-phase FIR filters, this visualizes the filter coefficients
            assuming that the output will be shifted by ``N // 2``.
          - For IIR filters, this changes the filter coefficient display
            by filtering backward and forward, and the frequency response
            by squaring it.
  
          .. versionadded:: 0.18
      plot : list | tuple | str
          A list of the requested plots from ``time``, ``magnitude`` and
          ``delay``. Default is to plot all three filter properties
          ('time', 'magnitude', 'delay').
  
          .. versionadded:: 0.21.0
      axes : instance of Axes | list | None
          The axes to plot to. If list, the list must be a list of Axes of
          the same length as the number of requested plot types. If instance of
          Axes, there must be only one filter property plotted.
          Defaults to ``None``.
  
          .. versionadded:: 0.21.0
      dlim : None | tuple
          The y-axis delay limits (s) to use (default:
          ``(-tmax / 2., tmax / 2.)``).
  
          .. versionadded:: 1.1.0
  
      Returns
      -------
      fig : matplotlib.figure.Figure
          The figure containing the plots.
  
      See Also
      --------
      mne.filter.create_filter
      plot_ideal_filter
  
      Notes
      -----
      .. versionadded:: 0.14
      """
      import matplotlib.pyplot as plt
  
      sfreq = float(sfreq)
      _check_option("fscale", fscale, ["log", "linear"])
      if isinstance(plot, str):
          plot = [plot]
      for xi, x in enumerate(plot):
          _check_option(
              "plot[%d]" % xi, x, ("magnitude", "delay", "time")
          )
  
      flim = _get_flim(flim, fscale, freq, sfreq)
      if fscale == "log":
          omega = np.logspace(
              np.log10(flim[0]), np.log10(flim[1]), 1000
          )
      else:
          omega = np.linspace(flim[0], flim[1], 1000)
      xticks, xticklabels = _filter_ticks(flim, fscale)
      omega /= sfreq / (2 * np.pi)
      if isinstance(
          h, dict
      ):  # IIR h.ndim == 2:  # second-order sections
          if "sos" in h:
              H = np.ones(len(omega), np.complex128)
              gd = np.zeros(len(omega))
              for section in h["sos"]:
                  this_H = freqz(section[:3], section[3:], omega)[1]
                  H *= this_H
                  if compensate:
                      H *= this_H.conj()  # time reversal is freq conj
                  else:
                      # Assume the forward-backward delay zeros out, which it
                      # mostly should
                      with warnings.catch_warnings(
                          record=True
                      ):  # singular GD
                          warnings.simplefilter("ignore")
                          gd += group_delay(
                              (section[:3], section[3:]), omega
                          )[1]
              n = estimate_ringing_samples(h["sos"])
              delta = np.zeros(n)
              delta[0] = 1
              if compensate:
                  delta = np.pad(delta, [(n - 1, 0)], "constant")
                  func = sosfiltfilt
                  gd += (len(delta) - 1) // 2
              else:
                  func = sosfilt
              h = func(h["sos"], delta)
          else:
              H = freqz(h["b"], h["a"], omega)[1]
              if compensate:
                  H *= H.conj()
              with warnings.catch_warnings(record=True):  # singular GD
                  warnings.simplefilter("ignore")
                  gd = group_delay((h["b"], h["a"]), omega)[1]
                  if compensate:
                      gd += group_delay(
                          (h["b"].conj(), h["a"].conj()), omega
                      )[1]
              n = estimate_ringing_samples((h["b"], h["a"]))
              delta = np.zeros(n)
              delta[0] = 1
              if compensate:
                  delta = np.pad(delta, [(n - 1, 0)], "constant")
                  func = filtfilt
              else:
                  func = lfilter
              h = func(h["b"], h["a"], delta)
          if title is None:
              title = "SOS (IIR) filter"
          if compensate:
              title += " (forward-backward)"
      else:
          H = freqz(h, worN=omega)[1]
          with warnings.catch_warnings(record=True):  # singular GD
              warnings.simplefilter("ignore")
              gd = group_delay((h, [1.0]), omega)[1]
          title = "FIR filter" if title is None else title
          if compensate:
              title += " (delay-compensated)"
  
      fig = None
      if axes is None:
          fig, axes = plt.subplots(len(plot), 1, layout="constrained")
      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 len(axes) != len(plot):
          raise ValueError(
              "Length of axes (%d) must be the same as number of "
              "requested filter properties (%d)"
              % (len(axes), len(plot))
          )
  
      t = np.arange(len(h))
      if dlim is None:
          dlim = np.abs(t).max() / 2.0
          dlim = [-dlim, dlim]
      if compensate:
          n_shift = (len(h) - 1) // 2
          t -= n_shift
          assert t[0] == -t[-1]
          gd -= n_shift
      t = t / sfreq
      gd = gd / sfreq
      f = omega * sfreq / (2 * np.pi)
      sl = slice(0 if fscale == "linear" else 1, None, None)
      mag = 10 * np.log10(np.maximum((H * H.conj()).real, 1e-20))
  
      if "time" in plot:
          ax_time_idx = np.where([p == "time" for p in plot])[0][0]
          axes[ax_time_idx].plot(t, h, color=color, linewidth=1.2)
          axes[ax_time_idx].grid(
              visible=True, which="major", axis="both", linewidth=0.15
          )
          axes[ax_time_idx].set(
              xlim=t[[0, -1]],
              xlabel="Time (s)",
              ylabel="Amplitude",
              title=title,
          )
      # Magnitude
      if "magnitude" in plot:
          ax_mag_idx = np.where([p == "magnitude" for p in plot])[0][0]
          axes[ax_mag_idx].plot(
              f[sl], mag[sl], color=color, linewidth=1.2, zorder=4
          )
          axes[ax_mag_idx].grid(
              visible=True, which="major", axis="both", linewidth=0.15
          )
          if freq is not None and gain is not None:
              plot_ideal_filter(
                  freq,
                  gain,
                  axes[ax_mag_idx],
                  fscale=fscale,
                  show=False,
              )
          axes[ax_mag_idx].set(
              ylabel="Magnitude (dB)", xlabel="", xscale=fscale
          )
          if xticks is not None:
              axes[ax_mag_idx].set(xticks=xticks)
              axes[ax_mag_idx].set(xticklabels=xticklabels)
          axes[ax_mag_idx].set(
              xlim=flim,
              ylim=alim,
              xlabel="Frequency (Hz)",
              ylabel="Amplitude (dB)",
          )
      # Delay
      if "delay" in plot:
          ax_delay_idx = np.where([p == "delay" for p in plot])[0][0]
          axes[ax_delay_idx].plot(
              f[sl], gd[sl], color=color, linewidth=1.2, zorder=4
          )
          axes[ax_delay_idx].grid(
              visible=True, which="major", axis="both", linewidth=0.15
          )
          # shade nulled regions
          for start, stop in zip(
              *_mask_to_onsets_offsets(mag <= -39.9)
          ):
              axes[ax_delay_idx].axvspan(
                  f[start],
                  f[stop - 1],
                  facecolor="k",
                  alpha=0.05,
                  zorder=5,
              )
          axes[ax_delay_idx].set(
              xlim=flim,
              ylabel="Group delay (s)",
              xlabel="Frequency (Hz)",
              xscale=fscale,
          )
          if xticks is not None:
              axes[ax_delay_idx].set(xticks=xticks)
              axes[ax_delay_idx].set(xticklabels=xticklabels)
          axes[ax_delay_idx].set(
              xlim=flim,
              ylim=dlim,
              xlabel="Frequency (Hz)",
              ylabel="Delay (s)",
          )
  
      adjust_axes(axes)
      plt_show(show)
      return fig
  
imports: import copy
  import io
  import os
  import os.path
  import warnings
  from collections import defaultdict
  from glob import glob
  from itertools import cycle
  from pathlib import Path
  import numpy
  from scipy.signal import filtfilt, freqz, group_delay, lfilter, sosfilt, sosfiltfilt
  from .._fiff.constants import FIFF
  from .._fiff.pick import _DATA_CH_TYPES_SPLIT, _picks_by_type, pick_channels, pick_info, pick_types
  from .._fiff.proj import make_projector
  from .._freesurfer import _check_mri, _mri_orientation, _read_mri_info, _reorient_image
  from ..defaults import DEFAULTS
  from ..filter import estimate_ringing_samples
  from ..fixes import _safe_svd
  from ..rank import compute_rank
  from ..surface import read_surface
  from ..transforms import _frame_to_str, apply_trans
  from ..utils import _check_option, _mask_to_onsets_offsets, _on_missing, _pl, fill_doc, get_subjects_dir, logger, verbose, warn
  from .utils import _figure_agg, _get_color_list, _prepare_trellis, _validate_type, plt_show
label_desc: Use image node_id 14 for calling a global function with example usage: plot_filter(h, sfreq, freq, gain, title, color, flim, fscale, alim, show, compensate, plot, axes) and returns: fig