293

I'm struggling to deal with my plot margins in matplotlib. I've used the code below to produce my chart:

plt.imshow(g)
c = plt.colorbar()
c.set_label("Number of Slabs")
plt.savefig("OutputToUse.png")

However, I get an output figure with lots of white space on either side of the plot. I've searched google and read the matplotlib documentation, but I can't seem to find how to reduce this.

4
  • 1
    Is the problem the amount of whitespace in the extent of the imshow figure, or the amount of border whitespace in the resultant png, around the figure, generated by savefig? Commented Oct 28, 2010 at 14:45
  • I think both - there seems to be a lot of space in both the viewing window and in the PNG. However, the important output is the png file produced by savefig - so that is what I'd like to sort. Commented Oct 28, 2010 at 15:36
  • I've just been cropping them in GIMP afterward. :/ Commented Nov 28, 2011 at 17:42
  • ax.margins(x=0.01) is what you're looking for! Commented Nov 9, 2020 at 12:52

13 Answers 13

394

One way to automatically do this is the bbox_inches='tight' kwarg to plt.savefig.

E.g.

import matplotlib.pyplot as plt
import numpy as np
data = np.arange(3000).reshape((100,30))
plt.imshow(data)
plt.savefig('test.png', bbox_inches='tight')

Another way is to use fig.tight_layout()

import matplotlib.pyplot as plt
import numpy as np

xs = np.linspace(0, 1, 20); ys = np.sin(xs)

fig = plt.figure()
axes = fig.add_subplot(1,1,1)
axes.plot(xs, ys)

# This should be called after all axes have been added
fig.tight_layout()
fig.savefig('test.png')
Sign up to request clarification or add additional context in comments.

8 Comments

Is there any way to make this the default?
If you have multiple subplots and want to save each of them, you can use this with fig.savefig() too. (plt.savefig() will not work in that case.)
All this does is crop the image after it's been rendered; if you're trying to enforce a particular resolution, the image will come out smaller.
@detly - Yep. That's exactly what it does (though it can crop "out" as well and make the image larger, as well). For what you're wanting, have a look at fig.tight_layout(). That function didn't exist when this answer was originally written, otherwise I'd mention it more prominently.
If someone have a problem, use fig = plt.gcf()
|
223

You can adjust the spacing around matplotlib figures using the subplots_adjust() function:

import matplotlib.pyplot as plt
plt.plot(whatever)
plt.subplots_adjust(left=0.1, right=0.9, top=0.9, bottom=0.1)

This will work for both the figure on screen and saved to a file, and it is the right function to call even if you don't have multiple plots on the one figure.

The numbers are fractions of the figure dimensions, and will need to be adjusted to allow for the figure labels.

4 Comments

The values assigned to the parameters and not how much to change it by, they are where to set the margin. In other words, if you want to bring the right edge margin in by 10%, you should set right=0.9, not right=0.1 matplotlib.sourceforge.net/api/…
It makes sense to point out that obviously you can specify negative values in plt.subplots_adjust(). Doing so even allows you to draw outside the figure area and also to deal with annoying margins.
This also works on GridSpec objects by calling the update method (see stackoverflow.com/a/20058199/1030876).
This just position the subplot on the canvas left=0.1,right=0.9 just makes left margin 10% of right marging,
77

All you need is

plt.tight_layout()

before your output.

In addition to cutting down the margins, this also tightly groups the space between any subplots:

x = [1,2,3]
y = [1,4,9]
import matplotlib.pyplot as plt
fig = plt.figure()
subplot1 = fig.add_subplot(121)
subplot1.plot(x,y)
subplot2 = fig.add_subplot(122)
subplot2.plot(y,x)
fig.tight_layout()
plt.show()

4 Comments

I think this is really the best method. It doesn't require saving the figure like `bbox='tight' and fixes all kinds of other layout issues in cramped figures.
this should be the correct answer because it behaves as you'd expect as it applies to the FIGURE instead of the image.
Strangely enough, this also changes the width of the actual plot (i.e. the peaks are closer together) compared to bbox_inches='tight', which just clips the white space around the edges but leaves the plot alone. I created the figure with plt.figure(figsize=(10,3)).
No, it is not always enough to achieve what you want.
40

Sometimes, the plt.tight_layout() doesn't give me the best view or the view I want. Then why don't plot with arbitrary margin first and do fixing the margin after plot? Since we got nice WYSIWYG from there.

import matplotlib.pyplot as plt

fig,ax = plt.subplots(figsize=(8,8))

plt.plot([2,5,7,8,5,3,5,7,])
plt.show()

Change border and spacing GUI here

Then paste settings into margin function to make it permanent:

fig,ax = plt.subplots(figsize=(8,8))

plt.plot([2,5,7,8,5,3,5,7,])
fig.subplots_adjust(
    top=0.981,
    bottom=0.049,
    left=0.042,
    right=0.981,
    hspace=0.2,
    wspace=0.2
)
plt.show()

2 Comments

This is really a nice hint - I never spotted the "export" button there.. Thanks.
This really helped me with the borders around subplots
20

