matplotlib 如何拥有颜色和标记类型的组合图例

anauzrmj  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(89)

我经常生成复杂的图,并努力简化图例。在这个特定的例子中,我绘制的是按多个模型和温度带分类的数据。我想包括两个不同的简化图例,并带有填充标记,而图中有未填充的标记,但我面临着一个挑战:修改图例的标记也会改变图中的标记。
我发现的生成所需图例的唯一方法是复制绘图句柄并使用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.deepcopy()复制图例,然后修改它。不幸的是,这也会复制图形。我尝试copy.copy(),这不起作用,因为它会在图中显示修改后的标记。

hk8txs48

hk8txs481#

下面的代码使用对seaborn的调用创建了一个类似的美学:


的数据

import seaborn as sns
ax = sns.relplot(df, x='deltaCV', y='deltaT', hue='selector', style='temp_band',
                 markers=True, dashes=False, errorbar=None,
                 height=5, aspect=1.5, kind='line')

字符串
以整洁的格式模拟数据:

import pandas as pd

x = np.concatenate([
    np.linspace(5, 95, 30),
])

y_base = x * 2.5 + 70
len_y = len(y_base)

df = pd.DataFrame(
    {'deltaCV': np.tile(x, 11),
     'deltaT': np.concatenate([y_base,
                               y_base - 5,
                               y_base - 15,
                               y_base - 20,
                               y_base - 30,
                               y_base - 45,
                               y_base - 50,
                               y_base - 65,
                               y_base - 85,
                               y_base - 95,
                               y_base - 110]),
     'selector': ['base']*len_y + ['p90']*len_y + ['base']*len_y +  ['p80']*len_y + ['p90']*len_y +\
                   ['p70']*len_y +  ['p80']*len_y + ['p70']*len_y + ['p90']*len_y +  ['p80']*len_y + ['p70']*len_y,
     'temp_band': [673]*len_y + [673]*len_y + [473]*len_y + [673]*len_y + [573]*len_y +\
                          [373]*len_y + [473]*len_y + [673]*len_y + [373]*len_y + [673]*len_y + [473]*len_y 
    }
)


相关问题