
小白python入门 - 65. 综合案例从原始表到分析报告1. 本课定位是什么、为何重要前 11 课拆开了能力本课按真实交付节奏串成一条可复现流水线原始导出 → 清洗 → KPI → 图/看板 →半页结论。这是数据分析岗位最常见的「周报/专题」形态。概念一句话分析报告问题 口径 证据表/图 发现 局限 建议可复现别人用同一数据与脚本得到同一数字贯穿业务域迷你电商paid GMV、渠道、城市、日趋势对比已学单课 API 练习 vs面向决策的交付物。2. 本质把业务问题翻译成可计算可展示的证据链本质把业务问题翻译成「可计算的指标与可展示的证据链」。交付件必须有口径说明时间窗、状态过滤、金额定义数字表可对账的 KPI图35 张关键图趋势 结构结论发现 / 建议 / 下一步数据需求局限样本偏差、缺失、未含退款等边界本课不接机器学习预测不部署在线 BI。3. 先跑为敬最小可跑流水线importpandasaspdimportmatplotlib.pyplotaspltfrompathlibimportPath plt.rcParams[font.sans-serif][Microsoft YaHei,SimHei]plt.rcParams[axes.unicode_minus]False# --- 第1步读原始数据 ---rawpd.read_csv(data/orders.csv,parse_dates[created_at])print(f原始行数:{len(raw)})# --- 第2步清洗复用第 58 课逻辑 ---defclean_orders(raw):dfraw.copy()df[amount]pd.to_numeric(df[amount],errorscoerce)df[status]df[status].astype(str).str.strip().str.lower()df[channel]df[channel].astype(str).str.strip().str.lower()channel_map{小程序:miniapp,微信:miniapp}df[channel]df[channel].map(channel_map).fillna(df[channel])dfdf.drop_duplicates(subset[order_id],keepfirst)dfdf[df[amount].isna()|(df[amount]0)]df[is_gmv]df[status].eq(paid)df[amount].notna()df[amount].gt(0)returndf cleanclean_orders(raw)Path(data).mkdir(exist_okTrue)clean.to_csv(data/orders_clean.csv,indexFalse)print(f清洗后行数:{len(clean)})# --- 第3步算 KPI ---gmv_dataclean.loc[clean[is_gmv]True]total_gmvgmv_data[amount].sum()print(f总 GMV:{total_gmv:.1f}元)你刚才跑了「读 → 洗 → 算」三步这就是最简流水线。下面加出图和报告。4. 流水线四段load_raw → clean_orders → build_kpis → export_charts write_report4.1 清洗复用第 58 课importpandasaspdfrompathlibimportPathdefload_raw(path):returnpd.read_csv(path,parse_dates[created_at])defclean_orders(raw):dfraw.copy()df[amount]pd.to_numeric(df[amount],errorscoerce)df[status]df[status].astype(str).str.strip().str.lower()df[channel]df[channel].astype(str).str.strip().str.lower()channel_map{小程序:miniapp,微信:miniapp}df[channel]df[channel].map(channel_map).fillna(df[channel])dfdf.drop_duplicates(subset[order_id],keepfirst)dfdf[df[amount].isna()|(df[amount]0)]df[is_gmv]df[status].eq(paid)df[amount].notna()df[amount].gt(0)returndf4.2 KPI复用第 6061 课defkpis(clean):算渠道 KPI 城市 KPI 日 GMVgclean.loc[clean[is_gmv]True].copy()by_channel(g.groupby(channel,as_indexFalse).agg(gmv(amount,sum),orders(order_id,count),uv(user_id,nunique)).assign(aovlambdax:x[gmv]/x[orders])# 客单价.sort_values(gmv,ascendingFalse))by_city(g.groupby(city,as_indexFalse).agg(gmv(amount,sum),orders(order_id,count)).sort_values(gmv,ascendingFalse))daily(g.set_index(created_at).sort_index()[amount].resample(D).sum().asfreq(D,fill_value0).rename(gmv).reset_index())return{by_channel:by_channel,by_city:by_city,daily:daily}4.3 出图第 6264 课任选importmatplotlib.pyplotaspltdefexport_charts(k,out_dir):导出关键图返回文件路径列表paths[]out_dirPath(out_dir)out_dir.mkdir(parentsTrue,exist_okTrue)# 图1渠道 GMV 柱状图chk[by_channel]fig,axplt.subplots(figsize(6,4),dpi120)ax.bar(ch[channel],ch[gmv])ax.set_title(各渠道 Paid GMV)ax.set_ylabel(GMV元)forx,yinzip(ch[channel],ch[gmv]):ax.text(x,y,f{y:.0f},hacenter,vabottom)p1out_dir/gmv_by_channel.pngfig.savefig(p1,bbox_inchestight)plt.close(fig)paths.append(p1)# 图2日 GMV 折线图dailyk[daily]fig,axplt.subplots(figsize(7,4),dpi120)ax.plot(daily[created_at],daily[gmv],markero)ax.set_title(日 GMV 趋势)fig.autofmt_xdate()p2out_dir/gmv_daily.pngfig.savefig(p2,bbox_inchestight)plt.close(fig)paths.append(p2)# 图3城市 GMV 柱状图cityk[by_city]fig,axplt.subplots(figsize(6,4),dpi120)ax.bar(city[city],city[gmv])ax.set_title(各城市 Paid GMV)ax.set_ylabel(GMV元)p3out_dir/gmv_by_city.pngfig.savefig(p3,bbox_inchestight)plt.close(fig)paths.append(p3)returnpaths5. 报告结构模板章节写什么1 背景与问题本周要回答什么2 口径时间窗、paid、amount0、去重键3 数据质量原始行数、去重、缺失、剔除比例4 核心发现3 条以内每条绑定数字5 图表结构 趋势6 建议与局限可执行动作 数据缺口口径示例可直接贴进 report.md- 时间窗2026-07-01 2026-07-31 - GMVstatuspaid 且 amount0 的 amount 之和 - 订单去重order_id 保留首条 - 未含退款冲减、未支付意向单6. 主函数一键跑通defmain():# 路径设置rootPath(day65)raw_dirroot/data/rawclean_dirroot/data/cleanoutput_dirroot/outputsraw_dir.mkdir(parentsTrue,exist_okTrue)clean_dir.mkdir(parentsTrue,exist_okTrue)output_dir.mkdir(parentsTrue,exist_okTrue)# 复制统一数据集到 day65 目录如无则用原始路径srcPath(data/orders.csv)ifsrc.exists():importshutil shutil.copy2(src,raw_dir/orders.csv)# 1. 读原始数据rawload_raw(raw_dir/orders.csv)# 2. 清洗cleanclean_orders(raw)assertclean[order_id].is_unique,order_id 不唯一!clean.to_csv(clean_dir/orders.csv,indexFalse)# 3. 算 KPIkkpis(clean)# 4. 出图chart_pathsexport_charts(k,output_dir)# 5. 写报告total_gmvk[by_channel][gmv].sum()# 防御如果有数据才取第一条iflen(k[by_channel])0:topk[by_channel].iloc[0]top_infof头部渠道{top[channel]}GMV {top[gmv]:.1f}订单{int(top[orders])}else:top_info无有效渠道数据reportf# 迷你电商一周分析示例 ## 口径 - paid 且 amount0order_id 去重 ## 质量 - 原始行数:{len(raw)}清洗后行数:{len(clean)}## 发现 1. 总 paid GMV {total_gmv:.1f}元 2.{top_info}3. 详见 outputs 图:{, .join(p.nameforpinchart_paths)}## 建议 - 对低 GMV 渠道做客单与转化专项需补访购数据 ## 局限 - 模拟数据未接退款与流量分母 (root/report.md).write_text(report,encodingutf-8)print(report)print(done)if__name____main__:main()预期输出形态打印总 GMV、头部渠道、done生成report.md与三张 PNG。代码说明——防御式编程if len(k[by_channel]) 0这行是防御式写法。如果数据为空iloc[0]会报错。加了判断就不会崩。这也是第 58 课除法前检查 0 除的思路——永远假设数据可能为空。7. 实战你要亲手改的三处任务目的换时间窗再跑体验口径参数化增加「城市 GMV」图复用 by_city已在 export_charts 里用 plotly 多写一个 HTML复习第 64 课验收清单raw 未被修改clean 唯一 order_id报告数字与by_channel表一致至少 2 张图 半页结论他人可只跑analyze.py复现8. 从 Notebook 开始的指引如果你更喜欢在 Jupyter Notebook 里做分析推荐的结构day65/ notebooks/ 01_explore.ipynb ← 探索、试错 02_clean.ipynb ← 清洗 03_kpi_charts.ipynb ← KPI 和图 src/ metrics.py ← 从 notebook 抽出来的函数可复用 data/raw/ ← 只读 outputs/ ← 图和报告Notebook → .py 的节奏在 Notebook 里探索、试代码确认可行的逻辑抽成函数写到.py最终的.py是可复现的Notebook 是思考过程9. 高阶技巧点到技巧说明配置 YAML 存口径业务改口径不改代码逻辑数据版本 hash记录 CSV 校验和hashlib.md5从 Notebook 抽库src/metrics.py供多报告复用衔接下阶段同一 clean 表进阶段 E 做预测/分类10. 踩坑回顾坑现象正确做法只有图没有口径无法争论对错报告首页写口径结论写「感觉」不可执行每条发现对应一个数Notebook 从头点到尾无函数难复用抽clean/kpi/plot改 raw 文件无法审计raw 只读DataFrame 为空时iloc[0]报错加if len(df) 0判断11. 总结复盘阶段 D 收官项内容本课端到端流水线 报告模板 验收清单阶段路线回顾54 工作流 → 55 NumPy → 56–60 表分析 → 61 时间 → 62–64 呈现 → 65 交付思维拔高分析的终点是决策与行动不是 DataFrame代码保证可复现文字保证可执行延伸学习BI 工具可选阶段 E 机器学习官方入口pandas · matplotlib · seaborn · plotly阶段能力自检能力自检问题环境能否干净 venv 跑通依赖术语能否用大白话解释 GMV/UV/AOV/口径清洗能否说明每条 drop/fillna 的业务理由指标能否用一句话定义 GMV/UV/AOV时间能否出日序列与环比呈现能否按问题选图并导出交付能否交出可复现报告附录统一数据集版本课直接使用第 54 课生成的data/orders.csv。main()函数会自动复制到day65/data/raw/目录。如需从零开始先运行 54 课第 9 节的gen_orders.py。