Matplotlib how to plot 1 colorbar for four 2d histogramMatplotlib 2 Subplots, 1 ColorbarHow do you change the size of figures drawn with matplotlib?Plot logarithmic axes with matplotlib in pythonHiding axis text in matplotlib plotsHow to change the font size on a matplotlib plotHow to put the legend out of the plotPlot two histograms at the same time with matplotlibWhen to use cla(), clf() or close() for clearing a plot in matplotlib?Save plot to image file instead of displaying it using MatplotlibMatplotlib 2 Subplots, 1 ColorbarHow to make IPython notebook matplotlib plot inline

Can I voluntarily exit from the US after a 20 year overstay, or could I be detained at the airport?

What is the size of Neverwinter?

Is there a difference between historical fiction and creative non-fiction?

How much money should I save in order to generate $1000/month for the rest of my life?

How to catch creatures that can predict the next few minutes?

Why do English transliterations of Arabic names have so many Qs in them?

Is there any problem with students seeing faculty naked in university gym?

how do you value what your leisure time is worth?

How do I know how many sub-shells deep I am?

Flow chart and a polyline

If LPG gas burners can reach temperatures above 1700 °C, then how do HCA and PAH not develop in extreme amounts during cooking?

What was Richie's big secret?

Which accidental continues through the bar?

What is the next number in the sequence 21, 21, 23, 20, 5, 25, 31, 24, ...?

Should I be able to see patterns in a HS256 encoded JWT?

Selection Sort Algorithm (Node.js)

Can a Dragon enter the feywild at will?

Can someone identify this old round connector?

What is the difference between 山道【さんどう】 and 山道【やまみち】?

I am confused with the word order when putting a sentence into passé composé with reflexive verbs

Why is there "Il" in "Il mio tesoro intanto"?

What do you call the fallacy of thinking that A causes B, when really A and multiple other factors jointly cause B?

Would it be easier to colonise a living world or a dead world?

Transiting through Switzerland by coach with lots of cash



Matplotlib how to plot 1 colorbar for four 2d histogram


Matplotlib 2 Subplots, 1 ColorbarHow do you change the size of figures drawn with matplotlib?Plot logarithmic axes with matplotlib in pythonHiding axis text in matplotlib plotsHow to change the font size on a matplotlib plotHow to put the legend out of the plotPlot two histograms at the same time with matplotlibWhen to use cla(), clf() or close() for clearing a plot in matplotlib?Save plot to image file instead of displaying it using MatplotlibMatplotlib 2 Subplots, 1 ColorbarHow to make IPython notebook matplotlib plot inline






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;









3















Before I start I want to say that I've tried follow this and this post on the same problem however they are doing it with imshow heatmaps unlike 2d histogram like I'm doing.



Here is my code(the actual data has been replaced by randomly generated data but the gist is the same):



import matplotlib.pyplot as plt
import numpy as np

def subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles):
fig, a = plt.subplots(2, 2)

a = a.ravel()
for idx, ax in enumerate(a):
image = ax.hist2d(x_data[idx], y_data[idx], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[idx], fontsize=12)
ax.set_xlabel(x_labels[idx])
ax.set_ylabel(y_labels[idx])
ax.set_aspect("equal")
cb = fig.colorbar(image[idx])
cb.set_label("Intensity", rotation=270)

# pad = how big overall pic is
# w_pad = how separate they're left to right
# h_pad = how separate they're top to bottom
plt.tight_layout(pad=-1, w_pad=-10, h_pad=0.5)

x1, y1 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x2, y2 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x3, y3 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x4, y4 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x_data = [x1, x2, x3, x4]
y_data = [y1, y2, y3, y4]
x_labels = ["x1", "x2", "x3", "x4"]
y_labels = ["y1", "y2", "y3", "y4"]
titles = ["1", "2", "3", "4"]
subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles)


And this is what it's generating:



So now my problem is that I could not for the life of me make the colorbar apply for all 4 of the histograms. Also for some reason the bottom right histogram seems to behave weirdly compared with the others. In the links that I've posted their methods don't seem to use a = a.ravel() and I'm only using it here because it's the only way that allows me to plot my 4 histograms as subplots. Help?
enter image description here



EDIT:
Thomas Kuhn your new method actually solved all of my problem until I put my labels down and tried to use plt.tight_layout() to sort out the overlaps. It seems that if I put down the specific parameters in plt.tight_layout(pad=i, w_pad=0, h_pad=0) then the colorbar starts to misbehave. I'll now explain my problem.



I have made some changes to your new method so that it suits what I want, like this



def test_hist_2d(x_data, y_data, x_labels, y_labels, titles):
nrows, ncols = 2, 2
fig, axes = plt.subplots(nrows, ncols, sharex=True, sharey=True)
##produce the actual data and compute the histograms
mappables=[]
for (i, j), ax in np.ndenumerate(axes):
H, xedges, yedges = np.histogram2d(x_data[i][j], y_data[i][j], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[i][j], fontsize=12)
ax.set_xlabel(x_labels[i][j])
ax.set_ylabel(y_labels[i][j])
ax.set_aspect("equal")
mappables.append(H)

##the min and max values of all histograms
vmin = np.min(mappables)
vmax = np.max(mappables)

##second loop for visualisation
for ax, H in zip(axes.ravel(), mappables):
im = ax.imshow(H,vmin=vmin, vmax=vmax, extent=[-2,2,-2,2])

##colorbar using solution from linked question
fig.colorbar(im,ax=axes.ravel())
plt.show()
# plt.tight_layout
# plt.tight_layout(pad=i, w_pad=0, h_pad=0)


Now if I try to generate my data, in this case:



phi, cos_theta = get_angles(runs)

detector_x1, detector_y1, smeared_x1, smeared_y1 = detection_vectorised(1.5, cos_theta, phi)
detector_x2, detector_y2, smeared_x2, smeared_y2 = detection_vectorised(1, cos_theta, phi)
detector_x3, detector_y3, smeared_x3, smeared_y3 = detection_vectorised(0.5, cos_theta, phi)
detector_x4, detector_y4, smeared_x4, smeared_y4 = detection_vectorised(0, cos_theta, phi)


Here detector_x, detector_y, smeared_x, smeared_y are all lists of data point
So now I put them into 2x2 lists so that they can be unpacked suitably by my plotting function, as such:



data_x = [[detector_x1, detector_x2], [detector_x3, detector_x4]]
data_y = [[detector_y1, detector_y2], [detector_y3, detector_y4]]
x_labels = [["x positions(m)", "x positions(m)"], ["x positions(m)", "x positions(m)"]]
y_labels = [["y positions(m)", "y positions(m)"], ["y positions(m)", "y positions(m)"]]
titles = [["0.5m from detector", "1.0m from detector"], ["1.5m from detector", "2.0m from detector"]]


I now run my code with



test_hist_2d(data_x, data_y, x_labels, y_labels, titles)


with just plt.show() turned on, it gives this:



enter image description here



which is great because data and visual wise, it is exactly what I want i.e. the colormap corresponds to all 4 histograms. However, since the labels are overlapping with the titles, I thought I would just run the same thing but this time with plt.tight_layout(pad=a, w_pad=b, h_pad=c) hoping that I would be able to adjust the overlapping labels problem. However this time it doesn't matter how I change the numbers a, b and c, I always get my colorbar lying on the second column of graphs, like this:



