60

Is there a way to set the color bar scale to log on a seaborn heat map graph?

I am using a pivot table output from pandas as an input to the call

sns.heatmap(df_pivot_mirror, annot=False, xticklabels=256, yticklabels=128, cmap=plt.cm.YlOrRd_r)
Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
fulatoro
  • 655
  • 1
  • 5
  • 8

4 Answers4

53

If you have a current install of seaborn, norm=LogNorm() in the call to heatmap works now. (Pointed out in the comments -- thank you.) Adding this to one of the seaborn examples:

import numpy as np
import seaborn as sns; sns.set_theme(style='white')
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm, Normalize
from matplotlib.ticker import MaxNLocator

flights = sns.load_dataset("flights")
flights = flights.pivot("month", "year", "passengers")


f3, ax5 = plt.subplots(1,1)
sns.heatmap(flights, square=True, norm=LogNorm())

Heatmap with lognorm colorbar, four tick labels

You can pass through colorbar arguments as keywords in the seaborn wrapper, but they sometimes collide with the seaborn choices:

sns.heatmap(flights, square=True, norm=LogNorm(), cbar_kws={'ticks':MaxNLocator(2), 'format':'%.e'})

Heatmap with lognorm colorbar, four tick labels with inconsistent numerical formatting

For comparison, this is the matplotlib heatmap without seaborn's improvements -- the colorbar arguments have both been applied:

f5, ax6 = plt.subplots(1,1)
im6 = plt.imshow(flights, norm=LogNorm())
cbar6 = ax.figure.colorbar(im6, ax=ax6, ticks=MaxNLocator(2), format='%.e')

Heatmap with lognorm colorbar, two tick labels, scientific number formatting

If you have to use an older install and LogNorm doesn't work in seaborn, see the previous versions of this answer for a workaround.

cphlewis
  • 15,759
  • 4
  • 46
  • 55
  • 3
    seaborn issue https://github.com/mwaskom/seaborn/issues/459 also makes it fixable (a different way) – cphlewis May 06 '16 at 08:31
  • 1
    The only thing is if some of your values are negative, then that's a problem. Hyperbolic inverse sin (arcsinh) works better for negative and 0 values. – wordsforthewise Jan 04 '18 at 04:57
  • 2
    There isn't an arcsinh built-in to matplotlib, but you can use SymLogNorm for negative values. Not sure if it handles 0's though. https://matplotlib.org/devdocs/tutorials/colors/colormapnorms.html#sphx-glr-tutorials-colors-colormapnorms-py – wordsforthewise Jan 04 '18 at 05:01
  • Is there a way to enable the logarithmic-color scale in a 2D histogram done by `sns.jointplot`? – Stefano Oct 05 '20 at 19:53
  • A log scale in [a distplot like this?](https://seaborn.pydata.org/generated/seaborn.jointplot.html) I think that deserves to be another question. – cphlewis Oct 09 '20 at 01:59
  • 3
    I just wanted to add that the issue with the hard-coded tick-locator seems to have been fixed in recent versions of seaborn. The only thing I had to do was to specify `norm=LogNorm()` and that resulted in logarithmic ticks as well. – Raven Dec 02 '20 at 09:40
  • You can use `LogNorm(clip=True)` to map 0s to the bottom of the colorscale. – thomaskeefe Oct 12 '22 at 18:21
  • 1
    in case anyone else ends up here looking for sns.histplot, you have to pass `vmin =None` and `vmax = None`, otherwise you get an error. – Jazz Weisman Feb 28 '23 at 00:16
27

Short Answer:

from matplotlib.colors import LogNorm

sns.heatmap(df, norm=LogNorm())
Tomas G.
  • 3,784
  • 25
  • 28
16

You can normalize the values on the colorbar with matplotlib.colors.LogNorm. I also had to manually set the labels in seaborn and ended up with the following code:

#!/usr/bin/env python3

import math

import numpy as np
import seaborn as sn
from matplotlib.colors import LogNorm

data = np.random.rand(20, 20)

log_norm = LogNorm(vmin=data.min().min(), vmax=data.max().max())
cbar_ticks = [math.pow(10, i) for i in range(math.floor(math.log10(data.min().min())), 1+math.ceil(math.log10(data.max().max())))]

sn.heatmap(
    data,
    norm=log_norm,
    cbar_kws={"ticks": cbar_ticks}
)

heatmap rand

user2084795
  • 704
  • 1
  • 7
  • 20
5

Responding to cphlewis (I don't have enough reputation), I solved this problem using cbar_kws; as I saw here: seaborn clustermap: set colorbar ticks.

For example cbar_kws={"ticks":[0,1,10,1e2,1e3,1e4,1e5]}.

from matplotlib.colors import LogNorm
s=np.random.rand(20,20)
sns.heatmap(s, norm=LogNorm(s.min(),s.max()),
            cbar_kws={"ticks":[0,1,10,1e2,1e3,1e4,1e5]},
            vmin = 0.001, vmax=10000)
plt.show()

Have a nice day.

Suzana
  • 4,251
  • 2
  • 28
  • 52
Jándrë
  • 89
  • 1
  • 2