I am having a hard time adding legend to matplotlib.pyplot
, my goal is to make parallel coordinates plot similar to the one
Parallel Coordinates plot in Matplotlib
Since my use-case is similar, I used the solution provided, except that I have only 2 observations, 1 in each group and I added
plt.legend(axes,style)
, so that legend is created, but when I run the code, I get the below warning and no legend.
:\Python27\lib\site-packages\matplotlib\legend.py:634: UserWarning: Legend does not support instances. A proxy artist may be used instead. See: http://matplotlib.org/users/legend_guide.html#using-proxy-artist "#using-proxy-artist".format(orig_handle)
I tried to go through documentation but could not find a solution.
I found another stackoverflow post listed below but still not quite clear on the usage of legends, especially on how to unpack subplots before passing to legend function. Could anyone please explain how it works.
Using a proxy artist inside a legend, matplotlib, Python
#!/usr/bin/python
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
def parallel_coordinates(data_sets, style=None):
dims = len(data_sets[0])
x = range(dims)
fig, axes = plt.subplots(1, dims-1, sharey=False)
if style is None:
style = ['r-']*len(data_sets)
# Calculate the limits on the data
min_max_range = list()
for m in zip(*data_sets):
mn = min(m)
mx = max(m)
if mn == mx:
mn -= 0.5
mx = mn + 1.
r = float(mx - mn)
min_max_range.append((mn, mx, r))
# Normalize the data sets
norm_data_sets = list()
for ds in data_sets:
nds = [(value - min_max_range[dimension][0]) /
min_max_range[dimension][2]
for dimension,value in enumerate(ds)]
norm_data_sets.append(nds)
data_sets = norm_data_sets
# Plot the datasets on all the subplots
for i, ax in enumerate(axes):
for dsi, d in enumerate(data_sets):
ax.plot(x, d, style[dsi])
ax.set_xlim([x[i], x[i+1]])
# Set the x axis ticks
for dimension, (axx,xx) in enumerate(zip(axes, x[:-1])):
axx.xaxis.set_major_locator(ticker.FixedLocator([xx]))
ticks = len(axx.get_yticklabels())
labels = list()
step = min_max_range[dimension][2] / (ticks - 1)
mn = min_max_range[dimension][0]
for i in xrange(ticks):
v = mn + i*step
labels.append('%4.2f' % v)
axx.set_yticklabels(labels)
# Move the final axis' ticks to the right-hand side
axx = plt.twinx(axes[-1])
dimension += 1
axx.xaxis.set_major_locator(ticker.FixedLocator([x[-2], x[-1]]))
ticks = len(axx.get_yticklabels())
step = min_max_range[dimension][2] / (ticks - 1)
mn = min_max_range[dimension][0]
labels = ['%4.2f' % (mn + i*step) for i in xrange(ticks)]
axx.set_yticklabels(labels)
# Stack the subplots
plt.subplots_adjust(wspace=0)
plt.legend(axes,style)
return plt
if __name__ == '__main__':
import random
base = [0, 0, 5, 5, 0]
scale = [1.5, 2., 1.0, 2., 2.]
data = [[base[x] + random.uniform(0., 1.)*scale[x]
for x in xrange(5)] for y in xrange(1)]
colors = ['r'] * 1
base = [3, 6, 0, 1, 3]
scale = [1.5, 2., 2.5, 2., 2.]
data.extend([[base[x] + random.uniform(0., 1.)*scale[x]
for x in xrange(5)] for y in xrange(1)])
colors.extend(['b'] * 1)
parallel_coordinates(data, style=colors).show()
Basically the error is because matplotlib
has no support for procedurally generating the legend, and is telling you that you will have to do so manually. For example
blue_line = mlines.Line2D([], [], color='blue', label='Blue')
red_line = mlines.Line2D([], [], color='red', label='Red')
plt.legend(handles=[blue_line, red_line])
which relies on importing matplotlib.lines
as mlines
. In the code in question this will generate
You can also use matplotlib.patches
if you prefer a patch instead of a line, i.e.
blue_patch = mpatches.Patch(color='blue', label='Blue')
red_patch = mpatches.Patch(color='red', label='Red')
plt.legend(handles=[blue_patch, red_patch])
which relies on importing matplotlib.patches
as mpatches
. This generates
You can add whatever custom handles
you need to the legend - mixing patches, lines, and markers is acceptable.