
《超简单:用 Python 让 Excel 飞起来》读书笔记:第6章 案例06 批量制作数据透视表

1. 问题背景与写作目标
这一篇继续整理《超简单:用 Python 让 Excel 飞起来》第 6 章中的案例内容,主题是 批量制作数据透视表。数据透视表本身并不陌生,很多人都会在 Excel 里通过拖字段的方式生成区域汇总、产品汇总、月份汇总。但真正让人头疼的是:当同样的透视表规则要在多张工作表、多个工作簿里重复执行时,手工操作就会变成低效劳动。
比如一个工作簿里有多个销售明细表,每张表都要按 销售区域、产品名称、销售利润 生成透视结果。如果手工处理,流程通常是:打开表、插入透视表、拖字段、选择求和、调整格式、复制结果、进入下一张表。表少还能忍,表一多就非常折磨。
这张图展示了本文的核心主题:用 Python + Excel 自动化批量制作数据透视表。

从这张图中我们可以看出,本文不是教你手工点击 Excel 的“插入数据透视表”,而是把透视表规则写进 Python 脚本里,让程序自动完成读取、分析、汇总和写回。也就是说,本文的核心不是“会做一次透视表”,而是把重复制作透视表这件事标准化、自动化、可交付化。
原理说明:Excel 数据透视表的本质,是按照一个或多个维度对明细数据进行分组,然后对数值字段进行求和、计数、平均等聚合操作。Python 中的 pandas 可以通过 pivot_table() 实现类似能力。

2. 目标效果:一键生成每张表的透视结果和汇总表
在写代码之前,必须先明确目标效果。否则脚本很容易写成“能跑,但不好用”的半成品。对于这类办公自动化脚本,我更关心最终交付物是否清晰,而不是代码看起来多高级。
这张图展示了脚本运行后的目标效果:左侧是多个原始数据表,中间通过一键生成动作,右侧形成汇总透视结果。

从这张图中我们可以看出,脚本要完成两个层面的输出。第一,对每张原始工作表分别生成透视结果;第二,额外生成一个 透视汇总 工作表,把所有工作表的透视结果集中展示。这样做的好处是:既保留每张表的独立分析结果,也方便最终汇报时集中查看。
本文设定的目标效果如下:
1. 对一个工作簿中的每张工作表,自动生成一个透视结果区;
2. 透视结果默认写回当前工作表右侧空白区域,例如 J1;
3. 自动生成一个 透视汇总 工作表,把各个 sheet 的透视结果分块集中展示;
4. 透视规则可配置,例如行字段、列字段、值字段、聚合方式;
5. 遇到空表、缺字段、数值列异常时,脚本要能跳过并输出提示。
推荐做法:透视脚本不要只追求“生成结果”,还要考虑结果如何被人阅读。右侧写回、汇总表集中展示、保留总计,都是为了让结果更像一个可交付报表,而不是实验代码的临时输出。

3. 核心原理:透视表不是魔法,而是分组加交叉汇总
很多人觉得数据透视表很神奇,是因为 Excel 把底层过程隐藏得很好。实际上,透视表的本质并不复杂:先按某些字段把数据分组,再对某个数值字段做聚合。
这张图展示了数据透视表的本质:从左侧明细数据出发,先按地区分组,再按产品交叉汇总,最终得到右侧的透视结果。

从这张图中我们可以看出,原始数据中的每一行只是明细记录,而透视表会把这些明细按“地区”和“产品”重新组织起来。比如华东地区的手机、笔记本、平板销售额分别是多少,华南地区分别是多少,最后再给出合计。这就是典型的交叉汇总。
在 pandas 中,对应的核心函数是 pivot_table():
pd.pivot_table(
data,
index="销售区域",
columns="产品名称",
values="销售利润",
aggfunc="sum",
fill_value=0,
margins=True,
margins_name="总计"
)
这里几个参数可以这样理解:
index:行字段,相当于 Excel 数据透视表中的“行”;
columns:列字段,相当于 Excel 数据透视表中的“列”;
values:值字段,也就是要统计的数值列;
aggfunc:聚合方式,例如求和、计数、平均;
margins=True:生成总计,类似 Excel 透视表中的总计行和总计列。
原理说明:当你把 Excel 里“拖字段”的动作翻译成 pandas 参数后,透视表就从一个手工操作变成了一条可复用的规则。规则一旦代码化,就可以批量执行。

4. 实现流程:pandas 负责透视,xlwings 负责读写 Excel
这类脚本不要把所有事情都塞给一个库。我的理解是:pandas 擅长处理数据,xlwings 擅长连接 Excel。两者配合起来,才适合做这种“读取 Excel 明细 → 生成透视结果 → 写回 Excel”的任务。
这张图展示了 pandas + xlwings 的自动化分工:读取源数据、生成透视结果、写回 Excel 报表。