In case anybody wonders how how to get rid of the rest of the white margin after applying plt.tight_layout() or fig.tight_layout(): With the parameter pad (which is 1.08 by default), you're able to make it even tighter: "Padding between the figure edge and the edges of subplots, as a fraction of the font size." So for example

plt.tight_layout(pad=0.05)

will reduce it to a very small margin. Putting 0 doesn't work for me, as it makes the box of the subplot be cut off a little, too.

1 Comment

I used pad=0.5 instead because I immediately ran into a case where this clips content: if the highest y-axis tick mark is near the top of the plot area then its label will be cut off. Nice tip though, I came looking for this.
19

Just use ax = fig.add_axes([left, bottom, width, height]) if you want exact control of the figure layout. eg.

left = 0.05
bottom = 0.05
width = 0.9
height = 0.9
ax = fig.add_axes([left, bottom, width, height])

Comments

11
plt.savefig("circle.png", bbox_inches='tight',pad_inches=-1)

3 Comments

"pad_inches=-1" cause my savefig only produce part of the figure.
Using a parameter in the savefig function is elegant, however the negative value for pad_inches is not necessarily needed in every case.
set it to 0, helps
6

inspired by Sammys answer above:

margins = {  #     vvv margin in inches
    "left"   :     1.5 / figsize[0],
    "bottom" :     0.8 / figsize[1],
    "right"  : 1 - 0.3 / figsize[0],
    "top"    : 1 - 1   / figsize[1]
}
fig.subplots_adjust(**margins)

Where figsize is the tuple that you used in fig = pyplot.figure(figsize=...)

1 Comment

This seems to leave suptitle (but not title) off center a bit.
6

With recent matplotlib versions you might want to try Constrained Layout:

constrained_layout automatically adjusts subplots and decorations like legends and colorbars so that they fit in the figure window while still preserving, as best they can, the logical layout requested by the user.

constrained_layout is similar to tight_layout, but uses a constraint solver to determine the size of axes that allows them to fit.

constrained_layout needs to be activated before any axes are added to a figure.

Too bad pandas does not handle it well...

Comments

4

The problem with matplotlibs subplots_adjust is that the values you enter are relative to the x and y figsize of the figure. This example is for correct figuresizing for printing of a pdf:

For that, I recalculate the relative spacing to absolute values like this:

pyplot.subplots_adjust(left = (5/25.4)/figure.xsize, bottom = (4/25.4)/figure.ysize, right = 1 - (1/25.4)/figure.xsize, top = 1 - (3/25.4)/figure.ysize)

for a figure of 'figure.xsize' inches in x-dimension and 'figure.ysize' inches in y-dimension. So the whole figure has a left margin of 5 mm, bottom margin of 4 mm, right of 1 mm and top of 3 mm within the labels are placed. The conversion of (x/25.4) is done because I needed to convert mm to inches.

Note that the pure chart size of x will be "figure.xsize - left margin - right margin" and the pure chart size of y will be "figure.ysize - bottom margin - top margin" in inches

Other sniplets (not sure about these ones, I just wanted to provide the other parameters)

pyplot.figure(figsize = figureSize, dpi = None)

and

pyplot.savefig("outputname.eps", dpi = 100)

1 Comment

Where did you get xsize and ysize from. I use those properties and I get AttributeError: 'Figure' object has no attribute 'xsize'
2

For me, the answers above did not work with matplotlib.__version__ = 1.4.3 on Win7. So, if we are only interested in the image itself (i.e., if we don't need annotations, axis, ticks, title, ylabel etc), then it's better to simply save the numpy array as image instead of savefig.

from pylab import *

ax = subplot(111)
ax.imshow(some_image_numpyarray)
imsave('test.tif', some_image_numpyarray)

# or, if the image came from tiff or png etc
RGBbuffer = ax.get_images()[0].get_array()
imsave('test.tif', RGBbuffer)

Also, using opencv drawing functions (cv2.line, cv2.polylines), we can do some drawings directly on the numpy array. http://docs.opencv.org/2.4/modules/core/doc/drawing_functions.html

Comments

2
# import pyplot
import matplotlib.pyplot as plt

# your code to plot the figure

# set tight margins
plt.margins(0.015, tight=True)

Comments

0

compact matplotlib template for subplots

As I have the same hussle each time I set up a new plot, I decided to document a template for this on stackoverflow.

template

import matplotlib.pylab as plt

def plot_figure_tempalte():
    nrows=2
    ncols=3

    fig, axs = plt.subplots(
        nrows=nrows, ncols=ncols, figsize=(14.9, 10.5),  # A6: 145mm x 105mm
        gridspec_kw=dict(
            left=.1, bottom=.05, right=.99, top=.8, wspace=.1, hspace=.3
        )
    )

    for rr in range(nrows):
        for cc in range(ncols):
            axs[rr, cc].set_title(f"row {rr}, col {cc}")

    fig.savefig("output.png", dpi=300)


if __name__ == "__main__":
    plot_figure_tempalte()

output.png

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.