enter image description here



Now changing a only makes the overall subplots bigger or smaller, and the best I could do was to adjust it with plt.tight_layout(pad=-10, w_pad=-15, h_pad=0), which looks like this



enter image description here



So it seems that whatever your new method is doing, it made the whole plot lost its adjustability. Your solution, as wonderful as it is at solving one problem, in return, created another. So what would be the best thing to do here?



Edit 2:



Using fig, axes = plt.subplots(nrows, ncols, sharex=True, sharey=True, constrained_layout=True) along with plt.show() gives



enter image description here



As you can see there's still a vertical gap between the columns of subplots for which not even using plt.subplots_adjust() can get rid of.










share|improve this question


























  • If you have a recent enough matplotlib colorbar(image[idx], ax=a) should work.

    – Jody Klymak
    Mar 29 at 1:34











  • @ThomasKühn I'm not sure if you've read my question. I've specifically said that the from the post in link you've just posted, I've tried their methods and they don't work for me.

    – user3613025
    Mar 29 at 9:43











  • I'm sorry, I apparently didn't ready it thoroughly enough.

    – Thomas Kühn
    Mar 29 at 9:57











  • Your new problem seems to be unrelated to histrograms; as usual minimal reproducible examples would help. The problem might be fixed in newer versions of matplotlib, hence reporting your matplotlib version is crucial. Also, you can try constrained_layout; which is an alternative to tight_layout and works a bit better in certain cases.

    – ImportanceOfBeingErnest
    Mar 30 at 3:31






  • 1





    Ok thank you so much!

    – user3613025
    Apr 1 at 10:58

















3















Before I start I want to say that I've tried follow this and this post on the same problem however they are doing it with imshow heatmaps unlike 2d histogram like I'm doing.



Here is my code(the actual data has been replaced by randomly generated data but the gist is the same):



import matplotlib.pyplot as plt
import numpy as np

def subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles):
fig, a = plt.subplots(2, 2)

a = a.ravel()
for idx, ax in enumerate(a):
image = ax.hist2d(x_data[idx], y_data[idx], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[idx], fontsize=12)
ax.set_xlabel(x_labels[idx])
ax.set_ylabel(y_labels[idx])
ax.set_aspect("equal")
cb = fig.colorbar(image[idx])
cb.set_label("Intensity", rotation=270)

# pad = how big overall pic is
# w_pad = how separate they're left to right
# h_pad = how separate they're top to bottom
plt.tight_layout(pad=-1, w_pad=-10, h_pad=0.5)

x1, y1 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x2, y2 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x3, y3 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x4, y4 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x_data = [x1, x2, x3, x4]
y_data = [y1, y2, y3, y4]
x_labels = ["x1", "x2", "x3", "x4"]
y_labels = ["y1", "y2", "y3", "y4"]
titles = ["1", "2", "3", "4"]
subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles)


And this is what it's generating:



So now my problem is that I could not for the life of me make the colorbar apply for all 4 of the histograms. Also for some reason the bottom right histogram seems to behave weirdly compared with the others. In the links that I've posted their methods don't seem to use a = a.ravel() and I'm only using it here because it's the only way that allows me to plot my 4 histograms as subplots. Help?
enter image description here



EDIT:
Thomas Kuhn your new method actually solved all of my problem until I put my labels down and tried to use plt.tight_layout() to sort out the overlaps. It seems that if I put down the specific parameters in plt.tight_layout(pad=i, w_pad=0, h_pad=0) then the colorbar starts to misbehave. I'll now explain my problem.



I have made some changes to your new method so that it suits what I want, like this



def test_hist_2d(x_data, y_data, x_labels, y_labels, titles):
nrows, ncols = 2, 2
fig, axes = plt.subplots(nrows, ncols, sharex=True, sharey=True)
##produce the actual data and compute the histograms
mappables=[]
for (i, j), ax in np.ndenumerate(axes):
H, xedges, yedges = np.histogram2d(x_data[i][j], y_data[i][j], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[i][j], fontsize=12)
ax.set_xlabel(x_labels[i][j])
ax.set_ylabel(y_labels[i][j])
ax.set_aspect("equal")
mappables.append(H)

##the min and max values of all histograms
vmin = np.min(mappables)
vmax = np.max(mappables)

##second loop for visualisation
for ax, H in zip(axes.ravel(), mappables):
im = ax.imshow(H,vmin=vmin, vmax=vmax, extent=[-2,2,-2,2])

##colorbar using solution from linked question
fig.colorbar(im,ax=axes.ravel())
plt.show()
# plt.tight_layout
# plt.tight_layout(pad=i, w_pad=0, h_pad=0)


Now if I try to generate my data, in this case:



phi, cos_theta = get_angles(runs)

detector_x1, detector_y1, smeared_x1, smeared_y1 = detection_vectorised(1.5, cos_theta, phi)
detector_x2, detector_y2, smeared_x2, smeared_y2 = detection_vectorised(1, cos_theta, phi)
detector_x3, detector_y3, smeared_x3, smeared_y3 = detection_vectorised(0.5, cos_theta, phi)
detector_x4, detector_y4, smeared_x4, smeared_y4 = detection_vectorised(0, cos_theta, phi)


Here detector_x, detector_y, smeared_x, smeared_y are all lists of data point
So now I put them into 2x2 lists so that they can be unpacked suitably by my plotting function, as such:



data_x = [[detector_x1, detector_x2], [detector_x3, detector_x4]]
data_y = [[detector_y1, detector_y2], [detector_y3, detector_y4]]
x_labels = [["x positions(m)", "x positions(m)"], ["x positions(m)", "x positions(m)"]]
y_labels = [["y positions(m)", "y positions(m)"], ["y positions(m)", "y positions(m)"]]
titles = [["0.5m from detector", "1.0m from detector"], ["1.5m from detector", "2.0m from detector"]]


I now run my code with



test_hist_2d(data_x, data_y, x_labels, y_labels, titles)


with just plt.show() turned on, it gives this:



enter image description here



which is great because data and visual wise, it is exactly what I want i.e. the colormap corresponds to all 4 histograms. However, since the labels are overlapping with the titles, I thought I would just run the same thing but this time with plt.tight_layout(pad=a, w_pad=b, h_pad=c) hoping that I would be able to adjust the overlapping labels problem. However this time it doesn't matter how I change the numbers a, b and c, I always get my colorbar lying on the second column of graphs, like this:



enter image description here



Now changing a only makes the overall subplots bigger or smaller, and the best I could do was to adjust it with plt.tight_layout(pad=-10, w_pad=-15, h_pad=0), which looks like this



enter image description here



So it seems that whatever your new method is doing, it made the whole plot lost its adjustability. Your solution, as wonderful as it is at solving one problem, in return, created another. So what would be the best thing to do here?



Edit 2:



Using fig, axes = plt.subplots(nrows, ncols, sharex=True, sharey=True, constrained_layout=True) along with plt.show() gives



enter image description here



As you can see there's still a vertical gap between the columns of subplots for which not even using plt.subplots_adjust() can get rid of.










