跳转至

自动数据报告 — 从原始 CSV 到自动交付的 Excel 与 PDF

这一章讲什么: 报告是决策的血液循环,但手工做报告的时间大半耗在重复动作上。 这一章把「每周从原始数据到成稿报告」变成一条管道:五个环节各写成函数, 整条管道一次跑通、每周复跑,数据换了换源头、管道不动。 读完你会拿到一个能直接搬进工作的模板,以及一个判断: 什么时候该要 Excel,什么时候该要 PDF。

1. 这一章讲什么

第五章自动化了「文件与网页」,这一章自动化「数据与结论」。 书里给的项目是职场最常见的一类:每周销售报告。 动机照旧是时间:手工周报里,第一个真正有信息量的句子写出来之前,几个小时已经没了10。

这一章的立场书里说得明白:AI 在这里的作用是加速工作流、帮你设计管道的结构、 建议高效的库、甚至为你的数据发现写叙述性摘要—— 但终点不是替代你的分析判断,把你从枯燥里解放出来,让你专注洞察11。

在全书链条里,这一章是第七章「管道思维」的放大版,也是第九章 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 或 SQLAlchemy12。本章主线用 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 当成字符串13。 书里点出这条提示的要点:数据集、列类型、预览步骤全说了——清晰的请求换来贴合意图的代码14。

清洗(cleaning)四件套:处理缺失值(NaN,pandas 里「缺失」的记号)、去重、 列名规范化、纠正数据类型15。提示:「去掉 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 生成代码之后,你必须验证结果。书里的三问: 必要的列都在吗?数据类型对吗?清洗步骤有没有误删重要的行?16 配套两行检查:print(df.isnull().sum()) 数每列还剩几个缺失;print(df.dtypes) 核对类型17。

包成函数,可复用为第一目标:

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), 什么时候这不合适」——建立理解,不只是自动化18。

3.2 汇总:从「一堆行」到「几个决策数」

原始数据第一眼不讲故事,汇总(summarization)把它变成指标。 pandas 三件核心操作:df.describe() 数值列统计概览; df["列"].value_counts() 各类别计数;df.groupby("列").agg() 按组聚合19。

提示:「汇总 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() 数每组的行数20。书里的配套心法:让 AI 按列类型与业务背景建议聚合; 聚合之后必 print 或 head 抽查 sanity check( sanity check,常识性抽查:数字的数量级对不对、 日期连续不连续)21。

NumPy 补数学。 需要均值、中位数、标准差(一组数偏离平均值有多远的度量)等纯数学操作时,numpy 补位:

import numpy as np
revenue = df["revenue"].to_numpy()
np.mean(revenue), np.median(revenue), np.std(revenue)

书里的小贴士:对照 pandas 的输出——两者都对,但 pandas 与带标签的数据更合; 让 AI 引导你用向量化操作(一次性对整列算,而不是逐行循环)——大数据集上快得多22。

汇总成一张「报告表」。 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、图表都行23。

3.3 可视化:数字长形状的地方

书里的句子值得原样记:数字很少自己说服人,它们需要形状、对比与语境。 一张平均值表格能告知,一张图能说服。24

作图前先答三问(这是本章的方法论亮点): 这张图要回答什么问题?报告给谁看?看完这张图可能做什么决定? 答完再写提示——想清楚这三问,提示自然具体25。

matplotlib:底子,要求你说清一切。 提示:「用 matplotlib 画销售月营收趋势线图: x 轴月份、y 轴总营收,标注清楚,适合放进报告。」产出Monthly Revenue Trend 的标准线图: groupby 出月度总额 → .plot(kind="line") → 加 xlabel/ylabel/title → tight_layout() 防裁字 → show()26。这张图回答一个问题:营收随时间怎么走。

seaborn:坐在 matplotlib 上,默认就更体面。 它省样板代码, 默认样式通常直接能进报告。什么时候优先 seaborn:做类别对比、看分布、画分组汇总27。 两个演示:

# 对比:每区域总营收(柱状图)
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 处理对齐与间距28。 直方图回答的问题也写在书里:大多数交易是大的还是小的?有没有拉高平均值的离群点(和其他值差得离谱的少数值)?29

汇总 + 可视化 + 导出是最强工作流(书里的原话):先 pandas 汇总、再画汇总、再导出。 可以让 AI 把三步连起来写,并要求「解释每一步」——这样学得才扎实,避免盲目复制30。

