33

I have a problem with plotting multiple subplots. I would like to set the PHYSICAL aspect ratio of the subplots to a fixed value. In my example I have 12 subplots (4 rows and 3 columns) on a landscape A4 figure. There all subplots are nicely placed on the whole figure, and for all subplots the height is nearly equal to the width.

But if I change the layout of my figure to portrait, the subplots are stretched vertically. And this is exactly what should not happen. I would like to have the same height and width of the subplots as on the landscape figure. Is there a possibility that the aspect ratio of the subplots stay the same?

Thanks in advance, Peter

EDIT: I have found a workaround. But this just works for non-logarithmic axes...

aspectratio=1.0
ratio_default=(ax.get_xlim()[1]-ax.get_xlim()[0])/(ax.get_ylim()[1]-ax.get_ylim()[0])
ax.set_aspect(ratio_default*aspectratio)
1
  • As of matplotlib version 3.8, the set_box_aspect method with aspect = 1 achieves what you want for both linear and log scales. Note that this method is different from the set_aspect method. For more details, see my answer below. Commented Feb 20, 2024 at 13:34

4 Answers 4

22

Actually, what you're wanting is quite simple... You just need to make sure that adjustable is set to 'box' on your axes, and you have a set aspect ratio for the axes (anything other than 'auto').

You can either do this with the adjustable kwarg when you create the subplots. Alternatively, you can do this after their creation by calling ax.set_adjustable('box'), or by calling ax.set_aspect(aspect, adjustable='box') (where aspect is either 'equal' or a number).

Now, regardless of how the figure is resized, the subplots will maintain the same aspect ratio.

For example:

import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = fig.add_subplot(2,1,1, adjustable='box', aspect=0.3)
ax2 = fig.add_subplot(2,1,2)

ax1.plot(range(10))
ax2.plot(range(10))

plt.show()

Now, compare how the top subplot responds to resizing, vs. how the bottom subplot responds:


The initial plot alt text

Resized to a vertical layout: alt text

Resized to a horizontal layout: alt text

Sign up to request clarification or add additional context in comments.

1 Comment

Hi! Thanks for your reply. Unfortunately, this sets only the aspect ratio of the axes of the subplots. Since I have many different ranges of my axes I would have to set this aspect ratio for every subplot individually. So what I wanted to do is to set the hight and width of my subplots (and not just the axes) to a specific aspect ratio.
7

Your workaround works for me. After plotting the data, I call the following function:

def fixed_aspect_ratio(ratio):
    '''
    Set a fixed aspect ratio on matplotlib plots 
    regardless of axis units
    '''
    xvals,yvals = gca().axes.get_xlim(),gca().axes.get_ylim()

    xrange = xvals[1]-xvals[0]
    yrange = yvals[1]-yvals[0]
    gca().set_aspect(ratio*(xrange/yrange), adjustable='box')

1 Comment

This worked well for me. I modified the function to accept an ax argument so that I could apply it to subplots. I also replaced the gca().axes with ax and gca() with ax.
4

In reply to the remark about the solution not working for logarithmic plots in the edit to the original question - you need to adapt as follows:

def fixed_aspect_ratio_loglog(ratio):
    '''
    Set a fixed aspect ratio on matplotlib loglog plots 
    regardless of axis units
    '''
    xvals,yvals = gca().axes.get_xlim(),gca().axes.get_ylim()

    xrange = log(xvals[1])-log(xvals[0])
    yrange = log(yvals[1])-log(yvals[0])
    gca().set_aspect(ratio*(xrange/yrange), adjustable='box')

(Adaptation for semilog plots should now be obvious)

2 Comments

Who is gca()?
@VictorEijkhout get current axes.
2

As of matplotlib version 3.8, the set_box_aspect method with aspect = 1 achieves what the other answers describe, for both linear and log scales. Note that this method is different from the set_aspect method. Here is an example that uses set_box_aspect with linearly-scaled axes:

import numpy as np
import matplotlib.pyplot as plt
y = np.random.rand(10) * 5 + 10 # example data
fig, ax = plt.subplots()
ax.plot(y)
ax.set_box_aspect(aspect=1)
ax.grid(visible=True, which="both")

enter image description here

The same example with log-scaled axes:

import numpy as np
import matplotlib.pyplot as plt
y = np.random.rand(10) * 5 + 10 # example data
fig, ax = plt.subplots()
ax.plot(y)
ax.set_xscale('log')
ax.set_yscale('log')
ax.set_box_aspect(aspect=1)
ax.grid(visible=True, which="both")

enter image description here

More examples that use set_box_aspect can be found 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.