matplotlib // space between subplots - python

I want to plot images (in the 1st row) along with some diagrams (the 2nd and 3rd rows) using subplots from matplotlib.pyplot. However, imshow fucntion adds some additional white space around images I can't get rid of. Here is my code and the plot I'm getting:
rcParams['figure.figsize'] = (16, 14)
_, axes = plt.subplots(3, 3)
axes[0][0].imshow(image)
axes[0][0].set_title('title')
axes[0][0].set_xticklabels(list())
axes[0][0].set_yticklabels(list())
axes[0][0].grid(False)
axes[0][1].imshow(image)
axes[0][1].set_title('title')
axes[0][1].set_xticklabels(list())
axes[0][1].set_yticklabels(list())
axes[0][1].grid(False)
axes[0][2].imshow(image)
axes[0][2].set_title('title')
axes[0][2].set_xticklabels(list())
axes[0][2].set_yticklabels(list())
axes[0][2].grid(False)
plt.savefig(file_name, bbox_inches='tight')
in the plot below you can clearly see that there is significantly more space between the 1st and 2nd rows:
I would like to have an equal space between all subplots. What would be the easiest way to do this?
Thanks in advance for any advice!
Best,
Alexey

This is because imshow is showing the image with square pixels. If the image as a ratio of e.g. 16:9, the subplot will be reshaped to fit the image size. They will therefore have a different shape from the other subplots (see the imshow documentation for more info).
From here, you have two solutions:
decrease the figure height in order to reduce manually the vertical space between subplots
prevent imshow to resize the axes based on the images. For this you can set the aspect ratio to automatic aspect="auto", and the image will fit the existing axes

Related

How to define the aspect ratio of a matplotlib figure?

Python produces an aspect ratio that is suitable for its content e.g., respects the structure of the font of each label, axis title, etc. This is the basic code using Jupyter Notebook:
fig, ax = plt.subplots()
ax.boxplot(dataLipid)
ax.set_title("Lipid contact analysis")
plt.xticks([1,2,3,4,5],["x4 Monomers","x2 Monomers\nDimer","x2 Dimers","Monomer\nTrimer", "x4mer"])
plt.show()
However, I want to save the image as a tiff, with a dpi of 600, and a width of 8.3cm (maximum height is an A4 page, but the nature of my question will make that irrelevant).
I'm using the code:
fig.savefig("bar.tiff", dpi=600, format="tiff", pil_kwards = {"compression":"tiff_lzm"})
This produces the following:
All good so far. Next, the Royal Soc. of Chemistry expect a single column image to be 8.3 cm in width (height, no more than the page).
My question:
Is there any way for Python to calculate the height of the figure given only the wdith, whilst maintaining the correct aspect ratio for the fonts, titles and ticks etc.? If I specify width=height, the image looks terrible:
fig.set_size_inches(3.26,3.26)
fig.savefig("bar.tiff", dpi=600, format="tiff", pil_kwards = {"compression":"tiff_lzm"})
Or is this a case where I define the size of the figure first, then adjust the font sizes as a separate step? I'm looking more for a one-fix solution as I have multiple figures of different size requirements (all being dpi=600 though) to produce.
Here you go:
dataLipid = np.random.uniform(0,1,(100,5)) * 90000
fig, ax = plt.subplots()
ax.boxplot(dataLipid)
ax.set_title("Lipid contact analysis")
plt.xticks([1,2,3,4,5],["x4 Monomers","x2 Monomers\nDimer","x2 Dimers","Monomer\nTrimer", "x4mer"])
fig.set_size_inches(3.26,3.26)
# rotate ticks
plt.xticks(rotation=45)
# set bottom margin
plt.subplots_adjust(left=0.2, bottom=0.3)
fig.savefig("bar.tiff", dpi=600, format="tiff", pil_kwards = {"compression":"tiff_lzm"})
There is no general solution as far as I know. So setting the correct margin depends on your content and your data. Rotating the ticks is always a good option to make them readable in case of close spacing.
You can use the Axes.set_aspect method.
# square plot
ax.set_aspect(1)
Also have a look at the tight_layout method to ensure everything is redrawn to fit in the figure.

Set a minimum tile size for seaborn heatmap

Using seaborn and matplotlib.pyplot in python is there a way to enforce a minimum/maximum size for the tiles in a heatmap?
I want to show the annotated values inside the tiles, but for more than a 9-10 samples the tiles become too small for the text. I dont want to set a fixed larger figure size to increase the tile size, since its not a one-time graph and I dont know how many samples will need to be displayed each time.
#graph plotting part
rcParams.update({"figure.constrained_layout.use": True})
ax = seaborn.heatmap(plotdata, cmap=seaborn.color_palette("flare", as_cmap=True), annot=True, linewidths=.5, fmt=".2f")
plt.show(ax)
Leads to:

