查看: 133|回复: 3

Python + xlwings 批量生成 Excel 数据透视表:pandas pivot_table 自动化脚本实践

[复制链接]
发表于 3 小时前 | 显示全部楼层 |阅读模式
在数据分析工作中,经常需要在多个工作表或工作簿中重复生成相同规则的数据透视表。手工操作效率低且容易出错,本文提供一种基于 Python 的自动化方案,利用 pandas 的 pivot_table() 完成分组聚合,结合 xlwings 直接将结果写回 Excel 文件,实现一键生成每张表的透视结果和汇总表。

## 核心原理:分组 + 交叉汇总
Excel 数据透视表的本质是按某些字段对明细数据分组,然后对数值字段进行求和、计数、平均等聚合。pandas 中的 pivot_table() 能够模拟这一过程,关键参数包括:
- index:行字段(相当于透视表的行区域)
- columns:列字段(相当于列区域)
- values:值字段(要聚合的数值列)
- aggfunc:聚合方式(如 sum、count、mean)
- margins=True:生成总计行/列
- margins_name:总计标签名称

当手工拖字段的操作被翻译成参数后,透视规则就变成了可复用的代码,方便批量执行。

## 实现流程:pandas 分析 + xlwings 读写
脚本分两个职责:pandas 负责数据清洗和透视生成,xlwings 负责打开工作簿、遍历工作表、读取数据、写入结果。整个流程如下:
1. 用 xlwings 打开 Excel 文件(建议以另存为方式保留原始数据)
2. 遍历所有工作表(排除汇总表本身)
3. 对每个工作表,读取连续数据区域(默认从 A1 开始 expand("table"))
4. 调用 make_pivot() 函数生成透视表 DataFrame
5. 将透视结果写回当前工作表右侧空白区域(如 J1 单元格)
6. 同时在“透视汇总”工作表中集中展示所有结果
7. 保存文件

