Based on the question and answer here (
Note the presence of only the final (group 3) entry in the legend.
Is there a way to get all line/path groups included in the legend so that (in this case) there are 3 items in the legend?
Bonus points if this can be handled entirely within the plot
function, avoiding having to pass out handles from the plot function.
This question is not asking about multiple separate legends.
CodePudding user response:
For something to appear in the legend, matplotlib uses the label=
keyword.
You can use ax.get_legend_handles_labels()
to find all the elements that have a label. Recombining these handles and labels can create your desired legend. Calling ax.legend
multiple times will erase the old legend and set a new one.
The test code replaces ax.plot(x, y, 'k--', ...
by ax.plot(x, y, '--', ...
. Note that here k
would color the line black, but the color is already set by the color=
keyword.
import matplotlib.pyplot as plt
import numpy as np
def plot(x, y, ax, col, group, **kwargs):
ax.plot(x, y, '--', color=col, label=f"group {group}: Mean")
ax.fill_between(x, y 10, y - 10, color=col, alpha=0.5, label='interval')
handles, labels = ax.get_legend_handles_labels()
print(handles, labels)
ax.legend(handles=[(h1, h2) for h1, h2 in zip(handles[::2], handles[1::2])],
labels=[l1 " " l2 for l1, l2 in zip(labels[::2], labels[1::2])])
fig, ax = plt.subplots()
x = np.linspace(1, 100, 100)
plot(x, x, ax, "C0", 1)
plot(x, x 30, ax, "C1", 2)
plot(x, x 60, ax, "C2", 3)
plt.tight_layout()
plt.show()
CodePudding user response:
I tried the following, thinking it would work
def plot(x, y, ax, col, group, **kwargs):
legend = ax.get_legend() or []
if legend:
handles = legend.legendHandles
labels = [txt.get_text() for txt in legend.get_texts()]
else:
handles = []
labels = []
hline, = ax.plot(x, y, ls='--', color=col)
hpatch = ax.fill_between(x, y 10, y-10, color=col, alpha=0.5)
handles = [(hline, hpatch)]
labels = [f"group {group}: Mean interval"]
ax.legend(handles, labels)
fig, ax = plt.subplots(figsize=(9, 6))
x = np.linspace(1, 100, 100)
plot(x, x, ax, "C0", 1)
plot(x, x 30, ax, "C1", 2)
plot(x, x 60, ax, "C2", 3)
However, I get this
If we have a look at the handles, it looks like it only shows the lines
ax.get_legend().legendHandles
[<matplotlib.lines.Line2D at 0x7f066b54b370>,
<matplotlib.lines.Line2D at 0x7f066b54b940>,
<matplotlib.lines.Line2D at 0x7f066b54bdc0>]
I was trying to see if this is a bug or intended behavior.
I also tried
from matplotlib.lines import Line2D
from matplotlib.patches import Patch
def plot(x, y, ax, col, group, **kwargs):
legend = ax.get_legend() or []
if legend:
handles = legend.legendHandles
labels = [txt.get_text() for txt in legend.get_texts()]
else:
handles = []
labels = []
hline, = ax.plot(x, y, ls='--', color=col)
hpatch = ax.fill_between(x, y 10, y-10, color=col, alpha=0.5)
label = f"group {group}: Mean interval"
handle = (Line2D([], [], color=col, label=label), Patch(color=col, alpha=0.5, label=label))
handles = [handle]
labels = [label]
ax.legend(handles, labels)
but the result is still the same