[python] matplotlib savefig() plots different from show()

When I use show() to plot the graphs in X, the graphs looks very good. However when I start to use savefig() to generate large amount of graphs, the savefig() generated graphs ' font, lines, polygons all look smaller than the show() generated graph. My environment is Ubuntu and the backend for show() is Qt4Agg. How can I make the show() plot and the savefig() plot looks consistent?

This question is related to python graph plot matplotlib

The answer is


I have fixed this in my matplotlib source, but it's not a pretty fix. However, if you, like me, are very particular about how the graph looks, it's worth it.

The issue seems to be in the rendering backends; they each get the correct values for linewidth, font size, etc., but that comes out slightly larger when rendered as a PDF or PNG than when rendered with show().

I added a few lines to the source for PNG generation, in the file matplotlib/backends/backend_agg.py. You could make similar changes for each backend you use, or find a way to make a more clever change in a single location ;)

Added to my matplotlib/backends/backend_agg.py file:

    # The top of the file, added lines 42 - 44
42  # @warning: CHANGED FROM SOURCE to draw thinner lines
43  PATH_SCALAR = .8
44  FONT_SCALAR = .95

    # In the draw_markers method, added lines 90 - 91
89  def draw_markers(self, *kl, **kw):
90      # @warning: CHANGED FROM SOURCE to draw thinner lines
91      kl[0].set_linewidth(kl[0].get_linewidth()*PATH_SCALAR)
92      return self._renderer.draw_markers(*kl, **kw)

    # At the bottom of the draw_path method, added lines 131 - 132:
130 else:
131     # @warning: CHANGED FROM SOURCE to draw thinner lines
132     gc.set_linewidth(gc.get_linewidth()*PATH_SCALAR)
133     self._renderer.draw_path(gc, path, transform, rgbFace)

    # At the bottom of the _get_agg_font method, added line 242 and the *FONT_SCALAR
241     font.clear()
242     # @warning: CHANGED FROM SOURCE to draw thinner lines
243     size = prop.get_size_in_points()*FONT_SCALAR
244     font.set_size(size, self.dpi)

So that suits my needs for now, but, depending on what you're doing, you may want to implement similar changes in other methods. Or find a better way to do the same without so many line changes!

Update: After posting an issue to the matplotlib project at Github, I was able to track down the source of my problem: I had changed the figure.dpi setting in the matplotlibrc file. If that value is different than the default, my savefig() images come out different, even if I set the savefig dpi to be the same as the figure dpi. So, instead of changing the source as above, I just kept the figure.dpi setting as the default 80, and was able to generate images with savefig() that looked like images from show().

Leon, had you also changed that setting?


Old question, but apparently Google likes it so I thought I put an answer down here after some research about this problem.

If you create a figure from scratch you can give it a size option while creation:

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(3, 6))

plt.plot(range(10)) #plot example
plt.show() #for control

fig.savefig('temp.png', dpi=fig.dpi)

figsize(width,height) adjusts the absolute dimension of your plot and helps to make sure both plots look the same.

As stated in another answer the dpi option affects the relative size of the text and width of the stroke on lines, etc. Using the option dpi=fig.dpi makes sure the relative size of those are the same both for show() and savefig().

Alternatively the figure size can be changed after creation with:

fig.set_size_inches(3, 6, forward=True)

forward allows to change the size on the fly.

If you have trouble with too large borders in the created image you can adjust those either with:

plt.tight_layout()
#or:
plt.tight_layout(pad=2)

or:

fig.savefig('temp.png', dpi=fig.dpi, bbox_inches='tight')
#or:
fig.savefig('temp.png', dpi=fig.dpi, bbox_inches='tight', pad_inches=0.5)

The first option just minimizes the layout and borders and the second option allows to manually adjust the borders a bit. These tips helped at least me to solve my problem of different savefig() and show() images.