share|improve this question


























  • If you have a recent enough matplotlib colorbar(image[idx], ax=a) should work.

    – Jody Klymak
    Mar 29 at 1:34











  • @ThomasKühn I'm not sure if you've read my question. I've specifically said that the from the post in link you've just posted, I've tried their methods and they don't work for me.

    – user3613025
    Mar 29 at 9:43











  • I'm sorry, I apparently didn't ready it thoroughly enough.

    – Thomas Kühn
    Mar 29 at 9:57











  • Your new problem seems to be unrelated to histrograms; as usual minimal reproducible examples would help. The problem might be fixed in newer versions of matplotlib, hence reporting your matplotlib version is crucial. Also, you can try constrained_layout; which is an alternative to tight_layout and works a bit better in certain cases.

    – ImportanceOfBeingErnest
    Mar 30 at 3:31






  • 1





    Ok thank you so much!

    – user3613025
    Apr 1 at 10:58













3












3








3








Before I start I want to say that I've tried follow this and this post on the same problem however they are doing it with imshow heatmaps unlike 2d histogram like I'm doing.



Here is my code(the actual data has been replaced by randomly generated data but the gist is the same):



import matplotlib.pyplot as plt
import numpy as np

def subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles):
fig, a = plt.subplots(2, 2)

a = a.ravel()
for idx, ax in enumerate(a):
image = ax.hist2d(x_data[idx], y_data[idx], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[idx], fontsize=12)
ax.set_xlabel(x_labels[idx])
ax.set_ylabel(y_labels[idx])
ax.set_aspect("equal")
cb = fig.colorbar(image[idx])
cb.set_label("Intensity", rotation=270)

# pad = how big overall pic is
# w_pad = how separate they're left to right
# h_pad = how separate they're top to bottom
plt.tight_layout(pad=-1, w_pad=-10, h_pad=0.5)

x1, y1 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x2, y2 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x3, y3 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x4, y4 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x_data = [x1, x2, x3, x4]
y_data = [y1, y2, y3, y4]
x_labels = ["x1", "x2", "x3", "x4"]
y_labels = ["y1", "y2", "y3", "y4"]
titles = ["1", "2", "3", "4"]
subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles)


And this is what it's generating:



So now my problem is that I could not for the life of me make the colorbar apply for all 4 of the histograms. Also for some reason the bottom right histogram seems to behave weirdly compared with the others. In the links that I've posted their methods don't seem to use a = a.ravel() and I'm only using it here because it's the only way that allows me to plot my 4 histograms as subplots. Help?
enter image description here



EDIT:
Thomas Kuhn your new method actually solved all of my problem until I put my labels down and tried to use plt.tight_layout() to sort out the overlaps. It seems that if I put down the specific parameters in plt.tight_layout(pad=i, w_pad=0, h_pad=0) then the colorbar starts to misbehave. I'll now explain my problem.



I have made some changes to your new method so that it suits what I want, like this



def test_hist_2d(x_data, y_data, x_labels, y_labels, titles):
nrows, ncols = 2, 2
fig, axes = plt.subplots(nrows, ncols, sharex=True, sharey=True)
##produce the actual data and compute the histograms
mappables=[]
for (i, j), ax in np.ndenumerate(axes):
H, xedges, yedges = np.histogram2d(x_data[i][j], y_data[i][j], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[i][j], fontsize=12)
ax.set_xlabel(x_labels[i][j])
ax.set_ylabel(y_labels[i][j])
ax.set_aspect("equal")
mappables.append(H)

##the min and max values of all histograms
vmin = np.min(mappables)
vmax = np.max(mappables)

##second loop for visualisation
for ax, H in zip(axes.ravel(), mappables):
im = ax.imshow(H,vmin=vmin, vmax=vmax, extent=[-2,2,-2,2])

##colorbar using solution from linked question
fig.colorbar(im,ax=axes.ravel())
plt.show()
# plt.tight_layout
# plt.tight_layout(pad=i, w_pad=0, h_pad=0)


Now if I try to generate my data, in this case:



phi, cos_theta = get_angles(runs)

detector_x1, detector_y1, smeared_x1, smeared_y1 = detection_vectorised(1.5, cos_theta, phi)
detector_x2, detector_y2, smeared_x2, smeared_y2 = detection_vectorised(1, cos_theta, phi)
detector_x3, detector_y3, smeared_x3, smeared_y3 = detection_vectorised(0.5, cos_theta, phi)
detector_x4, detector_y4, smeared_x4, smeared_y4 = detection_vectorised(0, cos_theta, phi)


Here detector_x, detector_y, smeared_x, smeared_y are all lists of data point
So now I put them into 2x2 lists so that they can be unpacked suitably by my plotting function, as such:



data_x = [[detector_x1, detector_x2], [detector_x3, detector_x4]]
data_y = [[detector_y1, detector_y2], [detector_y3, detector_y4]]
x_labels = [["x positions(m)", "x positions(m)"], ["x positions(m)", "x positions(m)"]]
y_labels = [["y positions(m)", "y positions(m)"], ["y positions(m)", "y positions(m)"]]
titles = [["0.5m from detector", "1.0m from detector"], ["1.5m from detector", "2.0m from detector"]]


I now run my code with



test_hist_2d(data_x, data_y, x_labels, y_labels, titles)


with just plt.show() turned on, it gives this:



enter image description here



which is great because data and visual wise, it is exactly what I want i.e. the colormap corresponds to all 4 histograms. However, since the labels are overlapping with the titles, I thought I would just run the same thing but this time with plt.tight_layout(pad=a, w_pad=b, h_pad=c) hoping that I would be able to adjust the overlapping labels problem. However this time it doesn't matter how I change the numbers a, b and c, I always get my colorbar lying on the second column of graphs, like this:



enter image description here



Now changing a only makes the overall subplots bigger or smaller, and the best I could do was to adjust it with plt.tight_layout(pad=-10, w_pad=-15, h_pad=0), which looks like this



enter image description here



So it seems that whatever your new method is doing, it made the whole plot lost its adjustability. Your solution, as wonderful as it is at solving one problem, in return, created another. So what would be the best thing to do here?



Edit 2:



Using fig, axes = plt.subplots(nrows, ncols, sharex=True, sharey=True, constrained_layout=True) along with plt.show() gives



enter image description here



As you can see there's still a vertical gap between the columns of subplots for which not even using plt.subplots_adjust() can get rid of.










share|improve this question
















Before I start I want to say that I've tried follow this and this post on the same problem however they are doing it with imshow heatmaps unlike 2d histogram like I'm doing.



Here is my code(the actual data has been replaced by randomly generated data but the gist is the same):



import matplotlib.pyplot as plt
import numpy as np

def subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles):
fig, a = plt.subplots(2, 2)

a = a.ravel()
for idx, ax in enumerate(a):
image = ax.hist2d(x_data[idx], y_data[idx], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[idx], fontsize=12)
ax.set_xlabel(x_labels[idx])
ax.set_ylabel(y_labels[idx])
ax.set_aspect("equal")
cb = fig.colorbar(image[idx])
cb.set_label("Intensity", rotation=270)

# pad = how big overall pic is
# w_pad = how separate they're left to right
# h_pad = how separate they're top to bottom
plt.tight_layout(pad=-1, w_pad=-10, h_pad=0.5)

