I provide a solution at the end, but first I tell which way does not lead to success.
I revisited this issue recently, and spent quite some time with trying various solutions, i.e. trying almost all possible combinations of transformations between the different coordinate systems, and their relationship with the tight_layout()
. I experimented only with backend_pdf
, so I can not tell about interactive media. But briefly, my conclusion is that no matter how you try to find out the positions and try to transform them, it is not possible at this level to align the axis labels. I guess somehow it should be possible, for example somehow internally matplotlib is able to align the axes of the subplots themselves.
Only with drawing into the pdf file twice and doing something like below inbetween, I could achieve better positions, but still not aligned:
# sorry for the `self`, this is from a class
def align_x_labels(self):
self.lowest_ax = min(self.axes.values(),
key = lambda ax: ax.xaxis.label.get_position()[1])
self.lowest_xlab_dcoo = self.lowest_ax.transData.transform(
self.lowest_ax.xaxis.label.get_position())
list(
map(
lambda ax: \
ax.xaxis.set_label_coords(
self.fig.transFigure.inverted().transform(
ax.transAxes.transform((0.5, 0.5)))[0],
self.fig.transFigure.inverted().transform(
self.lowest_xlab_dcoo)[1],
transform = self.fig.transFigure
),
self.axes.values()
)
)
It is a shame such a basic functionality can not be achieved, and it is obscure how the different coordinate spaces are transformed and rescaled at the different steps of plotting. I would appreciate very much to see a clear explanation of this, because the matplotlib webpage only outlines the architecture, presents simple cases, but fails to explain situations like this. Also I am surprised that methods accepting or returning coordinates do not tell in their docstring what types of coordinates are those. Finally I found very useful this tutorial.
Solution
At the end, instead of messing with the transformations, I created in the GridSpec
an additional row of zero height and invisible axes (same can be done with a zero width column for y axis labels). Then I added labels for these subplots, setting the verticalalignment
to top
.
# get one of the zero height phantom subplots to `self.ax`:
self.get_subplot(i, 1)
# set empty ticklabels:
self.ax.xaxis.set_ticklabels([])
self.ax.yaxis.set_ticklabels([])
# set the axis label:
self.ax.set_xlabel(labtext, fontproperties = self.fp_axis_lab)
# and this is matter of aesthetics
# sometimes bottom or center might look better:
self.ax.xaxis.label.set_verticalalignment('top')