如何用 Pandas 高效地从长格式数据构建宽格式 DataFrame

5次阅读

如何用 Pandas 高效地从长格式数据构建宽格式 DataFrame

本文介绍如何基于原始长格式 dataframe(含 obj_id、属性名、属性值三列),快速筛选并透视指定属性(如 weight、height 等),生成结构清晰的宽格式新 dataframe,避免低效循环,提升代码可读性与执行性能。

在实际数据分析中,尤其是从关系型数据库导出的数据,常以“长格式”(long format)存储:每行仅记录一个对象(obj_id)的一项属性(如 column B 为属性名,Column C 为对应值)。但后续建模或可视化往往需要“宽格式”(wide format)——即每个属性独占一列,每行代表一个对象的完整快照。例如,将 obj_id, Column B, Column C 的三列结构,转换为 obj_id, weight, height, eye_color 等多列结构。

直接使用 df.apply() + 内层 iterrows()(如原问题中的 myfunction)不仅语法冗长、逻辑易错,更因 Python 层面循环严重拖慢性能,尤其在处理数万行以上数据时尤为明显。pandas 提供了专为该场景设计的高效向量化方案:条件过滤 + pivot

✅ 推荐做法:先过滤再透视(推荐)

假设原始 DataFrame 名为 df,结构如下:

import pandas as pd  df = pd.DataFrame({     'obj_id': [1, 1, 1, 2, 2, 3, 4],     'Column B': ['weight', 'height', 'eye_color', 'weight', 'height', 'weight', 'weight'],     'Column C': [150, 5.8, 'blue', 160, 6.1, 140, 150] })

要提取 weight 属性并构建新 DataFrame,只需一行链式操作:

out = (df.query('`Column B` == "weight"')          .pivot(index='obj_id', columns='Column B', values='Column C')          .reset_index()          .rename_axis(columns=None))

输出结果为:

obj_id  weight 0       1     150 1       2     160 2       3     140 3       4     150

? 注意:列名 Column B 含空格,需用反引号 `Column B` 包裹,否则 query() 会报错。

? 扩展支持多个属性(如 weight + height + eye_color)

若需同时提取多个属性列,只需扩展过滤条件,并利用 pivot 的天然能力一次性完成宽表构建:

# 指定需保留的属性列表 keep_attrs = ['weight', 'height', 'eye_color']  out = (df.loc[df['Column B'].isin(keep_attrs)]          .pivot(index='obj_id', columns='Column B', values='Column C')          .reset_index()          .rename_axis(columns=None))

该方法自动对 obj_id 去重(若某 obj_id 缺失某属性,则对应单元格为 NaN),结果列顺序与 keep_attrs 中顺序一致(Pandas 2.0+ 支持 sort=False 进一步确保顺序)。

⚠️ 注意事项与最佳实践

  • 避免先 pivot 后筛选:如 df.pivot(…)[[‘weight’]],会在内存中先生成包含所有属性(可能上百列)的宽表,再丢弃不需要的列,造成显著内存浪费和性能下降。
  • 处理缺失值:若某些 obj_id 缺少指定属性,pivot 默认填充 NaN;如需填充默认值(如 0 或 ‘N/A’),可在 pivot 后接 .fillna(0)。
  • 数据类型统一:pivot 后各列类型由原始 Column C 决定。若需强制转换(如 weight 列转为整型),可添加 .astype({‘weight’: ‘int’}) —— 注意需确保无 NaN,否则需先 .fillna() 或用 Int64(支持空值的整型)。
  • 去重保障:确保 (obj_id, Column B) 组合唯一。若存在重复(如同一 obj_id 有两条 weight 记录),pivot 将报错 ValueError: Index contains duplicate entries。此时应先用 drop_duplicates(subset=[‘obj_id’, ‘Column B’], keep=’last’) 显式去重。

✅ 总结

用 query() 或 loc 先精准筛选目标属性行,再通过 pivot() 一次性完成索引重塑与列展开,是构建宽格式 DataFrame 的标准、高效、可维护方案。它完全规避了 Python 循环,充分利用 Pandas 底层优化,代码简洁且语义清晰。对于 5–10 个常用属性的提取任务,此模式可轻松扩展,是生产环境中的首选实践。

text=ZqhQzanResources