Reputation: 623
I have created this figure with matplotlib
(full code below):
I would like to add a vertical separator line between the fourth and fifth columns. I adapted this answer as follows, adding this after my main code:
# Get the bounding boxes of the axes including text decorations
r = fig0.canvas.get_renderer()
get_bbox = lambda ax: ax.get_tightbbox(r).transformed(fig0.transFigure.inverted())
xmin = get_bbox(ax10).x0 # ax10 is the 5th column, 2nd row with the left ticklabels
xmax = get_bbox(ax15).x1 # ax15 is the 4th column, 2nd row with the right ticklabels
xs = (xmax+xmin)/2
line = plt.Line2D([xs], [0,1], transform=fig0.transFigure, color='k')
fig0.add_artist(line)
However, the result is this:
The line is clearly not quite in the correct place. I've tried changing the axes I'm taking the x-coordinates from (ax10
and ax15
in the code above) but that doesn't help. What am I doing wrong?
I suspect it's because I'm using constrained_layout=True
with the figure? If possible I'd like to keep doing that as it groups the axes perfectly without the need for gridspec
.
Full code for the figure:
fig0, ((ax0, ax1, ax2, ax3, ax4, ax5), (ax6, ax7, ax8, ax9, ax10, ax11)) = plt.subplots(
2, 6, figsize=[18, 5], sharex=True, constrained_layout=True)
row0 = [ax0, ax1, ax2, ax3, ax4, ax5]
row1a = [ax6, ax7, ax8, ax9, ax10, ax11]
ax12, ax13, ax14, ax15, ax16, ax17 = [ax.twinx() for ax in row1a]
row1b = [ax12, ax13, ax14, ax15, ax16, ax17]
ax_list = [axs for axs in zip(row0, row1a, row1b)]
for row in [row0, row1a, row1b]:
for ax in row[1:4]:
ax.sharey(row[0])
row[4].sharey(row[5])
for row in [row0, row1a]:
for ax in row[1:4]:
ax.tick_params(labelleft=False)
ax4.yaxis.tick_right()
ax5.yaxis.tick_right()
ax4.tick_params(labelright=False)
ax11.tick_params(labelleft=False)
ax16.tick_params(labelright=False)
for ax in row1a[1:4]:
ax.tick_params(labelleft=False)
for ax in row1b[0:3]:
ax.tick_params(labelright=False)
for rate, axs in zip(rateslist, ax_list):
axa, axb, axc = axs
axa.plot(t.loc[rate[0]])
axb.plot(t.loc[rate[1]]/t.loc[rate[1], '1999'], color='g')
axc.plot(t.loc[rate[2]]/t.loc[rate[2], '1999'], color='r')
loc = mticker.MultipleLocator(base=8)
axb.xaxis.set_major_locator(loc)
axb.tick_params(axis='x', labelsize=6, labelrotation=90)
Upvotes: 0
Views: 358
Reputation: 5913
Your code above probably works fine if you do a draw before getting the bounding box.
The approach below works fine w/ constrained_layout, and it will allow you to resize the figure. It makes a fake axes for the line to go in, and uses the width_ratios to make that fake axes quite small:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms
fig = plt.figure(constrained_layout=True)
gs = fig.add_gridspec(2, 7, width_ratios=[1, 1, 1, 1, 0.01, 1, 1])
axs = np.zeros((2, 6), dtype='object')
for ind in range(6):
i = ind
if ind>=4:
i = ind+1
axs[0, int(ind)] = fig.add_subplot(gs[0, i])
axs[1, ind] = fig.add_subplot(gs[1, i])
axline = fig.add_subplot(gs[:, 4])
axline.axis('off')
trans = mtransforms.blended_transform_factory(
axline.transAxes, fig.transFigure)
axline.plot([0,0], [0, 1], 'r', transform=trans, clip_on=False)
plt.show()
Upvotes: 1