I'd like to generate a custom matplotlib
legend which, for each entry has two lines for each label as shown in this example:
From some research, it seems possible to simply provide two handles' to the
fig.legend(handles, labels)` method, (see this post as an example). However, as shown by the following example code, this simply overlays the lines on top of each other.
import matplotlib.lines as mlines
import matplotlib.pyplot as plt
blue_line = mlines.Line2D([], [], color='r')
green_line = mlines.Line2D([], [], linestyle='--', color='k')
fig, ax = plt.subplots(figsize=(5, 5))
handles = [(blue_line,green_line)]
labels = ['test']
fig.legend(handles=handles, labels=labels, fontsize=20)
So, I think I either need to transform one of the Line2D
objects, or generate a new Patch
object which contains two lines. However, I can't work out how to do this - is there a simple way to combine two patches, or have I missed a trick in combining the handles?
In case this helps others, the context is that I'm using the technique discussed here, that is a twin axis which is coloured to show two different plots simultaneously. However, the two lines have the same label, hence why I wanted to combined them together.
In the matplotlib legend guide there is a chapter about custom legend handlers. You could adapt it to your needs, e.g. like this:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.legend_handler import HandlerBase
class AnyObjectHandler(HandlerBase):
def create_artists(self, legend, orig_handle,
x0, y0, width, height, fontsize, trans):
l1 = plt.Line2D([x0,y0+width], [0.7*height,0.7*height],
linestyle='--', color='k')
l2 = plt.Line2D([x0,y0+width], [0.3*height,0.3*height], color='r')
return [l1, l2]
x = np.linspace(0, 3)
fig, axL = plt.subplots(figsize=(4,3))
axR = axL.twinx()
axL.plot(x, np.sin(x), color='k', linestyle='--')
axR.plot(x, 100*np.cos(x), color='r')
axL.set_ylabel('sin(x)', color='k')
axR.set_ylabel('100 cos(x)', color='r')
axR.tick_params('y', colors='r')
plt.legend([object], ['label'],
handler_map={object: AnyObjectHandler()})
plt.show()
In order to have multiple such entries, one can supply some tuple of parameters that the Handler
then uses to draw the legend.
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.legend_handler import HandlerBase
class AnyObjectHandler(HandlerBase):
def create_artists(self, legend, orig_handle,
x0, y0, width, height, fontsize, trans):
l1 = plt.Line2D([x0,y0+width], [0.7*height,0.7*height],
linestyle=orig_handle[1], color='k')
l2 = plt.Line2D([x0,y0+width], [0.3*height,0.3*height],
color=orig_handle[0])
return [l1, l2]
x = np.linspace(0, 3)
fig, axL = plt.subplots(figsize=(4,3))
axR = axL.twinx()
axL.plot(x, np.sin(x), color='k', linestyle='--')
axR.plot(x, 100*np.cos(x), color='r')
axL.plot(x, .3*np.sin(x), color='k', linestyle=':')
axR.plot(x, 20*np.cos(x), color='limegreen')
axL.set_ylabel('sin(x)', color='k')
axR.set_ylabel('100 cos(x)', color='r')
axR.tick_params('y', colors='r')
plt.legend([("r","--"), ("limegreen",":")], ['label', "label2"],
handler_map={tuple: AnyObjectHandler()})
plt.show()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With