You render your matplotlib plots to different devices (e.g., on-screen via Quartz versus to to-file via pdf using different functions (plot versus savefig) whose parameters are nearly the same, yet the default values for those parameters are not the same for both functions.

Put another way, the savefig default parameters are different from the default display parameters.

Aligning them is simple if you do it in the matplotlib config file. The template file is included with the source package, and named matplotlibrc.template. If you did not create one when you installed matplotlib, you can get this template from the matplotlib source, or from the matplotlib website.

Once you have customized this file the way you want, rename it to matplotlibrc (no extension) and save it to the directory .matplotlib (note the leading '.') which should be in your home directory.

The config parameters for saving figures begins at about line 314 in the supplied matplotlibrc.template (first line before this section is: ### SAVING FIGURES).

In particular, you will want to look at these:

savefig.dpi       : 100         # figure dots per inch
savefig.facecolor : white       # figure facecolor when saving
savefig.edgecolor : white       # figure edgecolor when saving
savefig.extension : auto        # what extension to use for savefig('foo'), or 'auto'

Below these lines are the settings for font type and various image format-specific parameters.

These same parameters for display, i.e., PLT.show(), begin at about line 277 a in the matplotlibrc.template (this section preceded with the line: ### FIGURE):

figure.figsize   : 8, 6          
figure.dpi       : 80            
figure.facecolor : 0.75       
figure.edgecolor : white     

As you can see by comparing the values of these two blocks of parameters, the default settings for the same figure attribute are different for savefig versus display (show).


savefig specifies the DPI for the saved figure (The default is 100 if it's not specified in your .matplotlibrc, have a look at the dpi kwarg to savefig). It doesn't inheret it from the DPI of the original figure.

The DPI affects the relative size of the text and width of the stroke on lines, etc. If you want things to look identical, then pass fig.dpi to fig.savefig.

E.g.

import matplotlib.pyplot as plt

fig = plt.figure()
plt.plot(range(10))
fig.savefig('temp.png', dpi=fig.dpi)

Examples related to python

programming a servo thru a barometer Is there a way to view two blocks of code from the same file simultaneously in Sublime Text? python variable NameError Why my regexp for hyphenated words doesn't work? Comparing a variable with a string python not working when redirecting from bash script is it possible to add colors to python output? Get Public URL for File - Google Cloud Storage - App Engine (Python) Real time face detection OpenCV, Python xlrd.biffh.XLRDError: Excel xlsx file; not supported Could not load dynamic library 'cudart64_101.dll' on tensorflow CPU-only installation

Examples related to graph

How to plot multiple functions on the same figure, in Matplotlib? Python equivalent to 'hold on' in Matlab How to combine 2 plots (ggplot) into one plot? how to draw directed graphs using networkx in python? What is the difference between dynamic programming and greedy approach? Plotting using a CSV file Python equivalent of D3.js Count number of times a date occurs and make a graph out of it How do I create a chart with multiple series using different X values for each series? Rotating x axis labels in R for barplot

Examples related to plot

Fine control over the font size in Seaborn plots for academic papers Why do many examples use `fig, ax = plt.subplots()` in Matplotlib/pyplot/python Modify the legend of pandas bar plot Format y axis as percent Simple line plots using seaborn Plot bar graph from Pandas DataFrame Plotting multiple lines, in different colors, with pandas dataframe Plotting in a non-blocking way with Matplotlib What does the error "arguments imply differing number of rows: x, y" mean? matplotlib get ylim values

Examples related to matplotlib

"UserWarning: Matplotlib is currently using agg, which is a non-GUI backend, so cannot show the figure." when plotting figure with pyplot on Pycharm How to increase image size of pandas.DataFrame.plot in jupyter notebook? How to create a stacked bar chart for my DataFrame using seaborn? How to display multiple images in one figure correctly? Edit seaborn legend How to hide axes and gridlines in Matplotlib (python) How to set x axis values in matplotlib python? How to specify legend position in matplotlib in graph coordinates Python "TypeError: unhashable type: 'slice'" for encoding categorical data Seaborn Barplot - Displaying Values