Saving matplotlib subplot figure to image file - python

I'm fairly new to matplotlib and am limping along. That said, I haven't found an obvious answer to this question.
I have a scatter plot I wanted colored by groups, and it looked like plotting via a loop was the way to roll.
Here is my reproducible example, based on the first link above:
import matplotlib.pyplot as plt
import pandas as pd
from pydataset import data
df = data('mtcars').iloc[0:10]
df['car'] = df.index
fig, ax = plt.subplots(1)
plt.figure(figsize=(12, 9))
for ind in df.index:
ax.scatter(df.loc[ind, 'wt'], df.loc[ind, 'mpg'], label=ind)
ax.legend(bbox_to_anchor=(1.05, 1), loc=2)
# plt.show()
# plt.savefig('file.png')
Uncommenting plt.show() yields what I want:
Searching around, it looked like plt.savefig() is the way to save a file; if I re-comment out plt.show() and run plt.savefig() instead, I get a blank white picture. This question, suggests this is cause by calling show() before savefig(), but I have it entirely commented out. Another question has a comment suggesting I can save the ax object directly, but that cuts off my legend:
The same question has an alternative that uses fig.savefig() instead. I get the same chopped legend.
There's this question which seems related, but I'm not plotting a DataFrame directly so I'm not sure how to apply the answer (where dtf is the pd.DataFrame they're plotting):
plot = dtf.plot()
fig = plot.get_figure()
fig.savefig("output.png")
Thanks for any suggestions.
Edit: to test the suggestion below to try tight_layout(), I ran this and still get a blank white image file:
fig, ax = plt.subplots(1)
plt.figure(figsize=(12, 9))
for ind in df.index:
ax.scatter(df.loc[ind, 'wt'], df.loc[ind, 'mpg'], label=ind)
ax.legend(bbox_to_anchor=(1.05, 1), loc=2)
fig.tight_layout()
plt.savefig('test.png')

Remove the line plt.figure(figsize=(12, 9)) and it will work as expected. I.e. call savefig before show.
The problem is that the figure being saved is the one created by plt.figure(), while all the data is plotted to ax which is created before that (and in a different figure, which is not the one being saved).
For saving the figure including the legend use the bbox_inches="tight" option
plt.savefig('test.png', bbox_inches="tight")
Of course saving the figure object directly is equally possible,
fig.savefig('test.png', bbox_inches="tight")
For a deeper understanding on how to move the legend out of the plot, see this answer.

Additional add-up on #ImportanceOfBeingErnest's answer, when bbox_inches='tight', 'pad_inches=0.1' may need to set to larger values.

Related

Problems with plt.subplots, which should be the best option?

