Patrick Allo
Patrick Allo

Reputation: 483

Invert LogNorm for correct display of tick_labels of colorbar for heatmap

I use (a version of) the following code to generate a heatmap with adjacent colorbar:

# imports
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from mpl_toolkits.axes_grid1 import make_axes_locatable

# create some dummy-data
matrix = np.array([[1, 2, 3],[2, 1, 3], [3, 1, 2]])
# scale the data
scaled = matrix / matrix.sum(axis=1).reshape(-1,1)

This is what scaled looks like (scaling does not make a difference in this example, but it does in the intended use-case where the scaled data are used for a hierarchical linkage):

array([[ 0.16666667,  0.33333333,  0.5       ],
   [ 0.33333333,  0.16666667,  0.5       ],
   [ 0.5       ,  0.16666667,  0.33333333]])

Now I create the plot (notice the use of LogNorm):

_, ax_heatmap = plt.subplots()
heatmap = ax_heatmap.pcolor(
    scaled, edgecolors='w',
    cmap=mpl.cm.viridis_r,
    norm=mpl.colors.LogNorm())
ax_heatmap.autoscale(tight=True)
ax_heatmap.set_aspect('equal')
divider_h = make_axes_locatable(ax_heatmap)
cax = divider_h.append_axes("right", "3%", pad="1%")
plt.colorbar(heatmap, cax=cax, ticks=np.unique(scaled))
cax.yaxis.set_major_formatter(
        mpl.ticker.FuncFormatter(
            lambda y, pos: ('{:.1f}'.format(y))))
plt.tight_layout()
plt.show()

enter image description here

The resulting figure is as intended, but the tick-labels on the color-bar do not correspond to the intended values, which should correspond to the values found in scaled. I know that the function supplied to FuncFormatter should be used to fix this, but it's unclear which combination of transformations it should invert (or if it's the use of LogNorm that is inappropriate).

Upvotes: 2

Views: 598

Answers (1)

Patrick Allo
Patrick Allo

Reputation: 483

Just found the solution. It appears that LogNorm has an inverse method. By first initialising the LogNorm object with the correct vmin and vmax, its inverse can be supplied to FuncFormatter

_, ax_heatmap = plt.subplots()
norm = mpl.colors.LogNorm(vmin=scaled.min(), vmax=scaled.max())
heatmap = ax_heatmap.pcolor(
    scaled, edgecolors='w',
    cmap=mpl.cm.viridis_r,
    norm=norm)
ax_heatmap.autoscale(tight=True)
ax_heatmap.set_aspect('equal')
divider_h = make_axes_locatable(ax_heatmap)
cax = divider_h.append_axes("right", "3%", pad="1%")
plt.colorbar(heatmap, cax=cax, ticks=np.unique(scaled))
cax.yaxis.set_major_formatter(
        mpl.ticker.FuncFormatter(
            lambda y, pos: ('{:.5f}'.format(norm.inverse(y)))))
plt.tight_layout()
plt.show()

enter image description here

Upvotes: 3

Related Questions