构建带标签的多时间序列整洁DataFrame,支持分组与时序分类
问题解答
一、结论:你提出的Tidy DataFrame范式是机器学习分类任务的最优选择
这种单值列、标签每行重复的结构完全契合机器学习模型(尤其是scikit-learn等主流库)的输入要求:
- 每个样本(某一日期下某一商品的记录)对应一行
- 所有特征(Serial、Group、Country等标签,Price等数值特征)对应列
- 便于后续特征工程(比如时间特征拆分、分组统计、缺失值处理等)
- 数据逻辑清晰,容易排查异常值或不一致问题
而转置回宽表结构(日期作为列)并不适合分类任务:这种结构更适合报表可视化或时序分析,但会导致特征维度冗余,且模型无法直接将多列时间值作为单一样本的输入(除非是专门的时序预测模型,而非分类任务)。
二、实现步骤(以Python pandas为例)
假设你的原始数据已读取为pandas DataFrame(第一列为行索引,对应Serial、Group、Country、日期等),具体实现如下:
1. 数据准备(模拟你的原始结构)
import pandas as pd # 模拟用户提供的转置数据集 raw_data = { '12345': ['Shoes', 'Uk', 50, 40, 55, 60, -10], '12346': ['T-shirt', 'DE', 10, 15, 20, 30, 5] } row_index = ['Group', 'Country', '2020_12', '2021_01', '2021_02', '2021_03', 'pct_change_2021_01'] df = pd.DataFrame(raw_data, index=row_index)
2. 分离元数据与价格时序数据
- 元数据:包含Serial、Group、Country等标签信息(原始数据中Serial是列名,需单独提取)
- 价格数据:包含所有以日期开头的行
# 提取元数据:将列名(Serial)转为列,结合Group、Country行 meta_df = pd.DataFrame({ 'Serial': df.columns, 'Group': df.loc['Group', :].values, 'Country': df.loc['Country', :].values }) # 提取价格时序数据并转为tidy格式 price_rows = [idx for idx in df.index if idx.startswith(('2020_', '2021_'))] price_wide = df.loc[price_rows, :].T # 转置后,行是Serial,列是日期 price_tidy = price_wide.reset_index().melt( id_vars='index', var_name='Date', value_name='Price' ).rename(columns={'index': 'Serial'})
3. 合并数据得到最终结构
# 合并元数据与tidy价格数据 final_df = price_tidy.merge(meta_df, on='Serial') # 调整列顺序为你需要的格式 final_df = final_df[['Date', 'Price', 'Serial', 'Group', 'Country']] print(final_df)
执行后输出的结果即为你期望的Tidy DataFrame:
Date Price Serial Group Country 0 2020_12 50 12345 Shoes Uk 1 2020_12 10 12346 T-shirt DE 2 2021_01 40 12345 Shoes Uk 3 2021_01 15 12346 T-shirt DE 4 2021_02 55 12345 Shoes Uk 5 2021_02 20 12346 T-shirt DE 6 2021_03 60 12345 Shoes Uk 7 2021_03 30 12346 T-shirt DE
4. 扩展:加入pct_change数据(可选)
如果需要将百分比变化数据也纳入结构,可按同样逻辑处理:
# 提取pct_change数据并转为tidy格式 pct_rows = [idx for idx in df.index if idx.startswith('pct_change_')] pct_wide = df.loc[pct_rows, :].T pct_tidy = pct_wide.reset_index().melt( id_vars='index', var_name='Change_Date', value_name='Pct_Change' ).rename(columns={'index': 'Serial'}) # 拆分Change_Date得到对应的日期 pct_tidy['Date'] = pct_tidy['Change_Date'].str.replace('pct_change_', '') # 合并到最终数据 final_df_with_pct = final_df.merge(pct_tidy[['Serial', 'Date', 'Pct_Change']], on=['Serial', 'Date'], how='left')
三、补充说明
- 如果你的原始数据是从文件读取(如CSV),可通过
pd.read_csv(file_path, index_col=0)直接将第一列设为行索引 - 若存在更多标签列(如Brand、Category),只需在元数据提取步骤中加入对应行即可,逻辑完全一致
内容的提问来源于stack exchange,提问作者NorrinRadd
相关产品推荐
相关产品推荐

