162

I'm trying to share two subplots axes, but I need to share the x axis after the figure was created. E.g. I create this figure:

import numpy as np
import matplotlib.pyplot as plt

t = np.arange(1000)/100.
x = np.sin(2*np.pi*10*t)
y = np.cos(2*np.pi*10*t)

fig = plt.figure()
ax1 = plt.subplot(211)
plt.plot(t,x)
ax2 = plt.subplot(212)
plt.plot(t,y)

# some code to share both x axes

plt.show()

Instead of the comment I want to insert some code to share both x axes. How do I do this? There are some relevant sounding attributes _shared_x_axes and _shared_x_axes when I check to figure axis (fig.get_axes()) but I don't know how to link them.

iacob
  • 20,084
  • 6
  • 92
  • 119
ymmx
  • 4,769
  • 5
  • 32
  • 64

4 Answers4

245

The usual way to share axes is to create the shared properties at creation. Either

fig=plt.figure()
ax1 = plt.subplot(211)
ax2 = plt.subplot(212, sharex = ax1)

or

fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)

Sharing the axes after they have been created should therefore not be necessary.

However if for any reason, you need to share axes after they have been created (actually, using a different library which creates some subplots, like here might be a reason), there would still be a solution:

Using

ax1.get_shared_x_axes().join(ax1, ax2)

creates a link between the two axes, ax1 and ax2. In contrast to the sharing at creation time, you will have to set the xticklabels off manually for one of the axes (in case that is wanted).

A complete example:

import numpy as np
import matplotlib.pyplot as plt

t= np.arange(1000)/100.
x = np.sin(2*np.pi*10*t)
y = np.cos(2*np.pi*10*t)

fig=plt.figure()
ax1 = plt.subplot(211)
ax2 = plt.subplot(212)

ax1.plot(t,x)
ax2.plot(t,y)

ax1.get_shared_x_axes().join(ax1, ax2)
ax1.set_xticklabels([])
# ax2.autoscale() ## call autoscale if needed

plt.show()

The other answer has code for dealing with a list of axes:

axes[0].get_shared_x_axes().join(axes[0], *axes[1:])
Trevor Boyd Smith
  • 18,164
  • 32
  • 127
  • 177
ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • 1
    The strange reason, by the way, is I saved some figure with pickle and I reload them with another program which looses the sharex property. – ymmx Mar 23 '17 at 13:06
  • 10
    This is useful to connect select subplots. For example, a figure with 4 subplots: two time series and two histograms. This allows you to selectively link the time series. – Hamid Sep 23 '17 at 23:51
  • 4
    API docs for the Grouper object: https://matplotlib.org/2.0.2/api/cbook_api.html?highlight=grouper#matplotlib.cbook.Grouper.join – michaelosthege Oct 25 '17 at 12:06
  • 9
    Ohh, I just figured out how to *unshare* an axis (which can be useful in a large grid) - on that axis, do `g = ax.get_shared_y_axes(); g.remove(a) for a in g.get_siblings(ax)]`. Thanks for the starting point! – naught101 Mar 02 '18 at 07:12
  • Actually, that doesn't remove the shared ticks :( – naught101 Mar 02 '18 at 07:26
  • @naught101 This Q&A is about sharing the axes. If you have a different problem, you may ask a new question. – ImportanceOfBeingErnest Mar 02 '18 at 11:04
  • So I ended up using this anyway, because it's an easier way to do a mostly-shared, but not completely shared grid. But I did notice that I had to do the grouping **before** I plotted the data, otherwise the axis scaling would not be identical (each plot would still be zoomed independently). You can see this in your example if you replace the t in the second plot call with `np.arange(1000)/50` - you get the same image, but with different ticks on the x axis (meaning the values for the first plot are completely wrong). – naught101 Mar 16 '18 at 01:22
  • 6
    @naught101 You can just call `ax2.autoscale()`. – ImportanceOfBeingErnest Mar 16 '18 at 01:27
  • 1
    autoscale is really important for me – Red One Jan 19 '22 at 15:55
  • This answer really helped me. I was in need of doing so because of having multiple figures, each of which with plots added after they were generated – Leonardo Maffei Mar 25 '22 at 01:23
  • Actually, for my old version of Matplotlib, autoscale does not undo the linking. Anyone know another way to toggle this feature? – Mastiff Jun 10 '22 at 16:07
  • Nice answer! I used the same principle on y-axis. `ax1.set_xticklabels([])` suppressed the ticks which was unwanted. But `ax1.autoscale` was necessary to get ylims update to same values – PiWi Jul 28 '22 at 14:46
  • 4
    The `.join` method ([`matplotlib.cbook.GrouperView.join`](https://matplotlib.org/stable/api/cbook_api.html#matplotlib.cbook.GrouperView.join)) is deprecated since version 3.6. How would you do without it ? – paime Jan 26 '23 at 08:46
  • @paime If I want ax1 to share its yaxis with ax2 and ax3, i am calling ax2.sharey(ax1), ax3.sharey(ax1) – jay Jun 01 '23 at 20:24
47

As of Matplotlib v3.3 there now exist Axes.sharex, Axes.sharey methods:

ax1.sharex(ax2)
ax1.sharey(ax3)
iacob
  • 20,084
  • 6
  • 92
  • 119
  • 7
    note: you can only use `Axes.sharex` oncer per object. on the second call it errors out. so if you need to call it more than once use the more complex `Axes.get_shared_x_axes().join()`. – Trevor Boyd Smith Mar 31 '22 at 16:28
8

Just to add to ImportanceOfBeingErnest's answer above:

If you have an entire list of axes objects, you can pass them all at once and have their axes shared by unpacking the list like so:

ax_list = [ax1, ax2, ... axn] #< your axes objects 
ax_list[0].get_shared_x_axes().join(ax_list[0], *ax_list)

The above will link all of them together. Of course, you can get creative and sub-set your list to link only some of them.

Note:

In order to have all axes linked together, you do have to include the first element of the axes_list in the call, despite the fact that you are invoking .get_shared_x_axes() on the first element to start with!

So doing this, which would certainly appear logical:

ax_list[0].get_shared_x_axes().join(ax_list[0], *ax_list[1:])

... will result in linking all axes objects together except the first one, which will remain entirely independent from the others.

Trevor Boyd Smith
  • 18,164
  • 32
  • 127
  • 177
pfabri
  • 885
  • 1
  • 9
  • 25
  • For everyone having trouble: add "autoscale", if it does not work (see the answers above) – Red One Jan 19 '22 at 15:56
  • 2
    The `.join` method ([`matplotlib.cbook.GrouperView.join`](https://matplotlib.org/stable/api/cbook_api.html#matplotlib.cbook.GrouperView.join)) is deprecated since version 3.6. How would you do without it ? – paime Jan 26 '23 at 08:47
  • I can't help I'm afraid as I'm still on an earlier version. But I encourage you to post your solution, if you find one. – pfabri Jan 27 '23 at 11:00
1

Function join has been deprecated and will be removed soon. Continuing with this function is not recommended.

You can use the method suggested by iacob but, as commented by Trevor Boyd Smith, sharex and sharey can only be called once on the same object.

Thus the solution is to select one single axis as the argument of calls from multiple axes which need to be associated with the first one, e.g. to set the same y-scale for axes ax1, ax2 and ax3:

  • Select ax1 as the argument for other calls.
  • Call ax2.sharey(ax1), ax3.sharey(ax1), and so on if required.
mins
  • 6,478
  • 12
  • 56
  • 75