☰
Python 数据处理进阶(十九)Pandas 与 Excel 的分工:哪些活该留给 Excel
2026/10/31 7:03:17 网站建设 项目流程

写在前面

前 18 篇讲了一路 Pandas 有多能干,这一篇唱个反调:有些活,Excel 确实比 Pandas 合适。

比如老板要一份「能自己改数、图表跟着变」的表;比如财务要按公式核对每一行;比如一份文件要在群里传阅、每个人签个字。这些场景硬用 Python 做,做出来的东西没人会用。

核心判断只有一句话:Pandas 负责「算」,Excel 负责「给人用」。具体怎么分工,这篇用代码把两边的边界走一遍。

先确认示例数据在手边:

importosimportmkdataifnotos.path.exists("data/sales.csv"):mkdata.main()print("示例数据就绪:",os.path.exists("data/sales.csv"))

这段会检查第 1 篇生成的data/目录在不在,不在就自动补一次;已经有数据时只会打印一行示例数据就绪: True,不影响后面的结果。

一、Excel 的杀手锏:公式会自己重算

Pandas 算完就是静态值,改一个数要重跑全流程;Excel 里改一个单元格,整张表的公式自动重算。这是它不可替代的地方。

下面用 openpyxl 写一个带公式的小表,再用 pandas 读回来,看看会发生什么。

importosimportpandasaspdfromopenpyxlimportWorkbook,load_workbook os.makedirs("output",exist_ok=True)path="output/公式演示.xlsx"wb=Workbook()ws=wb.active ws.title="报价"ws.append(["产品","数量","单价","金额"])rows=[("A-笔记本",3,49.9),("B-鼠标",5,29.9),("D-显示器",2,599.0)]fori,(name,qty,price)inenumerate(rows,start=2):ws.append([name,qty,price,f"=B{i}*C{i}"])# 金额写成公式ws.append(["合计",None,None,"=SUM(D2:D4)"])wb.save(path)print("已写入公式:",path)# pandas 读:公式没被 Excel 计算过,读到的是空值df_read=pd.read_excel(path,sheet_name="报价")print("pandas 直接读:")print(df_read.to_string(index=False))print("----")# data_only=True:读缓存的计算结果(没有缓存也是空)df_cached=pd.read_excel(path,sheet_name="报价",engine="openpyxl").fillna("(空)")print("把 NaN 标出来看:")print(df_cached.to_string(index=False))
已写入公式: output/公式演示.xlsx pandas 直接读: 产品 数量 单价 金额 A-笔记本 3.0 49.9 NaN B-鼠标 5.0 29.9 NaN D-显示器 2.0 599.0 NaN 合计 NaN NaN NaN ---- 把 NaN 标出来看: 产品 数量 单价 金额 A-笔记本 3.0 49.9 (空) B-鼠标 5.0 29.9 (空) D-显示器 2.0 599.0 (空) 合计 (空) (空) (空)

结论很关键:openpyxl 写进去的公式,文件里没有「计算结果」,除非用 Excel 打开过并保存。所以:

  • 你要做「给别人改的表」→ 写公式,让人用 Excel 打开;
  • 你要做「自己分析的数据」→ 别绕 Excel,pandas直接算完再写值;
  • 你要读别人表里的公式结果 → 用openpyxl.load_workbook(path, data_only=True),前提是那个文件被 Excel 保存过。
fromopenpyxlimportload_workbook path="output/公式演示.xlsx"wb=load_workbook(path,data_only=True)# 只读缓存值ws=wb["报价"]print("data_only 读到的 D 列:")forrowinws.iter_rows(min_row=2,values_only=True):print(" ",row)
data_only 读到的 D 列: ('A-笔记本', 3, 49.9, None) ('B-鼠标', 5, 29.9, None) ('D-显示器', 2, 599, None) ('合计', None, None, None)

二、透视表:两边各怎么写

「按城市和产品算销售额」这个需求,Excel 是拖字段,Pandas 是pivot_table。下面在 Excel 里写出透视结果,再用 Pandas 算一遍,对比两者。

importpandasaspd df=pd.read_csv("data/sales.csv",encoding="utf-8-sig")# Pandas 版透视表:行=城市,列=产品,值=金额求和pt=pd.pivot_table(df,index="城市",columns="产品",values="金额",aggfunc="sum",fill_value=0,margins=True,# 加一行/一列合计,对应 Excel 的「总计」margins_name="总计",).round(2)print("pandas 的 pivot_table:")print(pt.to_string())print("----")# 写到 Excel,方便对照着看pt.to_excel("output/透视表_pandas版.xlsx",sheet_name="透视")print("已导出 output/透视表_pandas版.xlsx")
pandas 的 pivot_table: 产品 A-笔记本 B-鼠标 C-键盘 D-显示器 总计 城市 上海 0.0 299.0 1246.0 0.0 1545.0 北京 49.9 0.0 178.0 0.0 227.9 广州 0.0 179.4 356.0 0.0 535.4 杭州 349.3 59.8 0.0 2396.0 2805.1 总计 399.2 538.2 1780.0 2396.0 5113.4 ---- 已导出 output/透视表_pandas版.xlsx

