如何用Pandas将含日期-特征键的宽格式数据转为长格式?
如何将含“日期.特征名”键的字典列表转换为长格式Pandas DataFrame?
我来帮你解决这个问题!你之前用pd.wide_to_long的时候参数搞反了,所以没得到预期结果。咱们一步步来修正:
问题分析
你的输入数据里,每个观测的键是{day}.{feature}格式,我们需要把特征名作为重复的前缀(stubnames),把日期部分提取为新的day列,最终得到包含observation_id、day、各特征列的长格式DataFrame。
正确解决方案(使用pd.wide_to_long)
这是最直接高效的方法,只需要调整参数的对应关系:
import pandas as pd x = [ {'observation_id': '1', '0.feature_1': 2, '0.feature_2': 2, '1.feature_1': 3, '1.feature_2': 1}, {'observation_id': '2', '0.feature_1': 7, '0.feature_2': 3, '1.feature_1': 4, '1.feature_2': 2}, {'observation_id': '3', '0.feature_1': 5, '0.feature_2': 2, '1.feature_1': 5, '1.feature_2': 3} ] # 1. 先将字典列表转为初始宽表 df_wide = pd.DataFrame(x) # 2. 使用wide_to_long转换为长表 df_long = pd.wide_to_long( df_wide, stubnames=['feature_1', 'feature_2'], # 重复的特征前缀 i='observation_id', # 标识每条原始观测的列 j='day', # 要提取的新列名(日期) sep='.', # 键名的分隔符 suffix='\d+' # 匹配数字类型的日期后缀 ).reset_index() # 3. 调整列顺序和示例输出一致 df_long = df_long[['observation_id', 'day', 'feature_1', 'feature_2']] print(df_long)
运行后会得到你想要的结果:
observation_id day feature_1 feature_2 0 1 0 2 2 1 1 1 3 1 2 2 0 7 3 3 2 1 4 2 4 3 0 5 2 5 3 1 5 3
为什么你的代码出错了?
你之前把stubnames设成了["0", "1"],这是日期值,但stubnames的正确用法是指定那些重复出现的特征前缀(也就是feature_1、feature_2),这样函数才能识别哪些列属于同一特征的不同日期值。
进阶:动态适配未知特征名
如果你的特征数量不固定(比如以后会新增feature_3),可以自动提取特征名,不需要手动写stubnames:
# 从列名中提取所有特征名 stubnames = list({col.split('.')[1] for col in df_wide.columns if '.' in col}) stubnames.sort() # 保证特征列顺序一致 df_long = pd.wide_to_long( df_wide, stubnames=stubnames, i='observation_id', j='day', sep='.', suffix='\d+' ).reset_index() # 调整列顺序 df_long = df_long[['observation_id', 'day'] + stubnames]
替代方案:melt + str.split + pivot
如果需要更灵活的处理逻辑(比如键名格式更复杂),可以用这个组合方法:
# 1. 将宽表转为极长格式 melted = df_wide.melt(id_vars='observation_id', var_name='day_feature', value_name='value') # 2. 拆分日期和特征名 melted[['day', 'feature']] = melted['day_feature'].str.split('.', expand=True) melted['day'] = melted['day'].astype(int) # 转为整数类型 # 3. 按特征名转回宽格式 df_long = melted.pivot( index=['observation_id', 'day'], columns='feature', values='value' ).reset_index() # 清理列名层级 df_long.columns.name = None df_long = df_long[['observation_id', 'day', 'feature_1', 'feature_2']]
内容的提问来源于stack exchange,提问作者Dar L.
相关产品推荐
相关产品推荐

