colors in legend do not match graph colors

Question:

I am trying to make customized legends showing only some of the lines plotted. However, the color in the legend does not always match the color of the plotted line. In the example legend 1 and legend 2 are fine, but legend 3 should show blue and green, not blue and orange. See attached image. How can I fix this?

legend 3 should be blue (0) and green (2)

Here is a mwe which I used to generate the image, but note that in the real program the plot function is not directly accessible and loaded from a different file. Also, the plot function should not be modified as it is used in other programs as well.

import matplotlib.pyplot as plt
import numpy as np

def my_plot_function(x,y,ax):
    #do complicated stuff here, manipulating y for example
    ax.plot(x,y)

x = np.linspace(0,1,100)
fig = plt.figure()
ax = fig.add_subplot(111)

my_plot_function(x, x, ax)
my_plot_function(x, x**2, ax)
my_plot_function(x, x**3, ax)
my_plot_function(x, x**4, ax)

lines = ax.get_lines()
print(lines[0])
print(lines[1])
print(lines[2])
print(lines[3])

fig.legend(lines, labels=range(4), loc=1, title="legend 1")
fig.legend([lines[0],lines[1]], labels=["0","1"], loc=2, title="legend 2")
fig.legend([lines[0],lines[2]], labels=["0","2"], loc=3, title="legend 3")
plt.show()

EDIT:
To clarify what I was asking for: Plotting needs to be done by my_plot_function which is defined in a separate file and may not be modified. Thus I cannot pass it additional keywords such as label.

Asked By: laolux

||

Answers:

When I run your code, I get a warning from legend.py:

UserWarning: You have mixed positional and keyword arguments, some input may be discarded.
  warnings.warn("You have mixed positional and keyword "...

This is probably the reason why it’s not working. You need to add the handles in your legend calls. I.e.

fig.legend(handles=[lines[0],lines[1]],...

By the way, it’s a good practice to assign a label when the plot is created.

ax.plot(x, y, label='some label')

Then you can call

handles, labels = ax.get_legend_handles_labels()

and use them in the legend like so

fig.legend(handles=(handles[0],handles[2]), labels=(labels[0],labels[2]),...)
Answered By: pktl2k

Use handles keyword argument:

fig.legend(handles=lines, labels=range(4), loc=1, title="legend 1")
fig.legend(handles=[lines[0],lines[1]], labels=["0","1"], loc=2, title="legend 2")
fig.legend(handles=[lines[0],lines[2]], labels=["0","2"], loc=3, title="legend 3")
Answered By: HYRY

When looking at the docs of figure.legend we can see that labels has to be used with handles (and the other way). You must put both handles and labels or none of them.

This works

fig.legend(lines, range(4), loc=1, title="legend 1")
fig.legend((lines[0],lines[1]), ("0","1"), loc=2, title="legend 2")
fig.legend((lines[0],lines[2]), ("0","2"), loc=3, title="legend 3")

and this works

fig.legend(handles=lines, labels=range(4), loc=1, title="legend 1")
fig.legend(handles=(lines[0],lines[1]), labels=("0","1"), loc=2, title="legend 2")
fig.legend(handles=(lines[0],lines[2]), labels=("0","2"), loc=3, title="legend 3")
Answered By: desmaxi

I would approach the problem slightly differently. In cases like yours, I would always recommend to pass the legend entry while plotting using the label keyword. Then, to use selected legends, you can use get_legend_handles_labels() and then pass the required items to fig.legend(). You do not have to specify the handles and labels arguments. But if you specify one (like you did for labels), you should also specify the other or else you get a warning

import matplotlib.pyplot as plt
import numpy as np

def plot(x,y,ax):
    ax.plot(x,y)

x = np.linspace(0,1,100)
fig = plt.figure()
ax = fig.add_subplot(111)

ax.plot(x, x, label='0')
ax.plot(x, x**2, label='1')
ax.plot(x, x**3, label='2')
ax.plot(x, x**4, label='3')
ax.legend(loc=1, title='legend 1')

h, l = ax.get_legend_handles_labels()

fig.legend([h[0],h[1]], [l[0], l[1]], loc=2, title="legend 2")
fig.legend([h[0],h[2]], [l[0], l[2]], loc=3, title="legend 3")
plt.show() 

enter image description here

An alternative approach answering @gboffi comment below is following where you don’t use the global legend to extract the values.

x = np.linspace(0,1,100)
fig = plt.figure()
ax = fig.add_subplot(111)

l0, = ax.plot(x, x, label='0')
l1, = ax.plot(x, x**2, label='1')
l2, = ax.plot(x, x**3, label='2')
l3, = ax.plot(x, x**4, label='3')

fig.legend([l0, l1], [l0.get_label(), l1.get_label()], loc=2, title="legend 2")
fig.legend([l0, l2], [l0.get_label(), l2.get_label()], loc=3, title="legend 3")
plt.show() 
Answered By: Sheldore
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.