3.4 导出:报告离开你的屏幕那一刻

为什么导出是「自动化成真」的分界:干系人不开 VS Code,他们开邮件附件。 他们期待能存档、转发、不看说明就能读的文件31。

选格式(书里的对照表):

Excel(.xlsx) PDF(.pdf)
适合 交互复查、继续筛选 执行摘要、固定版式
受众 财务与运营团队 正式汇报、官方归档
库 openpyxl(pandas 配合) ReportLab

同一数据集,两个都出——书里的管道就是双出口的32。

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)

一个文件两张表:原始数据 + 区域汇总33。再让 AI 加一层: 「扩展导出脚本:表头加粗、列宽自适应。」产物用 openpyxl 的 load_workbook 重新打开, 遍历第一行设 Font(bold=True),逐列算 max(len(str(cell.value))) 设宽度—— 报告从「机器生成」变成「像有设计」34。

PDF:你是在排版文档,不是倒数据。 ReportLab 用三种元件搭 PDF: Paragraph(段落)、Table(表格)、Flowable(可流动元素)——它奖励先规划35。 提示:「用 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)) 贴入36。 书里的纪律:尺寸布局可以让 AI 调,输出必须自己开一遍37。

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):「每周,我要一个脚本,吃下原始销售数据, 不经任何手工步骤,产出完成品报告。」——这句定义将指导你后面每一条提示38。

然后逐步骤推进,每步一条 CCG 提示 + 一段代码 + 一个「你应该看到」:

  1. 读与验。 「加载 CSV、正确解析日期、显示基本信息验证完整性。」看到:数据预览、日期解析成功、无缺列。这里看着不对,报告就已经坏了39。
  2. 清与验。 「扩展脚本:去掉缺失营收、确保营收是数值。」pd.to_numeric(..., errors="coerce")(转不动的变 NaN)+ dropna(subset=["revenue"])。不是追求完美,是追求一致40。
  3. 汇总。 「生成按区域汇总周总营收的代码。」一行 groupby。
  4. 画图。 「用 matplotlib 或 seaborn 画区域营收柱状图,存成图片。」关键在存(savefig + close),不弹窗、不等点击——自动化必须无头41。
  5. 导 Excel。 「导出原始数据 + 汇总,格式化。」
  6. 导 PDF。 「用 ReportLab 生成带标题、摘要段、表格、嵌入图表的 PDF。」42
  7. 函数化。 把逻辑包进 load_data() / clean_data(df) 等函数——脚本与系统的分界就在这一步43。
  8. 换数据重跑。 书里的最后一步叫「Final Reality Check」:换一份数据文件、重跑、确认报告重新生成正确。 自动化靠重复证明,不靠祈祷44。

3.6 章末 Key Terms 与收尾判断

本章 Key Terms 六个,照收8:

术语 含义
Data Ingestion(数据摄取) 从文件、数据库或 API 加载原始数据
Data Cleaning(数据清洗) 修格式、补缺、验类型,把数据备好
Pandas Python 的数据处理与分析库
Data Visualization(数据可视化) 用图形表示数据以支撑理解与决策
Report Pipeline(报告管道) 产出一致报告输出的一串自动化步骤
ReportLab 用代码程序化生成 PDF 的库
Openpyxl 读写 Excel 文件的库

本章最后一句值得原文带走: 报告会变、数据源会变,死线(deadline)不会—— 「现在,你有了一个能跟着变的系统」9。这句话就是整套管道的存在理由: 把「每周一次的手忙脚乱」换成「数据一换、按钮一按」。

3.7 排版的最后一公里:ReportLab 与 openpyxl 的分工再确认

§3.4 给了两个导出库的用法,这一节把「谁干什么」再钉一次,因为这是新手最容易搅浑的:

pandas 写数据,openpyxl 穿衣服。 df.to_excel(writer, sheet_name=...) 负责「把表放进去」; 加粗表头、自适应列宽、改字体颜色,全是 openpyxl 的活(重新 load_workbook 打开再改)4。 书里的原话:pandas writes data. openpyxl styles it.5 记住这句,你就不会在 pandas 的 API 里翻半天找「加粗」了——没有,就是另一个库的事。

ReportLab 是排版,不是数据倾倒。 PDF 的三种元件——Paragraph(段落)、Table(表格)、 Flowable(可流动元素)——按「文档流」依次排:先标题、再说明段、再表、再图6。 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)   # 主流程一行串起来