## 代码实现:批量透视脚本
以下脚本包含数值列清洗函数、透视生成函数和批量处理函数。请根据实际情况修改 input_xlsx 路径和参数配置。
  1. import pandas as pd
  2. import xlwings as xw
  3. def clean_to_number(s: pd.Series) -> pd.Series:
  4.     """
  5.     清洗带有货币符号、逗号、空格的文本数字,转为数值类型
  6.     例如:¥12,300 -> 12300
  7.     """
  8.     s = s.astype(str).str.strip()
  9.     s = s.str.replace(",", "", regex=False)
  10.     s = s.str.replace(r"[¥¥$ ]", "", regex=True)
  11.     s = s.str.replace(r"[^0-9\.\-]", "", regex=True)
  12.     return pd.to_numeric(s, errors="coerce")
  13. def make_pivot(
  14.     df: pd.DataFrame,
  15.     index_col: str,
  16.     value_col: str,
  17.     columns_col: str | None,
  18.     aggfunc: str = "sum"
  19. ):
  20.     """
  21.     根据配置字段生成透视表 DataFrame
  22.     """
  23.     tmp = df.copy()
  24.     need_cols = [index_col, value_col] + ([columns_col] if columns_col else [])
  25.     missing_cols = [c for c in need_cols if c not in tmp.columns]
  26.     if missing_cols:
  27.         raise KeyError(f"缺少必要列:{missing_cols}")
  28.     tmp[value_col] = clean_to_number(tmp[value_col]).fillna(0)
  29.     pivot = pd.pivot_table(
  30.         tmp,
  31.         index=index_col,
  32.         columns=columns_col if columns_col else None,
  33.         values=value_col,
  34.         aggfunc=aggfunc,
  35.         fill_value=0,
  36.         margins=True,
  37.         margins_name="总计"
  38.     )
  39.     # 按总计排序,方便阅读
  40.     try:
  41.         if columns_col and "总计" in pivot.columns:
  42.             pivot = pivot.sort_values(by="总计", ascending=False)
  43.         elif not columns_col:
  44.             pivot = pivot.sort_values(by=value_col, ascending=False)
  45.     except Exception:
  46.         pass
  47.     return pivot
  48. def batch_pivot_in_workbook(
  49.     input_xlsx: str,
  50.     index_col: str = "销售区域",
  51.     value_col: str = "销售利润",
  52.     columns_col: str | None = "产品名称",
  53.     aggfunc: str = "sum",
  54.     write_cell: str = "J1",
  55.     summary_sheet: str = "透视汇总",
  56.     start_cell: str = "A1",
  57.     save_as: str | None = None
  58. ):
  59.     """
  60.     批量为一个工作簿中的所有工作表生成透视表
  61.     """
  62.     app = xw.App(visible=False, add_book=False)
  63.     app.display_alerts = False
  64.     app.screen_updating = False
  65.     try:
  66.         wb = app.books.open(input_xlsx)
  67.         # 创建或清空汇总工作表
  68.         try:
  69.             sum_sht = wb.sheets[summary_sheet]
  70.             sum_sht.clear()
  71.         except Exception:
  72.             sum_sht = wb.sheets.add(summary_sheet, before=wb.sheets[0])
  73.         write_row = 1
  74.         for sht in wb.sheets:
  75.             if sht.name == summary_sheet:
  76.                 continue
  77.             try:
  78.                 rng = sht.range(start_cell).expand("table")
  79.                 if rng.value is None:
  80.                     print(f"[SKIP] {sht.name}:空表")
  81.                     continue
  82.                 df = rng.options(pd.DataFrame).value
  83.                 if df is None or df.empty:
  84.                     print(f"[SKIP] {sht.name}:无有效数据")
  85.                     continue
  86.                 df.columns = [str(c).strip() for c in df.columns]
  87.                 pivot = make_pivot(
  88.                     df,
  89.                     index_col=index_col,
  90.                     value_col=value_col,
  91.                     columns_col=columns_col,
  92.                     aggfunc=aggfunc
  93.                 )
  94.                 # 写回当前工作表右侧空白区域
  95.                 sht.range(write_cell).value = None
  96.                 sht.range(write_cell).options(index=True).value = pivot
  97.                 sht.autofit()
  98.                 # 写入汇总 Sheet
  99.                 title = f"【{sht.name}】透视结果:{index_col} × {columns_col or '无列字段'} / {value_col}({aggfunc})"
  100.                 sum_sht.range((write_row, 1)).value = title
  101.                 try:
  102.                     sum_sht.range((write_row, 1)).api.Font.Bold = True
  103.                 except Exception:
  104.                     pass
  105.                 write_row += 1
  106.                 sum_sht.range((write_row, 1)).options(index=True).value = pivot
  107.                 write_row = sum_sht.range((write_row, 1)).expand("table").last_cell.row + 2
  108.                 print(f"[OK] {sht.name}:已生成透视表 -> {write_cell}")
  109.             except Exception as e:
  110.                 print(f"[SKIP] {sht.name}:{e}")
  111.                 continue
  112.         try:
  113.             sum_sht.autofit()
  114.         except Exception:
  115.             pass
  116.         if save_as:
  117.             wb.save(save_as)
  118.             print(f"[DONE] 已另存为:{save_as}")
  119.         else:
  120.             wb.save()
  121.             print(f"[DONE] 已覆盖保存:{input_xlsx}")
  122.         wb.close()
  123.     finally:
  124.         app.quit()
  125. if __name__ == "__main__":
  126.     batch_pivot_in_workbook(
  127.         input_xlsx="产品销售统计表.xlsx",
  128.         index_col="销售区域",
  129.         value_col="销售利润",
  130.         columns_col="产品名称",
  131.         aggfunc="sum",
  132.         write_cell="J1",
  133.         summary_sheet="透视汇总",
  134.         start_cell="A1",
  135.         save_as="产品销售统计表_透视.xlsx"
  136.     )
复制代码

## 关键函数说明
1. clean_to_number():将带货币符号、逗号、空格的文本数字转为数值,避免求和错误。
2. make_pivot():生成透视表前先检查必要列是否存在,对数值列做清洗,然后调用 pd.pivot_table(),最后按总计排序。
3. batch_pivot_in_workbook():隐藏 Excel 应用界面,遍历所有工作表,读取数据区域,生成透视并写回。自动创建“透视汇总”工作表,集中展示所有透视结果。