x1, y1 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x2, y2 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x3, y3 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x4, y4 = np.random.uniform(-2, 2, 10000), np.random.uniform(-2, 2, 10000)
x_data = [x1, x2, x3, x4]
y_data = [y1, y2, y3, y4]
x_labels = ["x1", "x2", "x3", "x4"]
y_labels = ["y1", "y2", "y3", "y4"]
titles = ["1", "2", "3", "4"]
subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles)


And this is what it's generating:



So now my problem is that I could not for the life of me make the colorbar apply for all 4 of the histograms. Also for some reason the bottom right histogram seems to behave weirdly compared with the others. In the links that I've posted their methods don't seem to use a = a.ravel() and I'm only using it here because it's the only way that allows me to plot my 4 histograms as subplots. Help?
enter image description here



EDIT:
Thomas Kuhn your new method actually solved all of my problem until I put my labels down and tried to use plt.tight_layout() to sort out the overlaps. It seems that if I put down the specific parameters in plt.tight_layout(pad=i, w_pad=0, h_pad=0) then the colorbar starts to misbehave. I'll now explain my problem.



I have made some changes to your new method so that it suits what I want, like this



def test_hist_2d(x_data, y_data, x_labels, y_labels, titles):
nrows, ncols = 2, 2
fig, axes = plt.subplots(nrows, ncols, sharex=True, sharey=True)
##produce the actual data and compute the histograms
mappables=[]
for (i, j), ax in np.ndenumerate(axes):
H, xedges, yedges = np.histogram2d(x_data[i][j], y_data[i][j], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[i][j], fontsize=12)
ax.set_xlabel(x_labels[i][j])
ax.set_ylabel(y_labels[i][j])
ax.set_aspect("equal")
mappables.append(H)

##the min and max values of all histograms
vmin = np.min(mappables)
vmax = np.max(mappables)

##second loop for visualisation
for ax, H in zip(axes.ravel(), mappables):
im = ax.imshow(H,vmin=vmin, vmax=vmax, extent=[-2,2,-2,2])

##colorbar using solution from linked question
fig.colorbar(im,ax=axes.ravel())
plt.show()
# plt.tight_layout
# plt.tight_layout(pad=i, w_pad=0, h_pad=0)


Now if I try to generate my data, in this case:



phi, cos_theta = get_angles(runs)

detector_x1, detector_y1, smeared_x1, smeared_y1 = detection_vectorised(1.5, cos_theta, phi)
detector_x2, detector_y2, smeared_x2, smeared_y2 = detection_vectorised(1, cos_theta, phi)
detector_x3, detector_y3, smeared_x3, smeared_y3 = detection_vectorised(0.5, cos_theta, phi)
detector_x4, detector_y4, smeared_x4, smeared_y4 = detection_vectorised(0, cos_theta, phi)


Here detector_x, detector_y, smeared_x, smeared_y are all lists of data point
So now I put them into 2x2 lists so that they can be unpacked suitably by my plotting function, as such:



data_x = [[detector_x1, detector_x2], [detector_x3, detector_x4]]
data_y = [[detector_y1, detector_y2], [detector_y3, detector_y4]]
x_labels = [["x positions(m)", "x positions(m)"], ["x positions(m)", "x positions(m)"]]
y_labels = [["y positions(m)", "y positions(m)"], ["y positions(m)", "y positions(m)"]]
titles = [["0.5m from detector", "1.0m from detector"], ["1.5m from detector", "2.0m from detector"]]


I now run my code with



test_hist_2d(data_x, data_y, x_labels, y_labels, titles)


with just plt.show() turned on, it gives this:



enter image description here



which is great because data and visual wise, it is exactly what I want i.e. the colormap corresponds to all 4 histograms. However, since the labels are overlapping with the titles, I thought I would just run the same thing but this time with plt.tight_layout(pad=a, w_pad=b, h_pad=c) hoping that I would be able to adjust the overlapping labels problem. However this time it doesn't matter how I change the numbers a, b and c, I always get my colorbar lying on the second column of graphs, like this:



enter image description here



Now changing a only makes the overall subplots bigger or smaller, and the best I could do was to adjust it with plt.tight_layout(pad=-10, w_pad=-15, h_pad=0), which looks like this



enter image description here



So it seems that whatever your new method is doing, it made the whole plot lost its adjustability. Your solution, as wonderful as it is at solving one problem, in return, created another. So what would be the best thing to do here?



Edit 2:



Using fig, axes = plt.subplots(nrows, ncols, sharex=True, sharey=True, constrained_layout=True) along with plt.show() gives



enter image description here



As you can see there's still a vertical gap between the columns of subplots for which not even using plt.subplots_adjust() can get rid of.







python-3.x matplotlib subplot colorbar






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Mar 30 at 16:20







user3613025

















asked Mar 28 at 21:06









user3613025user3613025

827 bronze badges




827 bronze badges















  • If you have a recent enough matplotlib colorbar(image[idx], ax=a) should work.

    – Jody Klymak
    Mar 29 at 1:34











  • @ThomasKühn I'm not sure if you've read my question. I've specifically said that the from the post in link you've just posted, I've tried their methods and they don't work for me.

    – user3613025
    Mar 29 at 9:43











  • I'm sorry, I apparently didn't ready it thoroughly enough.

    – Thomas Kühn
    Mar 29 at 9:57











  • Your new problem seems to be unrelated to histrograms; as usual minimal reproducible examples would help. The problem might be fixed in newer versions of matplotlib, hence reporting your matplotlib version is crucial. Also, you can try constrained_layout; which is an alternative to tight_layout and works a bit better in certain cases.

    – ImportanceOfBeingErnest
    Mar 30 at 3:31






  • 1





    Ok thank you so much!

    – user3613025
    Apr 1 at 10:58

















  • If you have a recent enough matplotlib colorbar(image[idx], ax=a) should work.

    – Jody Klymak
    Mar 29 at 1:34











  • @ThomasKühn I'm not sure if you've read my question. I've specifically said that the from the post in link you've just posted, I've tried their methods and they don't work for me.

    – user3613025
    Mar 29 at 9:43











  • I'm sorry, I apparently didn't ready it thoroughly enough.

    – Thomas Kühn
    Mar 29 at 9:57











  • Your new problem seems to be unrelated to histrograms; as usual minimal reproducible examples would help. The problem might be fixed in newer versions of matplotlib, hence reporting your matplotlib version is crucial. Also, you can try constrained_layout; which is an alternative to tight_layout and works a bit better in certain cases.

    – ImportanceOfBeingErnest
    Mar 30 at 3:31






  • 1





    Ok thank you so much!

    – user3613025
    Apr 1 at 10:58
















If you have a recent enough matplotlib colorbar(image[idx], ax=a) should work.

– Jody Klymak
Mar 29 at 1:34





If you have a recent enough matplotlib colorbar(image[idx], ax=a) should work.

– Jody Klymak
Mar 29 at 1:34













@ThomasKühn I'm not sure if you've read my question. I've specifically said that the from the post in link you've just posted, I've tried their methods and they don't work for me.

– user3613025
Mar 29 at 9:43





@ThomasKühn I'm not sure if you've read my question. I've specifically said that the from the post in link you've just posted, I've tried their methods and they don't work for me.

– user3613025
Mar 29 at 9:43













