Stop Matplotlib Repeating Labels in Legend

Stop matplotlib repeating labels in legend

plt.legend takes as parameters

  1. A list of axis handles which are Artist objects
  2. A list of labels which are strings

These parameters are both optional defaulting to plt.gca().get_legend_handles_labels().
You can remove duplicate labels by putting them in a dictionary before calling legend. This is because dicts can't have duplicate keys.

For example:

For Python versions < 3.7

from collections import OrderedDict
import matplotlib.pyplot as plt

handles, labels = plt.gca().get_legend_handles_labels()
by_label = OrderedDict(zip(labels, handles))
plt.legend(by_label.values(), by_label.keys())

For Python versions > 3.7

As of Python 3.7, dictionaries retain input order by default. Thus, there is no need for OrderedDict form the collections module.

import matplotlib.pyplot as plt

handles, labels = plt.gca().get_legend_handles_labels()
by_label = dict(zip(labels, handles))
plt.legend(by_label.values(), by_label.keys())

Sample Image

Docs for plt.legend

Duplicate items in legend in matplotlib?

As the docs say, although it's easy to miss:

If label attribute is empty string or starts with “_”, those artists
will be ignored.

So if I'm plotting similar lines in a loop and I only want one example line in the legend, I usually do something like

ax.plot(x, y, label="Representatives" if i == 0 else "")

where i is my loop index.

It's not quite as nice to look at as building them separately, but often I want to keep the label logic as close to the line drawing as possible.

(Note that the matplotlib developers themselves tend to use "_nolegend_" to be explicit.)

How can I stop Matplotlib from repeating colors?

How about the colors of the rainbow? The key here is to use ax.set_prop_cycle to assign colors to each line.

NUM_COLORS = len(plist)

cm = plt.get_cmap('gist_rainbow')
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_prop_cycle('color', [cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
# Or,
# ax.set_prop_cycle(color=[cm(1.*i/NUM_COLORS) for i in range(NUM_COLORS)])
for i, p in enumerate(plist):
ax.plot(data1[i], np.exp(data2)[i], marker='o', label=str(p))

plt.legend()
plt.show()

Borrowed from here. Other options possible.

How to ignore some labels in subplots legend?

In this case, the easiest is to create a custom legend.

Some notes:

  • plt.tight_layout() can be used to fit the legend and the labels nicely into the plot; this is much more flexible than fig.subplots_adjust(), especially when later changes are made to the plots
  • fig.legend() uses the "current ax" (current subplot), which in this case is axes[2]
  • when putting the legend to the right of the plots, it helps to use loc='upper left' for the anchor point; 'loc' values that aren't at the left are too hard to control (bbox_to_anchor sets the position of the anchor point, measured in 'axes coordinates' of the given subplot)

Here is an example:

from matplotlib import pyplot as plt
from matplotlib.lines import Line2D
import pandas as pd

# Definition of the dataframe
df = pd.DataFrame(
{'Colors': {0: 'Red', 1: 'Blue', 2: 'Green', 3: 'Blue', 4: 'Red'}, 'X_values': {0: 1, 1: 2, 2: 3, 3: 4, 4: 5},
'Y_values': {0: 2, 1: 4, 2: 8, 3: 10, 4: 4}, 'MinY_values': {0: 1.5, 1: 3, 2: 7.5, 3: 8, 4: 2},
'MaxY_values': {0: 2.5, 1: 5, 2: 9.5, 3: 11, 4: 6}})

fig, axes = plt.subplots(3, 1, sharex=True, gridspec_kw={'hspace': 0.0})

for x_val, y_val, min_val, max_val, color in zip(df['X_values'], df['Y_values'],
df['MinY_values'], df['MaxY_values'], df['Colors']):
axes[0].errorbar(x_val, y_val, yerr=[[min_val], [max_val]], color=color, barsabove='True', fmt='o')
axes[0].axhline(y=5, color='black', linestyle='--')

for x_val, y_val, min_val, max_val, color in zip(df['X_values'], df['Y_values'],
df['MinY_values'], df['MaxY_values'], df['Colors']):
axes[1].errorbar(x_val, y_val, yerr=[[min_val], [max_val]], color=color, barsabove='True', fmt='o')
axes[1].axhline(y=5, color='black', linestyle='--')

for x_val, y_val, min_val, max_val, color in zip(df['X_values'], df['Y_values'],
df['MinY_values'], df['MaxY_values'], df['Colors']):
axes[2].errorbar(x_val, y_val, yerr=[[min_val], [max_val]], color=color, barsabove='True', fmt='o')
axes[2].axhline(y=5, color='black', linestyle='--')

# Legend
legend_handles = [Line2D([], [], color='red', label='It is a red point', marker='o', ls='-'),
Line2D([], [], color='blue', label='What a wonderful blue point', marker='o', ls='-'),
Line2D([], [], color='green', label='Sounds to be a green point', marker='o', ls='-')]
axes[0].legend(handles=legend_handles, bbox_to_anchor=(1.01, 1), loc='upper left', ncol=1)

plt.tight_layout()
plt.show()

custom legend

python matplotlib legend repeating

The comment from @Evert helped

You define your own set of (cyclic) colours.
Simple example at matplotlib.org/examples/color/color_cycle_demo.html , and available colours at matplotlib.org/gallery.html#color . – Evert

I modified pyplot like this above the function def:

import matplotlib.pyplot as plt
from cycler import cycler

palette = ['#ff0000', '#663600', '#a3cc00', '#80ffc3', '#0088ff', '#d9bfff', '#a6296c', '#8c4646', '#ff8800', '#5e664d', '#269991', '#1d3f73', '#7e468c', '#d96236', '#7f2200']

# 1. Setting prop cycle on default rc parameter
plt.rc( 'lines', linewidth = 4 )
plt.rc( 'axes', prop_cycle = ( cycler( 'color', palette ) ) )


Related Topics



Leave a reply



Submit