表格里那行合计,让求和结果多了 95%

发布时间:2026/10/25 3:23:13
表格里那行合计,让求和结果多了 95%

版权与内容来源声明
本文为原创整理。文中涉及官方文档、开源仓库、论文与公开报道的内容,均在附表 A 中标注来源;引用官方原文保持原样,不作改写。文中命令、版本号与界面截图以本文成文时的实测/核验结果为准,标注「待验证」的部分请以你本地环境实际输出为判断依据。本文不推荐任何不合规的软件获取方式,也不对任何收益结果作承诺。转载请注明出处。

一、上线当天,那行合计把结果抬高了 95%

九点十分,财务把两个数并排贴进群里:系统报本期收入 6,787 万,账面 3,478 万。差 3,309 万,正好是账面数的 95%。上线第一版跑出来的就是这个数,没人预感到它会错,因为 6,787 万这个量级和上季度对得上,只是「多了一点」。

先把 95% 的口径钉死,否则标题就成了标题党。这张表是我自己构造的模拟导出件,用于复现取数逻辑,不是真实业务数据,但把真实导出里最常见的四种脏格式都放齐了:合计行、万元与元混排、千分位逗号、带百分号的占比列,外加合并单元格导出后留下的空单元格。真值 34,781,280 元是我逐行手算的,95.14% 是「正则抓全表数字直接求和」这一种取数方式相对真值的净偏差。脚本和原始输出都在work/exp1024/,下面逐字贴出。

这个偏差值得单独写一篇,是因为它不属于「大了一个数量级」那种一眼假的错。它大得刚好够让数字看起来仍然像个正常的季度收入,落在一个谁都不会立刻质疑的区间里。真正难查的错误从来不是离谱的那种。

先把这张表和真值钉死

表格是这样导出的:区域列只有每个区域的第一行有名字,后面两行是空的——那本来是合并单元格,导出成文本以后合并信息就没了;收入列 8 行明细里 6 行用元、2 行用万元;金额带千分位;客户数是整数;占比列带百分号;最后一行是合计。

区域渠道收入客户数转化比例
华东直营8,932,150 元1,20412.4%
分销5,120,880 元9869.8%
线上1,680,000 元2,41015.2%
华南直营6,340,200 元1,10211.6%
分销3,980,050 元8648.9%
华北直营128.40 万元1,35013.1%
分销42.86 万元70210.4%
线上7,015,400 元1,88014.8%
合计—34,781,280 元10,498—

手算真值只做一件事:每行把万元换成元,只算明细行的收入列。六个元行加起来 33,068,680;128.40 万元是 1,284,000;42.86 万元是 428,600。三项相加 34,781,280,与表里那行合计一致——这说明合计行本身没算错,错的是后面怎么用它。

把 95% 拆到行上,最大的一笔就是那行合计

先看每一行分别给「抓全表数字求和」贡献了多少误差。误差的定义很简单:这段代码在这一行里抓到的所有数字之和,减掉这一行对真值的真实贡献。

🧪 实测环境:Python 3.13.12 / macOS 27.0.1 / 仅用标准库(re),无第三方依赖