I'm new in both python and stackoverflow... I come from the ggplot2 R background and I am still getting stacked with python. I don't understand why I have a null plot before my figure using matplotlib... I just have a basic pandas series and I want to plot some of the rows in a subplot, and some on the others (however my display is terrible and I don't know why/how to fix it). Thank you in advance!
df = organism_df.T
fig, (ax1,ax2) = plt.subplots(nrows=1,ncols=2,figsize=(5,5))
ax1 = df.iloc[[0,2,3,-1]].plot(kind='bar')
ax1.get_legend().remove()
ax1.set_title('Number of phages/bacteria interacting vs actual DB')
ax2 = df.iloc[[1,4,5,6,7]].plot(kind='bar')
ax2.get_legend().remove()
ax2.set_title('Number of different taxonomies with interactions')
plt.tight_layout()
The method plot from pandas would need the axes given as an argument, e.g., df.plot(ax=ax1, kind='bar'). In your example, first the figure (consisting of ax1 and ax2) is created, then another figure is created by the plot function (at the same time overwriting the original ax1 object) etc.

How to add two data sets on one bar graph using matplotlib

How do you put two data sets on the same bar graph? I tried this code. This should be simple enough to help anyone with the same problem?
x = groups1_table.plot.bar(color='blue')
x = groups2_table.plot.bar(color='red')
plt.show()
Any suggestions?
Use ax in matplotlib.
Since you have not posted MRE, I am assuming the data points.
You can proceed with something like this:
import matplotlib.pyplot as plt
x1=[1,2,3,4,5]
y1=[6,7,8,9,15]
x2=[16,17,18,16,19]
y2=[20,22,23,26,21]
fig, ax=plt.subplots()
ax=plt.bar(x1,y1,label='x_list')
ax=plt.bar(x2,y2,label='y_list')
plt.legend(loc='upper left')
plt.show()
Again you have to change the code to meet your preferences. Just know that you can place as many graphs as you want on a same plot. Just use the same axes object ax to plot them.
OUTPUT:

Remove text from figure when using dataframe.boxplot(by=...) [duplicate]

I'm trying to plot a figure without tickmarks or numbers on either of the axes (I use axes in the traditional sense, not the matplotlib nomenclature!). An issue I have come across is where matplotlib adjusts the x(y)ticklabels by subtracting a value N, then adds N at the end of the axis.
This may be vague, but the following simplified example highlights the issue, with '6.18' being the offending value of N:
import matplotlib.pyplot as plt
import random
prefix = 6.18
rx = [prefix+(0.001*random.random()) for i in arange(100)]
ry = [prefix+(0.001*random.random()) for i in arange(100)]
plt.plot(rx,ry,'ko')
frame1 = plt.gca()
for xlabel_i in frame1.axes.get_xticklabels():
xlabel_i.set_visible(False)
xlabel_i.set_fontsize(0.0)
for xlabel_i in frame1.axes.get_yticklabels():
xlabel_i.set_fontsize(0.0)
xlabel_i.set_visible(False)
for tick in frame1.axes.get_xticklines():
tick.set_visible(False)
for tick in frame1.axes.get_yticklines():
tick.set_visible(False)
plt.show()
The three things I would like to know are:
How to turn off this behaviour in the first place (although in most cases it is useful, it is not always!) I have looked through matplotlib.axis.XAxis and cannot find anything appropriate
How can I make N disappear (i.e. X.set_visible(False))
Is there a better way to do the above anyway? My final plot would be 4x4 subplots in a figure, if that is relevant.
Instead of hiding each element, you can hide the whole axis:
frame1.axes.get_xaxis().set_visible(False)
frame1.axes.get_yaxis().set_visible(False)
Or, you can set the ticks to an empty list:
frame1.axes.get_xaxis().set_ticks([])
frame1.axes.get_yaxis().set_ticks([])
In this second option, you can still use plt.xlabel() and plt.ylabel() to add labels to the axes.
If you want to hide just the axis text keeping the grid lines:
frame1 = plt.gca()
frame1.axes.xaxis.set_ticklabels([])
frame1.axes.yaxis.set_ticklabels([])
Doing set_visible(False) or set_ticks([]) will also hide the grid lines.
If you are like me and don't always retrieve the axes, ax, when plotting the figure, then a simple solution would be to do
plt.xticks([])
plt.yticks([])
I've colour coded this figure to ease the process.
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
You can have full control over the figure using these commands, to complete the answer I've add also the control over the spines:
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
# X AXIS -BORDER
ax.spines['bottom'].set_visible(False)
# BLUE
ax.set_xticklabels([])
# RED
ax.set_xticks([])
# RED AND BLUE TOGETHER
ax.axes.get_xaxis().set_visible(False)
# Y AXIS -BORDER
ax.spines['left'].set_visible(False)
# YELLOW
ax.set_yticklabels([])
# GREEN
ax.set_yticks([])
# YELLOW AND GREEN TOGHETHER
ax.axes.get_yaxis().set_visible(False)
I was not actually able to render an image without borders or axis data based on any of the code snippets here (even the one accepted at the answer). After digging through some API documentation, I landed on this code to render my image
plt.axis('off')
plt.tick_params(axis='both', left=False, top=False, right=False, bottom=False, labelleft=False, labeltop=False, labelright=False, labelbottom=False)
plt.savefig('foo.png', dpi=100, bbox_inches='tight', pad_inches=0.0)
I used the tick_params call to basically shut down any extra information that might be rendered and I have a perfect graph in my output file.
Somewhat of an old thread but, this seems to be a faster method using the latest version of matplotlib:
set the major formatter for the x-axis
ax.xaxis.set_major_formatter(plt.NullFormatter())
One trick could be setting the color of tick labels as white to hide it!
plt.xticks(color='w')
plt.yticks(color='w')
or to be more generalized (#Armin Okić), you can set it as "None".
When using the object oriented API, the Axes object has two useful methods for removing the axis text, set_xticklabels() and set_xticks().
Say you create a plot using
fig, ax = plt.subplots(1)
ax.plot(x, y)
If you simply want to remove the tick labels, you could use
ax.set_xticklabels([])
or to remove the ticks completely, you could use
ax.set_xticks([])
These methods are useful for specifying exactly where you want the ticks and how you want them labeled. Passing an empty list results in no ticks, or no labels, respectively.
You could simply set xlabel to None, straight in your axis. Below an working example using seaborn
from matplotlib import pyplot as plt
import seaborn as sns
tips = sns.load_dataset("tips")
ax = sns.boxplot(x="day", y="total_bill", data=tips)
ax.set(xlabel=None)
plt.show()
Just do this in case you have subplots
fig, axs = plt.subplots(1, 2, figsize=(16, 8))
ax[0].set_yticklabels([]) # x-axis
ax[0].set_xticklabels([]) # y-axis

use of tight_layout() in matplotlib with incomplete axis array:

I am creating PDFs of an array of axes. Sometimes the page is not full, ie not all axes have data. In this case, I want the unused axes not to show on the PDF. But I want the layout to be the same as if they were being used. I'm using tight_layout() to get non-overlapping axes and ylabels.
The following code shows first the case where the axes are used, then what happens if I delete the unused ones (tight_layout does not work properly), and then, if I instead just set them not to be visible, tight_layout() fails with a
AttributeError: 'NoneType' object has no attribute 'is_bbox'
error.
import numpy as np
import matplotlib.pyplot as plt
def prep_figure():
plt.close('all')
fig, axs = plt.subplots(4,3, figsize=(11,8.5))
axs=np.concatenate(axs)
for ii in range(5):
axs[ii].plot([1,2,3],[-10,-1,-10])
axs[ii].set_ylabel('ylabel')
axs[ii].set_xlabel('xlabel')
return fig,axs
fig,axs=prep_figure()
plt.tight_layout()
plt.show()
plt.savefig('tmp.pdf', )
# Try deleting extra axes
fig,axs=prep_figure()
for ii in range(5,12):
fig.delaxes(axs[ii])
plt.tight_layout()
plt.show()
plt.savefig('tmpd.pdf', )
# Try hiding extra axes
fig,axs=prep_figure()
for ii in range(5,12):
axs[ii].set_visible(False)
plt.tight_layout()
plt.show()
plt.savefig('tmph.pdf', )
I want the layout of the first version, but without the extra axes visible.
You could create the axes independently of the figure. I would also recommend this method because you have more control over the axes, for example you could have different shaped axes.
Code:
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
for ii in range(5):
ax = fig.add_subplot(4,3,ii+1)
ax.scatter(np.random.random(5),np.random.random(5))
ax.set_xlabel('xlabel')
ax.set_ylabel('ylabel')
fig.tight_layout()
fig.show()
Result:
The second case of deleting the axes works fine if it is used on its own (without the code from the first case executed) and if the figure is first saved and then shown,
fig,axs=prep_figure()
for ii in range(5,12):
fig.delaxes(axs[ii])
plt.tight_layout()
plt.savefig('tmpd.pdf', )
plt.show()
The third case works fine if again, the figure is saved before showing it, and instead of making it invisible, turning the axes off via ax.axis("off").
fig,axs=prep_figure()
for ii in range(5,12):
axs[ii].axis("off")
plt.tight_layout()
plt.savefig('tmph.pdf', )
plt.show()
The created pdf is the same in both cases:

remove colorbars from plot

I wrote some code to create a png of a raster object (self[:] = a np array).
it's supposed to be a method, to easily make a plot
Problem with the code is that it runs fine the first time,
but when i run this method multiple times i get a picture with multiple legends.
I tried to get rid of it with delaxes, but this legend is really stubborn.
Any Idea's how to solve this are welcome
Here's the code:
def plot(self,image_out,dpi=150, rotate = 60):
xur = self.xur()
xll = self.xll()
yur = self.yur()
yll = self.yll()
fig = plt.figure()
#tmp = range(len(fig.axes))
#tmp = tmp[::-1]
#for x in tmp:
# fig.delaxes(fig.axes[x])
ax = fig.add_subplot(111)
cax = ax.imshow(self[:],cmap='jet', extent = [yll,yur,xll,xur],
interpolation = 'nearest')
cbar = fig.colorbar()
plt.xticks(rotation=70)
plt.tight_layout(pad = 0.25)
plt.savefig(image_out,dpi=dpi)
return
You need to close the plot. I had this same problem
After plt.savefig, add plt.close()
A better option is to specify to colorbar which axes you would like to see it render into, see the example here.
I encountered the same problem and the answers in another post solved it
remove colorbar from figure in matplotlib
Please refer to the second answer
I had a similar problem and played around a little bit. I came up with two solutions which might be slightly more elegant:
Clear the whole figure and add the subplot (+colorbar if wanted) again.
If there's always a colorbar, you can simply update the axes with autoscale which also updates the colorbar.
I've tried this with imshow, but I guess it works similar for other plotting methods.
In particular, I used the first approach, which is to clear the figure by clf() and then re-add the axis each time.
You can remove the colorbar by its .remove() method:
cbar = fig.colorbar()
...
cbar.remove()

Categories