Matplotlib - saving an image without any whitespace nor gridlines visible - python

I have some really simple figures that I'm trying to generate for an experiment. They're just supposed to be bars with 3 colors, each color representing a different probability for an event.
I approached this by creating a horizontal stacked barplot in matplotlib, then I tried removing the gridlines and margins and everything extra just so that I could see only the bars. The following
df = pd.DataFrame({"Risk":[95], "No Effect" : [3], "Gain":[2]})
df.plot(kind='barh', legend = False, stacked=True, color=['#FFFF00', '#808080', '#4169e1'])
plt.axis('off')
plt.margins(x=0)
plt.margins(y=0)
plt.grid(b=None)
plt.savefig('static/images/debug3red.png', bbox_inches='tight', pad_inches=-1)
plt.show()
This code snippet is basically what I compiled from reviewing a bunch of posts about people trying to accomplish the same task.
It's almost up to par.
Here's the image I get from plt.show(). There are still margins present, but this technically shouldn't be a problem because my savefig() call ideally should have the correct parameters to remove those margins.
Now here's the image that's saved from the savefig() call.
The image is slightly zoomed in on, and cropped partially. You can't tell that the image has been zoomed in slightly from this image, there are other instances that I've seen that better showcase that property. But you can clearly see that the image is being cropped. There are no margins at least, but...
I'm close, but what's going wrong here and how can I actually accomplish my goal?
Edit: Also for those who might be wondering (because I've heard that "pad_inches=-1" isn't elegant)...
pad_inches = 0 produces the following (for a different set of probabilities)
Edit: Based off of this answer, Removing white space around a saved image in matplotlib
The following code removes the vertical margins, but doesn't remove the horizontal margins.
plt.gca().set_axis_off()
plt.subplots_adjust(top = 1, bottom = 0, right = 1, left = 0,
hspace = 0, wspace = 0)
plt.margins(0,0)
plt.savefig('static/images/debug5.png', bbox_inches='tight', pad_inches=0.0)
Resulting in...

Related

How can I use alpha with seaborn.pointplot? [duplicate]

I want to make a seaborn pointplot that has transparency so that I can clearly see the points located behind others of a different color.
I tried adding "alpha=0.3" to the call to pointplot and also tried the same within a catplot with kind='point'; however, neither of these results in the desired transparency (no error message is produced either).
sns.pointplot(x='aamm', y='posrate', hue='AA:XX', hue_order=[1,0], data=data, dodge=True, palette=palette, alpha=0.3)
I was hoping to get a plot with transparent points, but instead, I got one with normal opaque points. The dodge option doesn't seem to produce any noticeable effect either, in terms of separating overlapping points of different color.
Is there a way to add transparency to a seaborn pointplot or use something else to get a similar effect?
Thank you.
To the extent of my knowledge there is no more an alpha parameter that can be directly set in seaborn.
You can do the following thou:
Sample dataframe
df = pd.DataFrame(np.random.randint(low=0, high=1000, size=(50, 5)))
Plotting
ax = sns.pointplot(x=0, y=1, data=df, dodge=True,plot_kws=dict(alpha=0.3))
plt.setp(ax.collections, alpha=.3) #for the markers
plt.setp(ax.lines, alpha=.3) #for the lines

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.

Adjusting subplots to make space for colorbar

I was trying to plot some data and found constrained layout very helpful in maintaining margins and spaces between subplots. However, when I add a colorbar it reduces the width of all subplots and creates extra white space in the subplots above. This bcomes a problem when such plots are put up in reports where a lot of space goes waste because of extra space taken by colorbar.
I was wondering how I can avoid this and make only the image plots to resize when the colorbar is added without afecting the subplots above and no extra white space is created. An example code of the problem I am facing is:
fig, ax = plt.subplots(4,2, constrained_layout=True)
ax[0,0].plot(range(10))
ax[0,1].plot(range(10))
ax[1,0].plot(range(10))
ax[1,1].plot(range(10))
ax[2,0].pcolor(np.random.rand(2,2))
ax[2,1].pcolor(np.random.rand(2,2))
ax[3,0].pcolor(np.random.rand(2,2))
im = ax[3,1].pcolor(np.random.rand(2,2))
bar = fig.colorbar(im,ax=[[ax[2,0],ax[2,1]],[ax[3,0],ax[3,1]]])
It'll be better if I can get this done with contrantrained_layout=True.
I don't have much experience with adjusting the color bar, but what about the idea of adding a new axis and placing the color bar in the center? I set the placement values manually. I wanted to put the color bar in the bottom two graphs to make the widths the same, but I couldn't do that.
import matplotlib.pyplot as plt
fig, ax = plt.subplots(4,2, constrained_layout=True)
ax[0,0].plot(range(10))
ax[0,1].plot(range(10))
ax[1,0].plot(range(10))
ax[1,1].plot(range(10))
ax[2,0].pcolor(np.random.rand(2,2))
ax[2,1].pcolor(np.random.rand(2,2))
ax[3,0].pcolor(np.random.rand(2,2))
im = ax[3,1].pcolor(np.random.rand(2,2))
cax = fig.add_axes([0.48, 0.11, 0.05, 0.36])
bar = fig.colorbar(im,cax=cax,ax=[[ax[2,0],ax[2,1]],[ax[3,0],ax[3,1]]])
fig.subplots_adjust(wspace=0.7, hspace=0.5)
plt.show()

matplotlib // space between subplots

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

Change range withouth scaling in matplot

I have a question, I am making a program that displays a zoomed area of Peru but the axis shown are in the range of the image (e.g. 7000x7500) but i want to be in UTM range (e.g. x-axis between 500000-600000 and y-axis 9500000-9700000)
I have tried using plt.set_xlim and plt.sety_lim but no success, I think I have to use plt.autoscale(False) but it also didn't work or I used it wrong.
I create the figure and axes out of the main program
f = plt.figure(figsize=(5,5))
axe = f.add_axes([0, 0, 1, 1])
this is the function I call everytime I want to plot
def plotear(self, mapa):
axe.clear()
axe.imshow(mapa, cmap='gray', interpolation='nearest')
axe.set_xlim(0,10000) #This is just for testing
axe.set_ylim(0,10000) #This is just for testing
plt.autoscale(False)
self.canvas.draw()
Edit: #ImportanceOfBeingErnest's answer worked as expected! Now I am having another problem, in the canvas I am showing the image, the x-axis and y-axis doesnt visualize correctly, here is an image example
how could I fix it? thanks.
From the imshow documentation you'd find that there is an argument extent which can be used to scale the image.
extent : scalars (left, right, bottom, top), optional, default: None
The location, in data-coordinates, of the lower-left and upper-right corners. If None, the image is positioned such that the pixel centers fall on zero-based (row, column) indices.
In this case you'd use it like
ax.imshow(mapa, extent=[5e5, 6e5, 9.5e6, 9.7e6])
Answer to the edited question:
In the case of the image being too large, this is probably caused by you setting axe = f.add_axes([0, 0, 1, 1]). You should rather use ax = fig.add_subplot(111) and if the margins are not as you want then, setting plt.subplots_adjust( ... ) with the respective spacings.

Categories

Resources