使用 Matplotlib 使两个标记共享相同的标签并同时在图例中保留图块的黑边

Make two markers share the same labels and simultaneously keep the black edge of patch in legend using Matplotlib

我应该如何修改我的以下代码,使两个标记共享相同的标签,同时在图例中保留补丁的黑边?这是代码(我已经绘制了两次条形图以分离边缘颜色和填充颜色)和绘制的图片。我想做第二张图的标记。

import matplotlib
matplotlib.rcParams['pdf.fonttype'] = 42
matplotlib.rcParams['ps.fonttype'] = 42
import matplotlib.pyplot as plt

x_lable = ['3', '4', '5', '6', '7']

width = 0.3

Data1 = [96, 99, 100, 100, 100]
Data2 = [94, 96, 95, 96, 95]
Data3 = [48, 43, 42, 42, 37]

data_3 = [2228, 2621, 3165, 3761, 3763]
data_4 = [3895, 5670, 7354, 8999, 10731]
data_5 = [4355, 6373, 8279, 10105, 12018]

xcoordinate = [1, 3, 5, 7, 9]
xcoordinate_1 = [xcoordinate[0] + 0 * width, xcoordinate[1] + 0 * width, xcoordinate[2] + 0 * width, xcoordinate[3] + 0 * width, xcoordinate[4] + 0 * width]
xcoordinate_2 = [xcoordinate[0] + 1 * width, xcoordinate[1] + 1 * width, xcoordinate[2] + 1 * width, xcoordinate[3] + 1 * width, xcoordinate[4] + 1 * width]
xcoordinate_3 = [xcoordinate[0] + 2 * width, xcoordinate[1] + 2 * width, xcoordinate[2] + 2 * width, xcoordinate[3] + 2 * width, xcoordinate[4] + 2 * width]
xcoordinate_4 = [xcoordinate[0] + 3 * width, xcoordinate[1] + 3 * width, xcoordinate[2] + 3 * width, xcoordinate[3] + 3 * width, xcoordinate[4] + 3 * width]
xcoordinate_5 = [xcoordinate[0] + 4 * width, xcoordinate[1] + 4 * width, xcoordinate[2] + 4 * width, xcoordinate[3] + 4 * width, xcoordinate[4] + 4 * width]

fig = plt.figure()

# plot ax1
ax1 = fig.add_subplot(111)

ax1.bar(xcoordinate_3, data_3, width=width, label='GGG', color='none', edgecolor='orange', hatch='\\\')
ax1.bar(xcoordinate_4, data_4, width=width, label='SSS', color='none', edgecolor='blue', hatch='---')
ax1.bar(xcoordinate_5, data_5, width=width, label='KKK', color='none', edgecolor='orangered', hatch='///')

ax1.set_ylabel('BBB')
ax1.set_xlabel('AAA')

ax1.bar(xcoordinate_3, data_3, width=width, label='GGG', color='none', edgecolor='black')
ax1.bar(xcoordinate_4, data_4, width=width, label='SSS', color='none', edgecolor='black')
ax1.bar(xcoordinate_5, data_5, width=width, label='KKK', color='none', edgecolor='black')

handles1, labels1 = ax1.get_legend_handles_labels()
order = [0, 1, 2]
plt.legend([handles1[idx] + handles1[idx+3] for idx in order], [labels1[idx] for idx in order], loc=(2.5/10, 1.08), frameon=True, ncol=3, shadow=False, framealpha=1, labelspacing=1.05).get_frame().set_edgecolor('black')

# plot ax2
ax2 = ax1.twinx()

ax2.plot([i + 3 * width for i in xcoordinate], Data3, label='GGG', color='orange', linestyle='-', marker='p')
ax2.plot([i + 3 * width for i in xcoordinate], Data2, label='SSS', color='orangered', linestyle='-', marker='s')
ax2.plot([i + 3 * width for i in xcoordinate], Data1, label='KKK', color='limegreen', linestyle='-', marker='^')

ax2.set_ylabel('CCC')
plt.xticks([i + 3 * width for i in xcoordinate], x_lable)

handles2, labels2 = ax2.get_legend_handles_labels()
order = [0, 1, 2]
plt.legend([handles2[idx] for idx in order], [labels2[idx] for idx in order],  loc=(2.5/10, 1.005),  frameon=True, ncol=3, shadow=False, framealpha=1, labelspacing=1.05).get_frame().set_edgecolor('black')

plt.show()

绘制的图片:

我想要的:

当两个制造商共享同一个标签时,我想保留补丁的黑边。

官方指南中的第三个例子就是你想要的。要组合每个处理程序,请将它们设置为元组形式。如果不想让每个handler都堆叠起来,可以加个配置。

from matplotlib.legend_handler import HandlerLineCollection, HandlerTuple

#plt.legend([handles1[idx] + handles1[+3] for idx in order], [labels1[idx] for idx in order], loc=(2.5/10, 1.08), frameon=True, ncol=3, shadow=False, framealpha=1, labelspacing=1.05).get_frame().set_edgecolor('black')

plt.legend([(handles1[idx],handles2[idx]) for idx in order], [labels2[idx] for idx in order],  loc=(2.5/10, 1.005),  frameon=True, ncol=3, shadow=False, framealpha=1, labelspacing=1.05,handler_map={tuple: HandlerTuple(ndivide=None)}).get_frame().set_edgecolor('black')

看来,我们不能简单地使用HandlerTuple class任意分组句柄(或者我不够聪明)。然而,当ndivide定义的条目更多时,key又从位置1开始,所以我们可以这样写:

....
from matplotlib.legend_handler import HandlerTuple
...
order = [0, 1, 2]
plt.legend([(handles1[idx], handles2[idx], handles1[idx+3]) for idx in order], [labels1[idx] for idx in order], handler_map={tuple: HandlerTuple(ndivide=2)}, loc=(0.1, 1.04), frameon=True, ncol=3, shadow=False, framealpha=1, labelspacing=1.01, handlelength=5).get_frame().set_edgecolor('black')

....

为每个图例条目将 3 个键分组到 2 个位置。这给出了以下输出:

我还增加了手柄长度...惊喜...handlelength=5 因为否则,我们将看不到太多填充图案。