Hello all, complex vertical plots with a shared x-axis are very common in my field, but I’m having a bit of trouble with the figure layout and saving to PDF. In the following sample I have a host axis that contains four staggered and offset axes. This is a bit different scenario from normal and isn’t a standard twinx since it doesn’t take up the full vertical figure. In these kind of plots there could be many (2-8) time series slightly offset from each other (and overlapping in some cases) but all using the same x-axis (i.e. time; think ice cores or tree ring data on a common time axis). I have this mostly working well with inset_axes(), but it seems like the axis labels are not included in the constrained layout calculation. If I try to save to a 3" by 6" figure for a journal, only the host axis seems to be centered in the PDF. The sub axes labels are totally off the plot. If I include savefig(…, bbox_inches=‘tight’) the sub axes labels are now included, but the figure width has been extended, and the PDF is no longer 3" wide. This is a problem for scientific journals where the figure sizes are tightly controlled.
Two part question, A) is there a way to get the inset_axes() labels properly included in the constrained layout, and B) is there a better way to stack up a bunch of time series on the same x-axis while having multiple staggered y-axes? As in, is inset_axes() the best way to create this type of figure?
Thanks
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib as mpl
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
from matplotlib._layoutgrid import plot_children
y = np.random.rand(100)
def get_right_staggered_axis(host_ax, y_anchor, height):
ax = inset_axes(host_ax, width="100%", height="100%",borderpad=0, axes_kwargs=dict(facecolor='none'),
bbox_to_anchor=(.0, y_anchor, 1, height),
bbox_transform=host_ax.transAxes)
ax.sharex(host_ax)
ax.spines[['left', 'top','bottom']].set_visible(False)
ax.spines[['right']].set_visible(True)
ax.get_yaxis().tick_right()
ax.get_yaxis().set_label_position("right")
ax.get_xaxis().set_tick_params(labelbottom=False, bottom=False)
ax.set_in_layout(True)
return ax
def get_left_staggered_axis(host_ax, y_anchor, height):
ax = inset_axes(host_ax, width="100%", height="100%",borderpad=0, axes_kwargs=dict(facecolor='none'),
bbox_to_anchor=(.0, y_anchor, 1, height),
bbox_transform=host_ax.transAxes)
ax.sharex(host_ax)
ax.spines[['right', 'top','bottom']].set_visible(False)
ax.spines[['left']].set_visible(True)
ax.get_xaxis().set_tick_params(labelbottom=False, bottom=False)
ax.set_in_layout(True)
return ax
fig, host_ax = plt.subplots(nrows=1, ncols=1, layout='constrained', figsize=(3, 6))
host_ax.set_xlabel("Time",fontsize="small")
host_ax.spines[['right', 'top','left']].set_visible(False)
host_ax.get_yaxis().set_tick_params(labelbottom=False, left=False)
host_ax.spines['bottom'].set_position(('outward', 5))
ax = get_left_staggered_axis(host_ax, 0.75, 0.25)
ax.plot(x,np.random.rand(100)*10)
ax.set_ylabel("Axis 1")
ax = get_right_staggered_axis(host_ax, 0.5, 0.25)
ax.plot(x,np.random.rand(100)*10000)
ax.set_ylabel("Axis 2")
ax = get_left_staggered_axis(host_ax, 0.25, 0.25)
ax.plot(x,np.random.rand(100))
ax.set_ylabel("Axis 3\nlong label")
ax = get_right_staggered_axis(host_ax, 0.0, 0.25)
ax.plot(x,np.random.rand(100))
ax.set_ylabel("Axis 4\nreally\nlong label")
plot_children(fig)
fig.savefig("staggered_axis_test.pdf")
fig.savefig("staggered_axis_tight_test.pdf", bbox_inches='tight')

