Matplotlib: display legend keys for lines as patches by default
Question:
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.
Answers:
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()
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()
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:
- https://stackoverflow.com/a/57791790/12349101
- https://stackoverflow.com/a/73832036/12349101 (one line from answer of this post)
- https://stackoverflow.com/a/39500357/12349101
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()
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
Accoding to the legend guide, this can be done as follows:
from matplotlib.legend import Legend
from matplotlib.lines import Line2D
from matplotlib.patches import Patch
class ToPatch:
def legend_artist(legend, orig_handle, fontsize, handlebox):
patch = Patch(
color=orig_handle.get_color(),
label=orig_handle.get_label(),
)
handlebox.add_artist(patch)
return patch
Legend.update_default_handler_map({Line2D: ToPatch})
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.
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()
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()
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:
- https://stackoverflow.com/a/57791790/12349101
- https://stackoverflow.com/a/73832036/12349101 (one line from answer of this post)
- https://stackoverflow.com/a/39500357/12349101
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()
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
Accoding to the legend guide, this can be done as follows:
from matplotlib.legend import Legend
from matplotlib.lines import Line2D
from matplotlib.patches import Patch
class ToPatch:
def legend_artist(legend, orig_handle, fontsize, handlebox):
patch = Patch(
color=orig_handle.get_color(),
label=orig_handle.get_label(),
)
handlebox.add_artist(patch)
return patch
Legend.update_default_handler_map({Line2D: ToPatch})