4

I want to create a legend for a plot manually which does not directly refer to any data in the plot, such as:

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

plt.plot([0, 1], [3, 2])
line = Line2D([], [], label='abc', color='red', linewidth=1.5, marker='o',
              markerfacecolor='yellow', markeredgewidth=1.5, markersize=16)
plt.legend(handles=[line], numpoints=1)
plt.show()

Resulting plot

This works great except because I cannot find a way of setting the marker edge style (i.e. solid, dashed, dotted, etc.) as well. There is no markeredgestyle property or similar for a Line2D, and setting the linestyle instead does not seem to affect the marker edge style. Is there any workaround for this?

A few approaches come to my mind but I am not sure of how feasible they are:

  • Use something entirely different from Line2D. It would need to be showable in the legend, have the same formatting options as Line2D, plus the extra marker edge style. Not sure if such a class exists
  • Create a custom class derived from Line2D and implement that marker edge style myself
  • Create the data in the plot itself, then remove it from there while keeping it in the legend somehow. Not sure if a class which allows me to do this even in the actual chart exists. Note that it has to contain both a marker and a line. Closest thing I can think of is using scatter for the markers and plot for the lines, but that would show two legend entries (unless there is a way to combine those into a single one)

Ideally, the line style and the marker edge style could be different, but if there is a way to change the marker edge style which involves overwriting the line style I would also take it.

I am using matplotlib 1.5.3.

FernAndr
  • 1,448
  • 1
  • 14
  • 26
  • What exactly is a "marker edge style"? Do you want to change the color of the marker edge? I'm quite certain that we can solve any actual problem (like "I want the marker edge to look like ___") but we cannot implement some abstract "marker edge style" without knowng what it is supposed to do. – ImportanceOfBeingErnest Aug 21 '17 at 00:16
  • Given that the marker edge is in the end a line, I would expect that 'marker edge style' to be the same as 'line style', i.e. solid, dashed, dotted, etc. There are already markeredgecolor and markeredgewidth attributes (equivalent to color and linewidth for the lines), but not an equivalent markeredgestyle as I was hoping for – FernAndr Aug 21 '17 at 07:35

2 Answers2

7

You may use a special marker symbol, which has a dotted edge, e.g. marker=ur'$\u25CC$' (Complete STIX symbol table).

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

plt.plot([0, 1], [3, 2])
line = Line2D([], [], label='abc', color='red', linewidth=1.5, marker=ur'$\u25CC$',
              markeredgecolor='indigo', markeredgewidth=0.5, markersize=16)
plt.legend(handles=[line], numpoints=1)
plt.show()

enter image description here

This however cannot be filled.

On the other hand, a scatter plot does not have any connecting lines, such that the linestyle of a scatter affects indeed the marker edge. You may hence combine a Line2D and a scatter, where the line has no marker and constitutes the background line and the scatter is responsible for the marker.

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

plt.plot([0, 1], [3, 2])
line = Line2D([], [], label='abc', color='red', ls="-", linewidth=1.5)
sc1 = plt.scatter([],[],s=14**2,facecolors='yellow', edgecolors='blue',
            linestyle='--')
sc2 = plt.scatter([],[],s=14**2,facecolors='gold', edgecolors='indigo',
            linestyle=':', linewidth=1.5)
plt.legend([(line,sc1), (line,sc2)], ["abc", "def"], numpoints=1)

plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
3

I found a workaround based on my third approach and this answer. Basically, handles can be combined in the legend if you assign them a single label. Hence, you can create the line from a Line2D object and then the marker from the scatter function, with the latter allowing you to specify any marker formatting you might want:

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

plt.plot([0, 1], [3, 2])
line = Line2D([], [], color='red', linewidth=1.5)

marker = plt.scatter([], [], linewidth=2, edgecolor='black', s=75,
                     c='yellow', linestyle='dotted')
marker.remove()  # The scatter plot had no data, so no markers would have shown up anyway

plt.legend(handles=((line, marker),), labels=('abc',), numpoints=1, scatterpoints=1)
plt.show()

Resulting chart

FernAndr
  • 1,448
  • 1
  • 14
  • 26