I'm sorry, I apparently didn't ready it thoroughly enough.

– Thomas Kühn
Mar 29 at 9:57





I'm sorry, I apparently didn't ready it thoroughly enough.

– Thomas Kühn
Mar 29 at 9:57













Your new problem seems to be unrelated to histrograms; as usual minimal reproducible examples would help. The problem might be fixed in newer versions of matplotlib, hence reporting your matplotlib version is crucial. Also, you can try constrained_layout; which is an alternative to tight_layout and works a bit better in certain cases.

– ImportanceOfBeingErnest
Mar 30 at 3:31





Your new problem seems to be unrelated to histrograms; as usual minimal reproducible examples would help. The problem might be fixed in newer versions of matplotlib, hence reporting your matplotlib version is crucial. Also, you can try constrained_layout; which is an alternative to tight_layout and works a bit better in certain cases.

– ImportanceOfBeingErnest
Mar 30 at 3:31




1




1





Ok thank you so much!

– user3613025
Apr 1 at 10:58





Ok thank you so much!

– user3613025
Apr 1 at 10:58












1 Answer
1






active

oldest

votes


















1
















Edit:



As has been noted in the comments, the biggest problem here is actually to make the colorbar for many histograms meaningful, as ax.hist2d will always scale the histogram data it receives from numpy. It may therefore be best to first calculated the 2d histogram data using numpy and then use again imshow to visualise it. This way, also the solutions of the linked question can be applied. To make the problem with the normalisation more visible, I put some effort into producing some qualitatively different 2d histograms using scipy.stats.multivariate_normal, which shows how the height of the histogram can change quite dramatically even though the number of samples is the same in each figure.



import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec as gs
from scipy.stats import multivariate_normal

##opening figure and axes
nrows=3
ncols=3
fig, axes = plt.subplots(nrows,ncols)

##generate some random data for the distributions
means = np.random.rand(nrows,ncols,2)
sigmas = np.random.rand(nrows,ncols,2)
thetas = np.random.rand(nrows,ncols)*np.pi*2

##produce the actual data and compute the histograms
mappables=[]
for mean,sigma,theta in zip( means.reshape(-1,2), sigmas.reshape(-1,2), thetas.reshape(-1)):

##the data (only cosmetics):
c, s = np.cos(theta), np.sin(theta)
rot = np.array(((c,-s), (s, c)))
cov = rot@np.diag(sigma)@rot.T
rv = multivariate_normal(mean,cov)
data = rv.rvs(size = 10000)

##the 2d histogram from numpy
H,xedges,yedges = np.histogram2d(data[:,0], data[:,1], bins=50, range=[[-2, 2],[-2, 2]])

mappables.append(H)

##the min and max values of all histograms
vmin = np.min(mappables)
vmax = np.max(mappables)

##second loop for visualisation
for ax,H in zip(axes.ravel(),mappables):
im = ax.imshow(H,vmin=vmin, vmax=vmax, extent=[-2,2,-2,2])

##colorbar using solution from linked question
fig.colorbar(im,ax=axes.ravel())

plt.show()


This code produces a figure like this:



result of above code



Old Answer:



One way to solve your problem is to generate the space for your colorbar explicitly. You can use a GridSpec instance to define how wide your colorbar should be. Below your subplots_hist_2d() function with a few modifications. Note that your use of tight_layout() shifted the colorbar into a funny place, hence the replacement. If you want the plots closer to each other, I'd rather recommend to play with the aspect ratio of the figure.



def subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles):

## fig, a = plt.subplots(2, 2)
fig = plt.figure()
g = gs.GridSpec(nrows=2, ncols=3, width_ratios=[1,1,0.05])
a = [fig.add_subplot(g[n,m]) for n in range(2) for m in range(2)]
cax = fig.add_subplot(g[:,2])


## a = a.ravel()
for idx, ax in enumerate(a):
image = ax.hist2d(x_data[idx], y_data[idx], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[idx], fontsize=12)
ax.set_xlabel(x_labels[idx])
ax.set_ylabel(y_labels[idx])
ax.set_aspect("equal")
## cb = fig.colorbar(image[-1],ax=a)
cb = fig.colorbar(image[-1], cax=cax)
cb.set_label("Intensity", rotation=270)

# pad = how big overall pic is
# w_pad = how separate they're left to right
# h_pad = how separate they're top to bottom
## plt.tight_layout(pad=-1, w_pad=-10, h_pad=0.5)
fig.tight_layout()


Using this modified function, I get the following output:



result of OP's code with the above function






share|improve this answer



























  • That answer might be more suitable for the Matplotlib 2 Subplots, 1 Colorbar. As I see it the main and only difference here is not the positionning of the bar, but the color normalization. Here, you take the colorbar of the last image and place it such that one might think it applies to every plot - which is not true.

    – ImportanceOfBeingErnest
    Mar 29 at 11:16











  • @ImportanceOfBeingErnest You're right about the normalisation of the colorbar, but isn't this a problem also in the solutions you linked? There it appears that always the last mappable of the respective for loop is used. However, it shouldn't be too hard to normalise differently. I'll take a look later.

    – Thomas Kühn
    Mar 29 at 11:19












  • In case of an imshow it's easy to normalize, in case of the hist2d you would need to know the outcome of the histogram before calculating it. So I suppose the solution is to first calculate all histograms, then find the minimum and maximum, then plot them all with those numbers as vmin and vmax.

    – ImportanceOfBeingErnest
    Mar 29 at 11:24






  • 1





    @ImportanceOfBeingErnest that would probably anyway be the best solution, to use numpy.histogram2d to compute all data and then use imshow again to do the actual plots. Then one could fall back to the answers in the linked question.

    – Thomas Kühn
    Mar 29 at 11:29






  • 1





    @ThomasKühn thank you you legend

    – user3613025
    Mar 29 at 13:49












Your Answer






StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);














draft saved

draft discarded
















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55406844%2fmatplotlib-how-to-plot-1-colorbar-for-four-2d-histogram%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown

























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes









1
















Edit:



As has been noted in the comments, the biggest problem here is actually to make the colorbar for many histograms meaningful, as ax.hist2d will always scale the histogram data it receives from numpy. It may therefore be best to first calculated the 2d histogram data using numpy and then use again imshow to visualise it. This way, also the solutions of the linked question can be applied. To make the problem with the normalisation more visible, I put some effort into producing some qualitatively different 2d histograms using scipy.stats.multivariate_normal, which shows how the height of the histogram can change quite dramatically even though the number of samples is the same in each figure.



import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec as gs
from scipy.stats import multivariate_normal

##opening figure and axes
nrows=3
ncols=3
fig, axes = plt.subplots(nrows,ncols)

##generate some random data for the distributions
means = np.random.rand(nrows,ncols,2)
sigmas = np.random.rand(nrows,ncols,2)
thetas = np.random.rand(nrows,ncols)*np.pi*2

##produce the actual data and compute the histograms
mappables=[]
for mean,sigma,theta in zip( means.reshape(-1,2), sigmas.reshape(-1,2), thetas.reshape(-1)):

##the data (only cosmetics):
c, s = np.cos(theta), np.sin(theta)
rot = np.array(((c,-s), (s, c)))
cov = rot@np.diag(sigma)@rot.T
rv = multivariate_normal(mean,cov)
data = rv.rvs(size = 10000)

