pandas宽表转长表:非标准列名适配wide_to_long方案
Pandas宽表转长表:适配「数字_字段名」列名的解决方案
原始数据与目标格式
原始宽表结构
import pandas as pd data = [['Tom', 'run', '2022-01-26', 'run', '2027-01-26'], ['Max', 'stop', '2020-11-16', 'run', '2022-04-26'], ['Bob', 'run', '2021-10-03', 'stop', '2022-01-26'], ['Ben', 'run', '2020-03-11', 'stop', '2013-01-26'], ['Eva', 'stop', '2017-11-16', 'run', '2015-01-26']] df = pd.DataFrame(data, columns=['Patient', '1_Action', '1_Time', '2_Action', '2_Time'])
目标长表格式
Patient action time 0 Tom run 2022-01-26 1 Max stop 2020-11-16 2 Bob run 2021-10-03 3 Ben run 2020-03-11 4 Eva stop 2017-11-16 5 Tom run 2027-01-26 6 Max run 2022-04-26 7 Bob stop 2022-01-26 8 Ben stop 2013-01-26 9 Eva run 2015-01-26
问题说明
尝试使用pd.wide_to_long()时,因原始列名是「数字_字段名」(如1_Action),而该函数默认预期「字段名_数字」格式(如action_1),导致报错ValueError: the id variables need to uniquely identify each row。由于列数较多(约45列),无法手动修改列名,需批量处理或调整代码。
解决方案
方案1:批量修改列名适配wide_to_long
通过字符串处理反转列名中的数字和字段名部分,再使用原逻辑:
# 批量重命名列:将「数字_字段名」转为「字段名_数字」,保留Patient列 df.columns = ['Patient'] + [f"{col.split('_')[1].lower()}_{col.split('_')[0]}" for col in df.columns[1:]] # 调用wide_to_long并整理结果 df_long = pd.wide_to_long(df, stubnames=['action', 'time'], i='Patient', j='num', sep='_').reset_index(drop=True)
方案2:直接使用melt处理原列名
无需修改列名,通过分组拆分动作和时间字段后合并:
# 筛选动作列和时间列 action_cols = [col for col in df.columns if 'Action' in col] time_cols = [col for col in df.columns if 'Time' in col] # 分别转换动作和时间数据为长格式 df_action = df.melt(id_vars='Patient', value_vars=action_cols, var_name='num', value_name='action') df_time = df.melt(id_vars='Patient', value_vars=time_cols, var_name='num', value_name='time') # 合并并清理冗余列 df_long = pd.merge(df_action, df_time, on=['Patient', 'num']) df_long = df_long.drop('num', axis=1).sort_values('Patient').reset_index(drop=True)
两种方案均可生成目标长表格式,方案1贴合wide_to_long的原生逻辑,方案2无需改动原始列结构,适合需要保留原列名的场景。
内容的提问来源于stack exchange,提问作者juststarted
相关产品推荐
相关产品推荐