How to save a matplotlib.pypot figure as an image with required pixel size?

I have plotted matplotlib.pyplot plot. I have already removed the axes and title of the plot such that in jupyter notebook it looks like an image.
But I need to save that plot as an image to my local disk with required pixel resolution. In my case it's 40 X 98.
I have tried plt.savefig but I can't get the measurements accurately. I have provided my code snippet below. (spectrum) is my 2D array which is to be plotted as a fucntion of x and y axes.
spect = 20 * np.log10(spectrum)
fig, ax = plt.subplots(figsize=(1,1))
ax = sns.heatmap(spect,cmap='viridis',cbar=False,xticklabels=False, yticklabels=False)
ax.invert_yaxis()
plt.savefig('sample.png',bbox_inches = 'tight', pad_inches = 0)
Try adjusting the dpi argument in savefig. From the docs:
"dpi : the resolution in dots per inch".
https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.savefig.html
The actual number of pixels may vary depending on your screen resolution.
For a more detailed explanation, see this answer:
Specifying and saving a figure with exact size in pixels

mplot3d axis labels and colors

I have a 3D projection plot which is really a collection of 2D slices of some value. I also shade the underlying area with:
ax.add_collection3d(plt.fill_between())
I have a couple of related questions:
1) The plot looks fine but the axis labels look messed up - they are rendered on top of the tick labels. How can I space them out a bit?
2) How can I choose what camera angle is rendered? (I'm using Jupiter notebook).
3) The background of the plot (behind the grid, not the grid color itself) is this light blueish grey. how can make that white?
Thanks!
for number 1) ax.set_xlabel() has a labelpad parameters that can be set to any number of pixels.

matplotlib - subplots with fixed aspect ratio

I have a problem with plotting multiple subplots. I would like to set the PHYSICAL aspect ratio of the subplots to a fixed value.
In my example I have 12 subplots (4 rows and 3 columns) on a landscape A4 figure. There all subplots are nicely placed on the whole figure, and for all subplots the height is nearly equal to the width.
But if I change the layout of my figure to portrait, the subplots are stretched vertically.
And this is exactly what should not happen. I would like to have the same height and width of the subplots as on the landscape figure. Is there a possibility that the aspect ratio of the subplots stay the same?
Thanks in advance,
Peter
EDIT:
I have found a workaround. But this just works for non-logarithmic axes...
aspectratio=1.0
ratio_default=(ax.get_xlim()[1]-ax.get_xlim()[0])/(ax.get_ylim()[1]-ax.get_ylim()[0])
ax.set_aspect(ratio_default*aspectratio)
Actually, what you're wanting is quite simple... You just need to make sure that adjustable is set to 'box' on your axes, and you have a set aspect ratio for the axes (anything other than 'auto').
You can either do this with the adjustable kwarg when you create the subplots. Alternatively, you can do this after their creation by calling ax.set_adjustable('box'), or by calling ax.set_aspect(aspect, adjustable='box') (where aspect is either 'equal' or a number).
Now, regardless of how the figure is resized, the subplots will maintain the same aspect ratio.
For example:
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_subplot(2,1,1, adjustable='box', aspect=0.3)
ax2 = fig.add_subplot(2,1,2)
ax1.plot(range(10))
ax2.plot(range(10))
plt.show()
Now, compare how the top subplot responds to resizing, vs. how the bottom subplot responds:
The initial plot
Resized to a vertical layout:
Resized to a horizontal layout:
Your workaround works for me. After plotting the data, I call the following function:
def fixed_aspect_ratio(ratio):
'''
Set a fixed aspect ratio on matplotlib plots
regardless of axis units
'''
xvals,yvals = gca().axes.get_xlim(),gca().axes.get_ylim()
xrange = xvals[1]-xvals[0]
yrange = yvals[1]-yvals[0]
gca().set_aspect(ratio*(xrange/yrange), adjustable='box')
In reply to the remark about the solution not working for logarithmic plots in the edit to the original question - you need to adapt as follows:
def fixed_aspect_ratio_loglog(ratio):
'''
Set a fixed aspect ratio on matplotlib loglog plots
regardless of axis units
'''
xvals,yvals = gca().axes.get_xlim(),gca().axes.get_ylim()
xrange = log(xvals[1])-log(xvals[0])
yrange = log(yvals[1])-log(yvals[0])
gca().set_aspect(ratio*(xrange/yrange), adjustable='box')
(Adaptation for semilog plots should now be obvious)

Categories

Resources