如何使用Pandas高效实现多列动态的DataFrame重塑?
高效实现Pandas数据重塑(替代循环方案)
嘿,我明白你想摆脱逐行循环来高效完成数据重塑的需求——确实,原生Pandas的向量化方法比iterrows()循环快太多了,尤其是数据量大的时候。你提到的pivot思路方向有点偏,因为pivot是用来长表转宽表的,而你需要的是宽表转长表,所以应该用melt、wide_to_long这类专门的工具。下面给你几个可行的高效方案:
先看原始数据
首先我们再确认下输入的DataFrame:
import pandas as pd df = pd.DataFrame({ 'level1':['a', 'a'], 'level2':['b', 'b'], 'level3':[100, 101], 'id1':[111,222], 'id2':[333,444], 'foo_value':[0.1,0.2], 'bar_value':[0.3,0.4] })
方案一:两步melt(最直观高效)
这个方法分两步把宽表转成目标长表,完全避免循环:
# 第一步:把id1、id2展开成单独的id列 id_melted = df.melt( id_vars=['level1', 'level2', 'level3', 'foo_value', 'bar_value'], value_vars=['id1', 'id2'], value_name='id' # 指定新列名 ).drop('variable', axis=1) # 删掉不需要的中间列 # 第二步:把foo_value、bar_value展开成item和value列 final_df = id_melted.melt( id_vars=['level1', 'level2', 'level3', 'id'], value_vars=['foo_value', 'bar_value'], var_name='item', value_name='value' ) # 最后清理item列的后缀 final_df['item'] = final_df['item'].str.replace('_value', '') # 调整列顺序到你想要的格式 final_df = final_df[['id', 'item', 'value', 'level1', 'level2', 'level3']]
方案二:结合wide_to_long和melt
如果你习惯用wide_to_long处理带统一后缀的列,可以试试这个组合:
# 先处理value列,转成长表 value_long = pd.wide_to_long( df, stubnames=['foo', 'bar'], i=['level1', 'level2', 'level3', 'id1', 'id2'], j='item', suffix='_value' ).reset_index() # 再处理id列,展开成单独的id列 final_df = value_long.melt( id_vars=['level1', 'level2', 'level3', 'item'], value_vars=['id1', 'id2'], value_name='id' ).drop('variable', axis=1) # 调整列顺序 final_df = final_df[['id', 'item', 'value', 'level1', 'level2', 'level3']]
方案三:用stack实现
通过设置索引后堆叠列的方式,也能达到目标效果:
# 先把层级列设为索引 df_indexed = df.set_index(['level1', 'level2', 'level3']) # 堆叠id列 id_stack = df_indexed[['id1', 'id2']].stack().reset_index(name='id') id_stack = id_stack.drop('level_3', axis=1) # 堆叠value列并清理item名称 value_stack = df_indexed[['foo_value', 'bar_value']].stack().reset_index(name='value') value_stack['item'] = value_stack['level_3'].str.replace('_value', '') value_stack = value_stack.drop('level_3', axis=1) # 合并两个结果 final_df = pd.merge(id_stack, value_stack, on=['level1', 'level2', 'level3']) final_df = final_df[['id', 'item', 'value', 'level1', 'level2', 'level3']]
为什么之前pivot没成功?
简单说:pivot是把长表转换成宽表的工具,而你的需求是把宽表转成长表,方向刚好相反。所以用melt这类宽转长的工具才是正确的选择。
以上方案都是Pandas的内置向量化操作,性能比逐行循环高几个数量级,尤其是当你的DataFrame有几万甚至几十万行的时候,差距会非常明显。
内容的提问来源于stack exchange,提问作者abisko
相关产品推荐
相关产品推荐

