1

I have a figure on which I plot two axes-objects, ax1 and ax2, where ax2 = ax1.twinx().

How can I interchange the zorders on individual artists from each of the axes? E.g., I would like one artist from ax1 to be in the very background, then have all artists from ax2 on top of that, and then plot the rest of the artists from ax1. As an example, I would like to plot the following in the order in which it is written (i.e., (x3, y3) on top of (x2, y2) on top of (x1, y1))

import matplotlib.pyplot as plt

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.plot(x1, y1)
ax2.plot(x2, y2)
ax1.plot(x3, y3)

How can this be done?

This answer shows how to reverse the zorders of the axes, but not on individual items.

Bobson Dugnutt
  • 333
  • 1
  • 12

1 Answers1

4

You cannot simply adjust the z-order of artists across axes. The z-order of the Artists belonging to one Axes are relative to that axes only.

You can, however, obtain the desired effect, but no easily. Here are two options:

Using 3 axes

The simplest way in my opinion is to use 3 axes, with the 3rd axis being a complete "clone" of the first one, but with a z-order above ax2.

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()

ax3 = fig.add_axes(ax1.get_position(), sharex=ax1, sharey=ax1)
ax3.set_facecolor('none') #prevents axes background from hiding artists below
ax3.set_axis_off() # pervents superimposed ticks from being drawn

l_bottom, = ax1.plot([1,2,3], [4,6,6], lw=10, c='C1')
l2, = ax2.plot([1,2,3], [60,30,40], lw=10, c='C2')
l_top, = ax3.plot([1,2,3],[5,10,3], lw=10, c='C3')
ax3.legend([l_bottom, l2, l_top],['left','right','left'])
ax3.set_title('using a 3rd axe')

enter image description here

Using transforms

This methods only uses 2 axes, but plots the green line on ax2 using the data-coordinates from ax1. The problem with that method is that ax1 will not autoscale automatically, hence the need to call set_ylim(). Plus it could get quite confusing in a larger code to keep track of which transform is used by which artist.

fig, ax1 = plt.subplots()
ax2 = ax1.twinx()

l_bottom, = ax1.plot([1,2,3], [4,6,6], lw=10, c='C1')
l2, = ax2.plot([1,2,3], [60,30,40], lw=10, c='C2')
l_top, = ax2.plot([1,2,3],[5,10,3], lw=10, c='C3',transform=ax1.transData)
ax1.set_ylim(2.65,10.35)  # matches the limits on the previous graph
ax2.set_ylim(28.5,61.5)
ax2.legend([l_bottom, l2, l_top],['left','right','left'])
ax2.set_title('using transforms')

enter image description here

Diziet Asahi
  • 38,379
  • 7
  • 60
  • 75
  • Nice. This would benefit from using the same axis limits in both cases, since currently it looks like there is a difference between the two cases, which is not the case at all. – ImportanceOfBeingErnest Oct 09 '18 at 23:16
  • Thanks for the suggestion, I tweaked the code a bit and updated the resulting image in case this could be useful for somebody else – Diziet Asahi Oct 10 '18 at 07:37