I would like to make a complex legend in matplotlib. I made the following code
import matplotlib.pylab as plt
import numpy as np
N = 25
y = np.random.randn(N)
x = np.arange(N)
y2 = np.random.randn(25)
# serie A
p1a, = plt.plot(x, y, "ro", ms=10, mfc="r", mew=2, mec="r")
p1b, = plt.plot(x[:5], y[:5] , "w+", ms=10, mec="w", mew=2)
p1c, = plt.plot(x[5:10], y[5:10], "w*", ms=10, mec="w", mew=2)
# serie B
p2a, = plt.plot(x, y2, "bo", ms=10, mfc="b", mew=2, mec="b")
p2b, = plt.plot(x[15:20], y2[15:20] , "w+", ms=10, mec="w", mew=2)
p2c, = plt.plot(x[10:15], y2[10:15], "w*", ms=10, mec="w", mew=2)
plt.legend([p1a, p2a, (p1a, p1b), (p2a,p2b), (p1a, p1c), (p2a,p2c)],
["No prop", "No prop", "Prop +", "Prop +", "Prop *", "Prop *"], ncol=3, numpoints=1)
plt.show()
It produces plot like that:
But I would like to plot complex legend like here:
I also tried to do the legend with table
function but I can not put a patch object into the table to a proper position of a cell.
Matplotlib has native support for legends. Legends can be placed in various positions: A legend can be placed inside or outside the chart and the position can be moved. The legend() method adds the legend to the plot. In this article we will show you some examples of legends using matplotlib.
To change the position of a legend in Matplotlib, you can use the plt. legend() function. The default location is “best” – which is where Matplotlib automatically finds a location for the legend based on where it avoids covering any data points.
Is this solution close enough to your liking? It is slighty inspired by Ricardo's answer, but I only used one legend-object for each column, and then utilised the title
-keyword to set the title of each individual column. To put the markers in the center of each column I used handletextpad
with a negative value to push it backward. There are no legends to individual lines. I also had to insert some spaces into the title-strings to make them look equally big when drawn on screen.
I also noticed now when the figure was saved that additional tweaks to the exact position of the legend-boxes and are needed, but since I guess you might want to tweak more stuff in the code anyway I leave it for you. You might also need to play yourself with the handletextpad
to make them "perfectly" aligned.
import matplotlib.pylab as plt
import numpy as np
plt.close('all')
N = 25
y = np.random.randn(N)
x = np.arange(N)
y2 = np.random.randn(25)
# serie A
p1a, = plt.plot(x, y, "ro", ms=10, mfc="r", mew=2, mec="r")
p1b, = plt.plot(x[:5], y[:5] , "w+", ms=10, mec="w", mew=2)
p1c, = plt.plot(x[5:10], y[5:10], "w*", ms=10, mec="w", mew=2)
# serie B
p2a, = plt.plot(x, y2, "bo", ms=10, mfc="b", mew=2, mec="b")
p2b, = plt.plot(x[15:20], y2[15:20] , "w+", ms=10, mec="w", mew=2)
p2c, = plt.plot(x[10:15], y2[10:15], "w*", ms=10, mec="w", mew=2)
line_columns = [
p1a, p2a,
(p1a, p1b), (p2a, p2b),
(p1a, p1c), (p2a, p2c)
]
leg1 = plt.legend(line_columns[0:2], ['', ''], ncol=1, numpoints=1,
title='No prop', handletextpad=-0.4,
bbox_to_anchor=[0.738, 1.])
leg2 = plt.legend(line_columns[2:4], ['', ''], ncol=1, numpoints=1,
title=' Prop + ', handletextpad=-0.4,
bbox_to_anchor=[0.87, 1.])
leg3 = plt.legend(line_columns[4:6], ['', ''], ncol=1, numpoints=1,
title=' Prop * ', handletextpad=-0.4,
bbox_to_anchor=[0.99, 1.])
plt.gca().add_artist(leg1)
plt.gca().add_artist(leg2)
plt.gca().add_artist(leg3)
plt.gcf().show()
Edit
Maybe this will work better. You still have to tweak a few stuff, but the alignment-problem of the bboxes are away.
leg = plt.legend(line_columns, ['']*len(line_columns),
title='No Prop Prop + Prop *',
ncol=3, numpoints=1, handletextpad=-0.5)
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