Saving matplotlib subplot figure to image file
Asked Answered
B

2

6

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:

good plot

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:

chopped 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')
Baecher answered 23/12, 2017 at 23:4 Comment(12)
Have you tried plt.tight_layout()? That might helpLovelace
@Lovelace still a white file with plt.savefig(). If I run that before ax.figure.savefig(), it crops to the plot and I lose the legend entirely.Baecher
Hm...What happens if you use fig.savefig() in combination with the tight_layout?Lovelace
Well, its kind of cheating, but you could try to change the size of the figure plt.figure(figsize=(20,10))Lovelace
The approach is correct. Call savefig before show. (What is pydataset? Is it needed to reproduce the problem?)Anagoge
@Lovelace updated answer with exactly what I'm running based on your tight_layout suggestion. An answer (even if it's just rough) or a gist would be most helpful to make sure we're understanding each other. Namely, in my response I meant to explain that I think I was doing the two in combination.Baecher
@Anagoge note that I'm not calling them in conjunction; as stated, I always have one commented out. pydataset was the easiest way I found to load mtcars. Feel free to suggest another dataset and I can create another example.Baecher
Here is my suggestion for a Dataset: x=[0,1], y=[2,5]Anagoge
@Baecher working on it ;)Lovelace
@Anagoge did you look at the code? This doesn't incorporate colored groups. I can update to some simpler thing if desired. I'm coming from R where data(mtcars) is pretty standard. I don't know what's common in python for this.Baecher
The problem is completely independent on "colored groups". Remove those as well. See minimal reproducible example. In any case, I gave you a solution down below.Anagoge
@Anagoge creating a reproducible example was definitely my aim, though I see it could have been reduced further. Given that other plt.savefig() experiments worked, the subplots() command and adding layers to ax was my main suspect. Thank you for the answer!Baecher
A
10

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.

Anagoge answered 23/12, 2017 at 23:23 Comment(0)
I
1

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

Idyllist answered 22/10, 2019 at 21:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.