pandas dataframe 将行级子类目信息转换为列 重构为类目级宽表
类-子类目长表转类粒度宽表的实现方案
先明确pivot的适用边界
pivot 本身就是为「长表转宽表、把指定列的唯一值映射为新列名」设计的方法,完全匹配你现在的转换需求,适用场景的核心判断标准为:
- 表中不存在同一「索引值 + 待转列值」对应的多行重复冲突,不需要额外聚合计算,只需要把同个索引下不同待转列值对应的属性值平铺成独立列
- 如果出现重复键值对的情况,才需要替换为带聚合逻辑的
pivot_table,否则纯pivot的性能和易用性都更优。
分步实现流程
1. 前置准备:梳理输入表结构逻辑
先对齐你提到的字段规则,输入长表的固定字段为:
- 类层级字段(同个类目标识下值完全一致):
category_id(类目唯一标识)、catprop1、catprop2、Category target - 子类目层级字段(逐行随子类目变化):
subcategory_id(子类目唯一标识)、subcatprop1、subcatprop2
因为是长表结构,同个category_id会因为关联多个子类目重复出现多行,转宽的时候必须先把冗余的类层级属性单独提取,避免后续扁平化处理时出现列值错位。
2. 提取类层级的唯一属性主键
先对重复的类层级属性做去重,拿到每个类目对应的唯一属性和目标值,作为最终宽表的基础核心列:
cat_level_base = df[[ 'category_id', 'catprop1', 'catprop2', 'Category target' ]].drop_duplicates(subset='category_id').reset_index(drop=True)
3. 给同个类目下的子类目加顺序标记
展开子类目属性为列前,需要给同个类目下的所有子类目编固定序号,作为后续列名的拼接依据:
# 按类目分组,给组内子类目生成从0开始的顺序标识 df['subcat_order'] = df.groupby('category_id').cumcount()
如果需要固定子类目排序规则,可以在执行这行代码前先对df做sort_values排序,比如按子类目ID排序,保证顺序标记的一致性。
4. 执行pivot转换,生成子类目属性宽块
以类目标识为宽表主键,以子类目顺序为待转列字段,提取两个子类目属性为值,完成宽块转换:
subcat_wide = df.pivot( index='category_id', columns='subcat_order', values=['subcatprop1', 'subcatprop2'] )
这一步生成的subcat_wide是两层列索引结构(第一层是子类目属性名,第二层是子类目顺序号),也就是你提到的需要扁平化处理的部分。
5. 扁平化列名并拼接核心类属性
把两层列索引拼接为单层的可读列名,再和之前提取的类层级核心表拼接,得到最终的类粒度宽表:
# 扁平化两层列索引,生成类似subcat0_subcatprop1的列名 subcat_wide.columns = [ f'subcat{col_order}_{prop_name}' for prop_name, col_order in subcat_wide.columns ] # 把category_id从索引还原为普通列 subcat_wide = subcat_wide.reset_index() # 按类目ID拼接核心属性和展开的子类目属性 final_df = cat_level_base.merge(subcat_wide, on='category_id', how='left')
常见问题排查
- 如果执行pivot时报「索引值重复」类错误,说明同一个类目下的顺序标记存在重复,需要先按
category_id+subcategory_id做去重,避免同一子类目在同一类目下出现多行重复记录。 - 如果不需要按顺序标记列名,想直接用子类目ID作为列名前缀,直接把第三步的顺序标记替换为
subcategory_id字段即可,pivot时的columns参数直接传入该字段。 - 不同类目下的子类目数量不一致时,缺失的子类目属性列会自动填充为NaN,属于正常的宽表对齐结果,不影响最终「每行对应唯一类目」的粒度要求。
内容的提问来源于stack exchange,提问作者jorge
相关产品推荐
相关产品推荐

