batsc
batsc

Reputation: 73

Matplotlib: misaligned colorbar ticks?

I'm trying to plot data in the range 0-69 with a bespoke colormap. Here is an example:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap

colors = [(0.9, 0.9, 0.9),    # Value = 0
          (0.3, 0.3, 0.3),    # Value = 9
          (1.0, 0.4, 0.4),    # Value = 10
          (0.4, 0.0, 0.0),    # Value = 19
          (0.0, 0.7, 1.0),    # Value = 20
          (0.0, 0.1, 0.3),    # Value = 29
          (1.0, 1.0, 0.4),    # Value = 30
          (0.4, 0.4, 0.0),    # Value = 39
          (1.0, 0.4, 1.0),    # Value = 40
          (0.4, 0.0, 0.4),    # Value = 49
          (0.4, 1.0, 0.4),    # Value = 50
          (0.0, 0.4, 0.0),    # Value = 59
          (1.0, 0.3, 0.0),    # Value = 60
          (1.0, 0.8, 0.6)]    # Value = 69

# Create the values specified above
max_val = 69
values = [n for n in range(max_val + 1) if n % 10 == 0 or n % 10 == 9]

# Create colormap, first normalise values
values = [v / float(max_val) for v in values]
values_and_colors = [(v, c) for v, c in zip(values, colors)]
cmap = LinearSegmentedColormap.from_list('my_cmap', values_and_colors,
                                         N=max_val + 1)

# Create sample data in range 0-69
data = np.round(np.random.random((20, 20)) * max_val)

ax = plt.imshow(data, cmap=cmap, interpolation='nearest')
cb = plt.colorbar(ticks=range(0, max_val, 10))
plt.show()

enter image description here

I'm thoroughly puzzled as to why the colorbar ticks do not line up with the distinct separations between the color gradients (for which there are 10 colors each).

I've tried setting the data and view intervals from [0, 69] to [0, 70]:

cb.locator.axis.set_view_interval(0, 70)
cb.locator.axis.set_data_interval(0, 70)
cb.update_ticks()

but this doesn't appear to do anything.

Please can someone advise?

Upvotes: 1

Views: 838

Answers (1)

batsc
batsc

Reputation: 73

The simplest way to solve my problem was to set vmax in the definition of the mappable:

ax = plt.imshow(data, cmap=cmap, interpolation='nearest', vmax=max_val + 1)

It was being set at max_val because the Colorbar class has the call mappable.autoscale_None() in its __init__, which was setting vmax to data.max(), i.e. 69.

I think I am just a victim of using the LinearSegmentedColormap in the wrong way. I want discrete values assigned to specific colors, but the display of a colorbar associated with LinearSegmentedColormap assumes continuous data and therefore defaults to setting unspecified limits to data.min() and data.max(), i.e. in this case 0 and 69.

Upvotes: 1

Related Questions