# -*- coding: utf-8 -*-"""逐行误差归因:方法①(正则抓全表数字求和)里,每一行给结果贡献了多少误差。 误差 = 该方法在这一行计入的金额 − 这一行对真值的真实贡献。 本文构造的测试表,用于复现取数逻辑,不是真实业务数据。 """importre RAW="""| 区域 | 渠道 | 收入 | 客户数 | 转化比例 | | 华东 | 直营 | 8,932,150 元 | 1,204 | 12.4% | | | 分销 | 5,120,880 元 | 986 | 9.8% | | | 线上 | 1,680,000 元 | 2,410 | 15.2% | | 华南 | 直营 | 6,340,200 元 | 1,102 | 11.6% | | | 分销 | 3,980,050 元 | 864 | 8.9% | | 华北 | 直营 | 128.40 万元 | 1,350 | 13.1% | | | 分销 | 42.86 万元 | 702 | 10.4% | | | 线上 | 7,015,400 元 | 1,880 | 14.8% | | 合计 | — | 34,781,280 元 | 10,498 | — |"""NUM=re.compile(r"\d[\d,]*(?:\.\d+)?")defcells(line):return[c.strip()forcinline.strip().strip("|").split("|")]defrow_sum(line):returnsum(float(x.replace(",",""))forxinNUM.findall(line))defto_yuan(s):m=NUM.search(s)v=float(m.group().replace(",",""))ifmelse0.0returnv*10000if"万元"inselsev LINES=[lforlinRAW.splitlines()ifl.strip()][1:]TRUTH=sum(to_yuan(cells(l)[2])forlinLINESif"合计"notincells(l)[0])got_total,truth_total,buckets=0.0,0.0,{"合计行":0.0,"万元行":0.0,"其余明细":0.0}rows=[]fori,lineinenumerate(LINES,1):c=cells(line)got=row_sum(line)truth=to_yuan(c[2])if"合计"notinc[0]else0.0err=got-truth key="合计行"if"合计"inc[0]else("万元行"if"万元"inc[2]else"其余明细")buckets[key]+=err got_total+=got truth_total+=truth label="{}/{}".format(c[0]or"(空)",c[1])rows.append((str(i),label,got,truth,err))defw(s):returnsum(2iford(ch)>0x1100else1forchins)defpad(s,n):returns+" "*max(0,n-w(s))print(pad("行",4)+pad("区域/渠道",16)+pad("①计入",16)+pad("真值",16)+pad("误差",16)+"折算百分点")print("-"*84)forno,label,got,truth,errinrows:print(pad(no,4)+pad(label,16)+pad("{:,.2f}".format(got),16)+pad("{:,.2f}".format(truth),16)+pad("{:+,.2f}".format(err),16)+"{:+.2f}".format(err/TRUTH*100))print("-"*84)print(pad("—",4)+pad("方法① 合计",16)+pad("{:,.2f}".format(got_total),16)+pad("{:,.2f}".format(truth_total),16)+"{:+,.2f}".format(got_total-truth_total))print()print("按来源分组:")fork,vinbuckets.items():print(" "+pad(k,14)+pad("{:+,.2f} 元".format(v),22)+"{:+.2f} 个百分点".format(v/TRUTH*100))
行 区域/渠道 ①计入 真值 误差 折算百分点 ------------------------------------------------------------------------------------ 1 华东/直营 8,933,366.40 8,932,150.00 +1,216.40 +0.00 2 (空)/分销 5,121,875.80 5,120,880.00 +995.80 +0.00 3 (空)/线上 1,682,425.20 1,680,000.00 +2,425.20 +0.01 4 华南/直营 6,341,313.60 6,340,200.00 +1,113.60 +0.00 5 (空)/分销 3,980,922.90 3,980,050.00 +872.90 +0.00 6 华北/直营 1,491.50 1,284,000.00 -1,282,508.50 -3.69 7 (空)/分销 755.26 428,600.00 -427,844.74 -1.23 8 (空)/线上 7,017,294.80 7,015,400.00 +1,894.80 +0.01 9 合计/— 34,791,778.00 0.00 +34,791,778.00 +100.03 ------------------------------------------------------------------------------------ — 方法① 合计 67,871,223.46 34,781,280.00 +33,089,943.46 按来源分组: 合计行 +34,791,778.00 元 +100.03 个百分点 万元行 -1,710,353.24 元 -4.92 个百分点 其余明细 +8,518.70 元 +0.02 个百分点

第 9 行那一笔是 3,479 万,比另外八行加起来都大。它并不神秘:那行合计被当成了第九条明细,于是整张表被加了两遍。剩下两个方向——两个万元行因为「128.40」被当成 128.40 元而少了 171 万,六个元行因为顺带把客户数和占比也加进来而多了 8,518 元。

