Reputation: 363
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?.
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
Reputation: 25093
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
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