在数据分析工作中,经常需要在多个工作表或工作簿中重复生成相同规则的数据透视表。手工操作效率低且容易出错,本文提供一种基于 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 路径和参数配置。
- import pandas as pd
- import xlwings as xw
- def clean_to_number(s: pd.Series) -> pd.Series:
- """
- 清洗带有货币符号、逗号、空格的文本数字,转为数值类型
- 例如:¥12,300 -> 12300
- """
- s = s.astype(str).str.strip()
- s = s.str.replace(",", "", regex=False)
- s = s.str.replace(r"[¥¥$ ]", "", regex=True)
- s = s.str.replace(r"[^0-9\.\-]", "", regex=True)
- return pd.to_numeric(s, errors="coerce")
- def make_pivot(
- df: pd.DataFrame,
- index_col: str,
- value_col: str,
- columns_col: str | None,
- aggfunc: str = "sum"
- ):
- """
- 根据配置字段生成透视表 DataFrame
- """
- tmp = df.copy()
- need_cols = [index_col, value_col] + ([columns_col] if columns_col else [])
- missing_cols = [c for c in need_cols if c not in tmp.columns]
- if missing_cols:
- raise KeyError(f"缺少必要列:{missing_cols}")
- tmp[value_col] = clean_to_number(tmp[value_col]).fillna(0)
- pivot = pd.pivot_table(
- tmp,
- index=index_col,
- columns=columns_col if columns_col else None,
- values=value_col,
- aggfunc=aggfunc,
- fill_value=0,
- margins=True,
- margins_name="总计"
- )
- # 按总计排序,方便阅读
- try:
- if columns_col and "总计" in pivot.columns:
- pivot = pivot.sort_values(by="总计", ascending=False)
- elif not columns_col:
- pivot = pivot.sort_values(by=value_col, ascending=False)
- except Exception:
- pass
- return pivot
- def batch_pivot_in_workbook(
- input_xlsx: str,
- index_col: str = "销售区域",
- value_col: str = "销售利润",
- columns_col: str | None = "产品名称",
- aggfunc: str = "sum",
- write_cell: str = "J1",
- summary_sheet: str = "透视汇总",
- start_cell: str = "A1",
- save_as: str | None = None
- ):
- """
- 批量为一个工作簿中的所有工作表生成透视表
- """
- app = xw.App(visible=False, add_book=False)
- app.display_alerts = False
- app.screen_updating = False
- try:
- wb = app.books.open(input_xlsx)
- # 创建或清空汇总工作表
- try:
- sum_sht = wb.sheets[summary_sheet]
- sum_sht.clear()
- except Exception:
- sum_sht = wb.sheets.add(summary_sheet, before=wb.sheets[0])
- write_row = 1
- for sht in wb.sheets:
- if sht.name == summary_sheet:
- continue
- try:
- rng = sht.range(start_cell).expand("table")
- if rng.value is None:
- print(f"[SKIP] {sht.name}:空表")
- continue
- df = rng.options(pd.DataFrame).value
- if df is None or df.empty:
- print(f"[SKIP] {sht.name}:无有效数据")
- continue
- df.columns = [str(c).strip() for c in df.columns]
- pivot = make_pivot(
- df,
- index_col=index_col,
- value_col=value_col,
- columns_col=columns_col,
- aggfunc=aggfunc
- )
- # 写回当前工作表右侧空白区域
- sht.range(write_cell).value = None
- sht.range(write_cell).options(index=True).value = pivot
- sht.autofit()
- # 写入汇总 Sheet
- title = f"【{sht.name}】透视结果:{index_col} × {columns_col or '无列字段'} / {value_col}({aggfunc})"
- sum_sht.range((write_row, 1)).value = title
- try:
- sum_sht.range((write_row, 1)).api.Font.Bold = True
- except Exception:
- pass
- write_row += 1
- sum_sht.range((write_row, 1)).options(index=True).value = pivot
- write_row = sum_sht.range((write_row, 1)).expand("table").last_cell.row + 2
- print(f"[OK] {sht.name}:已生成透视表 -> {write_cell}")
- except Exception as e:
- print(f"[SKIP] {sht.name}:{e}")
- continue
- try:
- sum_sht.autofit()
- except Exception:
- pass
- if save_as:
- wb.save(save_as)
- print(f"[DONE] 已另存为:{save_as}")
- else:
- wb.save()
- print(f"[DONE] 已覆盖保存:{input_xlsx}")
- wb.close()
- finally:
- app.quit()
- if __name__ == "__main__":
- batch_pivot_in_workbook(
- input_xlsx="产品销售统计表.xlsx",
- index_col="销售区域",
- value_col="销售利润",
- columns_col="产品名称",
- aggfunc="sum",
- write_cell="J1",
- summary_sheet="透视汇总",
- start_cell="A1",
- save_as="产品销售统计表_透视.xlsx"
- )
复制代码
## 关键函数说明
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 自动化分析工具。 |