Matthew D.
Matthew D.

Reputation: 363

Color bars in multi subplots doesn't follow the same scale

I am trying to compare four two dimensional histograms. And i need the color bar in every one of these histograms. But, it's obvious that the color scale in all histograms is not the same. There is a way to make this scale the same?.

enter image description here

The code i used is down here:

plt.set_cmap('hot')


fig = plt.figure()

fig.set_size_inches(10, 10)

# Adds subplot on position 1
ax = fig.add_subplot(221,aspect='equal')
# Adds subplot on position 2
ax2 = fig.add_subplot(222, aspect='equal')

ax3 = fig.add_subplot(223, aspect='equal')

ax4 = fig.add_subplot(224, aspect='equal')


h = ax.hist2d(data,datay,bins=(100,100), rasterized=True,range=np.array([(-7.9, 7.9), (-7.9, 7.9)]))


ax.set_title('step size = 2.0 ', size= 16, fontname='Comic Sans MS')


h2 = ax2.hist2d(data2,datay2,bins=(100,100), rasterized=True,range=np.array([(-7.9, 7.9), (-7.9, 7.9)]))
ax2.set_title('step size = 5.0 ', size= 16, fontname='Comic Sans MS')



h3 = ax3.hist2d(data3,datay3,bins=(100,100), rasterized=True,range=np.array([(-7.9, 7.9), (-7.9, 7.9)]))
ax3.set_title('step size = 6.0 ', size= 16, fontname='Comic Sans MS')
    

h4 = ax4.hist2d(data4,datay4,bins=(100,100), rasterized=True,range=np.array([(-7.9, 7.9), (-7.9, 7.9)]))
ax4.set_title('step size = 8.0 ', size= 16, fontname='Comic Sans MS')


"""
DEFINING COLOR BARS
"""
divider = make_axes_locatable(ax)
cax = divider.append_axes('right', size='5%', pad=0.05)
fig.colorbar(h[3], cax=cax)


divider = make_axes_locatable(ax2)
cax = divider.append_axes('right', size='5%', pad=0.05)
fig.colorbar(h2[3], cax=cax)

divider = make_axes_locatable(ax3)
cax = divider.append_axes('right', size='5%', pad=0.05)
fig.colorbar(h3[3], cax=cax)

divider = make_axes_locatable(ax4)
cax = divider.append_axes('right', size='5%', pad=0.05)
fig.colorbar(h4[3], cax=cax)

ax2.set_yticks([])
ax4.set_yticks([])

plt.subplots_adjust(wspace=0.3)


plt.savefig('24pog.pdf') 
plt.savefig('24pog.png') 
plt.show()  

Upvotes: 1

Views: 731

Answers (2)

gboffi
gboffi

Reputation: 25093

enter image description here

The problem with plt.hist2d is, to apply the same "color scaling" to your four data instances, you must know in advance the binned values of the four histograms but you don't know those values until you have plotted the histograms.

A possible solution is just to plot twice the histograms, the second time you will know the full range of binned data, but it seems wasteful.

Another approach, you plot the four histograms, read the binned data range and then you change the attributes of the color meshes that hist2d's have returned, but it seems complicated.

In my opinion, the simplest, most economical approach is to mimic hist2d on our own, here you can see its source code (nb: self is an Axes instance)

def hist2d(self, x, y, bins=10, range=None, density=False, weights=None,
           cmin=None, cmax=None, **kwargs):
    ...
    h, xedges, yedges = np.histogram2d(x, y, bins=bins, range=range,
                                       density=density, weights=weights)
    ...
    pc = self.pcolormesh(xedges, yedges, h.T, **kwargs)
    self.set_xlim(xedges[0], xedges[-1])
    self.set_ylim(yedges[0], yedges[-1])

    return h, xedges, yedges, pc

Well, nothing extraordinarily fancy… so the plan is to call histogram2d on every x, y instance, save the results in a list, inspect the binned data to find the proper normalization and finally, using the data we saved from histogram2d, call pcolormesh — note that you can also use the same axis limits for all the plots, if you want.

Here is the code that I've used

In [49]: import matplotlib.pyplot as plt
    ...: from matplotlib.cm import ScalarMappable
    ...: from numpy.random import rand
    ...: from numpy import histogram2d
    ...: 
    ...: # generate data - I have not your data…
    ...: data = ((rand(10000+500*i), rand(10000+500*i)) for i in range(4))
    ...: 
    ...: # generate histograms (for you it's for x, y in ((x1,x2),(x2,y2),...)
    ...: hist = [histogram2d(x,y) for x, y in data]
    ...: # find min and max for each histogram
    ...: min_, max_ = zip(*((h.min(), h.max()) for h, xedges, yedges in hist))
    ...: 
    ...: norm = plt.Normalize(min(min_), max(max_))
    ...: sm = ScalarMappable(norm, 'hot')
    ...: 
    ...: fig, sp = plt.subplots(2, 2, constrained_layout=1)
    ...: sp = sp.flatten()
    ...: for ax, (h, xedges, yedges) in zip(sp, hist):
    ...:     ax.pcolormesh(xedges, yedges, h.T, norm=norm, cmap='hot')
    ...: plt.colorbar(sm, ax=sp, aspect=30)

Upvotes: 1

cvanelteren
cvanelteren

Reputation: 1703

You have to normalize the color range (look at cmax, cmin in the docs. Alternatively, you can use a ScalarMappable, i.e.


import matplotlib.pyplot as plt, numpy as np

norm = plt.cm.colors.Normalize(vmin = 0, vmax = 2)
cmap = 'hot'
sc = plt.cm.ScalarMappable(norm = norm, cmap =  cmap)

fig, ax = plt.subplots(2, 2)
for idx, axi in enumerate(ax.flat):
    axi.hist2d(*np.random.rand(2, 10), cmap = cmap)
    fig.colorbar(sc, ax = axi)
fig.show()

Upvotes: 2

Related Questions