Python中优雅高效转换多属性宽格式DataFrame为长格式的最优方法
如何将成对属性列的宽格式DataFrame转换为长格式(Python)
这是个非常典型的宽表转长表需求,针对你这种成对出现的属性列(attribute_X_name + attribute_X_rating),我首推用pandas的pd.wide_to_long方法——它不仅代码简洁优雅,而且是专门为这类场景优化的,处理大量行数据时效率也很高,完全适配你属性编号到13的场景。
先还原你的示例数据
首先我们先把你给出的示例转换成可运行的DataFrame:
import pandas as pd df_wide = pd.DataFrame({ 'id': [1, 3], 'name': ['Linda', 'Brian'], 'last': ['Smith', 'Lin'], 'attribute_1_name': ['Age', 'Hair'], 'attribute_1_rating': [23, 'Black'], 'attribute_2_name': ['Hair', 'Job'], 'attribute_2_rating': ['Brown', 'Barista'] })
用wide_to_long实现转换
这个方法的核心是通过列名的模式匹配,自动识别成对的属性列,不用手动列举所有13组属性:
df_long = pd.wide_to_long( df_wide, stubnames=['attribute_name', 'attribute_rating'], # 列名的前缀部分(去掉数字后缀的部分) i=['id', 'name', 'last'], # 转换后保持不变的标识符列 j='attribute_num', # 临时生成的属性编号列(可以后续删除) sep='_', # 列名中分隔前缀和数字的符号 suffix=r'\d+' # 匹配数字后缀(支持1到13甚至更多) ).reset_index() # 如果你不需要attribute_num这列,直接删掉就行 df_long = df_long.drop(columns='attribute_num')
转换后的结果完全符合你的要求:
id name last attribute_name attribute_rating 0 1 Linda Smith Age 23 1 1 Linda Smith Hair Brown 2 3 Brian Lin Hair Black 3 3 Brian Lin Job Barista
为什么这个方法最优?
- 优雅性:不用手动写循环生成1到13的列名,只要指定前缀和匹配规则,不管属性数量多少都能自动适配
- 高效性:
wide_to_long是pandas底层优化过的方法,处理百万级以上的行数据时,性能远优于手动循环或者多次melt再合并的方案
备选方案:用melt实现(不推荐)
如果你一定要用melt,也能实现,但代码会繁琐很多,而且效率更低:
# 分别处理name和rating列 melt_name = df_wide.melt( id_vars=['id', 'name', 'last'], value_vars=[f'attribute_{i}_name' for i in range(1, 14)], var_name='attribute_num', value_name='attribute_name' ) melt_rating = df_wide.melt( id_vars=['id', 'name', 'last'], value_vars=[f'attribute_{i}_rating' for i in range(1, 14)], var_name='attribute_num', value_name='attribute_rating' ) # 合并两个结果并清理 df_long_melt = pd.merge(melt_name, melt_rating, on=['id', 'name', 'last', 'attribute_num']) df_long_melt = df_long_melt.drop(columns='attribute_num').dropna()
这种方法需要手动生成所有属性列名,而且合并操作会带来额外的性能开销,所以只作为备选。
内容的提问来源于stack exchange,提问作者hashbrown lover
相关产品推荐
相关产品推荐

