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

如何将Pandas宽格式DataFrame转换为长格式并关联及格线列

Pandas宽表转长表:学生成绩数据格式转换

需求说明

现有一个Pandas DataFrame,存储了学生姓名、2023年1-6月(科学科目包含7月)的数学/科学成绩,以及每位学生每月的及格线。需要将这个宽格式数据转换为包含Name、Year、month、Subject、Marks、Passing_marks的长格式。

原始数据样例

Name  2023_01_Maths  2023_02_Maths  2023_03_Maths  2023_04_Maths  2023_05_Maths  2023_06_Maths  2023_01_Science  2023_02_Science  2023_03_Science  2023_04_Science  2023_05_Science  2023_06_Science  2023_07_Science  2023_01_Passing_marks  2023_02_Passing_marks  2023_03_Passing_marks  2023_04_Passing_marks  2023_05_Passing_marks  2023_06_Passing_marks
A              4              3             10              9              9              7                3                0                1               10                1                2                6                      4                      2                      9                      8                      3                      0
B              5              7              4              4              6              1                3                7                8                2                3                4                3                      5                      1                      4                      6                      5                     10
C              3              8              9              1             10              1                0                4                7                5                8                0               10                      2                      8                      8                      8                     10                      6
D              6              4              6              9              3              0               10                1                0                7                5                8                5                      4                      9                      6                      4                      0                      8
E              7              3              5              5              6             10                0                2               10                7               10                3                8                      1                      1                      0                      9                     10                      5
F              8              2              7              6              8              4                4                0                4                9                8                1                6                      7                      7                      4                      8                      1                      6
G              4              6              8              8              6              6                0                2                2               10                8               10                5                      9                      2                      6                      5                      3                      6
H              3              4              0              1              0              6               10                6                7               10                1                5                5                      5                      5                      3                      6                      4                      6
I              2              3              3              9              9             10               10                4                3                9                9                8                4                      1                      5                      1                      3                      2                      4
J              0              2              2              2              3              8                6                6                3                9               10                3                1                      9                      3                      6                      1                      0                      9

期望的长格式样例

Name  Year month Subject Marks Passing_marks
 A    2023  01    Maths   4       4
 A    2023  01    Science 3       4
 B    2023  01    Maths   5       5
 B    2023  01    Science 3       5

你尝试的代码(存在小问题)

# 变量名拼写错误:df_subjects 写成了 df_subject
df_subjects = df.set_index(['Name'])[['2023_01_Maths' , '2023_02_Maths' , '2023_03_Maths' , '2023_04_Maths' , '2023_05_Maths'  ,'2023_06_Maths' , '2023_01_Science' , '2023_02_Science',  '2023_03_Science'  ,'2023_04_Science',  '2023_05_Science'  ,'2023_06_Science' , '2023_07_Science']]
df_passing_marks = df.set_index(['Name'])[['2023_01_Passing_marks',  '2023_02_Passing_marks',  '2023_03_Passing_marks',  '2023_04_Passing_marks',  '2023_05_Passing_marks',  '2023_06_Passing_marks']]

# 这里变量名错误:df_subject 应该是 df_subjects
df_subject.columns = df_subject.columns.str.split("_", expand=True)
df_subject = df_subject.stack([0, 1, 2]).reset_index().rename(columns={0: 'Marks'})

# stack 参数问题,以及重命名列名错误:原列是0,不是'marks'
df_passing_marks.columns = df_passing_marks.columns.str.split("_", expand=True)
df_passing_marks = df_passing_marks.stack([0, 1, 2]).reset_index().rename(columns={'marks': 'Passing_marks'})

# 合并逻辑可行,但需要修正前面的变量名和列名
df_subject.merge(df_passing_marks[['Name', 'level_1', 'level_2', 'Passing_marks']], on=["Name", 'level_1', 'level_2'])

正确的实现方案

方案1:修正原有代码逻辑

先修正变量名和列名问题,再完成合并:

import pandas as pd

# 1. 处理成绩数据转长表
df_subjects = df.set_index(['Name'])[['2023_01_Maths', '2023_02_Maths', '2023_03_Maths', '2023_04_Maths', 
                                      '2023_05_Maths', '2023_06_Maths', '2023_01_Science', '2023_02_Science', 
                                      '2023_03_Science', '2023_04_Science', '2023_05_Science', '2023_06_Science', 
                                      '2023_07_Science']]
# 拆分列名为多级索引并命名层级
df_subjects.columns = df_subjects.columns.str.split("_", expand=True)
df_subjects.columns.names = ['Year', 'month', 'Subject']

# 堆叠成表并重置索引
df_subjects_long = df_subjects.stack(['Year', 'month', 'Subject']).reset_index(name='Marks')

# 2. 处理及格线数据转长表
df_passing = df.set_index(['Name'])[['2023_01_Passing_marks', '2023_02_Passing_marks', 
                                     '2023_03_Passing_marks', '2023_04_Passing_marks', 
                                     '2023_05_Passing_marks', '2023_06_Passing_marks']]
# 拆分列名并命名层级
df_passing.columns = df_passing.columns.str.split("_", expand=True)
df_passing.columns.names = ['Year', 'month', 'Type']

# 堆叠及格线,去除多余的Type列
df_passing_long = df_passing.stack(['Year', 'month']).reset_index(name='Passing_marks')
df_passing_long = df_passing_long.drop(columns='Type')

# 3. 合并两个长表,7月成绩无对应及格线将显示NaN
final_df = df_subjects_long.merge(df_passing_long, on=['Name', 'Year', 'month'], how='left')

# 调整列顺序为期望格式
final_df = final_df[['Name', 'Year', 'month', 'Subject', 'Marks', 'Passing_marks']]

方案2:使用pd.wide_to_long更简洁

wide_to_long是Pandas专门处理带前缀宽表转长表的工具,代码更简洁:

import pandas as pd

# 处理成绩列转长表
df_melt_subj = pd.wide_to_long(df, 
                               stubnames=['Maths', 'Science'], 
                               i='Name', 
                               j='Year_month', 
                               sep='_', 
                               suffix=r'\d+')
df_melt_subj = df_melt_subj.stack().reset_index(name='Marks')
df_melt_subj[['Year', 'month']] = df_melt_subj['Year_month'].str.split('_', expand=True)
df_melt_subj = df_melt_subj.rename(columns={'level_2': 'Subject'}).drop(columns='Year_month')

# 处理及格线列转长表
df_melt_pass = pd.wide_to_long(df, 
                               stubnames=['Passing_marks'], 
                               i='Name', 
                               j='Year_month', 
                               sep='_', 
                               suffix=r'\d+')
df_melt_pass = df_melt_pass.reset_index()
df_melt_pass[['Year', 'month']] = df_melt_pass['Year_month'].str.split('_', expand=True)
df_melt_pass = df_melt_pass.drop(columns='Year_month')

# 合并两个表
final_df = df_melt_subj.merge(df_melt_pass, on=['Name', 'Year', 'month'], how='left')

# 调整列顺序
final_df = final_df[['Name', 'Year', 'month', 'Subject', 'Marks', 'Passing_marks']]

运行上述代码后即可得到期望的长格式数据,其中7月的Science成绩对应的Passing_marks会显示NaN(原始数据无7月及格线)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 15:44:54