CreekGeek
CreekGeek

Reputation: 2329

sharing of xlabel across gridspec subplots/axes (partial row)

I'm having some intermittent issues sharing a single, centered xlabel across three subplots that 1) only span part of a gridspec row and 2) whose width relative to one another may vary.

Using the docs I've been able to sort-out the basic multiplot structure I'm looking for:

enter image description here

fig = plt.figure(figsize=(10,8), constrained_layout=True)
xlabel_234 = 'XLabel Thing2\n(to be centered under ax2, ax3, and ax4 with equal horiz. spacing btwn subplots)'
gs = fig.add_gridspec(nrows=14, ncols=1)

gs0 = gs[0:2]
gs1 = gs[2:].subgridspec(nrows=1, ncols=12)

ax1 = fig.add_subplot(gs0)
ax1.set_title('Something Short and Wide')
ax1.text(0.5, 0.5, 'ax1', ha='center')
ax1.set_xlabel('XLabel Thing1')

ax2 = fig.add_subplot(gs1[0, 0:1])
ax2.set_title('Something Tall\nand Narrow 2a')
ax2.text(0.5, 0.5, 'ax2', ha='center')

ax3 = fig.add_subplot(gs1[0, 1:6], sharey=ax2)
ax3.set_title('Something Tall\nand Narrow 2b')
ax3.text(0.5, 0.5, 'ax3', ha='center')
plt.setp(ax3.get_yticklabels(), visible=False)
ax3.set_xlabel(xlabel_234, x=0.7)   # *x offset is a bit of hack*

ax4 = fig.add_subplot(gs1[0, 6:9], sharey=ax2)
ax4.set_title('Something Tall\n and Narrow 2c')
ax4.text(0.5, 0.5, 'ax4', ha='center')
plt.setp(ax4.get_yticklabels(), visible=False)

ax5 = fig.add_subplot(gs1[0, 9:12])
ax5.set_title('Something Tall\nand Narrow 3')
ax5.text(0.5, 0.5, 'ax5', ha='center')
ax5.set_xlabel('XLabel Thing3')

I want to recognize this post for helping me sort-out the y-axis sharing. the visibility kwarg in setp is a huge help.

This post helped me (kinda) get a common xlabel centered, but it's a bit of a hack and can produce some odd behaviour (that affects spacing) depending on the relative widths of ax2, ax3, ax4. And I have to iteratively guess at the x offset value.

Is there a more precise way for me to do this? And, perhaps, standardize the xtick label formats (I know how to do them one-off). Like maybe a subgridspec-of-the-subgridspec? Alternatively, other docs show a 'width_ratios' kwarg (though there is not much info on implementing)...would this be a better approach (i.e. where subplot spacing might not be as sensitive)? Other?

Cheers

Upvotes: 2

Views: 1511

Answers (2)

Tojur
Tojur

Reputation: 191

Here's a solution that doesn't require changing the gridspec structure. Note that I only changed the final part to add a new groupal axis

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(10, 8), constrained_layout=True)
gs = fig.add_gridspec(nrows=14, ncols=1)

gs0 = gs[0:2]
gs1 = gs[2:].subgridspec(nrows=1, ncols=12)

ax1 = fig.add_subplot(gs0)
ax1.set_title('Something Short and Wide')
ax1.text(0.5, 0.5, 'ax1', ha='center')
ax1.set_xlabel('XLabel Thing1')

ax2 = fig.add_subplot(gs1[0, 0:1])
ax2.set_title('Something Tall\nand Narrow 2a')
ax2.text(0.5, 0.5, 'ax2', ha='center')

ax3 = fig.add_subplot(gs1[0, 1:6], sharey=ax2)
ax3.set_title('Something Tall\nand Narrow 2b')
ax3.text(0.5, 0.5, 'ax3', ha='center')
plt.setp(ax3.get_yticklabels(), visible=False)
# ax3.set_xlabel(xlabel_234, x=0.7)   # *x offset is a bit of hack*

ax4 = fig.add_subplot(gs1[0, 6:9], sharey=ax2)
ax4.set_title('Something Tall\n and Narrow 2c')
ax4.text(0.5, 0.5, 'ax4', ha='center')
plt.setp(ax4.get_yticklabels(), visible=False)

ax5 = fig.add_subplot(gs1[0, 9:12])
ax5.set_title('Something Tall\nand Narrow 3')
ax5.text(0.5, 0.5, 'ax5', ha='center')
ax5.set_xlabel('XLabel Thing3')

''' New group axes '''
ax_group = fig.add_subplot(gs1[0, 0:9])
ax_group.set_xticks([])
ax_group.set_yticks([])
ax_group.set_frame_on(False)
ax_group.set_xlabel("Group label!", labelpad=20)

enter image description here

Upvotes: 1

tdy
tdy

Reputation: 41327

A couple recent features (matplotlib 3.4.0+) can help here:

You can use these to reproduce your layout in various ways, but here's one example for reference:

  1. Create top/bottom subfigures (top subfig for ax0)
  2. Within the bottom subfigure, nest left/right subfigures (left subfig for ax1-ax3, right subfig for ax4)
  3. In the bottom-left subfigure, use a custom gridspec and supxlabel (for ax1-ax3)
fig = plt.figure(constrained_layout=True, figsize=(10, 8))

# create top/bottom subfigs
(subfig_t, subfig_b) = fig.subfigures(2, 1, hspace=0.05, height_ratios=[1, 3])

# put ax0 in top subfig
ax0 = subfig_t.subplots()
ax0.set_title('ax0')
subfig_t.supxlabel('xlabel0')

# create left/right subfigs nested in bottom subfig
(subfig_bl, subfig_br) = subfig_b.subfigures(1, 2, wspace=0.1, width_ratios=[3, 1])

# put ax1-ax3 in gridspec of bottom-left subfig
gs = subfig_bl.add_gridspec(nrows=1, ncols=9)
ax1 = subfig_bl.add_subplot(gs[0, :1])
ax2 = subfig_bl.add_subplot(gs[0, 1:6], sharey=ax1)
ax3 = subfig_bl.add_subplot(gs[0, 6:], sharey=ax1)
ax1.set_title('ax1')
ax2.set_title('ax2')
ax3.set_title('ax3')
ax2.get_yaxis().set_visible(False)
ax3.get_yaxis().set_visible(False)
subfig_bl.supxlabel('xlabel1-3')

# put ax4 in bottom-right subfig
ax4 = subfig_br.subplots()
ax4.set_title('ax4')
subfig_br.supxlabel('xlabel4')

nested subfigures with supxlabel

Upvotes: 3

Related Questions