五段各一个动词,主流程只剩组合。这就是「脚本与系统的分界」:脚本是一坨顺序执行的语句, 系统是「每段有名有姓、可以单独换」的函数集合7。第五段换数据源(比如从数据库读), 只动 load_data();换图表样式,只动 make_chart()——第八章的管道与第九章的 Flask 端点、 第四章的三函数拆分,全是同一个结构观在不同尺度上的重复。

3.9 本章的「三问」清单(写管道前自测)

动笔写任何报告管道前,先答书里的三问(第 38 段的三个问题扩成操作版):

  1. 这张图答什么问题?——答不上来就不画;
  2. 报告给谁看?——决定 Excel 还是 PDF(还是两个都要);
  3. 看完会做什么决定?——决定汇总表里该有什么指标。

三问答完,提示的 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)分开想47。

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」只有三行:换数据文件 → 重跑 → 确认报告重新生成正确48。 这里把这三行背后的检查清单补出来——换数据时最容易断的是四处:

  1. 日期列解析变了(新数据的日期格式不同,parse_dates 默默产 NaT);
  2. 列名变了(新数据多一个空格或大写,df["revenue"] 直接 KeyError);
  3. 数值列夹了文本(「N/A」混进 revenue,to_numeric(coerce) 把它们变缺失, 总行数骤降而你不察觉);
  4. 输出文件名撞车(同周的旧报告被覆盖——也许你要的正是覆盖,也许不是)。

书里没有列这张清单,但它那句「automation is proven through repetition」 就是这四处检查的总纲:每处断点都只在「换数据」时暴露,而不在第一次跑通时暴露。 所以管道的验收从来不是「能跑」,是「换三份数据都能跑」。

3.13 报告管道的通用化:书末那句「only the data source changes」

书里在管道目标一节写了一句很容易滑过去的话: 这套管道可以复用于销售报告、网站分析、员工绩效、运营指标—— 只有数据源改变(only the data source changes)49。

这句话是第八章真正的毕业标准。通用化的含义:

  • 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 轴总额,标注清楚,适合放进报告」, 图才长成人样50。

3.15 数据清洗的「留一手」纪律(书里两处提醒的合并)

第八章有两处小提醒,合起来是一条纪律:

  • 清洗前:留一份原始数据不动(keep a copy of the raw data untouched), 清洗搞砸了好重来51;
  • 清洗后:用 print/describe 抽查,确认清洗没误删重要行52。

合成一句话:进管道前的数据是你的后悔药,出清洗后的数据要过 sanity check。 「后悔药」具体怎么做,书里没细说,惯用做法是原始 CSV 只读复制一份进 data/raw/, 处理脚本从不写它(补充,不在书里,来自通用知识)。 sanity check 的最低配:df.describe() 看数值列的 min/max 是否离谱、 df.isnull().sum() 看缺失分布、len(df) 看行数对得上源文件的预期。 三行代码,拦住第九章「报告错了但没人发现」那种最尴尬的失败。

3.16 本章的「一句话」收成

把第八章折成一句话:先想清楚这张图答什么,再让管道替你每周答一遍。 前半句是 §3.3 的三问(图的问题/受众/后续决定),后半句是 §3.5 的九步 (定义 → 读 → 清 → 算 → 画 → 导 → 导 → 函数化 → 换数据重跑)。 两句话中间夹着全章的真正主角——结构:每段一个函数、每站可换、双出口并存。 书里说「你学会了设计报告工作流,不只是写脚本」53,设计的全部内容 就是这一句加九步。

3.17 报告的叙事层(书里一句带过、值得展开)

书里的 PDF 示例中,标题与摘要是这样写的:「Weekly Sales Report」+ 「This report summarizes revenue performance by region for the current week.」54—— 一句平淡的过渡语,却是报告管道的「叙事层」:数据层(表与图)之上, 还有一层「这段话把读者从标题带进结论」的文字。

自动化管道的叙事层怎么写?三个来源,由易到难:

  1. 固定模板:「本周总营收 X,最高区域 Y」用 f-string 填数——够用 80% 的场景;
  2. 规则化叙述:按书里的做法让 AI 写「若某区域环比降超 20%,加一句警示」 这类条件文案,判断规则你定;
  3. 模型生成摘要:把汇总表喂给模型写一段话——最强也最险, 必须有数字核对兜底(它可能编)。

