pyplot combine multiple line labels in legend
Solution 1
I'd make a small helper function personally, if i planned on doing it often;
from matplotlib import pyplot
import numpy
a = numpy.array([[ 3.57, 1.76, 7.42, 6.52],
[ 1.57, 1.2 , 3.02, 6.88],
[ 2.23, 4.86, 5.12, 2.81],
[ 4.48, 1.38, 2.14, 0.86],
[ 6.68, 1.72, 8.56, 3.23]])
def plotCollection(ax, xs, ys, *args, **kwargs):
ax.plot(xs,ys, *args, **kwargs)
if "label" in kwargs.keys():
#remove duplicates
handles, labels = pyplot.gca().get_legend_handles_labels()
newLabels, newHandles = [], []
for handle, label in zip(handles, labels):
if label not in newLabels:
newLabels.append(label)
newHandles.append(handle)
pyplot.legend(newHandles, newLabels)
ax = pyplot.subplot(1,1,1)
plotCollection(ax, a[:,::2].T, a[:, 1::2].T, 'r', label='data_a')
plotCollection(ax, a[:,1::2].T, a[:, ::2].T, 'b', label='data_b')
pyplot.show()
An easier (and IMO clearer) way to remove duplicates (than what you have) from the handles
and labels
of the legend is this:
handles, labels = pyplot.gca().get_legend_handles_labels()
newLabels, newHandles = [], []
for handle, label in zip(handles, labels):
if label not in newLabels:
newLabels.append(label)
newHandles.append(handle)
pyplot.legend(newHandles, newLabels)
Solution 2
Numpy solution based on will's response above.
import numpy as np
import matplotlib.pylab as plt
a = np.array([[3.57, 1.76, 7.42, 6.52],
[1.57, 1.20, 3.02, 6.88],
[2.23, 4.86, 5.12, 2.81],
[4.48, 1.38, 2.14, 0.86],
[6.68, 1.72, 8.56, 3.23]])
plt.plot(a[:,::2].T, a[:, 1::2].T, 'r', label='data_a')
handles, labels = plt.gca().get_legend_handles_labels()
Assuming that equal labels have equal handles, get unique labels and their respective indices, which correspond to handle indices.
labels, ids = np.unique(labels, return_index=True)
handles = [handles[i] for i in ids]
plt.legend(handles, labels, loc='best')
plt.show()
Solution 3
Matplotlib gives you a nice interface to collections of lines, LineCollection. The code is straight forward
import numpy
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
a = numpy.array([[ 3.57, 1.76, 7.42, 6.52],
[ 1.57, 1.2 , 3.02, 6.88],
[ 2.23, 4.86, 5.12, 2.81],
[ 4.48, 1.38, 2.14, 0.86],
[ 6.68, 1.72, 8.56, 3.23]])
xs = a[:,::2]
ys = a[:, 1::2]
lines = LineCollection([list(zip(x,y)) for x,y in zip(xs, ys)], label='data_a')
f, ax = plt.subplots(1, 1)
ax.add_collection(lines)
ax.legend()
ax.set_xlim([xs.min(), xs.max()]) # have to set manually
ax.set_ylim([ys.min(), ys.max()])
plt.show()
This results in the output below:
Solution 4
So using will's suggestion and another question here, I am leaving my remedy here
handles, labels = plt.gca().get_legend_handles_labels()
i =1
while i<len(labels):
if labels[i] in labels[:i]:
del(labels[i])
del(handles[i])
else:
i +=1
plt.legend(handles, labels)
And the new plot looks like,
Solution 5
A low tech solution is to make two plot calls. One that plots your data and a second one that plots nothing but carries the handle:
a = np.array([[ 3.57, 1.76, 7.42, 6.52],
[ 1.57, 1.2 , 3.02, 6.88],
[ 2.23, 4.86, 5.12, 2.81],
[ 4.48, 1.38, 2.14, 0.86],
[ 6.68, 1.72, 8.56, 3.23]])
plt.plot(a[:,::2].T, a[:, 1::2].T, 'r')
plt.plot([],[], 'r', label='data_a')
plt.legend(loc='best')
Here's the result:
Related videos on Youtube
Comments
-
hashmuke almost 2 years
I have data that results in multiple lines being plotted, I want to give these lines a single label in my legend. I think this can be better demonstrated using the example below,
a = np.array([[ 3.57, 1.76, 7.42, 6.52], [ 1.57, 1.2 , 3.02, 6.88], [ 2.23, 4.86, 5.12, 2.81], [ 4.48, 1.38, 2.14, 0.86], [ 6.68, 1.72, 8.56, 3.23]]) plt.plot(a[:,::2].T, a[:, 1::2].T, 'r', label='data_a') plt.legend(loc='best')
As you can see at Out[23] the plot resulted in 5 distinct lines. The resulting plot looks like this
Is there any way that I can tell the plot method to avoid multiple labels? I don't want to use custom legend (where you specify the label and the line shape all at once) as much as I can.
-
will over 9 yearsYou need newscast to create a custom legend. Can't paste a link because my phone is being crap. Google "matplotlib manually create legend". There is an SO answer which has everything you need a couple of hits down.
-
hashmuke over 9 years@will thanks, that is helpful
-
-
Philipp almost 4 yearsThis did not work when I tried it. Throws
No handles with labels found to put in legend.
when callingplt.legend()
-
Chien Nguyen almost 4 yearsThe complete code is:
import numpy as np import matplotlib.pyplot as plt a = np.array([[ 3.57, 1.76, 7.42, 6.52], [ 1.57, 1.2 , 3.02, 6.88], [ 2.23, 4.86, 5.12, 2.81], [ 4.48, 1.38, 2.14, 0.86], [ 6.68, 1.72, 8.56, 3.23]]) for i in range(len(a)): plt.plot(a[i,::2].T, a[i, 1::2].T, 'r', label='data_a' if i==0 else None) plt.legend(loc='best') plt.savefig('test_plot_legend.png')
-
Night Train almost 4 yearsanother very short solution:
legs = ax.get_legend_handles_labels(); list(zip(*[[legs[0][i], legs[1][i]] for i in [legs[1].index(l) for l in set(legs[1])]]))
-
alphazwest almost 3 years100% this should be the answer. Simple, pythonic, and generates the desired result.
-
zfj3ub94rf576hc4eegm almost 3 yearsIf you use a
set
fornewLabels
you'll avoid iterating over the entire thing when checking for membership. -
Pig about 2 years@zfj3ub94rf576hc4eegm
set
doesn't preserve the elements in the order in which they were added, so your labels could appear in the wrong order in the legend. Also, when dealing with so few elements, it's not necessarily faster to use a set. Also, I think it's unnecessary to optimise plotting code :D -
will about 2 years@zfj3ub94rf576hc4eegm the point of using set is not to optimize it, it's to prevent the legend entries being duplicated. if you want to preserve order, then just use an ordered set.