自动数据报告 — 从原始 CSV 到自动交付的 Excel 与 PDF
自动数据报告 — 从原始 CSV 到自动交付的 Excel 与 PDF
这一章讲什么: 报告是决策的血液循环,但手工做报告的时间大半耗在重复动作上。 这一章把「每周从原始数据到成稿报告」变成一条管道:五个环节各写成函数, 整条管道一次跑通、每周复跑,数据换了换源头、管道不动。 读完你会拿到一个能直接搬进工作的模板,以及一个判断: 什么时候该要 Excel,什么时候该要 PDF。
1. 这一章讲什么
第五章自动化了「文件与网页」,这一章自动化「数据与结论」。 书里给的项目是职场最常见的一类:每周销售报告。 动机照旧是时间:手工周报里,第一个真正有信息量的句子写出来之前,几个小时已经没了1。
这一章的立场书里说得明白:AI 在这里的作用是加速工作流、帮你设计管道的结构、 建议高效的库、甚至为你的数据发现写叙述性摘要—— 但终点不是替代你的分析判断,把你从枯燥里解放出来,让你专注洞察2。
在全书链条里,这一章是第七章「管道思维」的放大版,也是第九章 Web 应用的反面: 同一个数据处理核心,一个走向文件,一个走向用户。
2. 顶层全景:五段管道
① 摄取 pd.read_csv(parse_dates=[...]) 读入并预览
▼
② 清洗 去重、补缺(to_numeric + fillna)、列名规范化、验证(isnull/dtypes)
▼
③ 汇总 groupby().agg():每区域总营收、平均营收、交易数
▼
④ 可视化 matplotlib 画月度趋势(线图);seaborn 画区域对比(柱状)与分布(直方图)
▼
⑤ 导出 openpyxl 写多页 Excel(可加粗表头、自适应列宽);
ReportLab 排 PDF(标题 + 说明段 + 表格 + 嵌入图表)
│
▼
每站包成函数(load_data / clean_data / …)→ 换数据、定时跑,管道不变
图说:前两站保证「进去的是能信的数」,中间两站把数变成结论,最后一站把结论变成能发出去的成品。3. 核心原理
3.1 摄取与清洗:垃圾进,垃圾出
摄取(ingestion)是把原始数据装进 pandas。 按来源选工具:
CSV 用 pd.read_csv()、Excel 用 pd.read_excel()、API 用 requests/urllib、
数据库用 sqlite3 或 SQLAlchemy3。本章主线用 CSV。第一条提示就很具体:
「读 sales_data.csv,date 列解析成日期,打印前 10 行。」产出:
import pandas as pd
df = pd.read_csv("sales_data.csv", parse_dates=["date"])
print(df.head(10))parse_dates=["date"] 在读取时就把日期列转成真正的日期类型——
不然后面的「按月分组」会把 2025-01 当成字符串4。
书里点出这条提示的要点:数据集、列类型、预览步骤全说了——清晰的请求换来贴合意图的代码5。
清洗(cleaning)四件套:处理缺失值(NaN,pandas 里「缺失」的记号)、去重、 列名规范化、纠正数据类型6。提示:「去掉 sales_data.csv 的重复行; revenue 列缺失值填 0;列名统一成小写下划线。」产出:
df = df.drop_duplicates()
df["revenue"] = df["revenue"].fillna(0)
df.columns = [col.strip().lower().replace(" ", "_") for col in df.columns]
print(df.info())验证不能省——AI 生成代码之后,你必须验证结果。书里的三问:
必要的列都在吗?数据类型对吗?清洗步骤有没有误删重要的行?7
配套两行检查:print(df.isnull().sum()) 数每列还剩几个缺失;print(df.dtypes) 核对类型8。
包成函数,可复用为第一目标:
def load_and_clean_csv(file_path):
df = pd.read_csv(file_path, parse_dates=["date"])
df = df.drop_duplicates()
df["revenue"] = df["revenue"].fillna(0)
df.columns = [col.strip().lower().replace(" ", "_") for col in df.columns]
return df三条实操纪律(书里的 hands-on tips):摄取后必 df.head() 前几条;原始数据留一份不动,
清洗搞砸了好重来;不确定 AI 某步为什么这么做,就问——「解释为什么用 fillna(0),
什么时候这不合适」——建立理解,不只是自动化9。
3.2 汇总:从「一堆行」到「几个决策数」
原始数据第一眼不讲故事,汇总(summarization)把它变成指标。
pandas 三件核心操作:df.describe() 数值列统计概览;
df["列"].value_counts() 各类别计数;df.groupby("列").agg() 按组聚合10。
提示:「汇总 sales_data.csv:每区域总营收、每产品类目平均营收、每月交易笔数。」产出:
total_revenue = df.groupby("region")["revenue"].sum()
avg_revenue = df.groupby("product_category")["revenue"].mean()
df["month"] = df["date"].dt.to_period("M").dt.to_timestamp()
transactions = df.groupby("month").size()三个新记号:groupby("region")["revenue"].sum() 读作「按 region 分组,各组 revenue 求和」;
.dt.to_period("M") 把日期折成「月份」粒度再转回时间戳,这是按月聚合的标准手法;
.size() 数每组的行数11。书里的配套心法:让 AI 按列类型与业务背景建议聚合;
聚合之后必 print 或 head 抽查 sanity check( sanity check,常识性抽查:数字的数量级对不对、
日期连续不连续)12。
NumPy 补数学。 需要均值、中位数、标准差(一组数偏离平均值有多远的度量)等纯数学操作时,numpy 补位:
import numpy as np
revenue = df["revenue"].to_numpy()
np.mean(revenue), np.median(revenue), np.std(revenue)书里的小贴士:对照 pandas 的输出——两者都对,但 pandas 与带标签的数据更合; 让 AI 引导你用向量化操作(一次性对整列算,而不是逐行循环)——大数据集上快得多13。
汇总成一张「报告表」。 groupby().agg() 配 NamedAgg(给每个聚合起列名)一次成型:
summary = df.groupby("region").agg(
total_revenue=pd.NamedAgg(column="revenue", aggfunc="sum"),
avg_revenue=pd.NamedAgg(column="revenue", aggfunc="mean"),
transaction_count=pd.NamedAgg(column="revenue", aggfunc="count"),
)这张表就是报告的脊梁——喂给 Excel、PDF、图表都行14。
3.3 可视化:数字长形状的地方
书里的句子值得原样记:数字很少自己说服人,它们需要形状、对比与语境。 一张平均值表格能告知,一张图能说服。15
作图前先答三问(这是本章的方法论亮点): 这张图要回答什么问题?报告给谁看?看完这张图可能做什么决定? 答完再写提示——想清楚这三问,提示自然具体16。
matplotlib:底子,要求你说清一切。 提示:「用 matplotlib 画销售月营收趋势线图:
x 轴月份、y 轴总营收,标注清楚,适合放进报告。」产出Monthly Revenue Trend 的标准线图:
groupby 出月度总额 → .plot(kind="line") → 加 xlabel/ylabel/title → tight_layout() 防裁字 →
show()17。这张图回答一个问题:营收随时间怎么走。
seaborn:坐在 matplotlib 上,默认就更体面。 它省样板代码, 默认样式通常直接能进报告。什么时候优先 seaborn:做类别对比、看分布、画分组汇总18。 两个演示:
# 对比:每区域总营收(柱状图)
region_summary = df.groupby("region", as_index=False)["revenue"].sum()
sns.barplot(data=region_summary, x="region", y="revenue")
# 分布:单笔交易营收的直方图(bins=30 切成 30 个桶)
sns.histplot(df["revenue"], bins=30)柱状图的两个「你应该注意到」:没有循环、没有手动算柱高——seaborn 处理对齐与间距19。 直方图回答的问题也写在书里:大多数交易是大的还是小的?有没有拉高平均值的离群点(和其他值差得离谱的少数值)?20
汇总 + 可视化 + 导出是最强工作流(书里的原话):先 pandas 汇总、再画汇总、再导出。 可以让 AI 把三步连起来写,并要求「解释每一步」——这样学得才扎实,避免盲目复制21。
3.4 导出:报告离开你的屏幕那一刻
为什么导出是「自动化成真」的分界:干系人不开 VS Code,他们开邮件附件。 他们期待能存档、转发、不看说明就能读的文件22。
选格式(书里的对照表):
| Excel(.xlsx) | PDF(.pdf) | |
|---|---|---|
| 适合 | 交互复查、继续筛选 | 执行摘要、固定版式 |
| 受众 | 财务与运营团队 | 正式汇报、官方归档 |
| 库 | openpyxl(pandas 配合) | ReportLab |
同一数据集,两个都出——书里的管道就是双出口的23。
Excel:写数据 + 穿衣服。 pandas 负责写,openpyxl 负责样式:
with pd.ExcelWriter("weekly_sales_report.xlsx", engine="openpyxl") as writer:
df.to_excel(writer, sheet_name="Raw Data", index=False)
summary.to_excel(writer, sheet_name="Revenue by Region", index=False)一个文件两张表:原始数据 + 区域汇总24。再让 AI 加一层:
「扩展导出脚本:表头加粗、列宽自适应。」产物用 openpyxl 的 load_workbook 重新打开,
遍历第一行设 Font(bold=True),逐列算 max(len(str(cell.value))) 设宽度——
报告从「机器生成」变成「像有设计」25。
PDF:你是在排版文档,不是倒数据。 ReportLab 用三种元件搭 PDF: Paragraph(段落)、Table(表格)、Flowable(可流动元素)——它奖励先规划26。 提示:「用 ReportLab 生成一份 PDF 销售报告:标题、一段摘要、一张区域营收表。」骨架:
from reportlab.platypus import SimpleDocTemplate, Paragraph, Table
from reportlab.lib.styles import getSampleStyleSheet
doc = SimpleDocTemplate("weekly_sales_report.pdf")
styles = getSampleStyleSheet()
elements = [
Paragraph("Weekly Sales Report", styles["Title"]),
Paragraph("This report summarizes revenue performance by region for the current week.", styles["Normal"]),
Table([summary.columns.tolist()] + summary.values.tolist()),
]
doc.build(elements)嵌入图表两步行:matplotlib 先 plt.savefig("revenue_by_region.png") 存图,
ReportLab 的 Image 元件 elements.append(Image("revenue_by_region.png", width=400, height=300)) 贴入27。
书里的纪律:尺寸布局可以让 AI 调,输出必须自己开一遍28。
3.5 主线例子:周报管道的九步搭建
把前五节合成一个端到端(从原始数据到成品报告,全程不留手工步骤)项目。书里的文件夹约定:
weekly_report/
├── data/sales_data.csv
├── output/reports/ ← Excel、PDF 落这里
├── output/charts/ ← 图表 PNG 落这里
├── report_pipeline.py
└── requirements.txt ← pip install pandas numpy matplotlib seaborn openpyxl reportlab动手前的一句咒语(书里的 Step 1):「每周,我要一个脚本,吃下原始销售数据, 不经任何手工步骤,产出完成品报告。」——这句定义将指导你后面每一条提示29。
然后逐步骤推进,每步一条 CCG 提示 + 一段代码 + 一个「你应该看到」:
- 读与验。 「加载 CSV、正确解析日期、显示基本信息验证完整性。」看到:数据预览、日期解析成功、无缺列。这里看着不对,报告就已经坏了30。
- 清与验。 「扩展脚本:去掉缺失营收、确保营收是数值。」
pd.to_numeric(..., errors="coerce")(转不动的变 NaN)+dropna(subset=["revenue"])。不是追求完美,是追求一致31。 - 汇总。 「生成按区域汇总周总营收的代码。」一行 groupby。
- 画图。 「用 matplotlib 或 seaborn 画区域营收柱状图,存成图片。」关键在存(savefig + close),不弹窗、不等点击——自动化必须无头32。
- 导 Excel。 「导出原始数据 + 汇总,格式化。」
- 导 PDF。 「用 ReportLab 生成带标题、摘要段、表格、嵌入图表的 PDF。」33
- 函数化。 把逻辑包进
load_data()/clean_data(df)等函数——脚本与系统的分界就在这一步34。 - 换数据重跑。 书里的最后一步叫「Final Reality Check」:换一份数据文件、重跑、确认报告重新生成正确。 自动化靠重复证明,不靠祈祷35。
3.6 章末 Key Terms 与收尾判断
本章 Key Terms 六个,照收36:
| 术语 | 含义 |
|---|---|
| Data Ingestion(数据摄取) | 从文件、数据库或 API 加载原始数据 |
| Data Cleaning(数据清洗) | 修格式、补缺、验类型,把数据备好 |
| Pandas | Python 的数据处理与分析库 |
| Data Visualization(数据可视化) | 用图形表示数据以支撑理解与决策 |
| Report Pipeline(报告管道) | 产出一致报告输出的一串自动化步骤 |
| ReportLab | 用代码程序化生成 PDF 的库 |
| Openpyxl | 读写 Excel 文件的库 |
本章最后一句值得原文带走: 报告会变、数据源会变,死线(deadline)不会—— 「现在,你有了一个能跟着变的系统」37。这句话就是整套管道的存在理由: 把「每周一次的手忙脚乱」换成「数据一换、按钮一按」。
3.7 排版的最后一公里:ReportLab 与 openpyxl 的分工再确认
§3.4 给了两个导出库的用法,这一节把「谁干什么」再钉一次,因为这是新手最容易搅浑的:
pandas 写数据,openpyxl 穿衣服。 df.to_excel(writer, sheet_name=...) 负责「把表放进去」;
加粗表头、自适应列宽、改字体颜色,全是 openpyxl 的活(重新 load_workbook 打开再改)38。
书里的原话:pandas writes data. openpyxl styles it.39 记住这句,你就不会在 pandas 的
API 里翻半天找「加粗」了——没有,就是另一个库的事。
ReportLab 是排版,不是数据倾倒。 PDF 的三种元件——Paragraph(段落)、Table(表格)、
Flowable(可流动元素)——按「文档流」依次排:先标题、再说明段、再表、再图40。
SimpleDocTemplate 把这些元件收集起来,doc.build(elements) 一次成稿。
书里的提醒:PDF「奖励规划」——先想好文档结构再写代码,比 Excel 更讲究。
两个导出共用的一个细节:图表都要先 plt.savefig() 存成 PNG 再嵌入。
Excel 里嵌图也可以(把 PNG 插进 sheet),书里没展开,但机制相同
(补充,不在书里,来自通用知识)。
3.8 管道的五段函数化(书里的 Step 8 展开)
§3.5 的第 7 步「包成函数」是整个项目从脚本升级为系统的关键一跳,书里只给了两个函数的示意, 这里把五段全部函数化的形状补全:
def load_data(): # ① 摄取
return pd.read_csv("data/sales_data.csv", parse_dates=["date"])
def clean_data(df): # ② 清洗
df["revenue"] = pd.to_numeric(df["revenue"], errors="coerce")
return df.dropna(subset=["revenue"])
def summarize(df): # ③ 汇总
return df.groupby("region")["revenue"].sum().reset_index()
def make_chart(summary): # ④ 可视化
plt.figure(figsize=(8, 5))
sns.barplot(data=summary, x="region", y="revenue")
plt.title("Weekly Revenue by Region")
plt.tight_layout()
plt.savefig("output/charts/revenue_by_region.png")
plt.close()
def export(summary, df): # ⑤ 导出
with pd.ExcelWriter("output/reports/weekly_sales_report.xlsx", engine="openpyxl") as w:
df.to_excel(w, sheet_name="Raw Data", index=False)
summary.to_excel(w, sheet_name="Summary", index=False)
# ... PDF 同理
if __name__ == "__main__":
export(summarize(clean_data(load_data())), None) # 主流程一行串起来五段各一个动词,主流程只剩组合。这就是「脚本与系统的分界」:脚本是一坨顺序执行的语句,
系统是「每段有名有姓、可以单独换」的函数集合41。第五段换数据源(比如从数据库读),
只动 load_data();换图表样式,只动 make_chart()——第八章的管道与第九章的 Flask 端点、
第四章的三函数拆分,全是同一个结构观在不同尺度上的重复。
3.9 本章的「三问」清单(写管道前自测)
动笔写任何报告管道前,先答书里的三问(第 38 段的三个问题扩成操作版):
- 这张图答什么问题?——答不上来就不画;
- 报告给谁看?——决定 Excel 还是 PDF(还是两个都要);
- 看完会做什么决定?——决定汇总表里该有什么指标。
三问答完,提示的 CCG 骨架已经自动成型:背景 = 答 2,目标 = 答 1 + 3,约束 = 数据源与格式。 第八章的 AI 协作效率,一半取决于这三问在写提示之前已经想过。
3.10 两种格式的导出细节:书里没有明说的部分
书里给了 Excel 与 PDF 的完整代码,但没展开的两个细节,补在这里(补充,不在书里, 来自通用知识——都是第一次做报告时必踩的):
Excel 的数字格式。 to_excel 写进去的 float 可能显示成 150.0 或 150.000000。
要控制小数位,在导出前用 pandas 处理:summary["revenue"] = summary["revenue"].round(2);
要货币格式($1,234.56),才轮到 openpyxl 的 number_format = '"$"#,##0.00'。
判据照旧:数据层的事(pandas)与展示层的事(openpyxl)分开想42。
PDF 的中文字体。 ReportLab 默认字体不含中文字形,中文标题会直接变方块。
解法是用 reportlab.pdfbase 注册一个中文字体文件(如系统自带的宋体或下载的开源字体),
再在样式里引用。书里全英文示例不会碰到这个坑;你的中文报告第一步就会撞上——
早注册,早省事。
3.11 把管道挂到调度上(第五章接口的回接)
第八章的管道停在「手动跑」。接回第五章的调度,只差一个入口文件:
# run_weekly.py
from report_pipeline import load_data, clean_data, summarize, make_chart, export
import schedule, time
def job():
df = clean_data(load_data())
s = summarize(df)
make_chart(s)
export(s, df)
schedule.every().monday.at("08:30").do(job) # 每周一早上自动出报告
while True:
schedule.run_pending(); time.sleep(1)书里没有写这一段(第八章与第五章的调度没有显式接龙),但两章的接口完全咬合:
五段函数各管一件事,调度只负责按点调 job()。这就是「每站包成函数」在第八章结尾
埋的彩蛋——没有那一步函数化,这里无从下手。
3.12 可复用性的最后一道闸:换数据重跑(书里 Step 9 的展开)
§3.5 的第 9 步「Final Reality Check」只有三行:换数据文件 → 重跑 → 确认报告重新生成正确43。 这里把这三行背后的检查清单补出来——换数据时最容易断的是四处:
- 日期列解析变了(新数据的日期格式不同,
parse_dates默默产 NaT); - 列名变了(新数据多一个空格或大写,
df["revenue"]直接 KeyError); - 数值列夹了文本(「N/A」混进 revenue,
to_numeric(coerce)把它们变缺失, 总行数骤降而你不察觉); - 输出文件名撞车(同周的旧报告被覆盖——也许你要的正是覆盖,也许不是)。
书里没有列这张清单,但它那句「automation is proven through repetition」 就是这四处检查的总纲:每处断点都只在「换数据」时暴露,而不在第一次跑通时暴露。 所以管道的验收从来不是「能跑」,是「换三份数据都能跑」。
3.13 报告管道的通用化:书末那句「only the data source changes」
书里在管道目标一节写了一句很容易滑过去的话: 这套管道可以复用于销售报告、网站分析、员工绩效、运营指标—— 只有数据源改变(only the data source changes)44。
这句话是第八章真正的毕业标准。通用化的含义:
load_data()是唯一与「销售」绑定的函数——换成读 GA4 导出的 CSV、 读数据库的考勤表,其他四段不动;- 汇总维度(按区域)换成按渠道、按人,只改
summarize()里的 groupby 键; - 图表标题与 PDF 文案是唯一的「业务文案层」,值得抽成常量或参数。
一个管道能服务四种报表,比一个管道服务一种报表难四倍,也值钱四倍—— 这就是书里把「设计工作流」放在「写脚本」之上的原因:本章教你设计, 脚本只是设计的载体。
3.14 两种图的选择谱(书里散落的选型经验)
本章反复在 matplotlib 与 seaborn 之间切换,选择的规律书里没集中说,聚成一张谱:
| 要画什么 | 用什么 | 书里的例子 |
|---|---|---|
| 时间趋势(月营收) | matplotlib 线图 | §3.3 月度趋势 |
| 类别对比(区域营收) | seaborn 柱状图 | §3.3 区域对比 |
| 分布形态(单笔金额) | seaborn 直方图 | §3.3 分布 |
| 分组汇总 | seaborn(它默认处理对齐与间距) | 同上 |
记忆钩:趋势用 matplotlib,比较与分布用 seaborn。 更一般的判据: 你的问题越「精确控制每一条线」,越 matplotlib;越「快速看一个像样的默认图」, 越 seaborn。书里的两条提示也顺路记住:「Plot revenue data」这种裸请求必然产出平庸图; 换成「画月度营收趋势线,x 轴月份 y 轴总额,标注清楚,适合放进报告」, 图才长成人样45。
3.15 数据清洗的「留一手」纪律(书里两处提醒的合并)
第八章有两处小提醒,合起来是一条纪律:
- 清洗前:留一份原始数据不动(keep a copy of the raw data untouched), 清洗搞砸了好重来46;
- 清洗后:用 print/describe 抽查,确认清洗没误删重要行47。
合成一句话:进管道前的数据是你的后悔药,出清洗后的数据要过 sanity check。
「后悔药」具体怎么做,书里没细说,惯用做法是原始 CSV 只读复制一份进 data/raw/,
处理脚本从不写它(补充,不在书里,来自通用知识)。
sanity check 的最低配:df.describe() 看数值列的 min/max 是否离谱、
df.isnull().sum() 看缺失分布、len(df) 看行数对得上源文件的预期。
三行代码,拦住第九章「报告错了但没人发现」那种最尴尬的失败。
3.16 本章的「一句话」收成
把第八章折成一句话:先想清楚这张图答什么,再让管道替你每周答一遍。 前半句是 §3.3 的三问(图的问题/受众/后续决定),后半句是 §3.5 的九步 (定义 → 读 → 清 → 算 → 画 → 导 → 导 → 函数化 → 换数据重跑)。 两句话中间夹着全章的真正主角——结构:每段一个函数、每站可换、双出口并存。 书里说「你学会了设计报告工作流,不只是写脚本」48,设计的全部内容 就是这一句加九步。
3.17 报告的叙事层(书里一句带过、值得展开)
书里的 PDF 示例中,标题与摘要是这样写的:「Weekly Sales Report」+ 「This report summarizes revenue performance by region for the current week.」49—— 一句平淡的过渡语,却是报告管道的「叙事层」:数据层(表与图)之上, 还有一层「这段话把读者从标题带进结论」的文字。
自动化管道的叙事层怎么写?三个来源,由易到难:
- 固定模板:「本周总营收 X,最高区域 Y」用 f-string 填数——够用 80% 的场景;
- 规则化叙述:按书里的做法让 AI 写「若某区域环比降超 20%,加一句警示」 这类条件文案,判断规则你定;
- 模型生成摘要:把汇总表喂给模型写一段话——最强也最险, 必须有数字核对兜底(它可能编)。
书里停在第 1 种(模板句)49。叙事层是「自动报告」与「自动生成报告」的分界: 前者填模板,后者要校验——第九章「不信 AI 建议三问」在这里同样适用。
3.18 五段管道的失败模式表(每段一种典型死法)
把每段最典型的失败收进一张表——排障时先对号入座:
| 段 | 典型死法 | 首个排查动作 |
|---|---|---|
| 摄取 | 路径/编码错(文件找不到、中文 CSV 乱码) | 先 print(df.head()) 确认读进来了 |
| 清洗 | 误删行(条件写宽了) | 对比清洗前后 len(df) |
| 汇总 | 分组键拼错(大小写/空格) | print(summary.index) 看键名单 |
| 可视化 | 图空白(数据空或列名错) | 先 print 传给图的数据 |
| 导出 | PDF 元件顺序乱/Excel 被占用打不开 | 打开文件本身看;Excel 先关再写 |
表的第一列有个共同根源:每一段都默认上一段的输出是对的。 防御办法也共同:每段的出口加一行 print 或 assert——这正是 §3.1 那条 「摄取后必 head」的通用化。
3.19 全章的「成本观」收尾(我们的判断)
最后补一个书里没正面讨论、但对决定是否自动化至关要紧的视角:值得吗?
写一个周报管道的成本:搭建两小时(含与 AI 的来回),之后每周省 30–60 分钟。 回本周期大约两到四周——凡是周期超过一周的重复报表,自动化几乎都值; 一次性任务永远不值。这是书里那句「发现重复就自动化」的隐含前提,50 把它显式化,是为了防止「为自动化而自动化」: 有些一周做一次、每次十分钟的事,与其写脚本,不如设个提醒。
判断(我们的,不是书里的): 自动化的门槛公式 = 单次耗时 × 剩余次数 > 搭建耗时 × 2(留一倍缓冲给维护)。公式里的数因人因项目而异, 但「剩余次数」这个因子经常被忽略——项目两个月后结束的报表, 和要跑三年的报表,答案完全不同。 如果错,会错在: 维护成本被低估——管道会随数据源变化而腐坏, 真实回本周期常比公式算的更长;但这个误差不改变「高频重复值得自动化」的大方向。
3.20 图表教学的三段式:提示 → 代码 → 「你应该注意到」
第八章的可视化教学藏着一个全书独有的排版装置,值得单拎出来:每一种图(柱状、分布、分组) 都按固定三段呈现——先给「Prompt to ChatGPT」原文,再给「Sample Code Output」, 最后给一小段「What You Should Notice(你应该注意到)」51。
以柱状图那段的「你应该注意到」为例,书里列了四条观察:
- 没有循环——你没有手写 for 去摆柱子;
- 没有手动算柱高——seaborn 替你对齐与间距;
- 图从左往右自然可读;
- 结论一句:「这正是自动报告需要的——可预期、可读的输出」52。
这个装置的教学含义比四条观察本身更重要:它在教你怎么读 AI 给的代码。 新手拿到一段能跑的绘图代码,第一反应是「复制、运行、截图」;「你应该注意到」 逼你停一秒,回答「这段代码里少了什么」——没有循环、没有手算坐标。 认出一个库替你省掉了什么,才算真正认识了这个库; 只看见「它能跑」,下次换数据就傻眼。这与第七章「让 AI 给期望输出」是同一枚硬币的两面: 一个要求 AI 自证,一个教你自己看出门道。
判断(我们的,不是书里的): 「你应该注意到」其实是把代码评审(codereview) 的第一步做成了填空题——只问「少了什么」,不问「好不好」。这个降级是聪明的: 新手没有能力整体评审一段陌生代码,但「找出这里没出现循环」人人能做。 如果错,会错在: 对熟练读者,这种引导会显得多余;而且「少了什么」清单依赖 具体库的实现细节(换个绘图库,循环可能又回来了),照抄观察不如学方法。
这个装置在章内不止一处:直方图段的收尾是「这张图帮你回答两个问题—— 大多数交易是大的还是小的?有没有拉高平均值的离群点?」53—— 注意,这又是一个「问题先于图」的示范:图不是画完再想它说明什么, 而是先有问题、再选图、最后拿图回答问题。这与 §3.9 的「作图前先答三问」 首尾呼应:三问是作图前的输入,你应该注意到是成图后的验收。 两段连起来,就是本章图方法论的全部:问在前,图居中,验收在后。
4. 作者的判断与证据
有演示支撑的判断:
- 「清洗定义后续一切结果的准确性」——本章用第 2 步的「先清后算」顺序支撑, 是数据工作的标准共识31;
- 「可视化是沟通不是探索」——三问法(答什么问题/给谁看/导致什么决定)是本章的方法论高点16;
- 「Excel 与 PDF 服务不同受众,应共存」——双出口管道完整演示23。
经验之谈:
- 「fillna(0) 的适用性要问」——书里专门给了追问句式,这是诚实的教学;
- 「缺失库是报告脚本失败最常见的原因」——排障经验54。
推测:
- 「这是现代技术岗位最有价值的技能之一」——结语的价值判断,无证据,当激励读55。
5. 边界与局限
- 没有调度一章:管道能跑,但「每周自动」要接第 5 章的 schedule 或 cron——本章没把这步接上;
- 图表没有排版治理:多图报告的版式、分页、字号体系没展开,ReportLab 的高级排版(模板、样式表)只露一角;
- Excel 的高级特性缺席:公式、透视表、条件格式都没涉及——openpyxl 能做的远不止加粗表头;
- 数据验证只靠肉眼:三问(isnull/dtypes/列齐全)之外,没有值域校验、跨表一致性检查;
- 异常与告警空白:管道某站失败(文件不存在、列名变了)时的报错与通知,本章没有设计。
6. 可带走的
本章主线例子,一行写完: sales_data.csv → load_and_clean(解析日期→去重→填零→列名规范化) → groupby 区域汇总 → seaborn 柱状图存 PNG → openpyxl 双表 Excel → ReportLab 出含图表的 PDF → 包成函数 → 换数据重跑验证。
- 垃圾进垃圾出:摄取后必 head、清洗后必 isnull/dtypes;原始数据留备份;
- 清洗不是追求完美,是一致;
- 汇总表是报告的脊梁:groupby + NamedAgg 一次成型;
- 作图前三问:答什么、给谁看、导致什么决定;
- matplotlib 画趋势、seaborn 画对比与分布;自动化用 savefig,不用 show;
- Excel 交互用、PDF 归档用,同一数据两个都出;
- pandas 写数据、openpyxl 穿衣服(粗表头、自适应列宽);
- PDF 是排版:Paragraph + Table + Image 三元件;
- 脚本与系统的分界 = 函数化;
- 最后一步永远是换数据重跑——自动化靠重复证明。
7. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 手工报浪费时间 | Automating Data Reports | text/20-ch08-8-automating-data-reports.txt:18(搜「Hours can slip」) |
| 摄取四工具 | 同上 | text/20-ch08-8-automating-data-reports.txt:63(搜「read_csv」) |
| 清洗四件套与列名规范化 | 同上 | text/20-ch08-8-automating-data-reports.txt:122(搜「missing values」) · text/20-ch08-8-automating-data-reports.txt:138(搜「lowercase」) |
| 验证三问 | 同上 | text/20-ch08-8-automating-data-reports.txt:181(搜「necessary columns」) |
| 可复用函数与三条纪律 | 同上 | text/20-ch08-8-automating-data-reports.txt:230(搜「load_and_clean_csv」) · text/20-ch08-8-automating-data-reports.txt:267(搜「raw data untouched」) |
| 汇总三操作与按月聚合 | 同上 | text/20-ch08-8-automating-data-reports.txt:294(搜「describe()」) · text/20-ch08-8-automating-data-reports.txt:349(搜「to_period」) |
| NumPy 与向量化 | 同上 | text/20-ch08-8-automating-data-reports.txt:393(搜「to_numpy」) |
| NamedAgg 汇总表 | 同上 | text/20-ch08-8-automating-data-reports.txt:446(搜「total_revenue=pd.NamedAgg」) |
| 图表说服论与三问 | 同上 | text/20-ch08-8-automating-data-reports.txt:490(搜「persuade」) · text/20-ch08-8-automating-data-reports.txt:517(搜「What question」) |
| matplotlib 月趋势线 | 同上 | text/20-ch08-8-automating-data-reports.txt:563(搜「line chart」) |
| seaborn 对比与分布 | 同上 | text/20-ch08-8-automating-data-reports.txt:663(搜「bar chart」) · text/20-ch08-8-automating-data-reports.txt:749(搜「histplot」) |
| 干系人开邮件附件 | 同上 | text/20-ch08-8-automating-data-reports.txt:810(搜「email attachments」) |
| Excel/PDF 受众对照 | 同上 | text/20-ch08-8-automating-data-reports.txt:843(搜「Interactive review」) |
| openpyxl 穿衣 | 同上 | text/20-ch08-8-automating-data-reports.txt:948(搜「bold」) |
| ReportLab 三元件与嵌图 | 同上 | text/20-ch08-8-automating-data-reports.txt:1003(搜「Flowable」) · text/20-ch08-8-automating-data-reports.txt:1108(搜「savefig」) |
| 周报管道与九步 | 同上 | text/20-ch08-8-automating-data-reports.txt:1135(搜「What We are Building」) · text/20-ch08-8-automating-data-reports.txt:1251(搜「without manual steps」) |
| 自动化靠重复证明 | 同上 | text/20-ch08-8-automating-data-reports.txt:1579(搜「proven through repetition」) |
| Key Terms 七词 | 同上 | text/20-ch08-8-automating-data-reports.txt:1627(搜「Data Ingestion」) |
| 死线不会变 | 同上 | text/20-ch08-8-automating-data-reports.txt:1591(搜「Deadlines do not」) |
| pandas 写数据 openpyxl 穿衣 | 同上 | text/20-ch08-8-automating-data-reports.txt:942(搜「openpyxl styles it」) |
| 脚本与系统的分界 | 同上 | text/20-ch08-8-automating-data-reports.txt:1544(搜「load_data」) |
| 三问清单 | 同上 | text/20-ch08-8-automating-data-reports.txt:517(搜「What question」) |
Footnotes
-
出处:「Automating Data Reports」第 18 段(
text/20-ch08-8-automating-data-reports.txt:18,搜「Hours can slip」)。 ↩ -
出处:「Automating Data Reports」第 21 段(
text/20-ch08-8-automating-data-reports.txt:21,搜「not to replace」)。原文:「The end goal is not to replace your analytical judgment but to free you from tedious work, so that you can focus on insights.」 ↩ -
出处:「Automating Data Reports」第 63 段(
text/20-ch08-8-automating-data-reports.txt:63,搜「read_csv」)。 ↩ -
出处:「Automating Data Reports」第 97 段(
text/20-ch08-8-automating-data-reports.txt:97,搜「parse_dates」)。 ↩ -
出处:「Automating Data Reports」第 112 段(
text/20-ch08-8-automating-data-reports.txt:112,搜「matches your intent」)。 ↩ -
出处:「Automating Data Reports」第 122 段(
text/20-ch08-8-automating-data-reports.txt:122,搜「missing values」)。 ↩ -
出处:「Automating Data Reports」第 181 段(
text/20-ch08-8-automating-data-reports.txt:181,搜「necessary columns」)。 ↩ -
出处:「Automating Data Reports」第 197 段(
text/20-ch08-8-automating-data-reports.txt:197,搜「isnull」)。 ↩ -
出处:「Automating Data Reports」第 267 段(
text/20-ch08-8-automating-data-reports.txt:267,搜「raw data untouched」)与第 271 段(text/20-ch08-8-automating-data-reports.txt:271,搜「Explain why」)。 ↩ -
出处:「Automating Data Reports」第 294 段(
text/20-ch08-8-automating-data-reports.txt:294,搜「describe()」)。 ↩ -
出处:「Automating Data Reports」第 334 段(
text/20-ch08-8-automating-data-reports.txt:334,搜「Total Revenue per Region」)。 ↩ -
出处:「Automating Data Reports」第 362 段(
text/20-ch08-8-automating-data-reports.txt:362,搜「business context」)。 ↩ -
出处:「Automating Data Reports」第 29–30 段(
text/20-ch08-8-automating-data-reports.txt:393,搜「to_numpy」)。 ↩ -
出处:「Automating Data Reports」第 446 段(
text/20-ch08-8-automating-data-reports.txt:446,搜「total_revenue=pd.NamedAgg」)。 ↩ -
出处:「Automating Data Reports」第 490 段(
text/20-ch08-8-automating-data-reports.txt:490,搜「persuade」)。原文:「A table of averages can inform, but a chart can persuade.」 ↩ -
出处:「Automating Data Reports」第 517 段(
text/20-ch08-8-automating-data-reports.txt:517,搜「What question」)。 ↩ ↩2 -
出处:「Automating Data Reports」第 563 段(
text/20-ch08-8-automating-data-reports.txt:563,搜「line chart」)。 ↩ -
出处:「Automating Data Reports」第 634 段(
text/20-ch08-8-automating-data-reports.txt:634,搜「sits on top」)。 ↩ -
出处:「Automating Data Reports」第 712 段(
text/20-ch08-8-automating-data-reports.txt:712,搜「No loops」)。 ↩ -
出处:「Automating Data Reports」第 771 段(
text/20-ch08-8-automating-data-reports.txt:771,搜「small or large」)。 ↩ -
出处:「Automating Data Reports」第 801 段(
text/20-ch08-8-automating-data-reports.txt:801,搜「Explain each step」)。 ↩ -
出处:「Automating Data Reports」第 810 段(
text/20-ch08-8-automating-data-reports.txt:810,搜「email attachments」)。原文:「Stakeholders do not open VS Code. They open email attachments.」 ↩ -
出处:「Automating Data Reports」第 843 段(
text/20-ch08-8-automating-data-reports.txt:843,搜「Interactive review」)。 ↩ ↩2 -
出处:「Automating Data Reports」第 911 段(
text/20-ch08-8-automating-data-reports.txt:911,搜「ExcelWriter」)。 ↩ -
出处:「Automating Data Reports」第 948 段(
text/20-ch08-8-automating-data-reports.txt:948,搜「bold」)。 ↩ -
出处:「Automating Data Reports」第 1003 段(
text/20-ch08-8-automating-data-reports.txt:1003,搜「Flowable」)。 ↩ -
出处:「Automating Data Reports」第 1108 段(
text/20-ch08-8-automating-data-reports.txt:1108,搜「savefig」)。 ↩ -
出处:「Automating Data Reports」第 1120 段(
text/20-ch08-8-automating-data-reports.txt:1120,搜「run and inspect」)。 ↩ -
出处:「Automating Data Reports」第 1251 段(
text/20-ch08-8-automating-data-reports.txt:1251,搜「without manual steps」)。原文:「Each week, I want a script that takes raw sales data, and produces a finished report without manual steps.」 ↩ -
出处:「Automating Data Reports」第 1307 段(
text/20-ch08-8-automating-data-reports.txt:1307,搜「already broken」)。原文:「If something looks wrong here, the report is already broken.」 ↩ -
出处:「Automating Data Reports」第 1331 段(
text/20-ch08-8-automating-data-reports.txt:1331,搜「not fixing data for perfection」)。原文:「You are not fixing data for perfection. You are fixing it for consistency.」 ↩ ↩2 -
出处:「Automating Data Reports」第 1400 段(
text/20-ch08-8-automating-data-reports.txt:1400,搜「savefig」)与第 1420 段(text/20-ch08-8-automating-data-reports.txt:1420,搜「matters for automation」)。 ↩ -
出处:「Automating Data Reports」第 77–93 段(
text/20-ch08-8-automating-data-reports.txt:1135,搜「What We are Building」)。 ↩ -
出处:「Automating Data Reports」第 1544 段(
text/20-ch08-8-automating-data-reports.txt:1544,搜「load_data」)。原文:「This is what separates scripts from systems.」 ↩ -
出处:「Automating Data Reports」第 1579 段(
text/20-ch08-8-automating-data-reports.txt:1579,搜「proven through repetition」)。 ↩ -
出处:「Automating Data Reports」Key Terms(
text/20-ch08-8-automating-data-reports.txt:1627,搜「Data Ingestion」至:1645,搜「Openpyxl」)。 ↩ -
出处:「Automating Data Reports」结论段(
text/20-ch08-8-automating-data-reports.txt:1591,搜「Deadlines do not」)。原文:「Reports change. Data sources change. Deadlines do not. Now, you have a system that adapts.」 ↩ -
出处:「Automating Data Reports」第 948 段(
text/20-ch08-8-automating-data-reports.txt:948,搜「auto-adjust」)。 ↩ -
出处:「Automating Data Reports」第 942 段(
text/20-ch08-8-automating-data-reports.txt:942,搜「openpyxl styles it」)。原文:「Pandas writes data. openpyxl styles it.」 ↩ -
出处:「Automating Data Reports」第 1003 段(
text/20-ch08-8-automating-data-reports.txt:1003,搜「Flowable」)。 ↩ -
出处:「Automating Data Reports」第 1544 段(
text/20-ch08-8-automating-data-reports.txt:1544,搜「load_data」)。原文:「This is what separates scripts from systems.」 ↩ -
补充(不在书里,来自通用知识):Excel 数值格式用 pandas 的 round 或 openpyxl 的 number_format;PDF 中文需注册 TTF 中文字体(reportlab.pdfbase.ttfonts)。 ↩
-
出处:「Automating Data Reports」第 1569 段(
text/20-ch08-8-automating-data-reports.txt:1569,搜「Change the data file」)。 ↩ -
出处:「Automating Data Reports」第 1185 段(
text/20-ch08-8-automating-data-reports.txt:1185,搜「Only the data source changes」)。 ↩ -
出处:「Automating Data Reports」第 563 段(
text/20-ch08-8-automating-data-reports.txt:563,搜「line chart」)与第 554 段(text/20-ch08-8-automating-data-reports.txt:554,搜「Instead of asking」)。 ↩ -
出处:「Automating Data Reports」第 267 段(
text/20-ch08-8-automating-data-reports.txt:267,搜「raw data untouched」)。 ↩ -
出处:「Automating Data Reports」第 187 段(
text/20-ch08-8-automating-data-reports.txt:187,搜「inadvertently」)。 ↩ -
出处:「Automating Data Reports」结论段(
text/20-ch08-8-automating-data-reports.txt:1588,搜「design reporting workflows」)。原文:「you learned how to design reporting workflows, not just write scripts.」 ↩ -
出处:「Automating Data Reports」第 1064 段(
text/20-ch08-8-automating-data-reports.txt:1064,搜「summarizes revenue performance」)。 ↩ ↩2 -
(我们的成本观;「重复就自动化」的原文见
text/17-ch05-5-automating-tedious-tasks.txt:1446。) ↩ -
三段式装置的实例见「Data Visualization with Matplotlib and Seaborn」各小节:
text/20-ch08-8-automating-data-reports.txt:660(搜「Prompt to ChatGPT」)、:666(搜「Sample Code Output」)、:708(搜「What You Should Notice」)。 ↩ -
出处:「Automating Data Reports」第 708–716 段(
text/20-ch08-8-automating-data-reports.txt:708,搜「What You Should Notice」;:716,搜「predictable and readable」)。 ↩ -
出处:「Automating Data Reports」第 767 段(
text/20-ch08-8-automating-data-reports.txt:767,搜「This chart helps to answer」;:731,搜「Aggregates can hide」)。 ↩ -
出处:「Automating Data Reports」第 828 段(
text/20-ch08-8-automating-data-reports.txt:828,搜「most common reason」)。 ↩ -
出处:「Automating Data Reports」第 1591 段(
text/20-ch08-8-automating-data-reports.txt:1591,搜「most valuable abilities」)。 ↩