'Legend transparency, when using secondary axis
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?
Solution 1:[1]
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.
Solution 2:[2]
I used bbox_to_anchor to set the location of both legends, and they appeared on top. Didn't know why, but it worked. eg:
ax1.legend(bbox_to_anchor=(0.16,0.2))
ax2.legend(bbox_to_anchor=(1,0.2))
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | FObersteiner |
Solution 2 | Gang |