##the 2d histogram from numpy
H,xedges,yedges = np.histogram2d(data[:,0], data[:,1], bins=50, range=[[-2, 2],[-2, 2]])

mappables.append(H)

##the min and max values of all histograms
vmin = np.min(mappables)
vmax = np.max(mappables)

##second loop for visualisation
for ax,H in zip(axes.ravel(),mappables):
im = ax.imshow(H,vmin=vmin, vmax=vmax, extent=[-2,2,-2,2])

##colorbar using solution from linked question
fig.colorbar(im,ax=axes.ravel())

plt.show()


This code produces a figure like this:



result of above code



Old Answer:



One way to solve your problem is to generate the space for your colorbar explicitly. You can use a GridSpec instance to define how wide your colorbar should be. Below your subplots_hist_2d() function with a few modifications. Note that your use of tight_layout() shifted the colorbar into a funny place, hence the replacement. If you want the plots closer to each other, I'd rather recommend to play with the aspect ratio of the figure.



def subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles):

## fig, a = plt.subplots(2, 2)
fig = plt.figure()
g = gs.GridSpec(nrows=2, ncols=3, width_ratios=[1,1,0.05])
a = [fig.add_subplot(g[n,m]) for n in range(2) for m in range(2)]
cax = fig.add_subplot(g[:,2])


## a = a.ravel()
for idx, ax in enumerate(a):
image = ax.hist2d(x_data[idx], y_data[idx], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[idx], fontsize=12)
ax.set_xlabel(x_labels[idx])
ax.set_ylabel(y_labels[idx])
ax.set_aspect("equal")
## cb = fig.colorbar(image[-1],ax=a)
cb = fig.colorbar(image[-1], cax=cax)
cb.set_label("Intensity", rotation=270)

# pad = how big overall pic is
# w_pad = how separate they're left to right
# h_pad = how separate they're top to bottom
## plt.tight_layout(pad=-1, w_pad=-10, h_pad=0.5)
fig.tight_layout()


Using this modified function, I get the following output:



result of OP's code with the above function






share|improve this answer



























  • That answer might be more suitable for the Matplotlib 2 Subplots, 1 Colorbar. As I see it the main and only difference here is not the positionning of the bar, but the color normalization. Here, you take the colorbar of the last image and place it such that one might think it applies to every plot - which is not true.

    – ImportanceOfBeingErnest
    Mar 29 at 11:16











  • @ImportanceOfBeingErnest You're right about the normalisation of the colorbar, but isn't this a problem also in the solutions you linked? There it appears that always the last mappable of the respective for loop is used. However, it shouldn't be too hard to normalise differently. I'll take a look later.

    – Thomas Kühn
    Mar 29 at 11:19












  • In case of an imshow it's easy to normalize, in case of the hist2d you would need to know the outcome of the histogram before calculating it. So I suppose the solution is to first calculate all histograms, then find the minimum and maximum, then plot them all with those numbers as vmin and vmax.

    – ImportanceOfBeingErnest
    Mar 29 at 11:24






  • 1





    @ImportanceOfBeingErnest that would probably anyway be the best solution, to use numpy.histogram2d to compute all data and then use imshow again to do the actual plots. Then one could fall back to the answers in the linked question.

    – Thomas Kühn
    Mar 29 at 11:29






  • 1





    @ThomasKühn thank you you legend

    – user3613025
    Mar 29 at 13:49















1
















Edit:



As has been noted in the comments, the biggest problem here is actually to make the colorbar for many histograms meaningful, as ax.hist2d will always scale the histogram data it receives from numpy. It may therefore be best to first calculated the 2d histogram data using numpy and then use again imshow to visualise it. This way, also the solutions of the linked question can be applied. To make the problem with the normalisation more visible, I put some effort into producing some qualitatively different 2d histograms using scipy.stats.multivariate_normal, which shows how the height of the histogram can change quite dramatically even though the number of samples is the same in each figure.



import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec as gs
from scipy.stats import multivariate_normal

##opening figure and axes
nrows=3
ncols=3
fig, axes = plt.subplots(nrows,ncols)

##generate some random data for the distributions
means = np.random.rand(nrows,ncols,2)
sigmas = np.random.rand(nrows,ncols,2)
thetas = np.random.rand(nrows,ncols)*np.pi*2

##produce the actual data and compute the histograms
mappables=[]
for mean,sigma,theta in zip( means.reshape(-1,2), sigmas.reshape(-1,2), thetas.reshape(-1)):

##the data (only cosmetics):
c, s = np.cos(theta), np.sin(theta)
rot = np.array(((c,-s), (s, c)))
cov = rot@np.diag(sigma)@rot.T
rv = multivariate_normal(mean,cov)
data = rv.rvs(size = 10000)

##the 2d histogram from numpy
H,xedges,yedges = np.histogram2d(data[:,0], data[:,1], bins=50, range=[[-2, 2],[-2, 2]])

mappables.append(H)

##the min and max values of all histograms
vmin = np.min(mappables)
vmax = np.max(mappables)

##second loop for visualisation
for ax,H in zip(axes.ravel(),mappables):
im = ax.imshow(H,vmin=vmin, vmax=vmax, extent=[-2,2,-2,2])

##colorbar using solution from linked question
fig.colorbar(im,ax=axes.ravel())

plt.show()


This code produces a figure like this:



result of above code



Old Answer:



One way to solve your problem is to generate the space for your colorbar explicitly. You can use a GridSpec instance to define how wide your colorbar should be. Below your subplots_hist_2d() function with a few modifications. Note that your use of tight_layout() shifted the colorbar into a funny place, hence the replacement. If you want the plots closer to each other, I'd rather recommend to play with the aspect ratio of the figure.



def subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles):

## fig, a = plt.subplots(2, 2)
fig = plt.figure()
g = gs.GridSpec(nrows=2, ncols=3, width_ratios=[1,1,0.05])
a = [fig.add_subplot(g[n,m]) for n in range(2) for m in range(2)]
cax = fig.add_subplot(g[:,2])


## a = a.ravel()
for idx, ax in enumerate(a):
image = ax.hist2d(x_data[idx], y_data[idx], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[idx], fontsize=12)
ax.set_xlabel(x_labels[idx])
ax.set_ylabel(y_labels[idx])
ax.set_aspect("equal")
## cb = fig.colorbar(image[-1],ax=a)
cb = fig.colorbar(image[-1], cax=cax)
cb.set_label("Intensity", rotation=270)

# pad = how big overall pic is
# w_pad = how separate they're left to right
# h_pad = how separate they're top to bottom
## plt.tight_layout(pad=-1, w_pad=-10, h_pad=0.5)
fig.tight_layout()


Using this modified function, I get the following output:



result of OP's code with the above function






