如何简化Pandas散点图组合图例?拆分颜色与形状图例方案
问题描述
我用Pandas绘制了一幅散点图,涉及两个参数:干预类型(ventilation、filtration、source control、combination)和收益类型(health、productivity、both)。当前图例生成了两个参数的所有组合条目(共15个),我希望将图例简化为仅展示干预类型对应的点颜色、收益类型对应的点形状(或分为两个独立图例)。已尝试在循环前使用plt.close()但未解决问题,代码如下:
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns df = pd.read_csv('merged_data.csv',index_col=False) sns.set_theme(style="ticks") x_values = [] y_values = [] errors = [] colors = [] markers = [] color_map = { 'ventilation': 'blue', 'filtration': 'green', 'filtration ': 'green', 'source control': 'orange', 'combination': 'purple' } marker_map = { 'health': 'o', # Circle 'productivity': 's', # Square 'both': '^' # Triangle } for idx, row in df.iterrows(): x_values.extend([row['citation']] * row['n']) y_values.extend([row['net']] * row['n']) colors.extend([color_map[row['type']]] * row['n']) markers.extend([marker_map[row['benefit']]] * row['n']) plt.figure(figsize=(10, 6)) for type_, color in color_map.items(): for benefit, marker in marker_map.items(): mask = (df['type'] == type_) & (df['benefit'] == benefit) plt.scatter(df['citation'][mask].repeat(df['n'][mask]).values, df['netnew'][mask].repeat(df['n'][mask]).values, color=color, marker=marker, zorder=1, alpha=0.6, s = 60, edgecolor=color, linewidth=0.8, label=f"{type_} - {benefit}") plt.xticks(rotation=90) plt.ylabel('Net Benefit ($/person/year)', fontsize=14) plt.title('Health and Indirect Benefits of Indoor Air Quality Interventions', fontsize=14) plt.legend(title='Type and Benefit', bbox_to_anchor=(1.05, 1), loc='upper left') plt.axhline(0, color='grey', linestyle='--', linewidth=1, label='y=0') plt.tight_layout() plt.show()
解决方案:拆分双属性图例
核心问题是原代码为每个类型+收益的组合都添加了图例标签,导致条目冗余。下面提供两种实现方式:
方式1:创建两个独立图例(颜色对应干预类型,形状对应收益类型)
通过手动创建图例元素,分离颜色和形状的说明:
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from matplotlib.lines import Line2D df = pd.read_csv('merged_data.csv', index_col=False) # 先清洗数据:统一type字段的格式(去除空格) df['type'] = df['type'].str.strip() sns.set_theme(style="ticks") color_map = { 'ventilation': 'blue', 'filtration': 'green', 'source control': 'orange', 'combination': 'purple' } marker_map = { 'health': 'o', # 圆形 'productivity': 's', # 方形 'both': '^' # 三角形 } plt.figure(figsize=(10, 6)) # 绘制散点,仅为每种干预类型添加一次颜色标签 for type_, color in color_map.items(): color_label_added = False for benefit, marker in marker_map.items(): mask = (df['type'] == type_) & (df['benefit'] == benefit) if not mask.any(): continue # 仅第一次绘制该颜色时添加标签,避免重复 current_label = type_ if not color_label_added else None plt.scatter(df['citation'][mask].repeat(df['n'][mask]).values, df['netnew'][mask].repeat(df['n'][mask]).values, color=color, marker=marker, zorder=1, alpha=0.6, s=60, edgecolor=color, linewidth=0.8, label=current_label) color_label_added = True # 手动创建收益类型对应的形状图例元素 marker_legend = [ Line2D([0], [0], marker=marker, color='black', linestyle='None', markersize=8, label=benefit) for benefit, marker in marker_map.items() ] # 获取自动生成的干预类型颜色图例 color_handles, color_labels = plt.gca().get_legend_handles_labels() # 添加y=0的线到颜色图例 color_handles.append(Line2D([0], [0], color='grey', linestyle='--', linewidth=1)) color_labels.append('y=0') # 绘制第一个图例(干预类型+基准线) legend1 = plt.legend(handles=color_handles, labels=color_labels, title='干预类型', bbox_to_anchor=(1.05, 1), loc='upper left') # 保留第一个图例,再绘制第二个图例(收益类型) plt.gca().add_artist(legend1) plt.legend(handles=marker_legend, title='收益类型', bbox_to_anchor=(1.05, 0.7), loc='upper left') plt.xticks(rotation=90) plt.ylabel('净收益(美元/人/年)', fontsize=14) plt.title('室内空气质量干预的健康与间接收益', fontsize=14) plt.tight_layout() plt.show()
方式2:合并为一个图例,分块展示颜色和形状说明
如果需要在同一个图例中区分颜色和形状的含义,可以自定义图例元素:
# 前面的绘图代码和方式1一致,替换图例部分即可 # 创建自定义图例元素 custom_legend = [] # 添加干预类型颜色条目 for type_, color in color_map.items(): custom_legend.append(Line2D([0], [0], marker='o', color=color, linestyle='None', markersize=8, label=type_)) # 添加分隔线(可选,提升可读性) custom_legend.append(Line2D([0], [0], color='white', linestyle='None', label='')) # 添加收益类型形状条目 for benefit, marker in marker_map.items(): custom_legend.append(Line2D([0], [0], marker=marker, color='black', linestyle='None', markersize=8, label=benefit)) # 添加y=0基准线 custom_legend.append(Line2D([0], [0], color='grey', linestyle='--', linewidth=1, label='y=0')) plt.legend(handles=custom_legend, title='图例说明', bbox_to_anchor=(1.05, 1), loc='upper left')
内容的提问来源于stack exchange,提问作者ZHoskin
相关产品推荐
相关产品推荐

