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

Matplotlib子图全局图例样式匹配错误问题排查及可靠解决方案咨询

Fixing Matplotlib Global Legend Mismatch with Duplicate Parameters

I've run into this exact issue before! The problem boils down to how Matplotlib handles multiple lines generated from duplicate column names, and how fig.legend() automatically grabs handles when you don't explicitly specify them.

Why the Legend Breaks

When you plot a parameter that has duplicate columns (like A_data in your test data), ax.plot(df[x_axis], df[parameter], Stil[k]) doesn't just draw one line—it draws one line per matching column. For your first dataset, that means two red circle lines in the first subplot, and two black square lines for the second dataset.

When you call fig.legend(labels=Labels) without specifying handles, Matplotlib tries to collect all available handles from your subplots. Since the first subplot has twice as many handles as you need, it grabs the first two (both red circles) for your two labels—hence the broken legend.

The Reliable Fix: Manually Track Handles

Instead of letting Matplotlib guess which handles to use, explicitly capture one representative handle for each dataset. We'll save the first line drawn for each dataset (even if it's part of a group of duplicate lines) and use those to build the legend.

Here's how to modify your code:

Modified Reproducible Code

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import math

paths = ['Testdata1', 'Testdata2']
Sheets = [['List1'],['List2']]
Labels = ['Dataset1','Dataset2']
Stil = ['ro-', 'ks-']

Testdata1=pd.DataFrame({
    "B0_data":["B_data",3,3,4,1],
    "C0_data":["C_data",5,3,6,8],
    "A1_data":["A_data",1,3,5,7],
    "D0_data":["D_data",9,5,6,4],
    "":[0,0,0,0,0],
    "A0_data":["A_data",1.1,3.1,5.1,7.1],
    "P0_EFF_ME":["P_EFF_ME",0,10,20,30]
})
Testdata2=pd.DataFrame({
    "B0_data":["B_data",2.4,2.4,3.2,0.8],
    "C0_data":["C_data",4,2.4,4.8,6.4],
    "A1_data":["A_data",0.8,2.4,4,5.6],
    "D0_data":["D_data",7.2,4,4.8,3.2],
    "A0_data":["A_data",0.8,2.4,4,5.6],
    "P0_EFF_ME":["P_EFF_ME",0,8,16,24]
})

x_axis ="P_EFF_ME"
x_axis_label = "x-axes title"
ParamTitles = ["A-heading", "B-heading" , "C-heading" , "D-heading" ]
ParamLabels = ["A" , " B", " C", " D" ]
ParamSelect = ["A_data", "B_data" , "C_data" , "D_data" ] # Now works even with A_data first

num_plots = len(ParamSelect)
num_cols = min(num_plots, 2)
num_rows = math.ceil(num_plots / num_cols)

fig, axs = plt.subplots(num_rows, num_cols, figsize=(9, 3 * num_rows))
fig.subplots_adjust(hspace=0.5,wspace=0.3,top=0.9,bottom=0.075)

# Initialize list to hold one handle per dataset
handles = []

for k, path in enumerate(paths):
    for j, sheet in enumerate(Sheets[k]):
        if path == 'Testdata1':
            df = Testdata1.T
        else:
            df = Testdata2.T
        df.index=np.arange(0,df.shape[0],1)
        df.drop([0,2,3], axis=0,inplace=True)
        df.columns=df.iloc[0,:]
        df.index=np.arange(0,df.shape[0],1)
        df.drop([0], axis=0,inplace=True)
        
        for i, parameter in enumerate(ParamSelect):
            row = i // num_cols
            col = i % num_cols
            # Capture the lines returned by plot (may be multiple for duplicate params)
            lines = axs[row, col].plot(df[x_axis], df[parameter], Stil[k])
            # Save only the first line for each dataset (once per k)
            if len(handles) == k:
                handles.append(lines[0])
            
            axs[row, col].set_xlabel(x_axis_label)
            axs[row, col].set_ylabel(ParamLabels[i])
            axs[row, col].set_title(ParamTitles[i])

# Use the manually collected handles for the legend
fig.legend(handles=handles, labels=Labels, loc='outside upper center', bbox_to_anchor=(0.5, 1), ncol=len(Labels), frameon=False)
plt.show()

Key Changes Explained

  1. handles List Initialization: We create an empty list to store one representative line handle for each dataset.
  2. Capture Plot Lines: The plot() function returns a list of Line2D objects (one per column plotted). We save this list to lines.
  3. Save First Handle per Dataset: For each dataset (each k), we save the first line from the first plot we draw for that dataset. This ensures we get the correct style (red circles for Dataset1, black squares for Dataset2) regardless of which parameter we plot first.
  4. Explicit Handles in Legend: When calling fig.legend(), we pass the handles parameter with our manually collected handles, so Matplotlib uses exactly the styles we want.

This approach works no matter which parameter is plotted first, and avoids relying on Matplotlib's automatic handle collection which breaks with duplicate columns.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 06:42:46