Define aspect ratio when using twinx in new version of matplotlib

matplotlib, python, subplot, twinx

Solution

As I just commented on a respective matplotlib issue,

"aspect" in matplotlib always refers to the data, not the axes box. Therefore setting the aspect for twinned or shared axes and letting the box be adjustable actually only makes sense when the scales are the same - or differ by an offset (as opposed to any other linear or nonlinear function). Matplotlib does not perform any check on this, so it disallows for adjustable='box' in such case.

It seems to me that using aspect here is merely a workaround for getting a fixed ratio for the axes box. Matplotlib does not provide any clear codepath for that as of now, but one could e.g. force the axes box into a square space by adjusting the subplot parameters

import numpy as np
import matplotlib.pyplot as plt

def squarify(fig):
    w, h = fig.get_size_inches()
    if w > h:
        t = fig.subplotpars.top
        b = fig.subplotpars.bottom
        axs = h*(t-b)
        l = (1.-axs/w)/2
        fig.subplots_adjust(left=l, right=1-l)
    else:
        t = fig.subplotpars.right
        b = fig.subplotpars.left
        axs = w*(t-b)
        l = (1.-axs/h)/2
        fig.subplots_adjust(bottom=l, top=1-l)


x = np.linspace(0,1.6,50) + 50.0

fig, ax = plt.subplots()
ax2 = ax.twinx()

ax.set(xlim = [50.0, 51.6], ylim = [0.0, 1.1])
ax2.set(ylim = [0.0, 11.0])

ax.plot(x,np.sin(x-50.0),'b')
ax2.plot(x,np.cos(x-50.0)*10.,'r')

ax.grid(True, which='major',linestyle='solid')

squarify(fig)
fig.canvas.mpl_connect("resize_event", lambda evt: squarify(fig))

plt.show()

Also see this answer for more than one subplot.

If you want to use `mpl_toolkits` and make your hands dirty, this answer would be a good read.

Problem

Current version of matplotlib do not allow `box-forced` anymore, how should I do the same thing as the answer? I am using matplotlib 3.1.0. After I ploted another set of data on the same plot with `twinx()` function, I want to change the aspect ratio of the actual plot area to 1. Normally I do this and it works for non-twinx axis ``` ratio = 1 xleft, xright = ax.get_xlim() ybottom, ytop = ax.get_ylim() ax.set_aspect(abs((xright - xleft) / (ybottom - ytop)) * ratio) ``` For twinx axis, the above code do not work, but will not raise any error either. Then I found an answer here The code basically used the same method to set aspect ratio to 1, only with `box-forced` option. ``` import numpy as np import matplotlib.pyplot as plt x = np.linspace(0, 1.6, 50) + 50.0 fig, ax = plt.subplots() ax2 = ax.twinx() XLIM = [50.0, 51.6] YLIM = [0.0, 1.1, 0.0, 11.0] ax.plot(x, np.sin(x - 50.0), 'b') ax2.plot(x, np.cos(x - 50.0) * 10., 'r') # set aspect to 1 ax.set(adjustable='box-forced', xlim=XLIM, ylim=YLIM[:2], xticks=np.arange(XLIM[0], XLIM[1], 0.2), yticks=np.arange(YLIM[0], YLIM[1] + 0.1, 0.1)[:-1], aspect=(XLIM[1] - XLIM[0]) / (YLIM[1] - YLIM[0])) ax2.set(adjustable='box-forced', ylim=YLIM[2:], yticks=np.arange(YLIM[2], YLIM[3] + 1.0, 1.0), aspect=(XLIM[1] - XLIM[0]) / (YLIM[3] - YLIM[2])) ax.grid(True, which='major', linestyle='solid') plt.show() ``` This code in my python don't work, raises ``` ValueError: 'box-forced' is not a valid value for adjustable; supported values are 'box', 'datalim' ``` And if I change that to `'box'`, it gives ``` RuntimeError: Adjustable 'box' is not allowed in a twinned Axes. Use 'datalim' instead. ``` I am not sure from when the `box-forced` was removed. Now how should we set the aspect ratio in a 'box' manner? Thanks! For reference: matplotlib.axes.Axes.set_adjustable

Original source

Related problems