如何在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
相关产品推荐
相关产品推荐

