将DataFrame列中字典展开为新列:求高效替代方案
高效展开DataFrame中字典列的实现方案
问题背景
现有如下包含字典元素的DataFrame:
import pandas as pd data = {'player1':[{'y': -3.52297115326, 'x': -8.8917736553, 'group_name': 'home team', 'track_id': 2},{'y': -3.48258808681, 'x': -8.85969284603, 'group_name': 'home team', 'track_id': 2},{'y': -3.45125194958, 'x': -8.83364221028, 'group_name': 'home team', 'track_id': 2}], 'player2':[{'y': -3.52297115326, 'x': -8.8917736553, 'group_name': 'home team', 'track_id': 2},{'y': -3.48258808681, 'x': -8.85969284603, 'group_name': 'home team', 'track_id': 2},{'y': -3.45125194958, 'x': -8.83364221028, 'group_name': 'home team', 'track_id': 2}]} df = pd.DataFrame(data)
需要将每列的字典展开为独立列,最终输出如下结构:
data2 = {'y': [-3.52291,-3.482588,-3.451252],'x':[-8.891774,-8.859693,-8.833642],'group_name':['home_team','home_team','home_team'],'track_id':[2,2,2],'player2_y': [-3.52291,-3.482588,-3.451252],'player2_x':[-8.891774,-8.859693,-8.833642],'player2_group_name':['home_team','home_team','home_team'],'player2_track_id':[2,2,2]} df2 = pd.DataFrame(data2)
输出表格预览:
| y | x | group_name | track_id | player2_y | player2_x | player2_group_name | player2_track_id | |
|---|---|---|---|---|---|---|---|---|
| 0 | -3.522910 | -8.891774 | home_team | 2 | -3.522910 | -8.891774 | home_team | 2 |
| 1 | -3.482588 | -8.859693 | home_team | 2 | -3.482588 | -8.859693 | home_team | 2 |
| 2 | -3.451252 | -8.833642 | home_team | 2 | -3.451252 | -8.833642 | home_team | 2 |
原实现方法逐列apply(pd.Series)+concat,在数据量较大时效率极低:
df = pd.concat([df.drop(['player1'], axis=1), df['player1'].apply(pd.Series)], axis=1) df = pd.concat([df.drop(['player2'], axis=1), df['player2'].apply(pd.Series)], axis=1)
高效解决方案
方法1:使用pd.json_normalize批量处理(推荐)
json_normalize是pandas针对嵌套字典/JSON数据优化的工具,处理效率远高于apply(pd.Series)。通过循环遍历所有列,对每列字典数据标准化后添加前缀(首列除外),最后一次性合并结果:
# 初始化存储标准化结果的列表 result_dfs = [] for col in df.columns: # 标准化当前列的字典数据 normalized_df = pd.json_normalize(df[col]) # 非首列的字段添加列名前缀,避免列名冲突 if col != df.columns[0]: normalized_df = normalized_df.add_prefix(f"{col}_") result_dfs.append(normalized_df) # 合并所有标准化后的DataFrame df2 = pd.concat(result_dfs, axis=1) # 可选:将group_name中的空格替换为下划线,匹配示例输出 df2['group_name'] = df2['group_name'].str.replace(' ', '_') df2['player2_group_name'] = df2['player2_group_name'].str.replace(' ', '_')
方法2:列表推导式简化代码
逻辑与方法1一致,用列表推导式缩减代码行数:
# 批量生成标准化后的DataFrame列表 normalized_dfs = [ pd.json_normalize(df[col]).add_prefix(f"{col}_") if col != df.columns[0] else pd.json_normalize(df[col]) for col in df.columns ] # 合并结果 df2 = pd.concat(normalized_dfs, axis=1) # 处理group_name的空格 df2 = df2.replace({'group_name': {' ': '_'}, 'player2_group_name': {' ': '_'}})
效率优势说明
- 原始方法:每次
apply(pd.Series)和concat都会生成新的DataFrame并复制数据,中间环节多,数据量大时IO开销极高。 - 优化方法:
json_normalize基于底层优化实现,处理字典数据的速度比apply快一个数量级;批量处理后一次性合并,减少了中间数据复制的次数,大幅提升运行效率。
内容的提问来源于stack exchange,提问作者Stuart Macfarlane
相关产品推荐
相关产品推荐

