99

I have created a figure using matplotlib but I have realized the plot axis and the drawn line gets zoomed out.

enter image description here

Reading this earlier discussion thread, it explains how to set the figure size.

fig, ax = plt.subplots()
fig.set_size_inches(3, 1.5)
plt.savefig(file.jpeg, edgecolor='black', dpi=400, facecolor='black', transparent=True)

With the above code (other configurations removed for brevity), I do get a resulting image file with 1200 X 600 desired dimensions(should we say resolution too?) and desired file size.

The projected image is scaled out in an unusual way, annotations for example are enlarged. While I can set the size of the labels on the axis, the figure doesn't look proportional with respect to the scale since the bottom and right spines are large and so are the plotted lines.

The question, therefore, is, what configurations are going wrong?

shafee
  • 15,566
  • 3
  • 19
  • 47
qboomerang
  • 1,931
  • 3
  • 15
  • 20
  • 2
    I do not quite understand the issue. But what I can say is that when both, figure size in inches *and* dpi, should be fixed you need to change the size of the objects you perceive as "too large", i.e. setting the font size, the line width, tickmarks etc. smaller. Usually you would just either fix dpi *or* size in inches to achieve a desired figure. – ImportanceOfBeingErnest Dec 04 '17 at 13:05
  • @ImportanceOfBeingErnest I created a picture which turned out with the desired dimensions and the file size is within the desire range. However the graph drawn turned out to be too zoomed/scaled out. Reading other threads it seems there is a scaling and aspect-ratio issue that could be at play. Your suggestion to declare each element size sounds rather unusual as I would think that there should be a way to adjust all elements in the canvas automatically with respect to the previously set parameters of figsize() and dpi. – qboomerang Dec 04 '17 at 15:05
  • Let me try again: If e.g. the pixel dimension is to be `1200 X 600`, you would usually create a figure with size `(12 x 6)` inches and 100 dpi, in which case the lines are probably adequately sized. Using a dpi of 400 instead makes each point in the figure 4 times as large. Apparently that is not what you want. But if you need it, you can scale down the elements (e.g. make the linewith 0.25 instead of 1). (There is no scaling or aspect issue here, this is all how it works and as expected.) – ImportanceOfBeingErnest Dec 04 '17 at 15:47
  • Looking at the comment you mention, this is about something completely different; it tells you that you do not get a square axes, even if you use a square figure. This is completely unrelated to you problem here. – ImportanceOfBeingErnest Dec 04 '17 at 15:56
  • I think I get your point. My understanding of the relationship between dpi and figsize(x,y) was slightly different from what you have explained. Based on your comment, I have tested your suggestion and it looks promising. Perhaps I should change the title of the question... – qboomerang Dec 04 '17 at 16:05
  • [This question](https://stackoverflow.com/questions/44724369/scale-plot-size-of-matplotlib-plots-in-jupyter-notebooks) might be of interest here. Still, I'm not sure what an answer to this question here would be. Or is it already solved? – ImportanceOfBeingErnest Dec 04 '17 at 16:16
  • Indeed it works better that way to have a lower dpi and increase the value to the figure dimensions. What wasn't clear to me from the documentation is the relationship between dpi and figsize(x, y) parameters and the resulting resolution. – qboomerang Dec 04 '17 at 16:18

1 Answers1

217

Figure size (figsize) determines the size of the figure in inches. This gives the amount of space the axes (and other elements) have inside the figure. The default figure size is (6.4, 4.8) inches in matplotlib 2. A larger figure size will allow for longer texts, more axes or more ticklabels to be shown.

Dots per inches (dpi) determines how many pixels the figure comprises. The default dpi in matplotlib is 100. A figure of figsize=(w,h) will have

px, py = w*dpi, h*dpi  # pixels
# e.g.
# 6.4 inches * 100 dpi = 640 pixels

So in order to obtain a figure with a pixel size of e.g. (1200,600) you may chose several combinations of figure size and dpi, e.g.

figsize=(15,7.5), dpi= 80
figsize=(12,6)  , dpi=100
figsize=( 8,4)  , dpi=150
figsize=( 6,3)  , dpi=200
etc.

Now, what is the difference? This is determined by the size of the elements inside the figure. Most elements like lines, markers, texts have a size given in points.
Matplotlib figures use Points per inch (ppi) of 72. A line with thickness 1 point will be 1./72. inch wide. A text with fontsize 12 points will be 12./72. inch heigh.

Of course if you change the figure size in inches, points will not change, so a larger figure in inches still has the same size of the elements. Changing the figure size is thus like taking a piece of paper of a different size. Doing so, would of course not change the width of the line drawn with the same pen.

On the other hand, changing the dpi scales those elements. At 72 dpi, a line of 1 point size is one pixel strong. At 144 dpi, this line is 2 pixels strong. A larger dpi will therefore act like a magnifying glass. All elements are scaled by the magnifying power of the lens.

A comparisson for constant figure size and varying dpi is shown in the image below on the left. On the right you see a constant dpi and varying figure size. Figures in each row have the same pixel size.

enter image description here

Code to reproduce:

import matplotlib.pyplot as plt
%matplotlib inline

def plot(fs,dpi):
    fig, ax=plt.subplots(figsize=fs, dpi=dpi)
    ax.set_title("Figsize: {}, dpi: {}".format(fs,dpi))
    ax.plot([2,4,1,5], label="Label")
    ax.legend()

figsize=(2,2)
for i in range(1,4):
    plot(figsize, i*72)

dpi=72
for i in [2,4,6]:
    plot((i,i), dpi)
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • 3
    "Matplotlib figures use Points per inch (ppi) of 72" - Where did you find this from? I can't find this anywhere. The only thing I can find is that the default DPI is 100. – Lone Learner Aug 27 '19 at 18:11
  • 8
    @LoneLearner Yeah, there is only a very hidden reference in the documentation about it in the [transforms tutorial](https://matplotlib.org/tutorials/advanced/transforms_tutorial.html#using-offset-transforms-to-create-a-shadow-effect). And of course it's all over the code, e.g. [here](https://github.com/matplotlib/matplotlib/blob/40dfc353aa66b93fd0fbc55ca1f51701202c0549/lib/matplotlib/axes/_base.py#L694). – ImportanceOfBeingErnest Aug 27 '19 at 18:26
  • How do you set DPI to one number and PPI to another number? All I see is that you have changed the figure size and dpi for `subplots`? – CMCDragonkai Jul 13 '20 at 07:53
  • 1
    @ImportanceOfBeingErnest This makes absolutely no sense to me. Why do you get thicker lines with a higher DPI? – adam.hendry Jul 23 '20 at 06:42
  • @CMCDragonkai You cannot change the ppi in matplotlib. It's always 72. Per definition. – ImportanceOfBeingErnest Jul 23 '20 at 11:35
  • 2
    @A.Hendry dpi means dot (pixels) per inch. If you double the dpi you double the dots. A line that was 1 pixel wide would then be two pixels wide. Suppose you have a line of 1 point in width. It will be 1 point / ppi * dpi = 1 / 72 ppi * 72 dpi = 1 pixel. Now you double the dpi, hence 1 point / ppi * dpi = 1 / 72 ppi * 144 dpi = 2 pixel. – ImportanceOfBeingErnest Jul 23 '20 at 11:41
  • 2
    @ImportanceOfBeingErnest Thank you for your explanation. So why do the graphs get bigger on the left with higher dpi? Aren't they all supposed to be 2in x 2in? – adam.hendry Jul 23 '20 at 14:09
  • 1
    @ImportanceOfBeingErnest For digital image processing, if I load an image scanned at 300 dpi and one at 600 dpi, am I unable to differentiate because matplotlib always uses 72 dpi by default? – adam.hendry Jul 23 '20 at 14:15
  • @A.Hendry Yes they are 2 by 2 inches. The size in pixels varies of course - that is the point of the different dpi (with 72 dpi, you have 72 dots per inch, a 2 inch figure is therefore 144 dots (pixels) large; with 144 dpi you have 144 dots per inch, hence a 2 inch figure is 288 pixels large. Matplotlib does *not* use 72 dpi by default. You can *set* the dpi as shown in the answer. But it's unrelated to what your scanner does. When showing an image inside of matplotlib only the number of pixels counts. – ImportanceOfBeingErnest Jul 23 '20 at 17:39
  • 3
    @ImportanceOfBeingErnest any advice on increasing the DPI as you did here while keeping the text and line sizes about the same? You can go through each and every setting but it's a lot of settings to change.. – getup8 Nov 24 '20 at 07:10
  • The more precise formula used is `px, py = floor(w*dpi), floor(h*dpi)`, (or `px, py = int(w*dpi), int(h*dpi)`), although `ceil()` would have made sense too. So since,`dpi` and `w`,`h` are float numbers in matplotlib, the `floor()` needs to be clarified in answers. – Sohail Si Jun 04 '23 at 11:52