Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logarithmic colormap in matplotlib

Is it possible to define a matplitlib colormap which logarithmically interpolates between two given colors?

Background: When plotting arrays in matplotlib via the imshow(...) command, colormaps are used to assign a color to each data value. Often, predefined colormaps are used from matplotlib.cm but also more enhanced colormaps can be created using matplotlib.colors.LinearSegmentedColormap. Typically, in these colormaps the color value linearly changes with the data value.

Hint: My question is not, how to plot an array logarithmically. This is usually done, by keeping the linear colormap and some tricks (either by plotting log(array) and replacing the labels x on the colorbar by 10^x, or by explicitly changing the normalization behavior of the plot command). Here, I explicitly need the colormap instance.

like image 699
flonk Avatar asked Dec 20 '25 03:12

flonk


1 Answers

You can provide a fake Colormap that applies your desired normalisation, before passing it to the real colormap. Here is such a colormap implementation:

import matplotlib as mpl

class ReNormColormapAdaptor(mpl.colors.Colormap):
    """ Colormap adaptor that uses another Normalize instance
    for the colormap than applied to the mappable. """
    def __init__(self,base,cmap_norm,orig_norm=None):
        if orig_norm is None:
            if isinstance(base,mpl.cm.ScalarMappable):
                orig_norm = base.norm
                base = base.cmap
            else:
                orig_norm = mpl.colors.Normalize(0,1)
        self._base = base
        if (
            isinstance(cmap_norm,type(mpl.colors.Normalize))
            and issubclass(cmap_norm,mpl.colors.Normalize)
        ):
            # a class was provided instead of an instance. create an instance
            # with the same limits.
            cmap_norm = cmap_norm(orig_norm.vmin,orig_norm.vmax)
        self._cmap_norm = cmap_norm
        self._orig_norm = orig_norm

    def __call__(self, X, **kwargs):
        """ Re-normalise the values before applying the colormap. """
        return self._base(self._cmap_norm(self._orig_norm.inverse(X)),**kwargs)

    def __getattr__(self,attr):
        """ Any other attribute, we simply dispatch to the underlying cmap. """
        return getattr(self._base,attr)

Since the values it gets will already be normalised to [0,1), it needs to know that previous normalisation to undo it (given as orig_norm). Leave it empty if you want to apply the colormap to unnormalised values:

cmap = ReNormColormapAdaptor(mpl.cm.jet,mpl.colors.LogNorm(vmin,vmax))

If you already have a ScalarMappable, then you can pass it in stead of the colormap, from where both the colormap, the previous normalisation and the new normalisation limits will be taken:

import matplotlib.pyplot as plt

scalar_mappable = plt.imshow(C);
scalar_mappable.set_cmap(ReNormColormapAdaptor(
    scalar_mappable,
    mpl.colors.LogNorm
))
like image 197
burnpanck Avatar answered Dec 21 '25 17:12

burnpanck