Issue
Consider the following code
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
def make_Z(X,Y, offset=0.):
return np.sin(X) + np.cos(Y) + offset
class MidpointNormalize(colors.Normalize):
def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False):
self.midpoint = midpoint
colors.Normalize.__init__(self, vmin, vmax, clip)
def __call__(self, value, clip=None):
# I'm ignoring masked values and all kinds of edge cases to make a
# simple example...
x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1]
return np.ma.masked_array(np.interp(value, x, y), np.isnan(value))
################################################################################
x,y = np.linspace(-10, 10, 100), np.linspace(-10, 10, 100)
X, Y = np.meshgrid(x,y)
Z = make_Z(X,Y, offset=0.8)#; print(Z.shape)
tens = np.logspace(0., 3.0, num=4); print(tens)
Zs = [Z/i for i in tens]#; print(len(Zs))
min_Z = np.min(Zs)
max_Z = np.max(Zs)
norm = MidpointNormalize(vmin=min_Z, vmax=max_Z, midpoint=0.)
for Z in Zs:
print("min, max: %f, %f"%(np.min(Z), np.max(Z)))
fig, ax = plt.subplots()
Z_plot = ax.contourf(X,Y,Z, levels=100, norm=norm, cmap='seismic', vmin=min_Z, vmax=max_Z)
plt.colorbar(Z_plot, ax=ax)
plt.show()
print("-"*70)
This produces the following output:
Explanation: I make a Z value based on X and Y coordinates, then divide the Z value in each figure by powers of ten (100, 101, 102, 103), hence the plots becoming weaker and weaker further down.
Basically, everything is as expected. However, I would like the same colourbar to be applied to all figures. At the moment it looks like the same colormap is applied (which it should be), but I want the full "blue-on-bottom-red-above"- colorbar in all figures, even those that appear all white in colour. I am somewhat confident that I tried out all the arguments to contourf
and colorbar
, including the vmin
and vmax
arguments, but can not get the full range colorbar to be shown on all figures. Any hints?
EDIT:
I had the idea to maybe adjust the ylim
property of the colorbar, since it is after all just a regular axis. Defining the colorbar as cbar
and then doing cbar.ax.set_ylim(min_Z, max_Z)
results in gibberish, however.
Solution
Option 1
I don't know if this is acceptable for you, but you can replace contourf
with imshow
:
Z_plot = ax.imshow(Z, norm=norm, cmap='seismic', vmin=min_Z, vmax=max_Z, interpolation = 'bilinear')
Option 2
matplotlib.pyplot.colorbar
is linked to the mappable you pass it as the first argument. A trick can be to pass to colorbar
the non scaled contour
.
Try if this code fit for you:
for Z, ten in zip(Zs, tens):
print("min, max: %f, %f"%(np.min(Z), np.max(Z)))
fig, ax = plt.subplots()
plt.colorbar(ax.contourf(X,Y,Z*ten, levels=100, norm=norm, cmap='seismic', vmin=min_Z, vmax=max_Z), ax=ax)
Z_plot = ax.contourf(X, Y, Z, levels = 100, norm = norm, cmap = 'seismic', vmin = min_Z, vmax = max_Z)
plt.show()
print("-"*70)
Option 3
The same concept of above but this time I set up the mappable to pass to colorbar
in a more clean way.
for Z in Zs:
print("min, max: %f, %f"%(np.min(Z), np.max(Z)))
fig, ax = plt.subplots()
Z_plot = ax.contourf(X, Y, Z, levels = 100, norm = norm, cmap = 'seismic', vmin = min_Z, vmax = max_Z)
m = cm.ScalarMappable(cmap = cm.seismic, norm = colors.TwoSlopeNorm(vmin = np.min(Z), vcenter = 0, vmax = np.max(Z)))
m.set_array(Z)
m.set_clim(np.min(Z), np.max(Z))
cbar = plt.colorbar(m, boundaries = np.linspace(np.min(Z), np.max(Z), 100))
ticks = np.append(0, np.linspace(np.min(Z), np.max(Z), 9))
ticks.sort()
cbar.set_ticks(ticks)
plt.show()
print("-"*70)
Answered By - Zephyr
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.