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")
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.
How should I first apply constrained_layout
, then disable it, and adjust the axis positions?
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)
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)