如何将Seaborn kdeplot子图图例设为整个图表的全局图例?
Seaborn子图全局图例获取问题
问题说明
用Seaborn给子图画kdeplot(带hue参数)时,想做一个全局图例代替每个子图的单独图例,但调用ax.get_legend_handles_labels()得到的是空列表,没法靠原始数据重新做图例——颜色匹配没保障。复现代码如下:
from matplotlib import pyplot as plt import numpy as np import pandas as pd import seaborn as sns # 构造包含hue列的测试数据 data = pd.DataFrame( {"A": np.random.random(20), "B": np.random.random(20), "C": ["c", "C"] * 10 }) # 创建2行1列的子图 fig, [ax1, ax2] = plt.subplots(2, 1) # 在两个子图上分别绘制kdeplot sns.kdeplot(data=data, x="A", hue="C", ax=ax1) ax2 = sns.kdeplot(data=data, x="B", hue="C", ax=ax2) # 尝试从轴对象提取图例信息(结果为空列表) handles_1, labels_1 = ax1.get_legend_handles_labels() handles_2, labels_2 = ax2.get_legend_handles_labels() # 尝试基于原始数据设置全局图例 fig.legend(labels=data["C"].unique(), loc="upper right") # 尝试用提取的空数据设置全局图例 fig.legend(handles=handles_1, labels=labels_1, loc="center right") # 显示调试信息 plt.suptitle(f"ax1 legend: {len(labels_1)}, ax2 legend: {len(labels_1)} labels") plt.savefig("legend_issues.png")
运行后子图显示图例,但轴对象提取的handles和labels是空的,这不是bug,是找错了获取位置。
问题原因
Seaborn绘制带hue的图表时,会把自动生成的图例对象存在轴的legend_属性里,而不是matplotlib默认的图例容器中。get_legend_handles_labels()只能获取手动添加到轴的图例元素,读不到Seaborn存在legend_里的内容,所以返回空列表。
解决方法
方法1:从轴的legend_属性提取(最稳妥)
直接从子图的legend_属性里拿handles和labels,再创建全局图例,同时关掉子图自带的图例:
from matplotlib import pyplot as plt import numpy as np import pandas as pd import seaborn as sns data = pd.DataFrame( {"A": np.random.random(20), "B": np.random.random(20), "C": ["c", "C"] * 10 }) fig, [ax1, ax2] = plt.subplots(2, 1) # 绘制子图 sns.kdeplot(data=data, x="A", hue="C", ax=ax1) sns.kdeplot(data=data, x="B", hue="C", ax=ax2) # 移除子图自带的图例 ax1.legend_.remove() ax2.legend_.remove() # 从任意一个子图的legend_中提取图例元素 handles, labels = ax1.legend_.get_handles_labels() # 创建全局图例 fig.legend(handles=handles, labels=labels, loc="upper right") plt.suptitle("全局图例已正确生成") plt.savefig("global_legend.png") plt.show()
方法2:绘图时关闭子图图例,直接复用绘图元素
如果绘图时设置legend=False,可以直接从轴的线条/填充元素提取,再创建全局图例:
from matplotlib import pyplot as plt import numpy as np import pandas as pd import seaborn as sns data = pd.DataFrame( {"A": np.random.random(20), "B": np.random.random(20), "C": ["c", "C"] * 10 }) fig, [ax1, ax2] = plt.subplots(2, 1) # 绘制子图时关闭自带图例 sns.kdeplot(data=data, x="A", hue="C", ax=ax1, legend=False) sns.kdeplot(data=data, x="B", hue="C", ax=ax2, legend=False) # 从第一个子图提取线条和填充元素(对应hue的类别) handles = ax1.lines + ax1.patches labels = data["C"].unique() # 创建全局图例 fig.legend(handles=handles, labels=labels, loc="upper right") plt.suptitle("全局图例已正确生成") plt.savefig("global_legend_2.png") plt.show()
注意要点
- 只要所有子图的
hue类别一致,从任意一个子图提取图例元素即可 - 一定要移除子图自带的图例,避免重复显示
内容的提问来源于stack exchange,提问作者bli
相关产品推荐
相关产品推荐