share|improve this answer



























  • That answer might be more suitable for the Matplotlib 2 Subplots, 1 Colorbar. As I see it the main and only difference here is not the positionning of the bar, but the color normalization. Here, you take the colorbar of the last image and place it such that one might think it applies to every plot - which is not true.

    – ImportanceOfBeingErnest
    Mar 29 at 11:16











  • @ImportanceOfBeingErnest You're right about the normalisation of the colorbar, but isn't this a problem also in the solutions you linked? There it appears that always the last mappable of the respective for loop is used. However, it shouldn't be too hard to normalise differently. I'll take a look later.

    – Thomas Kühn
    Mar 29 at 11:19












  • In case of an imshow it's easy to normalize, in case of the hist2d you would need to know the outcome of the histogram before calculating it. So I suppose the solution is to first calculate all histograms, then find the minimum and maximum, then plot them all with those numbers as vmin and vmax.

    – ImportanceOfBeingErnest
    Mar 29 at 11:24






  • 1





    @ImportanceOfBeingErnest that would probably anyway be the best solution, to use numpy.histogram2d to compute all data and then use imshow again to do the actual plots. Then one could fall back to the answers in the linked question.

    – Thomas Kühn
    Mar 29 at 11:29






  • 1





    @ThomasKühn thank you you legend

    – user3613025
    Mar 29 at 13:49













1














1










1









Edit:



As has been noted in the comments, the biggest problem here is actually to make the colorbar for many histograms meaningful, as ax.hist2d will always scale the histogram data it receives from numpy. It may therefore be best to first calculated the 2d histogram data using numpy and then use again imshow to visualise it. This way, also the solutions of the linked question can be applied. To make the problem with the normalisation more visible, I put some effort into producing some qualitatively different 2d histograms using scipy.stats.multivariate_normal, which shows how the height of the histogram can change quite dramatically even though the number of samples is the same in each figure.



import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec as gs
from scipy.stats import multivariate_normal

##opening figure and axes
nrows=3
ncols=3
fig, axes = plt.subplots(nrows,ncols)

##generate some random data for the distributions
means = np.random.rand(nrows,ncols,2)
sigmas = np.random.rand(nrows,ncols,2)
thetas = np.random.rand(nrows,ncols)*np.pi*2

##produce the actual data and compute the histograms
mappables=[]
for mean,sigma,theta in zip( means.reshape(-1,2), sigmas.reshape(-1,2), thetas.reshape(-1)):

##the data (only cosmetics):
c, s = np.cos(theta), np.sin(theta)
rot = np.array(((c,-s), (s, c)))
cov = rot@np.diag(sigma)@rot.T
rv = multivariate_normal(mean,cov)
data = rv.rvs(size = 10000)

##the 2d histogram from numpy
H,xedges,yedges = np.histogram2d(data[:,0], data[:,1], bins=50, range=[[-2, 2],[-2, 2]])

mappables.append(H)

##the min and max values of all histograms
vmin = np.min(mappables)
vmax = np.max(mappables)

##second loop for visualisation
for ax,H in zip(axes.ravel(),mappables):
im = ax.imshow(H,vmin=vmin, vmax=vmax, extent=[-2,2,-2,2])

##colorbar using solution from linked question
fig.colorbar(im,ax=axes.ravel())

plt.show()


This code produces a figure like this:



result of above code



Old Answer:



One way to solve your problem is to generate the space for your colorbar explicitly. You can use a GridSpec instance to define how wide your colorbar should be. Below your subplots_hist_2d() function with a few modifications. Note that your use of tight_layout() shifted the colorbar into a funny place, hence the replacement. If you want the plots closer to each other, I'd rather recommend to play with the aspect ratio of the figure.



def subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles):

## fig, a = plt.subplots(2, 2)
fig = plt.figure()
g = gs.GridSpec(nrows=2, ncols=3, width_ratios=[1,1,0.05])
a = [fig.add_subplot(g[n,m]) for n in range(2) for m in range(2)]
cax = fig.add_subplot(g[:,2])


## a = a.ravel()
for idx, ax in enumerate(a):
image = ax.hist2d(x_data[idx], y_data[idx], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[idx], fontsize=12)
ax.set_xlabel(x_labels[idx])
ax.set_ylabel(y_labels[idx])
ax.set_aspect("equal")
## cb = fig.colorbar(image[-1],ax=a)
cb = fig.colorbar(image[-1], cax=cax)
cb.set_label("Intensity", rotation=270)

# pad = how big overall pic is
# w_pad = how separate they're left to right
# h_pad = how separate they're top to bottom
## plt.tight_layout(pad=-1, w_pad=-10, h_pad=0.5)
fig.tight_layout()


Using this modified function, I get the following output:



result of OP's code with the above function






share|improve this answer















Edit:



As has been noted in the comments, the biggest problem here is actually to make the colorbar for many histograms meaningful, as ax.hist2d will always scale the histogram data it receives from numpy. It may therefore be best to first calculated the 2d histogram data using numpy and then use again imshow to visualise it. This way, also the solutions of the linked question can be applied. To make the problem with the normalisation more visible, I put some effort into producing some qualitatively different 2d histograms using scipy.stats.multivariate_normal, which shows how the height of the histogram can change quite dramatically even though the number of samples is the same in each figure.



import numpy as np
import matplotlib.pyplot as plt
from matplotlib import gridspec as gs
from scipy.stats import multivariate_normal

##opening figure and axes
nrows=3
ncols=3
fig, axes = plt.subplots(nrows,ncols)

##generate some random data for the distributions
means = np.random.rand(nrows,ncols,2)
sigmas = np.random.rand(nrows,ncols,2)
thetas = np.random.rand(nrows,ncols)*np.pi*2

##produce the actual data and compute the histograms
mappables=[]
for mean,sigma,theta in zip( means.reshape(-1,2), sigmas.reshape(-1,2), thetas.reshape(-1)):

##the data (only cosmetics):
c, s = np.cos(theta), np.sin(theta)
rot = np.array(((c,-s), (s, c)))
cov = rot@np.diag(sigma)@rot.T
rv = multivariate_normal(mean,cov)
data = rv.rvs(size = 10000)

##the 2d histogram from numpy
H,xedges,yedges = np.histogram2d(data[:,0], data[:,1], bins=50, range=[[-2, 2],[-2, 2]])

mappables.append(H)

##the min and max values of all histograms
vmin = np.min(mappables)
vmax = np.max(mappables)

##second loop for visualisation
for ax,H in zip(axes.ravel(),mappables):
im = ax.imshow(H,vmin=vmin, vmax=vmax, extent=[-2,2,-2,2])

##colorbar using solution from linked question
fig.colorbar(im,ax=axes.ravel())

plt.show()


This code produces a figure like this:



result of above code



Old Answer:



One way to solve your problem is to generate the space for your colorbar explicitly. You can use a GridSpec instance to define how wide your colorbar should be. Below your subplots_hist_2d() function with a few modifications. Note that your use of tight_layout() shifted the colorbar into a funny place, hence the replacement. If you want the plots closer to each other, I'd rather recommend to play with the aspect ratio of the figure.



def subplots_hist_2d(x_data, y_data, x_labels, y_labels, titles):

## fig, a = plt.subplots(2, 2)
fig = plt.figure()
g = gs.GridSpec(nrows=2, ncols=3, width_ratios=[1,1,0.05])
a = [fig.add_subplot(g[n,m]) for n in range(2) for m in range(2)]
cax = fig.add_subplot(g[:,2])


