3

I would like perform the type of legend shown in the figure enter image description here.

That figure I did with some tricks just to represent what I really want so it does not show well the color of the pink markers in the legend.

In the figure the circles represent the same parameter but for two different models; the same is true for the triangular and square markers. I want to place the two circular markers referring to the same parameter in the first line of the legend, and analogously for the two other markers on the lines below this one. Thank you.

Code:

import matplotlib.pyplot as plt

q1 = [100.0, 60.0, 200.0, 300.0]
NO1 = [0.35799999999999998, 0.33100000000000002, 0.22900000000000001,     0.17799999999999999]
No1 = [0.34599999999999997, 0.29899999999999999, 0.20699999999999999, 0.14999999999999999]
Nb1 = [0.46600000000000003, 0.45600000000000002, 0.27800000000000002, 0.24399999999999999]

q2 = [60.0, 100.0, 200.0, 300.0]
NO2 = [0.44700000000000001, 0.29199999999999998, 0.28299999999999997, 0.253]
No2 = [0.38900000000000001, 0.28499999999999998, 0.311, 0.251]
Nb2 = [0.44, 0.34899999999999998, 0.45900000000000002, 0.39400000000000002]

fig, ax = plt.subplots(figsize = (6,3))

ax.plot(q1, NO1, marker = 'o', markerfacecolor = 'none', markeredgewidth = 1, color = 'gray', linestyle = '', markersize = 8, label = '$N$ in parameter a')
ax.plot(q2, NO2, marker = 'o', markerfacecolor = 'none', markeredgewidth = 1, color = 'palevioletred', linestyle = '', markersize = 8)#, label = '$N$ in parameter a')
ax.plot(q1, No1, marker = '^', markerfacecolor = 'none', markeredgewidth = 1, color = 'gray', linestyle = '', markersize = 8, label = '$N$ in parameter b')
ax.plot(q2, No2, marker = '^', markerfacecolor = 'none', markeredgewidth = 1, color = 'palevioletred', linestyle = '', markersize = 8)#, label = '$N$ in parameter b')  
ax.plot(q1, Nb1, marker = 's', markerfacecolor = 'none', markeredgewidth = 1, color = 'gray', linestyle = '', markersize = 8, label = '$N$ in parameter c') 
ax.plot(q2, Nb2, marker = 's', markerfacecolor = 'none', markeredgewidth = 1, color = 'palevioletred', linestyle = '', markersize = 8)#, label = '$N$ in parameter c')
#plt.legend(loc='upper right', bbox_to_anchor=(0.945, 1))
plt.xlabel('x')
plt.ylabel('$N$')
plt.xticks([60, 100, 200, 300])
plt.minorticks_on()
plt.tick_params(direction = 'in', bottom = True, top = True, left = True, right = True, which = 'major')    
plt.tick_params(direction = 'in', bottom = False, top = False, left = True, right = True, which = 'minor')  
user140259
  • 450
  • 2
  • 5
  • 16

1 Answers1

7

You may use a HandlerTuple handler and provide tuples of the artists to show in each row as handles to the legend.

import matplotlib.pyplot as plt
import matplotlib.legend_handler

q1 = [100.0, 60.0, 200.0, 300.0]
NO1 = [0.358, 0.331, 0.229, 0.178]
No1 = [0.346, 0.299, 0.207, 0.15]
Nb1 = [0.466, 0.456, 0.278, 0.244]

q2 = [60.0, 100.0, 200.0, 300.0]
NO2 = [0.447, 0.292, 0.283, 0.253]
No2 = [0.389, 0.285, 0.311, 0.251]
Nb2 = [0.44, 0.349, 0.459, 0.394]

fig, ax = plt.subplots(figsize = (6,3))

prop = dict(markerfacecolor = 'none', markeredgewidth = 1,
            linestyle = '', markersize = 8,)
l1, = ax.plot(q1, NO1, marker = 'o', color = 'gray', label = '$N$ in parameter a', **prop)
l2, = ax.plot(q2, NO2, marker = 'o', color = 'palevioletred', **prop)
l3, = ax.plot(q1, No1, marker = '^', color = 'gray', label = '$N$ in parameter b', **prop)
l4, = ax.plot(q2, No2, marker = '^', color = 'palevioletred', **prop)
l5, = ax.plot(q1, Nb1, marker = 's', color = 'gray', label = '$N$ in parameter c', **prop) 
l6, = ax.plot(q2, Nb2, marker = 's', color = 'palevioletred', **prop)


handles = [(l1,l2), (l3,l4), (l5,l6)]
_, labels = ax.get_legend_handles_labels()

ax.legend(handles = handles, labels=labels, loc='upper right', 
          handler_map = {tuple: matplotlib.legend_handler.HandlerTuple(None)})

plt.xlabel('x')
plt.ylabel('$N$')

plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • Thanks for your reply. Yesterday I thought your code worked. Did you make any changes? Today I have this error when compiling. " handler_map = {tuple: matplotlib.legend_handler.HandlerTuple(None)}) TypeError: __init__() takes 1 positional argument but 2 were given " Would you please tell me what the error is? – user140259 Jul 31 '18 at 12:13
  • I did not change anything (you would see if the answer got edited similar to how you see that you edited your question 16 hours ago). Did you run this in two different versions of matplotlib or python? Does it work when using `HandlerTuple(ndivide=None)`? – ImportanceOfBeingErnest Jul 31 '18 at 12:17
  • It also gives error: " HandlerBase.__init__(self, **kwargs) TypeError: __init__() got an unexpected keyword argument 'ndivide' " – user140259 Jul 31 '18 at 12:21
  • What does `import matplotlib; print(matplotlib.__version__)` give you? – ImportanceOfBeingErnest Jul 31 '18 at 12:23
  • It gives: 2.0.0 – user140259 Jul 31 '18 at 12:26
  • Ok. Two options: Do you want me to try and find a solution which works with 2.0, or are you able to update your matplotlib version? – ImportanceOfBeingErnest Jul 31 '18 at 12:28
  • Indeed the problem was the version of matplotlib. Thank you very much. – user140259 Jul 31 '18 at 13:12