The legend of the plot of a secondary axis somehow is transparent to the plot of the other axis. A minimal example to reproduce the problem:
import matplotlib.pyplot as plt
ax1 = plt.subplot(111)
ax2 = ax1.twinx()
ax2.plot([1, 2, 3], [0.3, 0.2, 0.1], 'r')
ax1.plot([1, 2, 3], [1, 2, 3], 'b', label='ax1')
ax1.legend(loc=2)
plt.show()
The output I get is:
As you can see, the legend for the blue plot is overdrawn by the red plot. Rearranging the drawing commands, changing the alpha value or changing the z-orders of the objects doesn't help.
Is there any way to make the legend opaque to all plots?
EDIT: @tcaswell
: While your answer works for a single legend, it doesn't work if both axes have a separate legend. In the following code I added a label for ax2
:
import matplotlib.pyplot as plt
plt.figure()
ax1 = plt.subplot(111)
ax2 = ax1.twinx()
ax2.plot([1, 2, 3], [0.3, 0.2, 0.1], 'r', label='ax2')
ax1.plot([1, 2, 3], [1, 2, 3], 'b', label='ax1')
ax1.legend(loc=2)
ax2.legend(loc=1)
ax1.set_zorder(1) # make it on top
ax1.set_frame_on(False) # make it transparent
ax2.set_frame_on(True) # make sure there is any background
plt.show()
with the following result:
While your more general approach solves this problem, using Figure.legend
unfortunately places the legend outside of the plot. Placing them explicitly with loc
is a bit tedious and doesn't work well when scaling the plot. Is there a better solution?
The location of the legend. The strings 'upper left', 'upper right', 'lower left', 'lower right' place the legend at the corresponding corner of the axes/figure.
To place the legend outside of the axes bounding box, one may specify a tuple (x0, y0) of axes coordinates of the lower left corner of the legend. places the legend outside the axes, such that the upper left corner of the legend is at position (1.04, 1) in axes coordinates.
To change the position of a legend in Matplotlib, you can use the plt. legend() function. The default location is “best” – which is where Matplotlib automatically finds a location for the legend based on where it avoids covering any data points.
In Matplotlib, to set a legend outside of a plot you have to use the legend() method and pass the bbox_to_anchor attribute to it. We use the bbox_to_anchor=(x,y) attribute. Here x and y specify the coordinates of the legend.
You are running into issues because of the way that matplotlib renders the plots. By default the second axes is rendered after the first one (they have the same zorder
so they render in the order they were added).
To get what you want you just need to tweak a few things about your axes:
figure()
ax1 = plt.subplot(111)
ax2 = ax1.twinx()
ax2.plot([1, 2, 3], [0.3, 0.2, 0.1], 'r')
ax1.plot([1, 2, 3], [1, 2, 3], 'b', label='ax1')
ax1.legend(loc=2)
ax1.set_zorder(1) # make it on top
ax1.set_frame_on(False) # make it transparent
ax2.set_frame_on(True) # make sure there is any background
plt.show()
We set the zorder
of ax1
to be higher so it is rendered later, but if we do just that, the second axes isn't visible at all as it as all drawn under the frame (white background and box) of ax1
. To fix that we turn the frame off on ax1
(so we can see ax2
). However, now we have no background or bounding box at all. We can then turn the frame back on for ax2
, which gives us the desired effect.
The above method is ad-hoc and not general, if you want to make sure your axes is above all axes, you need to use Figure.ledgend()
, which is a figure
, not axes
feature. Currently, it won't auto-magically find your labels so you have to pass in the handles and labels explicitly:
fig = figure()
ax1 = plt.subplot(111)
ax2 = ax1.twinx()
ln2, = ax2.plot([1, 2, 3], [0.3, 0.2, 0.1], 'r', label='ax2')
ln1, = ax1.plot([1, 2, 3], [1, 2, 3], 'b', label='ax1')
interesting_lines = [ln1, ln2]
fig.legend(*zip(*[(il, il.get_label()) for il in interesting_lines]), loc=2)
plt.show()
Note that this legend is now placed using figure coordinates.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With