使用plt.twinx()处理子图多图例时遇报错,求解决方案
双轴子图整合图例时出现AttributeError的解决方法
问题现象
使用plt.twinx()为子图设置双轴不同刻度后,尝试将子图所有标签整合到单个图例框时触发错误:
AttributeError: 'list' object has no attribute 'get_label'
其中柱状图子图的图例可正常整合,但折线+散点图的子图报错。
问题原因
plt.plot()返回的是Line2D对象的列表(即使仅绘制一条线),而plt.scatter()返回PathCollection对象、plt.bar()返回BarContainer对象;- 若直接将
plt.plot()返回的列表传入plt.legend(handles=...),图例无法识别列表类型,需取出列表中的实际绘图对象(索引为0的元素)。
解决方案
折线+散点图子图(第一个子图)
- 对
plt.plot()返回的对象,通过索引[0]取出实际的Line2D对象; - 散点图返回的PathCollection对象可直接使用;
- 收集所有绘图对象后,统一传入
plt.legend(handles=...)。
柱状图子图(第二个子图)
plt.bar()返回的BarContainer对象本身支持图例识别,直接收集后传入即可(原有代码此部分逻辑正常)。
修正后的完整代码
import matplotlib print('matplotlib: {}'.format(matplotlib.__version__)) # matplotlib: 3.2.2 # Generate data import pandas as pd df = pd.DataFrame(dict( Date = [1,2,3], Male = [10,20,30], Female = [20,30,10], Others = [700,500,200] )) print(df) # Date Male Female Others # 0 1 10 20 700 # 1 2 20 30 500 # 2 3 30 10 200 # Create plots import numpy as np import matplotlib.pyplot as plt Userx = 'foo' fig, ax = plt.subplots(nrows=2, ncols=1 , figsize=(20,10)) # 第一个子图:折线+散点+双轴 plt.subplot(211) # 直接取出plt.plot返回列表中的Line2D对象 linechart1, = plt.plot(df['Date'], df['Male'], color='orange', marker=".", markersize=5, label=f"Leg1 for {Userx}") scatterchart2 = plt.scatter(df['Date'], df['Female'], color='#9b5777', marker='d', s=70, label=f"Leg2 for {Userx}") plt.ylabel('Scale1', fontsize=15) # 双轴 ax_twin1 = plt.twinx() linechart3, = ax_twin1.plot(df['Date'], df['Others'], color='black', marker=".", markersize=5, label=f"Leg3 for {Userx}") plt.ylabel('Scale2', fontsize=15) # 收集所有绘图对象,整合图例 all_handles1 = [linechart1, scatterchart2, linechart3] plt.legend(handles=all_handles1, loc='best', fontsize=15) plt.xlabel('Timestamp [24hrs]', fontsize=15, color='darkred') plt.ticklabel_format(style='plain') # 第二个子图:柱状图+双轴 plt.subplot(212) barchart1 = plt.bar(df['Date'], df['Male'], color='green', label=f"Leg1 for {Userx}", width=1, hatch='o') barchart2 = plt.bar(df['Date'], df['Female'], color='blue', label=f"Leg2 for {Userx}", width=0.9, hatch='O') plt.ylabel('Scale1', fontsize=15) plt.ticklabel_format(style='plain') # 双轴 ax_twin2 = plt.twinx() barchart3 = ax_twin2.bar(df['Date'], df['Others'], color='orange', label=f"Leg3 for {Userx}", width=0.9, hatch='/', alpha=0.1) plt.ylabel('Scale2', fontsize=15) plt.ticklabel_format(style='plain') plt.xlabel('Timestamp [24hrs]', fontsize=15, color='darkred') # 整合图例 bar123 = [barchart1, barchart2, barchart3] plt.legend(handles=bar123, loc='best', fontsize=15) plt.show()
关键修正点
- 将
linechart1 = plt.plot(...)改为linechart1, = plt.plot(...),直接取出列表中的Line2D对象; - 双轴的
linechart3同样使用linechart3, = ax_twin1.plot(...)的写法; - 收集所有有效绘图对象后,统一调用一次
plt.legend()整合所有标签。
内容的提问来源于stack exchange,提问作者Mario
相关产品推荐
相关产品推荐

