Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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: Faulty plot

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:

Faulty plot with two legends

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?

like image 348
bzn Avatar asked Jun 17 '13 23:06

bzn


People also ask

Where should we position a legend in an axis?

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.

How do you display the legend outside the plot?

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.

How do I fix the legend position in Matplotlib?

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.

How do you add a legend outside of a graph in PLT?

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.


1 Answers

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.

like image 67
tacaswell Avatar answered Sep 17 '22 23:09

tacaswell