一句判断:要找「谁把数字搞大的」,不用逐个单元格看,先看合计行那一笔的绝对值是不是和真值同量级。同量级就说明表格被加了两次。

误差互相抵消,才是它能活过上线评审的原因

如果只有一个方向的错,这事反而好办。多出来的正好是一整份合计,任何人扫一眼都会问「是不是加了两遍」。真正让它在评审里过关的,是 +100.03 和 −4.92 抵了一下,净剩 95.14——一个「像是口径差异」的数。

这也是我后来定下的第一条判断:两个方向相反的取数错误同时存在时,你不会得到「错得更明显」,只会得到「错得更像真的」。所以「偏差不大」不能当成「逻辑没问题」的证据,偏差小的组合恰恰是最难拆的。

同一个问题在公开评测里也一直是短板。金融财报的混合表格问答基准 TAT-QA 里,最好的模型拿到 58.0 F1分数,人类专家是 90.8(见附表 A 第 1、2 条);FinQA 的摘要写得更直接——预训练模型在金融表格的多步数值推理上「fall far short of expert humans」(第 3 条)。差距不在「会不会加」,而在「该加哪些单元格」;这一步的判断,模型和我那版脚本犯的是同一类错。

后面四章是同一张表的四道闸门,顺序不能换。每道闸门都建立在前一道的产物上:行定性决定哪些行算明细,单位归一作用在明细上,补空值作用在明细的维度列上,列角色决定哪几列能进求和。很多人会说「加个校验就行」,但校验解决不了这类问题——校验的对象还没定出来。

二、第一道闸门:先给每一行定性,再谈求和

求和之前必须先知道每一行是明细还是汇总,否则合计行会变成第九条明细。

四道闸门的分工先摊开。下面这张表里「单独漏掉它」那一列,是把另外三道都装好、只留一个缺口跑同一张表得到的偏差——四个数由work/exp1024/p04_ablate.py打印,与本篇两段脚本用的是同一张表、同一套换算。

闸门拦什么单独漏掉它(本表偏差)落地动作
1 行定性合计行、小计行、其中行被当明细+100.00%(69,562,560 元)每行打row_role,汇总行只进对账、不进求和
2 单位归一万元与元在同一列里直接相加−4.92%(33,068,851.26 元)单位抽成独立字段,先乘因子再相加
3 空值补齐合并单元格留下的空区域让整行消失−52.40%(16,556,350 元)只对维度列向下填,度量列禁止填
4 列角色客户数、占比被当成金额+0.03%(34,791,874.20 元)每列声明角色,求和白名单只放金额与数量

四道一起漏是 95.14%,不是把四个数相加——第 1 行和第 2 行方向相反,抵掉了一部分。标题里那个 95% 就是四道全漏的净偏差。

合计行不是数据,是校验器

把汇总行从「参与求和」改成「参与对账」,是第一道闸门要建立的唯一习惯。它的正确用法是:明细行求和得到 X,合计行给出 Y,检查 X 是否等于 Y。相等才继续往下走;不相等就停下来问导出方,问题在口径不在代码。

识别汇总行不能只靠匹配「合计」两个字。真实导出里它可能写成「合 计」(中间有空格)、「总计」、「小计」、「其中:线上」、「Total」,也可能因为合并单元格而只剩一个空的首列加一行加粗。可靠的判据有两个:一是位置与形态,二是算术关系——这一行的金额是否等于它上面连续若干行的和。第二个判据比第一个硬,因为它不依赖文案。

漏掉这道闸门的代价是 +100.00%,也就是 6,956 万对 3,478 万。这个数量级一旦被看到就会被修,所以它反而是四道里最安全的一个缺口。

只装这一道门,结果从 +95% 翻到 −56%

这是全文我最想让你记住的一段。同一张表、同一段抓数代码,只把合计行剔掉,结果不是变准,是变成另一个方向的错。

🧪 实测环境:Python 3.13.12 / macOS 27.0.1 / 仅用标准库(re),无第三方依赖

