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.

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