docs: def imshow(arr, x=None, ax=None, vmin=None, vmax=None, percentile=True,
             strip=False, features=None, conf=0.95, sort_by=None,
             line_kwargs=None, fill_kwargs=None, imshow_kwargs=None, figsize=(5, 12),
             width_ratios=(4, 1), height_ratios=(4, 1),
             subplot_params=dict(wspace=0.1, hspace=0.1),
             subset_by=None, subset_order=None,):
      """
      Do-it-all function to help with plotting heatmaps
  
      Parameters
      ----------
      arr : array-like
  
      x : 1D array
          X values to use.  If None, use range(arr.shape[1])
  
      ax : matplotlib.Axes
          If not None, then only plot the array on the provided axes.  This will
          ignore any additional arguments provided that apply to figure-level
          configuration or to the average line plot.  For example, `figsize`,
          `width_ratios`, `height_ratios`, `subplot_params`, `line_kwargs`, and
          `fill_kwargs` will all be ignored.
  
      vmin, vmax : float
  
      percentile : bool
          If True, then treat values for `vmin` and `vmax` as percentiles rather
          than absolute values.
  
      strip : bool
          Include a strip plot alongside the array
  
      features : pybedtools.BedTool or string filename
          Features used to construct the array
  
      conf : float
          Confidence interval to use in line plot.
  
      sort_by : array-like
          Use the provided array to sort the array (e.g., an array of expression
          values).  This array will be argsorted to get the proper order.
  
      line_kwargs, fill_kwargs : dict
          Passed directly to `ci_plot`.
  
      figsize : tuple
          (Width, height) of the figure to create.
  
      imshow_kwargs : dict
          Passed directly to matplotlib.pyplot.imshow.  By default, arguments
          used are `origin='lower'`, `aspect="auto"` and a colormap from
          colormap_adjust.smart_colormap generated using the provided `vmin` and
          `vmax`.
  
      width_ratios, height_ratios: tuple
          These tuples are passed to the `new_shell` function.  The default
          values set up a 2x2 configuration of panels for heatmap, line plot,
          colorbar axes, and optional strip plot.  However modifying
          `width_ratios` or `height_ratios` can be used to create more or fewer panels.
  
      subplot_params : dict
          Passed to Figure.subplots_adjust
  
      subset_by : array
          An array of any type (but usually int or str) that contains a class
          label for each row in the heatmap array.  For example, to subset by
          expression, an array the values of "up", "down", or "unchanged" at each
          of the positions could be provided.
  
          Note that the heatmap array is first sorted by `sort_by` and then split
          into groups according to `subset_by`, so each subset remains sorted by
          `sort_by`.
  
      subset_order : list-like
          This provides the order in which the subsets are plotted.  Since the
          default imshow arguments contain `origin="lower"`, these will be
          plotted in order starting at the bottom of the heatmap.
  
      """
      if ax is None:
          fig = new_shell(
              figsize=figsize,
              strip=strip,
              subplot_params=subplot_params,
              width_ratios=width_ratios,
              height_ratios=height_ratios)
  
      if x is None:
          x = np.arange(arr.shape[1] + 1)
  
      if percentile:
          if vmin is None:
              vmin = arr.min()
          else:
              vmin = mlab.prctile(arr.ravel(), vmin)
          if vmax is None:
              vmax = arr.max()
          else:
              vmax = mlab.prctile(arr.ravel(), vmax)
      else:
          if vmin is None:
              vmin = arr.min()
          if vmax is None:
              vmax = arr.max()
  
      cmap = colormap_adjust.smart_colormap(vmin, vmax)
      _imshow_kwargs = dict(origin='lower', cmap=cmap, vmin=vmin, vmax=vmax,
                            aspect='auto')
      if imshow_kwargs is not None:
          _imshow_kwargs.update(imshow_kwargs)
  
      # previously we did an argsort first; with subsetting we don't want to do
      # that yet....
      #if sort_by is not None:
      #    ind = np.argsort(sort_by)
      #else:
      #    ind = np.arange(arr.shape[0])
  
      if sort_by is None:
          sort_by = np.arange(arr.shape[0])
  
      if ax is None:
          array_ax = fig.array_axes
      else:
          array_ax = ax
  
      # If not provided, assume all in the same subset.
      if subset_by is None:
          subset_by = np.zeros(arr.shape[0])
  
      # Ensure always array, since we're doing indexing tricks
      if not isinstance(subset_by, np.ndarray):
          subset_by = np.array(subset_by)
  
      # If not provided, use sorted order
      if subset_order is None:
          subset_order = sorted(np.unique(subset_by))
  
      inds = []
      for cls in subset_order:
          subset_ind = np.nonzero(subset_by == cls)[0]
          subset_sort_by = sort_by[subset_ind]
          subset_argsort_by = np.argsort(subset_sort_by)
          inds.append(subset_ind[subset_argsort_by])
      ind = np.concatenate(inds)
  
      mappable = array_ax.imshow(
          arr[ind, :],
          extent=(x.min(), x.max(), 0, arr.shape[0]),
          **_imshow_kwargs
      )
  
      if line_kwargs is None:
          line_kwargs = {}
      if fill_kwargs is None:
          fill_kwargs = {}
  
      if isinstance(line_kwargs, dict):
          line_kwargs = [line_kwargs]
      if isinstance(fill_kwargs, dict):
          fill_kwargs = [fill_kwargs]
  
      _line_kwargs = itertools.cycle(line_kwargs)
      _fill_kwargs = itertools.cycle(fill_kwargs)
  
      if ax is None:
          plt.colorbar(mappable, fig.cax)
          for subset_ind, label, _lkw, _fkw in zip(inds, subset_order, _line_kwargs, _fill_kwargs):
              ci_plot(
                  x,
                  arr[subset_ind],
                  ax=fig.line_axes,
                  line_kwargs=_lkw,
                  fill_kwargs=_fkw,
              )
          return fig
      else:
          return ax.figure