You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Matplotlib中实现绘图与图例标记样式分离的优雅方案

问题描述

我常绘制复杂图表,希望简化图例。当前场景中,按**多类模型(颜色区分)和温度带(标记类型区分)**对数据分类绘图,需求如下:

  • 绘图使用空心标记
  • 生成两个独立的简化图例,图例使用实心标记

遇到的难题:修改图例标记会同时改变绘图中的标记。目前找到的唯一可行方法是用copy.deepcopy()复制绘图句柄并修改副本,但该方法会生成额外的图表副本,部分标记显示修改后的样式,效果不理想。作为Python初学者,希望获得更优雅的解决方案,避免上述弊端。

现有代码
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.legend_handler import HandlerTuple
from matplotlib import container
import copy
%matplotlib tk

# Read data
df_main  = pd.read_csv(outputfile_head_tail,index_col=0)

# Indices by Model
base = df_main.index.str.contains("Base")
p90 = df_main.index.str.contains("90")
p80 = df_main.index.str.contains("80")
p70 = df_main.index.str.contains("70")

# Calculate plot values
df_main['dt_h'] = abs(df_main['ti_input'] - df_main['this_ave'])
df_main['dt_cv'] = abs(df_main['to_input'] - df_main['ti_input'])

# Plot presets
fontsize = 24
marker_edge_width = 2
marker_size = 10
plt.rcParams['font.size'] = fontsize
plt.rcParams["font.family"] = "serif"
plt.rcParams["font.serif"] = ["Times New Roman"] + plt.rcParams["font.serif"]
colors = ['brown','r','g', 'b','k', 'orange', 'teal', 'm']
symbols = ['o','v','P','D','*', 'p','X', 's','>', 'h','<', 'd','^']

# Temperature Bands
bands = [273,373,473,573,673]

# Containers for Making Legends
color_collection = []
type_legend_handle = []
type_legend_lable = []
marker_collection = []
temp_legend_handle = []
temp_legend_label = []

# Make 1 figures with corresponding plots
fig1, dThvsdTcv = plt.subplots()
fig1.set_size_inches(14,10,forward=True)

# Model selector
selector = ['base', 'p90', 'p80', 'p70']

# Color is assigned based on Model
for type,color in zip(selector,colors):
    df = df_main.loc[locals()[type]]

    # Marker Shapes are assigned based on Temperature Bands
    for idx, band in enumerate(bands):
        marker = symbols[idx]
        
        # Select Cases that are ±50K of the Temperature Band
        if df.loc[(df['to_input'] > band-50) & (df['to_input'] <= band+50)].shape[0]!=0:

            # Generate pltkwargs
            pltkwargs       = dict(linestyle=':', color = color,
                                markeredgecolor = color, markeredgewidth = marker_edge_width,
                                marker=marker, markersize=marker_size, fillstyle = 'none', markerfacecolor=color)
            
            # Plot the data and collect plot handle for legend
            leg = dThvsdTcv.errorbar(df.loc[(df['to_input'] > band-50) & (df['to_input'] <= band+50),'dt_cv'], 
                                     df.loc[(df['to_input'] > band-50) & (df['to_input'] <= band+50),'dt_h'], 
                                     label = f'{type}_{band}K', 
                                     **pltkwargs)
            
            # Collect one plot handle and label per markers for each Temperature Band
            if marker not in marker_collection:
                marker_collection.append(marker)
                temp_legend_handle.append(leg)
                temp_legend_label.append(f'{band}')

            # Collect one plot handle and label per color for each Model
            if color not in color_collection:
                color_collection.append(color)
                type_legend_handle.append(leg)
                type_legend_lable.append(f'{type}')

# Use deep copy of the Model plot handels to modify the symbols for the legend. Legend symbols should be filled
th_copy = copy.deepcopy(type_legend_handle)
typehandles = [(h[0]) if isinstance(h,container.ErrorbarContainer) else h for h in th_copy]
for tleg in typehandles:
        tleg.set_fillstyle('full')
        tleg.set_linestyle('None')

# Create a legend using the modified symbols and the collected lables        
typelegend = dThvsdTcv.legend(typehandles,type_legend_lable, loc='lower right', handler_map={tuple: HandlerTuple(ndivide=None)})

# The text of the label for the Model Legend has to match the symbol
for idx, tleg_lab in enumerate(typelegend.get_texts()):
    tleg_lab.set_color(colors[idx])
dThvsdTcv.add_artist(typelegend)

# Use a deep copy of the Temperature Bands plot handles to modify symbol fill.
temp_leg_handle_copy = copy.deepcopy(temp_legend_handle)
handles = [(h[0]) if isinstance(h,container.ErrorbarContainer) else h for h in temp_leg_handle_copy]
for leg in handles:
        leg.set_fillstyle('full')          #leg.lines[0].set_fillstyle('full')
        leg.set_markeredgecolor('k')       #leg.lines[0].set_markeredgecolor('k')
        leg.set_markerfacecolor('k')       #leg.lines[0].set_markerfacecolor('k')
        leg.set_linestyle('None')        
dThvsdTcv.legend(handles,temp_legend_label, loc = 'upper left')

dThvsdTcv.set_ylabel(r'$ΔT_h[K]$')
dThvsdTcv.set_xlabel(r'$ΔT_{CV}[K]$')
fig1.tight_layout()
当前绘图效果

当前绘图效果

我曾尝试在绘图后修改标记,但这会同时改变绘图中的标记;使用copy.copy()也无效,修改后的标记会直接显示在绘图中。

内容的提问来源于stack exchange,提问作者Shekaib Musa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 06:09:51