## a = a.ravel()
for idx, ax in enumerate(a):
image = ax.hist2d(x_data[idx], y_data[idx], bins=50, range=[[-2, 2],[-2, 2]])
ax.set_title(titles[idx], fontsize=12)
ax.set_xlabel(x_labels[idx])
ax.set_ylabel(y_labels[idx])
ax.set_aspect("equal")
## cb = fig.colorbar(image[-1],ax=a)
cb = fig.colorbar(image[-1], cax=cax)
cb.set_label("Intensity", rotation=270)

# pad = how big overall pic is
# w_pad = how separate they're left to right
# h_pad = how separate they're top to bottom
## plt.tight_layout(pad=-1, w_pad=-10, h_pad=0.5)
fig.tight_layout()


Using this modified function, I get the following output:



result of OP's code with the above function







share|improve this answer














share|improve this answer



share|improve this answer








edited Mar 29 at 12:59

























answered Mar 29 at 10:14









Thomas KühnThomas Kühn

6,4663 gold badges25 silver badges40 bronze badges




6,4663 gold badges25 silver badges40 bronze badges















  • That answer might be more suitable for the Matplotlib 2 Subplots, 1 Colorbar. As I see it the main and only difference here is not the positionning of the bar, but the color normalization. Here, you take the colorbar of the last image and place it such that one might think it applies to every plot - which is not true.

    – ImportanceOfBeingErnest
    Mar 29 at 11:16











  • @ImportanceOfBeingErnest You're right about the normalisation of the colorbar, but isn't this a problem also in the solutions you linked? There it appears that always the last mappable of the respective for loop is used. However, it shouldn't be too hard to normalise differently. I'll take a look later.

    – Thomas Kühn
    Mar 29 at 11:19












  • In case of an imshow it's easy to normalize, in case of the hist2d you would need to know the outcome of the histogram before calculating it. So I suppose the solution is to first calculate all histograms, then find the minimum and maximum, then plot them all with those numbers as vmin and vmax.

    – ImportanceOfBeingErnest
    Mar 29 at 11:24






  • 1





    @ImportanceOfBeingErnest that would probably anyway be the best solution, to use numpy.histogram2d to compute all data and then use imshow again to do the actual plots. Then one could fall back to the answers in the linked question.

    – Thomas Kühn
    Mar 29 at 11:29






  • 1





    @ThomasKühn thank you you legend

    – user3613025
    Mar 29 at 13:49

















  • That answer might be more suitable for the Matplotlib 2 Subplots, 1 Colorbar. As I see it the main and only difference here is not the positionning of the bar, but the color normalization. Here, you take the colorbar of the last image and place it such that one might think it applies to every plot - which is not true.

    – ImportanceOfBeingErnest
    Mar 29 at 11:16











  • @ImportanceOfBeingErnest You're right about the normalisation of the colorbar, but isn't this a problem also in the solutions you linked? There it appears that always the last mappable of the respective for loop is used. However, it shouldn't be too hard to normalise differently. I'll take a look later.

    – Thomas Kühn
    Mar 29 at 11:19












  • In case of an imshow it's easy to normalize, in case of the hist2d you would need to know the outcome of the histogram before calculating it. So I suppose the solution is to first calculate all histograms, then find the minimum and maximum, then plot them all with those numbers as vmin and vmax.

    – ImportanceOfBeingErnest
    Mar 29 at 11:24






  • 1





    @ImportanceOfBeingErnest that would probably anyway be the best solution, to use numpy.histogram2d to compute all data and then use imshow again to do the actual plots. Then one could fall back to the answers in the linked question.

    – Thomas Kühn
    Mar 29 at 11:29






  • 1





    @ThomasKühn thank you you legend

    – user3613025
    Mar 29 at 13:49
















That answer might be more suitable for the Matplotlib 2 Subplots, 1 Colorbar. As I see it the main and only difference here is not the positionning of the bar, but the color normalization. Here, you take the colorbar of the last image and place it such that one might think it applies to every plot - which is not true.

– ImportanceOfBeingErnest
Mar 29 at 11:16





That answer might be more suitable for the Matplotlib 2 Subplots, 1 Colorbar. As I see it the main and only difference here is not the positionning of the bar, but the color normalization. Here, you take the colorbar of the last image and place it such that one might think it applies to every plot - which is not true.

– ImportanceOfBeingErnest
Mar 29 at 11:16













@ImportanceOfBeingErnest You're right about the normalisation of the colorbar, but isn't this a problem also in the solutions you linked? There it appears that always the last mappable of the respective for loop is used. However, it shouldn't be too hard to normalise differently. I'll take a look later.

– Thomas Kühn
Mar 29 at 11:19






@ImportanceOfBeingErnest You're right about the normalisation of the colorbar, but isn't this a problem also in the solutions you linked? There it appears that always the last mappable of the respective for loop is used. However, it shouldn't be too hard to normalise differently. I'll take a look later.

– Thomas Kühn
Mar 29 at 11:19














In case of an imshow it's easy to normalize, in case of the hist2d you would need to know the outcome of the histogram before calculating it. So I suppose the solution is to first calculate all histograms, then find the minimum and maximum, then plot them all with those numbers as vmin and vmax.

– ImportanceOfBeingErnest
Mar 29 at 11:24





In case of an imshow it's easy to normalize, in case of the hist2d you would need to know the outcome of the histogram before calculating it. So I suppose the solution is to first calculate all histograms, then find the minimum and maximum, then plot them all with those numbers as vmin and vmax.

– ImportanceOfBeingErnest
Mar 29 at 11:24




1




1





@ImportanceOfBeingErnest that would probably anyway be the best solution, to use numpy.histogram2d to compute all data and then use imshow again to do the actual plots. Then one could fall back to the answers in the linked question.

– Thomas Kühn
Mar 29 at 11:29





@ImportanceOfBeingErnest that would probably anyway be the best solution, to use numpy.histogram2d to compute all data and then use imshow again to do the actual plots. Then one could fall back to the answers in the linked question.

– Thomas Kühn
Mar 29 at 11:29




1




1





@ThomasKühn thank you you legend

– user3613025
Mar 29 at 13:49





@ThomasKühn thank you you legend

– user3613025
Mar 29 at 13:49




















draft saved

draft discarded















































Thanks for contributing an answer to Stack Overflow!


  • Please be sure to answer the question. Provide details and share your research!

But avoid


  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55406844%2fmatplotlib-how-to-plot-1-colorbar-for-four-2d-histogram%23new-answer', 'question_page');

);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Kamusi Yaliyomo Aina za kamusi | Muundo wa kamusi | Faida za kamusi | Dhima ya picha katika kamusi | Marejeo | Tazama pia | Viungo vya nje | UrambazajiKuhusu kamusiGo-SwahiliWiki-KamusiKamusi ya Kiswahili na Kiingerezakuihariri na kuongeza habari

Swift 4 - func physicsWorld not invoked on collision? The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags

Access current req object everywhere in Node.js ExpressWhy are global variables considered bad practice? (node.js)Using req & res across functionsHow do I get the path to the current script with Node.js?What is Node.js' Connect, Express and “middleware”?Node.js w/ express error handling in callbackHow to access the GET parameters after “?” in Express?Modify Node.js req object parametersAccess “app” variable inside of ExpressJS/ConnectJS middleware?Node.js Express app - request objectAngular Http Module considered middleware?Session variables in ExpressJSAdd properties to the req object in expressjs with Typescript