从这张图中我们可以看出,左侧是源数据表,中间是 Python 自动化引擎,右侧是生成后的透视结果。pandas 主要负责 pivot_table() 分析,xlwings 主要负责打开工作簿、读取工作表、写入结果、保存文件。
整体流程可以拆成下面几步:
推荐做法:在企业办公场景中,建议优先另存为新文件,而不是直接覆盖原文件。因为透视结果属于加工结果,一旦覆盖原始工作簿,后续出问题不好回退。

5. 完整代码:批量生成透视表并写回汇总
下面这段代码按“可落地使用”的标准做了增强:自动跳过空表和缺列,数值列支持清洗,列字段支持可选,生成结果既写回每张表右侧,也写入统一的 透视汇总 工作表。
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"
)
风险提醒:这段脚本默认表头在 A1 开始,并且数据区域是连续的。如果你的 Excel 表格前面有标题行、合并单元格、空行,expand("table") 读取到的数据区域可能不完整,需要调整 start_cell。
原理说明:make_pivot() 函数负责生成透视表,batch_pivot_in_workbook() 函数负责批量遍历和写回。这样拆开以后,后续要调整透视规则时,不需要改整个脚本。

6. 效果验证:透视表要像交付品,而不是实验输出
脚本运行成功以后,不要只看控制台输出 [DONE]。真正要验证的是:透视结果是否写到正确位置,字段是否完整,总计是否正确,汇总表是否便于阅读。
这张图展示了比较理想的交付效果:左侧保留原始明细,右侧写回透视结果,并且总计清晰可见。

从这张图中我们可以看出,透视结果写在右侧空白区域,不会破坏原始数据。左边是明细,右边是结论,阅读时可以直接对照检查。这种布局比单独生成一个零散结果文件更适合实际汇报。
我建议至少验证以下几项:
1. 每张源工作表右侧是否生成了透视结果;
2. 是否生成了 透视汇总 工作表;
3. 透视表中是否包含 总计 行或列;
4. 汇总金额是否与原始明细金额合计一致;
5. 是否存在被跳过的工作表,跳过原因是否合理。
推荐做法:正式交付前,随机抽一张工作表,用 Excel 手工做一次透视表,对照脚本生成结果。只要两边总计一致,基本可以证明脚本逻辑是可靠的。

7. 常见问题与踩坑记录
批量制作数据透视表最容易踩的坑,不是 pivot_table() 不会写,而是源数据不规范。
坑 1:字段名不一致。比如有的表叫“销售区域”,有的表叫“区域”,还有的表叫“ 销售区域 ”。脚本按列名匹配时会直接受影响,所以代码中使用了 strip() 清理字段名前后空格。
坑 2:数值列不是纯数字。如果销售利润列里有 ¥12,300、12,300元、空值、短横线,这些内容直接参与求和会出问题。因此代码中增加了 clean_to_number() 做数值清洗。
坑 3:透视结果覆盖原数据。如果写回位置选择不合理,例如把结果写到 A1,就会覆盖原始明细。建议写到右侧空白区,例如 J1 或更靠后的列。
坑 4:工作表名称冲突。如果源工作表已经有 透视汇总,脚本需要清空旧汇总表或重新创建,否则结果可能混乱。
坑 5:覆盖保存风险。如果直接覆盖源文件,脚本异常时可能影响原始数据。更稳妥的做法是使用 save_as 另存为新文件。
经验判断:批量脚本最重要的不是“能跑”,而是遇到异常数据时能说明原因。比如空表、缺列、字段错误、金额列异常,都应该有明确提示,而不是静默失败。

8. 总结与进阶建议
这一节的核心,不是记住 pd.pivot_table() 的参数,而是理解数据透视表背后的自动化逻辑:先把明细数据标准化,再按字段分组聚合,最后把结果写回 Excel,形成可以交付的分析结果。
我认为这篇笔记最值得带走的经验有三点。
第一,透视表本质是分组和聚合。不要把它看成 Excel 里的神秘功能。只要理解 index、columns、values、aggfunc,就能把手工拖字段转换成代码规则。
第二,脚本要按交付标准设计。右侧写回、汇总表集中展示、总计清晰、异常有提示,这些都比单纯“生成一张表”更重要。
第三,验证不能省。透视结果涉及金额、销量、订单数时,必须核对总计。如果总计对不上,说明读取范围、字段匹配或数值清洗至少有一个环节存在问题。
后续如果继续升级,可以把行字段、列字段、值字段、聚合方式做成配置文件,甚至做成图形界面。这样就能从“读书笔记里的脚本”升级为一个真正可复用的 Excel 自动化分析工具。
🔝 返回顶部
4390

被折叠的 条评论
为什么被折叠?