# -*- coding: utf-8 -*-"""三种取数逻辑对比:合计行 / 万元与元混排 / 千分位 / 百分比列 / 合并单元格留空。 本文构造的测试表,用于复现取数逻辑,不是真实业务数据。 """importre RAW="""| 区域 | 渠道 | 收入 | 客户数 | 转化比例 | | 华东 | 直营 | 8,932,150 元 | 1,204 | 12.4% | | | 分销 | 5,120,880 元 | 986 | 9.8% | | | 线上 | 1,680,000 元 | 2,410 | 15.2% | | 华南 | 直营 | 6,340,200 元 | 1,102 | 11.6% | | | 分销 | 3,980,050 元 | 864 | 8.9% | | 华北 | 直营 | 128.40 万元 | 1,350 | 13.1% | | | 分销 | 42.86 万元 | 702 | 10.4% | | | 线上 | 7,015,400 元 | 1,880 | 14.8% | | 合计 | — | 34,781,280 元 | 10,498 | — |"""defcells(line):return[c.strip()forcinline.strip().strip("|").split("|")]deffirst_num(s):m=re.search(r"\d[\d,]*(?:\.\d+)?",s)returnfloat(m.group().replace(",",""))ifmelse0.0defto_yuan(s):v=first_num(s)returnv*10000if"万元"inselsev ROWS=[cells(l)forlinRAW.splitlines()ifl.strip()]BODY=ROWS[1:]# 手算真值:逐行把万元换成元,只算明细行的收入列TRUTH=sum(to_yuan(r[2])forrinBODYif"合计"notinr[0])# ① 正则抓全表数字直接求和(带千分位处理)M1=sum(float(x.replace(",",""))forxinre.findall(r"\d[\d,]*(?:\.\d+)?",RAW))# ①-变体 正则只写 \d+,千分位被切成三段M1B=sum(float(x)forxinre.findall(r"\d+",RAW))# ② 剔除合计行后,按区域汇总(空区域被视为无归属行丢掉,单位不归一)M2=0.0forrinBODY:if"合计"inr[0]orr[0]=="":continueM2+=first_num(r[2])# ③ 单位归一 + 剔除合计行 + 区域向下补齐 + 只取收入列M3,last=0.0,""forrinBODY:if"合计"inr[0]:continuelast=r[0]orlast# 合并单元格留空:用上一行的区域名补上M3+=to_yuan(r[2])defw(s):returnsum(2iford(ch)>0x1100else1forchins)defpad(s,n):returns+" "*max(0,n-w(s))defdev(x):return(x-TRUTH)/TRUTH*100print(pad("口径",24)+pad("结果(元)",18)+"偏差")print("-"*56)forname,valin[("① 全表数字直接求和",M1),("①-变体 \\d+ 不认千分位",M1B),("② 剔合计后按区域汇总",M2),("③ 归一+剔合计+补空值",M3)]:print(pad(name,24)+pad("{:,.2f}".format(val),18)+"{:+.2f}%".format(dev(val)))print(pad("真值(逐行手算)",24)+pad("{:,.2f}".format(TRUTH),18)+"—")print("-"*56)print("③ 与真值之差:{:,.2f} 元".format(M3-TRUTH))
口径 结果(元) 偏差 -------------------------------------------------------- ① 全表数字直接求和 67,871,223.46 +95.14% ①-变体 \d+ 不认千分位 11,314.00 -99.97% ② 剔合计后按区域汇总 15,272,478.40 -56.09% ③ 归一+剔合计+补空值 34,781,280.00 +0.00% 真值(逐行手算) 34,781,280.00 — -------------------------------------------------------- ③ 与真值之差:0.00 元

方法②是「只装上第一道闸门」的样子:合计行剔掉了,但空区域的 5 行被当成无归属数据丢了,万元行也还在被当成元。结果从 +95.14% 变成 −56.09%,绝对值还变大了。这就是工程上最难查的一类改动:它修掉了一个你能看见的错,同时激活了两个你看不见的错,而改完以后数字「变了」,很容易被当成「修好了」。