## 常见踩坑与解决方案
- 字段名不一致:不同工作表可能叫“销售区域”或“区域”,或带有空格。脚本中统一对列名执行 strip() 清理前后空格,但若名称差异过大仍需人工调整参数。
- 数值列含非数字字符:如“¥12,300”、“12,300元”、“-”等,clean_to_number() 会先移除逗号和货币符号,再转为数值,无法转换的填充为0。
- 写回位置选择不合理:建议写回右侧空白区域(如 J1),避免覆盖原始明细。
- 汇总工作表名称冲突:如果源文件已有“透视汇总”表,脚本先清空其内容,否则新建。
- 数据区域不连续:expand("table") 依赖于连续矩形区域,如果表格前有空行或合并单元格,需调整 start_cell 参数。

## 效果验证要点
执行成功后,建议人工抽查:
1. 每张源工作表右侧是否有透视结果?
2. “透视汇总”工作表是否包含所有工作表的透视结果?
3. 总计行/列数值是否与原始数据合计一致?
4. 被跳过的工作表是否合理(如空表、缺列)?

如果总计不符,重点排查数据读取范围、字段匹配和数值清洗环节。可以手工对一张表做一次 Excel 透视表,与脚本结果对比。

## 总结
本文提供的脚本将数据透视表的操作从手工拖字段转化为可复用的 pandas 规则,并利用 xlwings 实现批量自动写回。核心要点:理解 pivot_table() 的参数含义;注重数值清洗;设计交付级输出(右侧写回+汇总表+总计)。后续可进一步将行字段、列字段、聚合方式参数化,甚至封装成图形界面,升级为通用的 Excel 自动化分析工具。
回复

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python + xlwings 批量生成 Excel 数据透视表:pandas pivot_table 自动化脚本实践

这个方案思路很清晰,把 pandas 的 pivot_table 和 xlwings 配合起来确实能解决重复拖字段的痛点。有个小建议:实际工作中,很多 Excel 表格的数值列可能还夹杂着百分号或千分位格式,`clean_to_number` 函数可以考虑再加一步 `str.replace("%", "")` 并乘以 0.01 的处理,不然百分比字段会被转成空值。另外,`write_cell` 固定为 J1 有时会覆盖右侧已有公式或数据,如果能在写入前先检查一下目标区域是否为空,或者动态计算右侧空白列起始位置会更稳妥。整体框架很好,感谢分享。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python + xlwings 批量生成 Excel 数据透视表:pandas pivot_table 自动化脚本实践

楼主分享的这套方案非常实用!pivot_table 配合 xlwings 确实能把 Excel 里的重复性透视工作彻底解放出来。我之前也手动做过类似的多工作表透视,改参数改到头晕,现在看到这种封装成函数的方式,直接感动到想给自己之前的手工操作点个蜡 😂 另外想请教一下:对于字段名中带空格或特殊符号的情况,你的 `clean_to_number` 只处理了数值列,那索引列或列字段如果有不规则文本(比如换行符、前后空格)是否需要提前清洗?还是说 pandas 的 pivot_table 会自动忽略?我有时候会遇到因为列名不一致导致透视结果分成两行的问题。
回复 支持 反对

使用道具 举报

发表于 2 小时前 | 显示全部楼层

Re: Python + xlwings 批量生成 Excel 数据透视表:pandas pivot_table 自动化脚本实践

感谢分享,方案思路很清晰,把 Excel 透视表的逻辑用 pandas 参数化后确实能实现一键批量生成,省去大量手工重复操作。代码里 `clean_to_number` 对带有货币符号的常见格式做了兼容处理,很实用。想请教一下:如果原始数据中数值列是已存储为数值类型(非文本),调用清理函数时会不会因为强制 `astype(str)` 再正则替换而引入性能损失?另外,对于明细行数较大的工作表(比如几十万行),实际使用中 pandas 的 `pivot_table` 和 xlwings 写入是否有明显的瓶颈?有没有考虑过在写入前将透视结果转为 `list` 或 `numpy` 数组来加速?期待交流更多细节。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-6-29 14:06 , Processed in 0.029967 second(s), 18 queries , Gzip On, Redis On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部