Add a legend that covers the lines of the twinx axis

880 Views Asked by At

I have this python code.

  1. it twinx the axis ax and plots some function on both axis
  2. I plot the legend on ax1

The problem is that the legend is not covering the curves of ax2

It is possible to automatically positioning the legend on ax by covering the lines of ax2.

Note that in fig.legend the option loc="best" is not available. And I need the automatic positioning inside the area of the plot.

Tnx

import matplotlib.pyplot as plt
import numpy as np

# Set the x values for the sine and cosine functions
x = np.linspace(0, 2*np.pi, 100)

# Create the figure and an axis
fig, ax = plt.subplots()
ax2 = ax.twinx()

# Plot the sine and cosine functions on the axis
ax.plot(x, np.sin(x), label='Sine')
ax.plot(x, np.cos(x), label='Cosine')

ax2.plot(x, np.cos(x+1), label='Cosine 2', color="red")
ax2.plot(x, x, label='Cosine 2', color="green")

# Add a title and labels to the axis
ax.set_title('Sine and Cosine Functions')
ax.set_xlabel('X')
ax.set_ylabel('Y')

# Get the line legends from the axis
lines, labels = ax.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()

# Add a legend to the figure
ax.legend(lines + lines2, labels + labels2, framealpha=1.0)
ax.get_legend().set_zorder(10)

# Display the plot
plt.show()

Bellow is the output of the code:

The output of the code above:

3

There are 3 best solutions below

0
On

References:

https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.legend.html https://matplotlib.org/stable/gallery/misc/zorder_demo.html

The zorder of a legend is highest by default, so you don't need to modify it if you want the legend on the top of everything.

Option 1:

You can accomplish this in terms of a Figure object instead of an Axes object with plt.legend(lines + lines2, labels + labels2, framealpha=1.0, loc='lower left').

Option 2:

Or you can set the legend on ax2 instead of ax with ax2.legend(lines + lines2, labels + labels2, framealpha=1.0, loc='lower left'). This returns the same result as in option 1.

import matplotlib.pyplot as plt
import numpy as np

# Set the x values for the sine and cosine functions
x = np.linspace(0, 2 * np.pi, 100)

# Create the figure and an axis
fig, ax = plt.subplots()
ax2 = ax.twinx()

# Plot the sine and cosine functions on the axis
ax.plot(x, np.sin(x), label='Sine')
ax.plot(x, np.cos(x), label='Cosine')

ax2.plot(x, np.cos(x + 1), label='Cosine 2', color="red")
ax2.plot(x, x, label='Cosine 2', color="green")

# Add a title and labels to the axis
ax.set_title('Sine and Cosine Functions')
ax.set_xlabel('X')
ax.set_ylabel('Y')

# Get the line legends from the axis
lines, labels = ax.get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()

# Add a legend to the figure
plt.legend(lines + lines2, labels + labels2, framealpha=1.0, loc='lower left')

# Display the plot
plt.show()

Results (loc='lower left' and loc='lower center'):

enter image description here

enter image description here

Updated:

Reference: https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.twinx.html

With option 2 in my previous answer, I edited line 36 to ax2.legend(lines + lines2, labels + labels2, framealpha=1.0, loc='lower right') and line 43 to ax22.legend(lines1 + lines22, labels1 + labels22, framealpha=1.0, loc='lower left') in your newly provided code.

If you have multiple subfigures, using Axes objects are much more flexible.

It is now working correctly:

enter image description here

0
On

Thanks for the answer.

But my problem is more complex: I need to do this job in tiled subplots where every plot area is twinx.

By using the plt.label it seems to be impossible to select the plot-tile where to put the legend.

With the code below the problem is there. The lines of the twined are not covered by the legend.

Any suggestion?

import matplotlib.pyplot as plt
import numpy as np

# Set the x values for the sine and cosine functions
x = np.linspace(0, 2 * np.pi, 100)

# Create the figure and an axis
fig, ax = plt.subplots(2, 1)
ax2 = ax[0].twinx()
ax22 = ax[1].twinx()

# Plot the sine and cosine functions on the axis
ax[0].plot(x, np.sin(x), label='Sine 0')
ax[0].plot(x, np.cos(x), label='Cosine 0')

# Plot the sine and cosine functions on the axis
ax[1].plot(x, np.sin(x), label='Sine')
ax[1].plot(x, np.cos(x), label='Cosine')

ax2.plot(x, np.cos(x + 1), label='Cosine 2', color="red")
ax2.plot(x, x, label='Cosine B', color="green")

ax22.plot(x, np.cos(x + 2), label='Line 2', color="red")
ax22.plot(x, x, label='Cosine 2', color="green")

# Add a title and labels to the axis
ax[0].set_title('Sine and Cosine Functions')
ax[0].set_xlabel('X')
ax[0].set_ylabel('Y')

# Get the line legends from the axis
lines, labels = ax[0].get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()

# Add a legend to the figure
ax[0].legend(lines + lines2, labels + labels2, framealpha=1.0, loc='lower right')


lines1, labels1 = ax[1].get_legend_handles_labels()
lines22, labels22 = ax22.get_legend_handles_labels()

# Add a legend to the figure
ax[1].legend(lines1 + lines22, labels1 + labels22, framealpha=1.0, loc='lower left')

# Display the plot
plt.show()

enter image description here

0
On

I finally found the solution:

That is controlling the z-order of the Axis and the alpha of the axis with the highest priority.

the matplotlib functions are:

Axes.set_zorder

https://matplotlib.org/3.2.2/api/_as_gen/matplotlib.axes.Axes.set_zorder.html

&

Patch.set_alpha

https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Patch.html

Replace the last part of the code in the third answer with the above code, and it produces the desired result.

# Get the line legends from the axis
lines, labels = ax[0].get_legend_handles_labels()
lines2, labels2 = ax2.get_legend_handles_labels()


##############################################################################
############################################################################## 
# Set the z-order of axis 2 to a low "priority"
ax2.set_zorder(0)

# Set the z-order of the axis 0 to a higher priority.
# And to make visible axis 2 set the alpha of the background at 0.
ax[0].set_zorder(1)
ax[0].patch.set_alpha(0.0)
 
ax[0].legend(lines + lines2, labels + labels2, framealpha=1.0, loc='lower left')
##############################################################################
##############################################################################
 
lines1, labels1 = ax[1].get_legend_handles_labels()
lines22, labels22 = ax22.get_legend_handles_labels()
 
# Add a legend to the figure
ax22.legend(lines1 + lines22, labels1 + labels22, framealpha=1.0, loc='lower left')
 
# Display the plot
plt.show()

enter image description here