对照记住三句话:

  • Excel 的「行/列/值」=pivot_table(index=, columns=, values=);
  • Excel 的「值字段设置(求和/计数/平均)」=aggfunc;
  • Excel 的「总计」=margins=True。

pivot_table默认会丢掉缺失分组,要保留就加dropna=False——这就是为什么表里那个「空城市」在 pandas 版里能看到,在手工透视表里常被忽略。

三、导出「给人用」的 Excel:三个要素

既然是交付,就不能只写数据。至少做到三点:表头清楚、列宽合适、关键列有格式。

importosimportpandasaspdfromopenpyxlimportload_workbookfromopenpyxl.stylesimportAlignment,Font,PatternFillfromopenpyxl.utilsimportget_column_letter df=pd.read_csv("data/sales.csv",encoding="utf-8-sig")df["金额"]=df["金额"].fillna((df["数量"]*df["单价"]).round(2))clean=df[df["金额"]>0].drop_duplicates()os.makedirs("output",exist_ok=True)path="output/交付版_销售明细.xlsx"withpd.ExcelWriter(path,engine="openpyxl")aswriter:clean.to_excel(writer,sheet_name="明细",index=False)clean.groupby("城市",dropna=False)["金额"].sum().round(2).reset_index().to_excel(writer,sheet_name="按城市",index=False)book=load_workbook(path)forwsinbook.worksheets:forcellinws[1]:cell.fill=PatternFill("solid",fgColor="0F766E")cell.font=Font(color="FFFFFF",bold=True)cell.alignment=Alignment(horizontal="center")fori,colinenumerate(ws.iter_cols(values_only=True),start=1):width=max(len(str(v))*2forvincol)ws.column_dimensions[get_column_letter(i)].width=min(max(width+4,10),32)ws.freeze_panes="A2"# 金额列(第 7 列)加千分位ifws.title=="明细":forcellinws["G"][1:]:cell.number_format="#,##0.00"book.save(path)print("已导出:",path)print("sheet:",load_workbook(path).sheetnames)
已导出: output/交付版_销售明细.xlsx sheet: ['明细', '按城市']

格式三件套的取舍:表头填色让人一眼分清表头,列宽避免####,冻结首行让滚动时不丢字段名。这三样加上number_format,就是「看起来是正规交付物」的最低标准。

四、分工表:照着选就行

需求选 Pandas选 Excel
数据量 10 万行以上✅会卡
每周重复做同一种汇总✅ 脚本跑手工重做
需要保留公式、让人改数✅ 公式自动重算
需要图表随数据联动✅
多人传阅、要签字审批✅
要合并几十个文件✅concat手工粘贴
复杂文本清洗✅str系列公式很痛苦
一次性临时分析都行✅ 更快
交付物要「看起来正式」生成后交 Excel✅

图 1:按「数据量」和「人工协作需求」画一个象限,就能判断该用 Pandas 还是 Excel;四象限中间那条线不是硬边界,是经验分水岭。

一句话总结:Pandas 管计算和清洗,Excel 管呈现和协作。最好的形态是两者接力——Pandas 算完、格式化成 xlsx、交给 Excel 做最后的图表和分发。

常见问题

Q1:read_excel读公式列全是 NaN。

两个原因:一是文件从没用 Excel 打开保存过(没有缓存值),二是你读的是公式本身。想读缓存值:load_workbook(path, data_only=True),然后逐行取;pandas.read_excel默认读的是公式文本或空值。

Q2:用 openpyxl 打开文件报BadZipFile。

这文件不是真正的 xlsx(可能是 CSV 改了后缀,或者导出失败了)。先file看一眼,或pd.read_csv试试。

Q3:写公式时行列引用总是错位。

记住 openpyxl 是 1-based、而且表头占第 1 行:数据从第 2 行开始,f"=B{i}*C{i}"里的i要用enumerate(..., start=2)。

小结

这一篇的核心不是学新 API,而是建立边界感:Pandas 算得快、适合批量,但结果是静态的;Excel 算得慢、但公式会重算、图表会联动、人能直接改。让 Pandas 干它擅长的(清洗、汇总、批量),让 Excel 干它擅长的(呈现、协作、交互),中间用to_excel和read_excel交接。

下一篇聊一个更硬核的话题:交出数据之前,怎么用 10 个校验确保它没问题。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询