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;
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?
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:
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:
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
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
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
|
show 3 more comments
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?
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:
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:
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
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
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
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 tryconstrained_layout
; which is an alternative totight_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
|
show 3 more comments
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?
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:
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:
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
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
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
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?
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:
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:
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
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
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
python-3.x matplotlib subplot colorbar
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 tryconstrained_layout
; which is an alternative totight_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
|
show 3 more comments
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 tryconstrained_layout
; which is an alternative totight_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
|
show 3 more comments
1 Answer
1
active
oldest
votes
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:
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:
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 animshow
it's easy to normalize, in case of thehist2d
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 usenumpy.histogram2d
to compute all data and then useimshow
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
|
show 1 more comment
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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:
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:
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 animshow
it's easy to normalize, in case of thehist2d
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 usenumpy.histogram2d
to compute all data and then useimshow
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
|
show 1 more comment
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:
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:
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 animshow
it's easy to normalize, in case of thehist2d
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 usenumpy.histogram2d
to compute all data and then useimshow
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
|
show 1 more comment
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:
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:
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:
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:
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 animshow
it's easy to normalize, in case of thehist2d
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 usenumpy.histogram2d
to compute all data and then useimshow
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
|
show 1 more comment
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 animshow
it's easy to normalize, in case of thehist2d
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 usenumpy.histogram2d
to compute all data and then useimshow
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
|
show 1 more comment
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 totight_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