书里停在第 1 种(模板句)54。叙事层是「自动报告」与「自动生成报告」的分界: 前者填模板,后者要校验——第九章「不信 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 分钟。 回本周期大约两到四周——凡是周期超过一周的重复报表,自动化几乎都值; 一次性任务永远不值。这是书里那句「发现重复就自动化」的隐含前提,55 把它显式化,是为了防止「为自动化而自动化」: 有些一周做一次、每次十分钟的事,与其写脚本,不如设个提醒。

判断(我们的,不是书里的): 自动化的门槛公式 = 单次耗时 × 剩余次数 > 搭建耗时 × 2(留一倍缓冲给维护)。公式里的数因人因项目而异, 但「剩余次数」这个因子经常被忽略——项目两个月后结束的报表, 和要跑三年的报表,答案完全不同。 如果错,会错在: 维护成本被低估——管道会随数据源变化而腐坏, 真实回本周期常比公式算的更长;但这个误差不改变「高频重复值得自动化」的大方向。

3.20 图表教学的三段式:提示 → 代码 → 「你应该注意到」

第八章的可视化教学藏着一个全书独有的排版装置,值得单拎出来:每一种图(柱状、分布、分组) 都按固定三段呈现——先给「Prompt to ChatGPT」原文,再给「Sample Code Output」, 最后给一小段「What You Should Notice(你应该注意到)」2。

以柱状图那段的「你应该注意到」为例,书里列了四条观察:

  1. 没有循环——你没有手写 for 去摆柱子;
  2. 没有手动算柱高——seaborn 替你对齐与间距;
  3. 图从左往右自然可读;
  4. 结论一句:「这正是自动报告需要的——可预期、可读的输出」3。

这个装置的教学含义比四条观察本身更重要:它在教你怎么读 AI 给的代码。 新手拿到一段能跑的绘图代码,第一反应是「复制、运行、截图」;「你应该注意到」 逼你停一秒,回答「这段代码里少了什么」——没有循环、没有手算坐标。 认出一个库替你省掉了什么,才算真正认识了这个库; 只看见「它能跑」,下次换数据就傻眼。这与第七章「让 AI 给期望输出」是同一枚硬币的两面: 一个要求 AI 自证,一个教你自己看出门道。

判断(我们的,不是书里的): 「你应该注意到」其实是把代码评审(codereview) 的第一步做成了填空题——只问「少了什么」,不问「好不好」。这个降级是聪明的: 新手没有能力整体评审一段陌生代码,但「找出这里没出现循环」人人能做。 如果错,会错在: 对熟练读者,这种引导会显得多余;而且「少了什么」清单依赖 具体库的实现细节(换个绘图库,循环可能又回来了),照抄观察不如学方法。

这个装置在章内不止一处:直方图段的收尾是「这张图帮你回答两个问题—— 大多数交易是大的还是小的?有没有拉高平均值的离群点?」1—— 注意,这又是一个「问题先于图」的示范:图不是画完再想它说明什么, 而是先有问题、再选图、最后拿图回答问题。这与 §3.9 的「作图前先答三问」 首尾呼应:三问是作图前的输入,你应该注意到是成图后的验收。 两段连起来,就是本章图方法论的全部:问在前,图居中,验收在后。

4. 作者的判断与证据

有演示支撑的判断:

  • 「清洗定义后续一切结果的准确性」——本章用第 2 步的「先清后算」顺序支撑, 是数据工作的标准共识40;
  • 「可视化是沟通不是探索」——三问法(答什么问题/给谁看/导致什么决定)是本章的方法论高点25;
  • 「Excel 与 PDF 服务不同受众,应共存」——双出口管道完整演示32。

经验之谈:

  • 「fillna(0) 的适用性要问」——书里专门给了追问句式,这是诚实的教学;
  • 「缺失库是报告脚本失败最常见的原因」——排障经验45。

推测:

  • 「这是现代技术岗位最有价值的技能之一」——结语的价值判断,无证据,当激励读46。

5. 边界与局限

  • 没有调度一章:管道能跑,但「每周自动」要接第 5 章的 schedule 或 cron——本章没把这步接上;
  • 图表没有排版治理:多图报告的版式、分页、字号体系没展开,ReportLab 的高级排版(模板、样式表)只露一角;
  • Excel 的高级特性缺席:公式、透视表、条件格式都没涉及——openpyxl 能做的远不止加粗表头;
  • 数据验证只靠肉眼:三问(isnull/dtypes/列齐全)之外,没有值域校验、跨表一致性检查;
  • 异常与告警空白:管道某站失败(文件不存在、列名变了)时的报错与通知,本章没有设计。

