0

For background, see the legend guide.

I want to display legend keys for Line2D objects as Patches (with the same color and label), by default. What is the cleanest way to do this? I tried using update_default_handler_map with a handler_map but keep getting errors.

user76284
  • 1,269
  • 14
  • 29

2 Answers2

2

You can display legend as patches doing the following:

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

colors = [['#01FF4F','#00FFff'],
           ['#FFEB00','#FFFF00']]
categories = ['A','B']
# categories and colors inside a dict
legend_dict=dict(zip(categories,colors))
# setting up lines for the plot and list for patches
patchList = []
fig, ax = plt.subplots()
# assigning each inner color for each categories to their respective plot lines and legend/patches
for key in legend_dict:
        data_key = mpatches.Patch(facecolor=legend_dict[key][0],
                                  edgecolor=legend_dict[key][1], label=key)
        ax.plot(np.random.randn(100).cumsum(), color=legend_dict[key][0], label=legend_dict[key][1])
        patchList.append(data_key)

ax.legend(handles=patchList, ncol=len(categories), fontsize='small')
plt.show()

Which output: enter image description here

I didn't know matplotlib before making this answer, so I had to mix two or three SO post to get this far (and trying/failing for a bit). Here they are, in a non-special order:

Since you mentioned Line2d:

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.lines import Line2D

colors = [['#01FF4F','#00FFff'],
           ['#FFEB00','#FFFF00']]
categories = ['A','B']
# categories and colors inside a dict
legend_dict=dict(zip(categories,colors))
# setting up lines for the plot and list for patches
patchList = []
fig, ax = plt.subplots()
# assigning each inner color for each categories to their respective plot lines and legend/patches
for key in legend_dict:
        data_key = mpatches.Patch(facecolor=legend_dict[key][0],
                                  edgecolor=legend_dict[key][1], label=key)
        ax.plot(Line2D(np.random.randn(100).cumsum(), np.random.randn(100).cumsum()).get_data(), color=legend_dict[key][0], label=legend_dict[key][1])
        patchList.append(data_key)

ax.legend(handles=patchList, ncol=len(categories), fontsize='small')
plt.show()

Output: enter image description here

One intriguing and related to patches as legend post that I can't help but link to: Make patches bigger used as legend inside matplotlib

lastly, here is a decent excerpt related to customizing legend on matplotlib: https://jakevdp.github.io/PythonDataScienceHandbook/04.06-customizing-legends.html

Nordine Lotfi
  • 463
  • 2
  • 5
  • 20
  • 1
    Thank you for your answer. I'm looking for a way to do this automatically by default (hence the comment about update_default_handler_map), i.e. once in the entire program rather than once in each plotting routine. – user76284 Nov 18 '22 at 02:43
  • I don't think I know enough about matplotlib (what I posted above are merely examples ). I did find however something you could do that would help you do that: https://stackoverflow.com/a/57697692/12349101 By using a class, you could then pass that to the `update_default_handler_map` and it would work as it is. @user76284 – Nordine Lotfi Nov 19 '22 at 13:54
0

Making this appear by default probably wouldn't work, as there are too many differences between lines and patches. Lines can have a line width, markes, line styles, ... . Patches can have an outline, hatching, ....

For a simple situation with just a colored, you might use rectangles:

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

fig, ax = plt.subplots()
ax.plot(np.random.randn(100).cumsum(), color='tomato', label='red line')
ax.plot(np.random.randn(100).cumsum(), color='cornflowerblue', alpha=0.6, label='blue line')

handles, labels = ax.get_legend_handles_labels()
new_handles = [h if type(h) != matplotlib.lines.Line2D
               else plt.Rectangle((0, 0), 0, 0, lw=0, color=h.get_color(), alpha=h.get_alpha()) for h in handles]

ax.legend(new_handles, labels)
plt.show()

showing lines as patches in legend

JohanC
  • 71,591
  • 8
  • 33
  • 66
  • "there are too many differences between lines and patches" I just want the patches to have the same color (and label). – user76284 Sep 24 '22 at 17:31
  • I would like to make it a default setting, as stated in the question. In particular, I want to avoid doing this manually for every plot. (Aside: Why rectangle rather than patch, which likewise has a color parameter?) – user76284 Sep 24 '22 at 20:16
  • I meant falling into the trap of these functions gets you into similar troubles as editing the source code. It really is a bad idea. – JohanC Sep 25 '22 at 19:36