4

I am plotting using the contourf function from matplotlib and would like to add a colorbar, I've noticed that sometimes the ticks don't go the max/min values.

Is there a clean way to force it to set ticks at these values?

Note: Checking the max and min of z shows that the colorbar represents values from approx -1 to 1, therefor I would expect this ot be reflected such that one can see the range from the colobar, in addition to some ticks in between.

Plot and code demonstrating what I am talking about:

Heatmap with colorbar

import matplotlib.pyplot as plt
import numpy as np

# Data to plot.
x, y = np.meshgrid(np.arange(7), np.arange(10))
z = np.sin(0.5 * x) * np.cos(0.52 * y)

fig, ax = plt.subplots()
cs = ax.contourf(x, y, z, levels=25)
ax.grid(c="k", ls="-", alpha=0.3)
fig.colorbar(cs, ax=ax)

fig.savefig("example.png", bbox_inches="tight")
Kins
  • 547
  • 1
  • 5
  • 22
Morten Nissov
  • 392
  • 3
  • 13

1 Answers1

2

The cleanest way seems to be to give explicit levels to contourf. If no explicit levels are given, contourf seems to choose its own, depending on the minimum and maximum value in the data, and also tries to find "nice looking" numbers. After that, ticks get set to a subset of these numbers, such that a tick always coincides with a real level. (If you use colorbar(..., ticks=...) those ticks will not necessarily coincide with the levels.)

As the sine and cosine don't reach -1 and 1 exact in the given example, they are not part of the range.

The following code shows how the ticks depend on the chosen levels. With np.linspace(-1, 1, 24) the levels aren't nice round numbers, but matplotlib still chooses a subset to show.

import matplotlib.pyplot as plt
import numpy as np

x, y = np.meshgrid(np.arange(7), np.arange(10))
z = np.sin(0.5 * x) * np.cos(0.52 * y)

fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(12, 3))
for ax in (ax1, ax2):
    numcontours = 25 if ax == ax1 else 24
    cs = ax.contourf(x, y, z, levels=np.linspace(-1, 1, numcontours))
    ax.grid(c="k", ls="-", alpha=0.3)
    fig.colorbar(cs, ax=ax)
    ax.set_title(f'{numcontours} levels from -1 to 1')
plt.show()

comparing contourf plots

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • When I said -1 and 1, the max was actually 0.997.... so I just rounded it to 1. – Morten Nissov Mar 11 '21 at 16:35
  • My remark was about trying to make sense of what matplotlib does behind the scenes. It was not meant as a remark upon your remark ... – JohanC Mar 11 '21 at 16:38
  • Ah okay I misunderstood, so the reason the colorbar doens't extend to the min/max values is because they're not "nice"? – Morten Nissov Mar 11 '21 at 16:41
  • It seems to have a hard time deciding what is "nice" and what the best way is to find close enough "nice" values. – JohanC Mar 11 '21 at 16:56