Skip to content

Commit f1ed957

Browse files
committed
Allow sharex/y after axes creation.
This intentionally does not allow unsharing or changing the shared axes, as there are bigger questions on the API there. The API is named `Axes.sharex()` to allow for a later `Axes.unsharex()`, though (`set_sharex()` would be fine, but `set_unsharex()`? or perhaps `set_sharex(None)`, though). An example use case is creating a grid with `subplots()`, but with custom sharing relationships between the subplots -- e.g., sharex/sharey across all, except the first row of axes which only shares x with their column and the first column which only shares y with their lines.
1 parent f9e37f7 commit f1ed957

File tree

3 files changed

+70
-27
lines changed

3 files changed

+70
-27
lines changed

doc/api/axes_api.rst

+5-2
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,8 @@ Adding Artists
446446
Axes.add_table
447447

448448

449-
Twinning
450-
========
449+
Twinning and sharing
450+
====================
451451

452452
.. autosummary::
453453
:toctree: _as_gen
@@ -457,6 +457,9 @@ Twinning
457457
Axes.twinx
458458
Axes.twiny
459459

460+
Axes.sharex
461+
Axes.sharey
462+
460463
Axes.get_shared_x_axes
461464
Axes.get_shared_y_axes
462465

examples/subplots_axes_and_figures/subplots_demo.py

+17
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,23 @@
176176
for ax in axs.flat:
177177
ax.label_outer()
178178

179+
###############################################################################
180+
# If you want a more complex sharing structure, you can first create the
181+
# grid of axes with no sharing, and then call `.axes.Axes.sharex` or
182+
# `.axes.Axes.sharey` to add sharing info a posteriori.
183+
184+
fig, axs = plt.subplots(2, 2)
185+
axs[0, 0].plot(x, y)
186+
axs[0, 0].set_title("main")
187+
axs[1, 0].plot(x, y**2)
188+
axs[1, 0].set_title("shares x with main")
189+
axs[1, 0].sharex(axs[0, 0])
190+
axs[0, 1].plot(x + 1, y + 1)
191+
axs[0, 1].set_title("unrelated")
192+
axs[1, 1].plot(x + 2, y + 2)
193+
axs[1, 1].set_title("also unrelated")
194+
fig.tight_layout()
195+
179196
###############################################################################
180197
# Polar axes
181198
# """"""""""

lib/matplotlib/axes/_base.py

