def _apply_xy_lims(ax: Axes, h: Union[Histogram1D, Histogram2D], data: np.ndarray, kwargs: dict):
    """Apply axis limits and scales from kwargs.

    Note: if exponential binning is used, the scale defaults to "log"

    Parameters
    ----------
    data : np.ndarray
        The frequencies or densities or otherwise manipulated data
    kwargs: dict
        xscale : Optional[str]
            If "log", the horizontal axis will use logarithmic scale
        yscale : Optional[str]
            If "log", the vertical axis will use logarithmic scale
        xlim : { "keep", "auto" } or tuple(float)
            "auto" (default) - the axis will fit first and last bin edges
            "keep" - let matlotlib figure this out
            tuple - standard parameter for set_xlim
        ylim : { "keep", "auto" } or tuple(float)
            "auto" (default)
                - the axis will fit first and last bin edges (2D)
                - the axis will exceed a bit the maximum value (1D)
            "keep" - let matlotlib figure this out
            tuple - standard parameter for set_ylim
        invert_y : Optional[bool]
            If True, higher values go down

    See Also
    --------
    plt.Axes.set_xlim, plt.Axes.set_ylim, plt.Axes.set_xscale, plt.Axes.set_yscale
    """
    ylim = kwargs.pop("ylim", "auto")
    xlim = kwargs.pop("xlim", "auto")
    invert_y = kwargs.pop("invert_y", False)
    xscale = yscale = None

    from ..binnings import ExponentialBinning

    if ylim is not "keep":
        if isinstance(ylim, tuple):
            pass
        elif ylim:
            ylim = ax.get_ylim()
            if h.ndim == 1:
                xscale = kwargs.pop("xscale", "log" if isinstance(h.binning, ExponentialBinning) else None)
                yscale = kwargs.pop("yscale", None)
                if data.size > 0 and data.max() > 0:
                    ylim = (0, max(ylim[1], data.max() +
                                   (data.max() - ylim[0]) * 0.1))
                if yscale == "log":
                    ylim = (abs(data[data > 0].min()) * 0.9, ylim[1] * 1.1)
            elif h.ndim == 2:
                xscale = kwargs.pop("xscale", "log" if isinstance(h.binnings[0], ExponentialBinning) else None)
                yscale = kwargs.pop("yscale", "log" if isinstance(h.binnings[1], ExponentialBinning) else None)
                if h.shape[1] >= 2:
                    ylim = (h.get_bin_left_edges(1)[0],
                            h.get_bin_right_edges(1)[-1])
                    if yscale == "log":
                        if ylim[0] <= 0:
                            raise RuntimeError(
                                "Cannot use logarithmic scale for non-positive bins.")
            else:
                raise RuntimeError("Invalid dimension: {0}".format(h.ndim))

            if invert_y:
                ylim = ylim[::-1]
                # ax.xaxis.tick_top()
                # ax.xaxis.set_label_position('top')
        ax.set_ylim(ylim)

    if xlim is not "keep":
        if isinstance(xlim, tuple):
            pass
        elif xlim:
            xlim = ax.get_xlim()
            if h.shape[0] >= 1:
                if h.ndim == 1:
                    xlim = (h.bin_left_edges[0], h.bin_right_edges[-1])
                elif h.ndim == 2:
                    xlim = (h.get_bin_left_edges(0)[
                            0], h.get_bin_right_edges(0)[-1])
                else:
                    raise RuntimeError(
                        "Invalid dimension: {0}".format(h.ndim))
                if xscale == "log":
                    if xlim[0] <= 0:
                        raise RuntimeError(
                            "Cannot use logarithmic scale for non-positive bins.")
        ax.set_xlim(*xlim)

    if xscale:
        ax.set_xscale(xscale)
    if yscale:
        ax.set_yscale(yscale)