6. 可带走的

本章主线例子,一行写完: sales_data.csv → load_and_clean(解析日期→去重→填零→列名规范化) → groupby 区域汇总 → seaborn 柱状图存 PNG → openpyxl 双表 Excel → ReportLab 出含图表的 PDF → 包成函数 → 换数据重跑验证。

  1. 垃圾进垃圾出:摄取后必 head、清洗后必 isnull/dtypes;原始数据留备份;
  2. 清洗不是追求完美,是一致;
  3. 汇总表是报告的脊梁:groupby + NamedAgg 一次成型;
  4. 作图前三问:答什么、给谁看、导致什么决定;
  5. matplotlib 画趋势、seaborn 画对比与分布;自动化用 savefig,不用 show;
  6. Excel 交互用、PDF 归档用,同一数据两个都出;
  7. pandas 写数据、openpyxl 穿衣服(粗表头、自适应列宽);
  8. PDF 是排版:Paragraph + Table + Image 三元件;
  9. 脚本与系统的分界 = 函数化;
  10. 最后一步永远是换数据重跑——自动化靠重复证明。

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」)

  1. 出处:「Automating Data Reports」第 767 段(text/20-ch08-8-automating-data-reports.txt:767,搜「This chart helps to answer」;:731,搜「Aggregates can hide」)。 ↩

  2. 三段式装置的实例见「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」)。 ↩

  3. 出处:「Automating Data Reports」第 708–716 段(text/20-ch08-8-automating-data-reports.txt:708,搜「What You Should Notice」;:716,搜「predictable and readable」)。 ↩

  4. 出处:「Automating Data Reports」第 948 段(text/20-ch08-8-automating-data-reports.txt:948,搜「auto-adjust」)。 ↩

  5. 出处:「Automating Data Reports」第 942 段(text/20-ch08-8-automating-data-reports.txt:942,搜「openpyxl styles it」)。原文:「Pandas writes data. openpyxl styles it.」 ↩

  6. 出处:「Automating Data Reports」第 1003 段(text/20-ch08-8-automating-data-reports.txt:1003,搜「Flowable」)。 ↩

  7. 出处:「Automating Data Reports」第 1544 段(text/20-ch08-8-automating-data-reports.txt:1544,搜「load_data」)。原文:「This is what separates scripts from systems.」 ↩

  8. 出处:「Automating Data Reports」Key Terms(text/20-ch08-8-automating-data-reports.txt:1627,搜「Data Ingestion」至 :1645,搜「Openpyxl」)。 ↩

  9. 出处:「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.」 ↩

  10. 出处:「Automating Data Reports」第 18 段(text/20-ch08-8-automating-data-reports.txt:18,搜「Hours can slip」)。 ↩

  11. 出处:「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.」 ↩

  12. 出处:「Automating Data Reports」第 63 段(text/20-ch08-8-automating-data-reports.txt:63,搜「read_csv」)。 ↩

  13. 出处:「Automating Data Reports」第 97 段(text/20-ch08-8-automating-data-reports.txt:97,搜「parse_dates」)。 ↩

  14. 出处:「Automating Data Reports」第 112 段(text/20-ch08-8-automating-data-reports.txt:112,搜「matches your intent」)。 ↩

  15. 出处:「Automating Data Reports」第 122 段(text/20-ch08-8-automating-data-reports.txt:122,搜「missing values」)。 ↩

  16. 出处:「Automating Data Reports」第 181 段(text/20-ch08-8-automating-data-reports.txt:181,搜「necessary columns」)。 ↩

  17. 出处:「Automating Data Reports」第 197 段(text/20-ch08-8-automating-data-reports.txt:197,搜「isnull」)。 ↩

  18. 出处:「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」)。 ↩

  19. 出处:「Automating Data Reports」第 294 段(text/20-ch08-8-automating-data-reports.txt:294,搜「describe()」)。 ↩

  20. 出处:「Automating Data Reports」第 334 段(text/20-ch08-8-automating-data-reports.txt:334,搜「Total Revenue per Region」)。 ↩

  21. 出处:「Automating Data Reports」第 362 段(text/20-ch08-8-automating-data-reports.txt:362,搜「business context」)。 ↩

  22. 出处:「Automating Data Reports」第 29–30 段(text/20-ch08-8-automating-data-reports.txt:393,搜「to_numpy」)。 ↩

  23. 出处:「Automating Data Reports」第 446 段(text/20-ch08-8-automating-data-reports.txt:446,搜「total_revenue=pd.NamedAgg」)。 ↩

  24. 出处:「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.」 ↩

  25. 出处:「Automating Data Reports」第 517 段(text/20-ch08-8-automating-data-reports.txt:517,搜「What question」)。 ↩↩

  26. 出处:「Automating Data Reports」第 563 段(text/20-ch08-8-automating-data-reports.txt:563,搜「line chart」)。 ↩

  27. 出处:「Automating Data Reports」第 634 段(text/20-ch08-8-automating-data-reports.txt:634,搜「sits on top」)。 ↩

  28. 出处:「Automating Data Reports」第 712 段(text/20-ch08-8-automating-data-reports.txt:712,搜「No loops」)。 ↩

  29. 出处:「Automating Data Reports」第 771 段(text/20-ch08-8-automating-data-reports.txt:771,搜「small or large」)。 ↩

  30. 出处:「Automating Data Reports」第 801 段(text/20-ch08-8-automating-data-reports.txt:801,搜「Explain each step」)。 ↩

  31. 出处:「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.」 ↩

  32. 出处:「Automating Data Reports」第 843 段(text/20-ch08-8-automating-data-reports.txt:843,搜「Interactive review」)。 ↩↩

  33. 出处:「Automating Data Reports」第 911 段(text/20-ch08-8-automating-data-reports.txt:911,搜「ExcelWriter」)。 ↩

  34. 出处:「Automating Data Reports」第 948 段(text/20-ch08-8-automating-data-reports.txt:948,搜「bold」)。 ↩

  35. 出处:「Automating Data Reports」第 1003 段(text/20-ch08-8-automating-data-reports.txt:1003,搜「Flowable」)。 ↩

  36. 出处:「Automating Data Reports」第 1108 段(text/20-ch08-8-automating-data-reports.txt:1108,搜「savefig」)。 ↩

  37. 出处:「Automating Data Reports」第 1120 段(text/20-ch08-8-automating-data-reports.txt:1120,搜「run and inspect」)。 ↩

  38. 出处:「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.」 ↩

  39. 出处:「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.」 ↩

  40. 出处:「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.」 ↩↩

  41. 出处:「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」)。 ↩

  42. 出处:「Automating Data Reports」第 77–93 段(text/20-ch08-8-automating-data-reports.txt:1135,搜「What We are Building」)。 ↩

  43. 出处:「Automating Data Reports」第 1544 段(text/20-ch08-8-automating-data-reports.txt:1544,搜「load_data」)。原文:「This is what separates scripts from systems.」 ↩

  44. 出处:「Automating Data Reports」第 1579 段(text/20-ch08-8-automating-data-reports.txt:1579,搜「proven through repetition」)。 ↩

  45. 出处:「Automating Data Reports」第 828 段(text/20-ch08-8-automating-data-reports.txt:828,搜「most common reason」)。 ↩

  46. 出处:「Automating Data Reports」第 1591 段(text/20-ch08-8-automating-data-reports.txt:1591,搜「most valuable abilities」)。 ↩

  47. 补充(不在书里,来自通用知识):Excel 数值格式用 pandas 的 round 或 openpyxl 的 number_format;PDF 中文需注册 TTF 中文字体(reportlab.pdfbase.ttfonts)。 ↩

  48. 出处:「Automating Data Reports」第 1569 段(text/20-ch08-8-automating-data-reports.txt:1569,搜「Change the data file」)。 ↩

  49. 出处:「Automating Data Reports」第 1185 段(text/20-ch08-8-automating-data-reports.txt:1185,搜「Only the data source changes」)。 ↩

  50. 出处:「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」)。 ↩

  51. 出处:「Automating Data Reports」第 267 段(text/20-ch08-8-automating-data-reports.txt:267,搜「raw data untouched」)。 ↩

  52. 出处:「Automating Data Reports」第 187 段(text/20-ch08-8-automating-data-reports.txt:187,搜「inadvertently」)。 ↩

  53. 出处:「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.」 ↩

  54. 出处:「Automating Data Reports」第 1064 段(text/20-ch08-8-automating-data-reports.txt:1064,搜「summarizes revenue performance」)。 ↩↩

  55. (我们的成本观;「重复就自动化」的原文见 text/17-ch05-5-automating-tedious-tasks.txt:1446。) ↩