+48-25
Original file line numberDiff line numberDiff line change
@@ -429,12 +429,8 @@ def __init__(self, fig, rect,
429429
self._anchor = 'C'
430430
self._stale_viewlim_x = False
431431
self._stale_viewlim_y = False
432-
self._sharex = sharex
433-
self._sharey = sharey
434-
if sharex is not None:
435-
self._shared_x_axes.join(self, sharex)
436-
if sharey is not None:
437-
self._shared_y_axes.join(self, sharey)
432+
self._sharex = None
433+
self._sharey = None
438434
self.set_label(label)
439435
self.set_figure(fig)
440436

@@ -453,6 +449,11 @@ def __init__(self, fig, rect,
453449
self._rasterization_zorder = None
454450
self.cla()
455451

452+
if sharex is not None:
453+
self.sharex(sharex)
454+
if sharey is not None:
455+
self.sharey(sharey)
456+
456457
# funcs used to format x and y - fall back on major formatters
457458
self.fmt_xdata = None
458459
self.fmt_ydata = None
@@ -945,6 +946,42 @@ def _gen_axes_spines(self, locations=None, offset=0.0, units='inches'):
945946
return OrderedDict((side, mspines.Spine.linear_spine(self, side))
946947
for side in ['left', 'right', 'bottom', 'top'])
947948

949+
def sharex(self, other):
950+
"""
951+
Share the x-axis with *other*.
952+
953+
This is equivalent to passing ``sharex=other`` when constructing the
954+
axes, and cannot be used if the x-axis is already being shared with
955+
another axes.
956+
"""
957+
if self._sharex is not None and other is not self._sharex:
958+
raise ValueError("x-axis is already shared")
959+
self._shared_x_axes.join(self, other)
960+
self._sharex = other
961+
self.xaxis.major = other.xaxis.major # Ticker instances holding
962+
self.xaxis.minor = other.xaxis.minor # locator and formatter.
963+
x0, x1 = other.get_xlim()
964+
self.set_xlim(x0, x1, emit=False, auto=other.get_autoscalex_on())
965+
self.xaxis._scale = other.xaxis._scale
966+
967+
def sharey(self, other):
968+
"""
969+
Share the y-axis with *other*.
970+
971+
This is equivalent to passing ``sharey=other`` when constructing the
972+
axes, and cannot be used if the y-axis is already being shared with
973+
another axes.
974+
"""
975+
if self._sharey is not None and other is not self._sharey:
976+
raise ValueError("y-axis is already shared")
977+
self._shared_y_axes.join(self, other)
978+
self._sharey = other
979+
self.yaxis.major = other.yaxis.major # Ticker instances holding
980+
self.yaxis.minor = other.yaxis.minor # locator and formatter.
981+
y0, y1 = other.get_ylim()
982+
self.set_ylim(y0, y1, emit=False, auto=other.get_autoscaley_on())
983+
self.yaxis._scale = other.yaxis._scale
984+
948985
def cla(self):
949986
"""Clear the current axes."""
950987
# Note: this is called by Axes.__init__()
@@ -968,38 +1005,25 @@ def cla(self):
9681005
self.callbacks = cbook.CallbackRegistry()
9691006

9701007
if self._sharex is not None:
971-
# major and minor are axis.Ticker class instances with
972-
# locator and formatter attributes
973-
self.xaxis.major = self._sharex.xaxis.major
974-
self.xaxis.minor = self._sharex.xaxis.minor
975-
x0, x1 = self._sharex.get_xlim()
976-
self.set_xlim(x0, x1, emit=False,
977-
auto=self._sharex.get_autoscalex_on())
978-
self.xaxis._scale = self._sharex.xaxis._scale
1008+
self.sharex(self._sharex)
9791009
else:
9801010
self.xaxis._set_scale('linear')
9811011
try:
9821012
self.set_xlim(0, 1)
9831013
except TypeError:
9841014
pass
985-
9861015
if self._sharey is not None:
987-
self.yaxis.major = self._sharey.yaxis.major
988-
self.yaxis.minor = self._sharey.yaxis.minor
989-
y0, y1 = self._sharey.get_ylim()
990-
self.set_ylim(y0, y1, emit=False,
991-
auto=self._sharey.get_autoscaley_on())
992-
self.yaxis._scale = self._sharey.yaxis._scale
1016+
self.sharey(self._sharey)
9931017
else:
9941018
self.yaxis._set_scale('linear')
9951019
try:
9961020
self.set_ylim(0, 1)
9971021
except TypeError:
9981022
pass
1023+
9991024
# update the minor locator for x and y axis based on rcParams
10001025
if rcParams['xtick.minor.visible']:
10011026
self.xaxis.set_minor_locator(mticker.AutoMinorLocator())
1002-
10031027
if rcParams['ytick.minor.visible']:
10041028
self.yaxis.set_minor_locator(mticker.AutoMinorLocator())
10051029

@@ -1081,11 +1105,10 @@ def cla(self):
10811105

10821106
self._shared_x_axes.clean()
10831107
self._shared_y_axes.clean()
1084-
if self._sharex:
1108+
if self._sharex is not None:
10851109
self.xaxis.set_visible(xaxis_visible)
10861110
self.patch.set_visible(patch_visible)
1087-
1088-
if self._sharey:
1111+
if self._sharey is not None:
10891112
self.yaxis.set_visible(yaxis_visible)
10901113
self.patch.set_visible(patch_visible)
10911114

0 commit comments

Comments
 (0)