0
votes

I want to have a row of subplots with the same aspect ratio. Let's say I want to plot two different functions that have different extents on the y axis.

There seem to be lots of questions on this but the examples seem to conveniently have the same axes for all subplots. In any case none that I found were helpful to me.

I've tried the following

import numpy as np
import matplotlib.pyplot as plt

xdata = np.arange(0,2,.01)
ydata1 = xdata
ydata2 = xdata ** 2

fig, ax = plt.subplots(1,2)

ax[0].plot(xdata,ydata1)
ax[1].plot(xdata,ydata2)

ax[0].set_aspect(.5)
ax[1].set_aspect(.5)
plt.show(fig)

plots

However, matplotlib appears to be using the x and y axis data to determine the aspect ratio and because the y axes have different extents the subplots come out different. This is super weird to me. How do I make them the same physical aspect ratio?

I guess one can try extracting the the limits for each subplot and calculate the right aspect ratio individually but this seems to be a very roundabout way and I would expect such a basic functionality as making all subplots look the same to be built in.

EDIT So what I want is for both subplots to have the same aspect ratio in the human sense, that is that the ratio of the width of the figure to the height is the same for both. For example in Mathematica I would achieve this by simply setting the AspectRatio attribute:

xdata = Range[0, 2, .01];
ydata1 = {#, #} & /@ xdata;
ydata2 = {#, #^2} & /@ xdata;
fig = GraphicsRow[
   ListLinePlot[#, AspectRatio -> 1/GoldenRatio, Frame -> True, 
      ImageSize -> 300] & /@ {ydata1, ydata2}];
Export["fig.png", fig]

Made by Mathematica

2
fig, ax = plt.subplots(1,2, sharex=True, sharey=True)? - Quang Hoang
@Philo I think you're misinterpreting what setting the aspect ratio does in matplotlib. you're also not very clear on how you want the figure to look, maybe you could draw a diagram? (ASCII art is fine) - Sam Mason
@SamMason I've added a clarification to the question. soyapencil's answer seems to confirm my interpretation though. - Philo
at the moment the only control you have is to set the figure size and hope the resulting axes come out with an appropriate aspect ratio. this is demonstrated by soyapencil and is what @ImportanceOfBeingErnest is referring to - Sam Mason

2 Answers

2
votes

Short answer: use the figsize keyword argument when making subplots:

import numpy as np
import matplotlib.pyplot as plt

xdata = np.arange(0,2,.01)
ydata1 = xdata
ydata2 = xdata ** 2

fig = plt.figure(figsize=(10,6))
ax = fig.subplots(1,2)

ax[0].plot(xdata,ydata1)
ax[1].plot(xdata,ydata2)

# Squares
ax[0].plot([0,1,1,0],[0,0,1,1])
ax[1].plot([0,1,1,0],[0,0,1,1])

plt.show(fig)

figsize is a tuple in inches, with the first element as the width in the x-direction, the second element as the width in the y-direction.

It applies to the whole figure; you will need to tweak it to get a suitable shape for your subplots.

enter image description here

Longer answer: .set_aspect(num) sets the y-axis to x-axis ratio for each plot.

So, when you used .set_aspect(.5) on each axis, you told matplotlib to reconfigure each axis such that the y-axis is 0.5 times as big as the x-axis. This means that a 1x1 square actually looks like a rectangle on each plot:

import numpy as np
import matplotlib.pyplot as plt

xdata = np.arange(0,2,.01)
ydata1 = xdata
ydata2 = xdata ** 2

fig, ax = plt.subplots(1,2)

ax[0].plot(xdata,ydata1)
ax[1].plot(xdata,ydata2)

# Squares
ax[0].plot([0,1,1,0],[0,0,1,1])
ax[1].plot([0,1,1,0],[0,0,1,1])

ax[0].set_aspect(.5)
ax[1].set_aspect(.5)
plt.show(fig)

That's why your plots are different shapes; the axis limits are different, but you've set the size of shapes on both plots to be the same.

enter image description here

1
votes

The question isn't entirely clear on what the desired outcome is. Possibly you want to have a fixed data aspect with out changing the axes?

import numpy as np
import matplotlib.pyplot as plt

xdata = np.arange(0,2,.01)
ydata1 = xdata
ydata2 = xdata ** 2

fig, ax = plt.subplots(1,2)

ax[0].plot(xdata,ydata1)
ax[1].plot(xdata,ydata2)

ax[0].set_aspect(.5, adjustable="datalim")
ax[1].set_aspect(.5, adjustable="datalim")
plt.show()

enter image description here