方法①的那条变体单独说一句:正则写成\d+(不认千分位),8,932,150 会被切成 8、932、150 三段,全表结果掉到 11,314,偏差 −99.97%。这个错法反而安全,因为数字小到没人会信。

三、第二、第三道闸门:单位先归一,空值先补齐

单位不一致和空值这两件事,单独看都比合计行轻,但一个方向是反的,另一个会让整行数据凭空消失——装完第一道门之后,剩下的错就藏在这两处。

单位词经常藏在合并单元格里

第一道闸门筛出来的明细行,收入列里同时存在「元」和「万元」。正确的做法不是写一个「数值 + 单位」的解析器,而是先把单位词抽成一个独立字段,再用数值乘因子:元乘 1,万元乘 10000。数值和单位分开之后,「这行到底是什么单位」就能被单独检查、单独报错。

这一节真正要记的不是换算,而是单位词缺失时不许默认成「元」。真实导出里,单位往往只在合并单元格的第一行写一次,或者在表头上写一次(「金额(万元)」),下面的行全是裸数字。这时候空着的行继承的是上面那个单位,不是默认单位。默认成「元」,就是把 128.40 万元的 1,284,000 记成 128.40 元——本表里这一条吃掉了 171 万。

这个现象在公开数据集上被当成表格问答的固有难点。半结构化的表格本来就没有统一表示,单元格里可以塞「北京,中国」这种复合内容,也可以塞「200 km」这种数值加单位(见附表 A 第 4 条,ACL 2015 的 WikiTableQuestions)。对模型来说这是语义歧义,对脚本来说这是默认值选错了——两者是同一个坑的两个入口。

向下填只能用在维度列

合并单元格在导出成文本之后,就是一个空单元格。这不是某个库的缺陷,是纯文本表格表达能力的边界:GFM 规范里,数据行的单元格数量少于表头时,「empty cells are inserted」,也就是说合并信息根本无处可放,只能变成空格。

不补这 5 个空区域会怎样:16,556,350 元,偏差 −52.40%,因为按区域汇总时它们找不到归属,被整行丢掉。补的方法在 pandas 里叫ffill,官方定义是「用上一个有效观测向前填充」(见附表 A 第 6 条),还带一个limit参数限制连续填充的个数。

但这条规则有个必须写死的限制:ffill只对维度列做,不对度量列做。对金额列向下填,会把上一行的钱复制到下一行,造出一笔不存在的收入——比丢行更难发现,因为总额看起来更「完整」。合并单元格在业务上只表达「这几行属于同一个区域」,从不表达「这几行的金额一样」。

还有一个边界值得单独校验:如果某个区域的合并块被分页截断,块首那行可能是空的,ffill会把上一个区域的名称填给它。所以补完之后要加一条检查——每组的首行必须是一个新的区域名,出现空首行就报错,而不是默默填上。

四、第四道闸门:只让金额列进求和

一张表里能相加的列通常只有一列,其余列都是「看着像数字」的东西。

本表的客户数列和占比列给结果贡献了 10,594.20 元,偏差 +0.03%,是四道闸门里最小的一笔。这个数按行拆开是两块:六个元行 8,518.70 元,两个万元行 2,075.50 元——第一章那张逐行归因表里只有前一块,因为后一块已经算进「万元行」那一组了。单独看它没有杀伤力,但它是唯一一类「永远不会被发现」的错误:每行多出 1,216 元这种量级,被当成四舍五入放过,行数一多就变成一条恒定的系统性偏移。合计行那 3,479 万会被财务抓出来,这 10,594 元不会——它只会让你每次对账都差一点点,永远差一点点。

五类列角色,只有两类可以相加

给每一列声明角色,比判断「这个单元格是不是数字」可靠得多。我按取数需要把列分成五类:维度(区域、渠道、月份)、金额(收入、成本)、数量(客户数、订单数)、占比(同比、转化比例、毛利率)、编号(合同号、SKU)。求和的白名单只放金额和数量。

