9

I have 2 subplots:

plt.subplot(1, 2, 1)
plt.plot(x, y)
plt.subplot(1, 2, 2)
plt.plot(u, v)

The range of u and v are [0,1] for both, and the range of x and y is random, and x is different from y. I would like to make a both subplots squared, so the length of the x axis should be equal to the length of the y axis. For the second subplot it is easy using another SO question:

plt.subplot(1, 2, 1)
plt.plot(x, y)
plt.subplot(1, 2, 2)
plt.plot(u, v)
plt.axis('scaled')

However this method does not work for the first subplot, it scales down the y axis to something really small as the values are in a small range, while x axis is basically in range [0,1], so it has the same scaling as the second subplot.

How can i scale the y axis of the first subplot to be equal to the other axis lengths?

Gábor Erdős
  • 3,599
  • 4
  • 24
  • 56

3 Answers3

12

You want your subplots to be squared. The function plt.axis accepts 'square' as a parameter and it really means it: it will make the current axes squared, both in pixels and in data units.

x = np.arange(2)
y = x / 3
u = v = [0, 1]

plt.subplot(121)
plt.plot(x, y)
plt.axis('square')

plt.subplot(122)
plt.plot(u, v)
plt.axis('square')

enter image description here

Unfortunately this will expand the Y axis limits way beyond the Y data range, which is not what you want. What you need is the subplot's aspect ratio to be the inverse of the data ranges ratio. AFAIK there isn't any convenience function or method for this but you can write your own.

def make_square_axes(ax):
    """Make an axes square in screen units.

    Should be called after plotting.
    """
    ax.set_aspect(1 / ax.get_data_ratio())

plt.subplot(121)
plt.plot(x, y)
make_square_axes(plt.gca())

plt.subplot(122)
plt.plot(u, v)
make_square_axes(plt.gca())

enter image description here

Stop harming Monica
  • 12,141
  • 1
  • 36
  • 56
3

@Stop harming Monica is a great answer. I also find a new method to achieve this. (using ax.set_box_aspect)

The box aspect is the ratio between axes height and axes width in physical units, independent of the data limits.

which I think is exactly what you want.

For details, see demo and API Reference

Example (with code in @Stop harming Monica)

import matplotlib.pyplot as plt
import numpy as np

x = np.arange(2)
y = x / 3
u = v = [0, 1]

plt.subplot(121)
plt.plot(x, y)
plt.gca().set_box_aspect(1)

plt.subplot(122)
plt.plot(u, v)
plt.axis('square')

plt.show()

enter image description here

Note

ax.set_box_aspect is a new method. The version of matplotlib should be at least 3.3.0.

user343233
  • 99
  • 6
2

Since you need equal axis, you should set plt.axis('equal') instead of 'scaled'. In fact, even for the second scenario, the 'equal' keyword should be used to give a square figure

x = np.linspace(1, 0.1, 10)
y = np.linspace(1, 1, 10)
fig, ax = plt.subplots(figsize=(5, 5))
ax.plot(x, y, '.')
ax.axis('equal')
plt.show()

Square plot with equal aspect ratio

Note that setting the figsize to (length, length) gives an actual square, otherwise the default figure size is occupied.

krm
  • 847
  • 8
  • 13
  • In that case, try setting `figsize` to the (2, 2) or (n, n) in general or see [this](https://stackoverflow.com/questions/3207850/creating-square-subplots-of-equal-height-and-width-in-matplotlib?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa) if you are trying to this for subplots. Either ways, maybe you should update the question to specify this requirement – krm Apr 27 '18 at 09:10
  • I think the question is clear, i indicated the subplots, and i also indicated that each axis should have the same length. In this answer, the length of the Y axis is equal to 2*X axis length, in case of two subplots. – Gábor Erdős Apr 27 '18 at 09:19
  • You seem to have deleted the comment where you specified that 'ylim' has to still work after doing this. Anyways, does figsize=(2, 4) help in the case you are shooting for 2 subplots arrange in two rows? Or figsize=(4,2) incase you want columns? – krm Apr 27 '18 at 09:34
  • Setting the figsize wont equalize the axis lengths. With (5, 10) there is still some placing between the plots etc, which makes the lengths uneven. – Gábor Erdős Apr 27 '18 at 10:26