2

I am trying to plot a colorbar that uses a limited number of colors (discrete). The problem is that the colors don't change where the major and minor ticks appear. The issue is worse with a log scale (which is what I need), but also happen with a linear scale!

You can see an example here: enter image description here

One can readily note that the colors change either before or after the tick marks. I opened the image with Gimp, annotated the horizontal position for the center pixel of each tick mark, and they are at the correct position. Hence I'm sure the problem lies with the drawing of the colored rectangles.

Here is a zoom for both colorbars: enter image description here

And this is the code to generate this image:

import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import numpy as np

# fix the seed to reproduce the output exactly
np.random.seed(19680801)

# 2d normal distribution
data = np.random.normal(loc=0, scale=1., size=(2, 1000))
hist = np.histogram2d(data[0,:], data[1,:], range=[[-4, 4],[-3, 3]])
x = hist[1]
y = hist[2]
h = hist[0]/np.max(hist[0])

fig, ax = plt.subplots(1,2)

# plot with linear scale
ax[1].set_title('linear scale')
im1 = ax[1].pcolormesh(x, y, h, cmap=plt.get_cmap('jet',5))
cb1 = fig.colorbar(im1, ax=ax[1], orientation='horizontal')
cb1.ax.xaxis.set_major_locator  (mticker.FixedLocator((0,1)))
cb1.ax.xaxis.set_minor_locator  (mticker.FixedLocator((.2,.4,.6,.8)))
cb1.ax.xaxis.set_minor_formatter(mticker.FormatStrFormatter('%.1f'))
cb1.ax.tick_params(axis='x',which='major',length=7)
cb1.ax.tick_params(axis='x',which='minor',length=4,colors='red')

# plot with log scale
ax[0].set_title('log scale')
im0 = ax[0].pcolormesh(x, y, h, cmap=plt.get_cmap('jet', 6), 
                       norm=matplotlib.colors.LogNorm(vmin=0.01,vmax=1))
                       
cb0 = fig.colorbar(im0, ax=ax[0], orientation='horizontal')

cb0.ax.xaxis.set_major_locator  (mticker.FixedLocator((.01,.1,1)))
cb0.ax.xaxis.set_minor_locator  (mticker.FixedLocator((.02,.05,.2,.5)))
cb0.ax.xaxis.set_major_formatter(mticker.FormatStrFormatter('%.2f'))
cb0.ax.xaxis.set_minor_formatter(mticker.FormatStrFormatter('%.2f'))
cb0.ax.tick_params(axis='x',which='major',length=7)
cb0.ax.tick_params(axis='x',which='minor',length=4,colors='red')

fig.savefig('teste.png',dpi=300)

Looking at some old threads here, I could notice that this problem was already there many years ago (see tick #5 in the first response here: Matplotlib discrete colorbar).

How can we do this?

hbarbosa
  • 101
  • 3
  • The ticks are snapped to the nearest pixels. If you save at 1200 dpi, the linear tick is indeed centred on the coloured rectangle (which does not get pixel snapping). For the log space, `np.logspace(-2, 0, 7)` yields `array([0.01, 0.02154435, 0.04641589, 0.1 , 0.21544347, 0.46415888, 1. ])`, so your fixed ticks will not line up with the square edges. – Jody Klymak Feb 24 '21 at 19:18

0 Answers0