占比列的处理要单独说:它不能相加,只能加权。把 8 个渠道的转化比例直接加起来得到 96.2%,这个数没有任何含义;要还原整体的转化比例,分子分母都得回到金额或数量上去算。本表的 96.2 就是「直接相加」的产物,它进了总额,而它本来连加都不该加。

判别的依据也可以用列内形态兜底:整列都带百分号、整列都是长度固定的短整数、整列都是字母数字混合,这三种基本可以排除在求和之外。形态只是兜底,列头才是主判据——因为形态会被一行异常值破坏,而列头是导出方给的契约。

千分位是最好发现的那一类,也是最容易漏的

千分位属于「处理对了没有收益,处理错了立刻爆炸」的那一类。本表里正则写\d+,结果掉到 11,314 元,偏差 −99.97%,任何人看一眼都会退回重写。所以它反而是四类脏格式里最不需要担心的。

官方库的做法是把千分位当成一个声明式的参数,而不是事后replace:pandas 的read_csv有一个thousands参数,定义就是「数值中充当千分位分隔符的字符」(见附表 A 第 7 条)。声明式的好处是它能和类型推断一起生效,不会出现「先替换再转数字、结果把负号或小数点也吃掉」的连锁问题。

顺带说一个同源的坑:这个坑在多语言环境里更常见——有些区域用逗号做小数点、用句点做千分位。如果你的表来自多个区域,thousands和decimal必须成对声明,只声明一个会让数值静默错位。

取数脚本与复核用例:本篇两个脚本、单因素消融脚本,以及这张测试表的四道闸门检查用例,我放在资料包里,扫码即可获取:

五、这套闸门的最小基建,和什么时候该停手

四道闸门不需要框架,需要的是四样很小的东西:一张列角色表、一次行定性、一份单位字典、一条对账断言。

列角色表就是每列属于五类里的哪一类,写在读表代码的最前面,别散落在业务逻辑里。行定性就是给每行打一个row_role(明细 / 小计 / 合计),汇总行只进对账管道。单位字典很小,元 1、万元 10000、亿元 100000000,缺一个词就抛错,不猜。对账断言是一行判断:明细和与合计行的差,必须小于容差。

容差不是随手写的 1。它由合计行的精度决定:合计写成「34,781,280 元」时容差可以是 1 元;写成「3,478.13 万元」(两位小数的万元)时,单个单元格的舍入误差上限就是 0.5 × 10000 × 0.01 = 50 元。容差取错方向,要么天天误报,要么真错也放过去。

什么时候不该走这条路

如果数据源本来就有结构化接口——数据库表、HTTP 接口、xlsx 里的原始单元格——不要走「从表格文本里抓数」这条路。这四道闸门全部是为「只能拿到一张排版过的表」准备的,有类型的字段直接取值,四道门一道都不需要。判断依据很简单:能不能拿到「某行某列的原始值」,能拿到就别抓文本。

只有输入是 PDF、截图、别人发来的合并单元格版 xlsx、网页表格时,这套闸门才有意义。而这时候闸门的顺序也不能换:先定行,再定单位,再补空值,最后选列。反过来做会出具体问题——先补齐再剔合计,合计行那个空区域会把上一区域的名称拉到自己头上,于是「按区域对账」多出一行无主数据。

最后给一张停手表,四个条件同时满足就可以收工,任何一个不满足就继续查,别急着上业务:

判据阈值不满足时怎么办
明细和与合计行的差≤ 合计行精度对应的容差(两位小数万元取 50 元)先问导出方口径,别改代码
参与求和的行数等于表格数据行数减一(减掉汇总行)回去补空值,别先怀疑数据源
单位词覆盖情况全表出现的单位词全部命中字典补字典,不许给默认值
空值的分布只出现在维度列,且已向下补齐度量列出现空值,当作缺数据上报

