formatting the colorbar ticklabels with SymLogNorm normalization in matplotlib

时间:2016-08-31 18:24:06

标签: python matplotlib

TL;DR

How can you...

  • Force the LogFormatter to use scientific notation for every label? Now it uses it for values smaller than 0 or larger than 1000. It does not seem to expose any set_powerlimit method that I can find, either. Is there any way get it right or should you use a different formatter? which one?

  • Get the scientific notations with the exponents as superscripts like in the first plot attached, instead of things like -1e+02? The plt.xscale('symlog') call also gets it right for an x axis, so it doesn't look like a limitation of the scale itself...

Of course, if there were a simpler way to get nicely formatted xticks and labels on a colormap with symlog scaling, that'd be great too. But honestly, looking at the colorbars that the documentation itself exhibits, I don't have much hope... :-/


Starting from the beginning...

Matplotlib offers a few normalizations that can be used with colorbar. This is nicely explained in the documentation.

Among them, the logarithmic one (mpl.colors.LogNorm) works specially well, as it

  1. places the xticks evenly distributed.
  2. formats the ticklabels with a nice looking scientific notation**.

by itself. A minimal example:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors, ticker

data = np.arange(4).reshape(-1,1)+np.arange(4).reshape(1,-1)
data = 10**(data/2.)

plt.figure(figsize=(4,3))
plt.imshow(data, interpolation="None", cmap="gray", norm=colors.LogNorm())
plt.colorbar()
plt.show()

imshow with LogNorm normalization

On the other hand, the symmetric logarithmic one (matplotlib.colors.SymLogNorm) does neither. This SO answer defines a wrapper function for imshow that goes a long way towards the desired results, but it does not quite get there yet.

A minimal example with an adaptation of that function:

def imshow_symlog(arr, vmin=None, vmax=None, logthresh=5, logstep=1,
                  linscale=1, **kwargs):
    # Adapted from https://stackoverflow.com/a/23118662

    vmin = arr.min() if vmin is None else vmin
    vmax = arr.max() if vmax is None else vmax
    img=plt.imshow(arr,
                   vmin=float(vmin), vmax=float(vmax),
                   norm=colors.SymLogNorm(10**-logthresh, linscale=linscale),
                   **kwargs)

    maxlog=int(np.ceil(np.log10(vmax)))
    minlog=int(np.ceil(np.log10(-vmin)))

    #generate logarithmic ticks
    tick_locations=([-(10**x) for x in xrange(-logthresh, minlog+1, logstep)][::-1]
                    +[0.0]
                    +[(10**x) for x in xrange(-logthresh,maxlog+1, logstep)] )

    cb=plt.colorbar(ticks=tick_locations, format=ticker.LogFormatter())
    return img,cb

data2 = data - data[::-1,::-1]
plt.figure(figsize=(4,3))  
img, cb = imshow_symlog(data2, interpolation="None", cmap="gray", logthresh=0)
plt.show()

imshow with SymLogNorm normalization

1 个答案:

答案 0 :(得分:1)

Change the formatter in the function to LogFormatterMathtext:

cb=plt.colorbar(ticks=tick_locations, format=ticker.LogFormatterMathtext())

The formatters obviously lack nice (if any) documentation, but this one seems to do what you want:

enter image description here