pythonmatplotlib

Adjusting axe position after applying constrained_layout


Using constrained_layout, I can get this plot:

import matplotlib.pyplot as plt
import numpy as np

N = 200
var_xx = 1**2  # var x = std x squared
var_yy = 1**2
cov_xy = 0.5
cov = np.array([[var_xx, cov_xy], [cov_xy, var_yy]])

rng = np.random.default_rng()
pairs = rng.multivariate_normal([0, 0], cov, size=N, check_valid="raise")


mosaic = [[".", "top"], ["left", "main"]]
fig, axarr = plt.subplot_mosaic(mosaic, constrained_layout=True, width_ratios=[0.5, 1], height_ratios=[0.5, 1])

axarr["main"].scatter(pairs[:, 0], pairs[:, 1], alpha=0.5)
axarr["top"].hist(pairs[:, 0], bins=20)
axarr["left"].hist(pairs[:, 0], bins=20, orientation="horizontal")

axarr["left"].sharey(axarr["main"])
axarr["top"].sharex(axarr["main"])

axarr["top"].tick_params(labelbottom=False)
axarr["main"].tick_params(labelleft=False)
ticklabels = axarr["top"].get_yticklabels()

axarr["main"].set_xlabel("x")
axarr["left"].set_ylabel("y")
axarr["left"].set_xlabel("PDF")
axarr["top"].set_ylabel("PDF")

enter image description here

The horizontal spacing between the subplots is larger than the vertical one, due to constrained layout leaving space for the tick and axis labels of the top subplot. I would like to ignore this and reduce the horizontal spacing to the same as the vertical one.

One approach I tried was to set the main axe position after, hence by adding this at the end of the code:

pos_main = axarr["main"].get_position().transformed(fig.dpi_scale_trans)
pos_top = axarr["top"].get_position().transformed(fig.dpi_scale_trans)
pos_left = axarr["left"].get_position().transformed(fig.dpi_scale_trans)

space = pos_top.ymin - pos_main.ymax
pos_main.update_from_data_x([pos_left.xmax + space, pos_main.xmax])
axarr["main"].set_position(pos_main.transformed(fig.dpi_scale_trans.inverted()))

However, this disables completely constrained_layout for this axe, hence leading to poor results.

enter image description here

How should I first apply constrained_layout, then disable it, and adjust the axis positions?


Solution

  • Other options:

    Matplotlib v3.10 will soon be released. You can install the pre-release version with

    pip install matplotlib==3.10.0rc1
    

    In the new release, we can tell constrained layout to ignore an axis:

    axarr["top"].yaxis.set_in_layout(False)
    

    enter image description here

    Before v3.10, we could make the axis invisible so constrained layout ignores it and then make it visible again after turning the layout engine off.

    axarr["top"].yaxis.set_visible(False)
    fig.canvas.draw()
    fig.set_layout_engine("none")
    axarr["top"].yaxis.set_visible(True)