回到那 95%

那 95% 不是一个算术错误,是四次「这行应该也算吧」的叠加:合计行「应该也算」、万元「不用换」、空区域「跳过就行」、客户数「反正是数字」。四次判断单独看都说得过去,合起来就是一个让财务退单的数。

可执行的那一条判断是这样:如果你的取数链路上同时出现「合计行」和「金额列里有两种单位」,先把这张表的四行抄进代码再上线,别等对账;如果两者都没有、输入又来自结构化接口,那这件事不值得花时间,把精力放回业务口径上。最该停手的时候,是合计行与明细和对不上的时候——那不是代码问题,是口径问题,改代码只会把错藏得更深。

表格取数的对照清单:上面这张停手表和四道闸门的判据,我整理成了一页速查,和本篇两个脚本放在一起,扫码即可获取:

附表 A:本文引用事实与出处对照表

#事实出处(文档名 + 发布方 + 链接)本文位置
1表格与文本混合的问答里,数值推理是常态,原文为「numerical reasoning is usually required to infer the answer, such as addition, subtraction, multiplication, division, counting, comparison/sorting, and the compositions」《TAT-QA: A Question Answering Benchmark on a Hybrid of Tabular and Textual Content in Finance》,ACL-IJCNLP 2021,https://aclanthology.org/2021.acl-long.254/第一章
2同一篇摘要给出:所提模型 58.0 F1分数,人类专家 90.8 F1分数同上第一章
3金融报表上的深度问答需要复杂数值推理与「heterogeneous representations」的理解;论文称预训练模型「fall far short of expert humans」《FinQA: A Dataset of Numerical Reasoning over Financial Data》,EMNLP 2021,https://aclanthology.org/2021.emnlp-main.300/第一章
4半结构化表格的单元格可以塞复合内容(正文举例为城市加国家、数值加单位),数据集含 22,033 个问题《Compositional Semantic Parsing on Semi-Structured Tables》,ACL-IJCNLP 2015,https://aclanthology.org/P15-1142/第三章
5数据行单元格数少于表头时,「empty cells are inserted」;表头单元格数必须与分隔行一致《GitHub Flavored Markdown Spec》,GitHub,https://github.github.com/gfm/第三章
6前向填充的定义为「Fill NA/NaN values by propagating the last valid observation to next valid」,limit限制连续填充的个数《pandas.DataFrame.ffill》,pandas 3.1.0 文档,https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.ffill.html第三章
7thousands参数定义为「Character acting as the thousands separator in numerical values」《pandas.read_csv》,pandas 3.1.0 文档,https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html第四章
8本文测试表为构造数据,非真实业务数据;真值 34,781,280 元由「万元 × 10000 + 元」逐行手算,脚本与原始输出见work/exp1024/p04_sum3.py、p04_attr.py、p04_ablate.py本文实跑,2026-10-24第一、二章

写在最后:这篇用到的资料

写这篇文章时,把相关的官方文档和源码又翻了一遍,顺手也整理了几份配套的东西:

  • 大模型学习路线图:从零基础到能自己动手做 Agent,按阶段说明每一步该学什么、哪些可以先跳过
  • 《LangChain + LangGraph + MCP 智能体开发实战》视频课:7 个模块,从私有化部署、Embedding+RAG 到 MCP+Agent 全流程
  • AI 大模型知识库(在线可查):Agent Skills 从入门到落地、Claude Skills 完全指南等专题,按目录浏览即可
  • 640 套 AI 大模型行业报告 + 经典 PDF 书籍:看行业落地案例和别人怎么做的时候用得上
  • 大模型零基础到精通教学视频:跟着敲一遍,比只读文档快得多

资料是我自己整理的,放在下面这个码上,扫码即可获取:






添加时备注「AI」,优先通过。

资料按「先路线、再动手、最后查漏」的顺序整理好了,建议先看学习路线那一份,照着它挑一条适合自己当前基础的路径再往下看。