· compliance· 约 19 分钟精读

分母是谁:合规报告封面百分比的四种算法

核心要点速览 · TL;DR 概要

同一份台账,按资产算、按有测量结论的记录算、按全部记录算,会得到三个不同的百分比;空台账在两端分别印 0% 与 100%。本文按 TagWorks 与 SlingGuard 的汇总函数逐项核对分子、分母、排除规则与舍入,说明自造指标与法定记录之间的边界。

分母是谁:合规报告封面百分比的四种算法

配电间的临时办公桌上,检验员把最后一台角磨机的读数敲进平板,点下导出。报告第一页的汇总区只有几行:登记 40 台、合规 31 台、30 天内到期 3 台、逾期 2 台、终身合格率 97%。甲方项目经理在手机上看完这一页,回了一句“整体不错”。

他没有问 97% 的分母是什么。多数人不会问。百分比是一个已经被训练成“结论”的形状:看到它就默认背后有一张完整的表,每一台设备都称过一次。实际上,这一页上的 31 台与 97% 回答的不是同一个问题,甚至不是同一批对象。

31 除以 40 是 77.5%。同一页上,一个可以算成 77.5% 的计数与一个 97% 的比率相差 19.5 个百分点。这不是算错,而是两个不同层级的量被并排放在了一起:一个是资产级的状态计数,一个是记录级的结论占比。本文把这两个量拆开,再加上 SlingGuard 的两个百分比,一共四种算法,逐项核对它们的分子、分母、排除规则与空值约定。

产品留存的一份 25 台样本报告里,在役 18 台、30 天内到期 1 台、逾期 1 台,最后一行仍印着 100% 的通过率。这两个数并不矛盾:逾期是资产状态,通过率是记录结论,它们统计的对象不同。但把这两行排在一起,读者很难不把它们读成同一件事。

一、资产级:Compliant 是一个状态,不是一次检验

TagWorks 的汇总函数 ReportExporter.summary 返回十个字段。与“合格”有关的第一个是 compliant,它的定义只有一句:状态等于 ok 的资产数量。

ok 由状态机给出,优先级是:最新结论为失败,记为 failed;最新检验无法确立通过,记为 incomplete;从未检验,记为 untested;否则比较下次到期日与当前时间,已过期记为 due,30 天内到期记为 soon,其余记为 ok。

这条链意味着 compliant 同时要求两件事:最近一次检验能够确立 PASS,且距下次到期还有 30 天以上。前者看证据,后者看日历。一台今天刚通过、但 20 天后就到期的设备,状态是 soon,不计入 compliant;它没有被判不合格,只是不在“当前合规”这个格子里。

所以 31 这个数字的读法是“31 台设备此刻既通过、又不在 30 天预警窗口内”。它是一张当下的状态快照,与历史上检验了多少次无关。

二、记录级:97% 的分子要求三件事同时成立

同一份报告的最后一行是 Lifetime pass rate。它的分母不是 40 台设备,而是一组检验记录。

summary 里的筛选条件有四条:记录对应的资产仍然存在;记录日期不晚于当前时间;记录类型是 test 或 retest;记录的测量结论不是 notMeasured。满足这四条才进入分母。

分子再加一道闸门:establishesPass 必须为真。这个函数展开后要求三件事同时成立:结论不是失败;目视结果是“未发现缺陷”;电学读数全部落在阈值内(Class I 的接地连续性不大于 100 毫欧、绝缘电阻不小于 1 兆欧;Class II 只要求绝缘)。

三条里少一条,记录就留在分母里、进不了分子。这正是“终身合格率”与“电学合格率”的区别:一条读数合格但目视结果为空的老记录,会永久地把比率往下拉,而且从报告上看不出原因。

反过来,一条只勾了目视、没有填绝缘电阻的记录,测量结论是 notMeasured,会被整体排除在分母之外。它既不加分也不减分,像没有发生过。一条目视发现缺陷、但没有做电学测量的记录,同样被排除;而它的状态却会因为 evidenceState 走到失败分支,把设备标成 FAILED。同一份数据里,设备状态说“失败”,合格率的分母里却没有它。

这不是实现错误,而是两个函数服务两个目的:状态机要回答“这台设备现在能不能用”,汇总要回答“有测量结论的记录里有多少条完整通过”。问题在于,两个答案被印在了同一页,中间没有一行字说明它们统计的不是同一批对象。

三、复检次数会把比率拉向谁

记录级分母按记录计数,不按设备计数。一台设备一年检十二次,它在分母里占十二个位置;一台从未检过的设备占零个位置。

把一组具体数字摊开:40 台设备、128 条记录,其中 96 条满足“有测量结论的检验或复检”,93 条完整通过。终身合格率是 93 除以 96,96.875%,四舍五入为 97%。它不是 93 除以 128(72.7%),也不是 31 除以 40(77.5%)。

再往下拆:31 台合规设备里,如果 5 台各检了 12 次,这 5 台就贡献 60 条记录,占分母的 62.5%;其余 26 台合计 36 条。5 台设备的复检频次决定了这个比率的六成权重。一台设备检得越勤,它在分母里占的位置越多;这与设备数量无关。

这个加权方式在统计上并不错误,但它与读者的直觉相反:读者以为百分比描述的是“这批设备”,实际描述的是“这批记录”。

四、SlingGuard 的两个百分比:一个在界面,一个在纸上

SlingGuard 的汇总函数里有两条比率,分别服务两个表面。

界面上的圆环叫 Fleet Register Status,算法是“在役数加 30 天内到期数,除以总数减报废数”。空册时返回 100%。

这里有两个口径选择值得记录。第一,30 天内到期的设备被算进分子;同样的物理事实,在 TagWorks 的资产状态里是 soon,不算 compliant。第二,报废设备从分母里剔除;把一台隔离设备改成报废,分母减一,比率可能上升。这不是设备变好了,是分母变小了。

报告 PDF 印的是另一条:Entered pass outcomes,算法是通过结论的记录数除以全部检验记录数。隔离与报废结论都进分母,所以一次隔离会把比率拉低。界面上的 Fleet Register Status 不进 PDF,PDF 上的 Entered pass outcomes 也不进界面。两个表面各印一个数,读者拿在手里无从对照。

还有一项被算进了分母、却没有印在任何一行上:uninspected。汇总结构里有这个字段,报告封面没有对应的计数行,界面的六格指标卡也没有这一格。它只以“分母中的一部分”存在:未检验的吊具拉低舰队状态,读者却数不出有多少台未检验。报告上那几行计数相加小于总数,差额里就可能含着它。

用一组 20 台的台账举例:3 台报废,剩下 17 台;2 台在役、1 台 30 天内到期、4 台逾期、5 台隔离、5 台未检验。舰队状态是 3 除以 17,17.6%,印 18%。记录侧 12 条,10 条通过,通过结论占比 83%。同一批设备,界面 18%,PDF 83%。

同一份台账,四个分母资产级、测量记录级与全部记录级,各自得出不同的百分比TagWorks · 报告封面SlingGuard · 报告封面资产级设备状态31 / 40 = 77.5%封面只印 31 台,不印这个比率3 / 17 = 18%30 天内到期的一台也算进分子记录级检验证据93 / 96 = 97%封面印的就是这一行10 / 12 = 83%隔离与报废结论都进分母空台账零样本0 / 0 = 0%没有样本时印 0,读作全不合格0 / 0 = 100%没有样本时印 100,读作全合格分母变了,结论就变了,而封面不会告诉你它用的是哪一个。四个分母里,只有一个是标准要求的记录;其余三个是产品自己定义的汇总。

五、同一台设备,两个状态机

两个应用对“合格”的语法不同。TagWorks 的状态由检验证据与到期日推出;SlingGuard 的状态由记录结论、手工隔离标记与到期日推出,没有独立的“失败”态,失败通过隔离或报废表达。

物理事实TagWorks 状态SlingGuard 状态是否进各自“合格”分子
检验通过,距到期 200 天okIn Service是 / 是
检验通过,距到期 20 天soonDue Soon否 / 是
已过到期日dueOverdue否 / 否
目视发现缺陷failedQuarantined,若结论为隔离否 / 否
读数不全incomplete由录入结论决定否 / 视结论
从未检验untestedUninspected否 / 否
报废无此状态,仍留在册Scrapped不适用 / 否,且退出分母

这张表解释了一个常见的误读:把两个应用的百分比放在同一张汇报材料里比较。它们的分子包含不同的状态集合,分母也经过不同的剔除。18% 与 97% 之间没有可比性,因为它们连“总数”都不是同一个东西。

六、四舍五入:整数百分比把样本量抹掉了

两处比率都用同一个表达式收尾:先算浮点百分比,再调用 rounded(),最后转成整数。Swift 的 rounded() 默认是“四舍五入、远离零”,17.5 变 18,96.875 变 97,72.656 变 73。

整数百分比读起来干净,代价是样本量消失。1 条通过 1 条是 100%,300 条通过 300 条也是 100%;2 条通过 3 条是 67%,200 条通过 300 条也是 67%。报告上没有分母,读者无法区分这两种情况。

在小样本上,一个整数百分点的摆动可能只对应一条记录。现场一个班组、十几台设备、一个月十几条记录,是这类工具的典型规模;在这个规模上,“97%”与“100%”之间的差别,常常就是一条记录的差别。

七、空值与相反约定

没有样本时,TagWorks 的 passRatePercent 返回 0;SlingGuard 的 registerStatusRatePercent 返回 100。

同一个“没有数据”,一个印成 0%,读作“全不合格”;一个印成 100%,读作“全合格”。两种读法都不成立,但两种印法都进入了版面,因为汇总区需要一个数字。

SlingGuard 还有一条更隐蔽的路径:所有设备都报废时,总数减报废数为零,比率同样返回 100%。一本全是报废吊具的台账,会得到“舰队状态 100%”。

工程上更诚实的返回类型是可选整数:没有样本时返回空,界面印一个破折号,旁边注明“0 条记录”。这不需要新的数据结构,只需要让“没有数据”和“数据是零”在类型上分开。

八、为什么这是合规问题,而不是界面问题

AS/NZS 3760 要求的是逐台记录:设备标识、检验日期、结果、仪器与人员。LOLER 1998 要求的是彻底检验报告与记录保存。两份规范都没有定义“合格率”这个汇总指标。

合格率是产品自造的,用来帮助阅读。自造指标可以印,但必须能被复核:分子是什么、分母是什么、哪些记录被排除、时间窗从哪一天开始、口径在哪一版改过。

现在的报告把自造指标印在法定字段同一页、同一字号层级。读者没有理由区分哪个是标准要求的、哪个是产品加的。免责声明写了“不替代法定判断”,但声明在小字区,百分比在大字区;阅读顺序决定了哪个先被相信。

更现实的问题是转发。一封邮件把封面截图发出去,97% 会跟着走,分母不会。接收方看到的是一句可以被引用的话,而不是一个需要条件的统计量。

九、五条工程线

  1. 百分比后面印分数。写成“93 / 96,97%”,而不是单独一个“97%”。分母是百分比的一部分,不该在最后一步被四舍五入掉。

  2. 资产级与记录级分区。把“资产状态”与“记录结论”放进两个版块,各自标题写明统计对象;不要在同一列相邻两行放两个不同分母的比率。

  3. 空态返回空值。把汇总的比率字段改成可选整数,零样本返回空;“全部报废”与“没有设备”都落到空,而不是 100%。

  4. 口径写进报告。在汇总区下方加一行定义:分子、分母、排除规则、时间窗、口径版本。口径改动时升版本号,历史报告保留旧版本号。

  5. 汇总函数单一入口加固定样本测试。summary 已经是纯函数,输入资产、记录与当前时间,输出结构体。可以写一组固定样本:1 台设备 5 条记录、1 条旧记录无目视、1 条记录无读数、1 条未来日期,断言分母等于 3。界面与 PDF 都读同一个结构体,不要各自再算一次。

十、能力边界

  • 百分比只描述本机记录,不描述设备的真实安全状态。
  • 分母随记录增删变化;删除设备会级联删除记录,历史比率不可复现,除非导出时冻结了分子与分母。
  • 记录级比率按复检次数加权,不能当作资产平均通过率。
  • 空台账与全报废台账无法用百分比区分。
  • 整数百分比不携带样本量,小样本上的一个百分点可能只对应一条记录。
  • 状态函数读取全部记录,比率函数只读取今天及以前的记录;两者对同一条未来日期记录的处理不同。
  • 报告免责声明不能抵消封面数字的暗示;阅读顺序先于声明。
  • 没有跨设备或服务端的口径核对,两个应用之间也没有共同的指标定义。

十一、结语

百分比不是错在算得不准。它错在只印了结果,没有印问题。

一个可以被复核的比率,至少要让读者看见三件事:分子是什么、分母是什么、哪些记录被排除在外。这三件事都不需要联网,也不需要新的存储格式,只需要在排版时多留一行,在类型上把“没有数据”与“数据为零”分开。

报告是给不相信口头结论的人看的。它上面每一个数字,都应该经得起被追问一句“除以多少”。

参考资料

  • [1] Standards Australia / Standards New Zealand. AS/NZS 3760:2022 在用电气设备检验与测试. as-nzs-3760
  • [2] UK Health and Safety Executive. LOLER 1998 起重设备与吊具检验规范. loler
  • [3] US Department of Labor. OSHA 29 CFR 1910.184 吊索具标准. osha-1910-184
  • [4] Apple. Swift 浮点数的舍入行为. rounded
  • [5] Apple. UIGraphicsPDFRenderer 本地 PDF 渲染. uigraphicspdfrenderer
  • [6] ISO. ISO 3534-1 统计学词汇与符号. iso-3534-1
Share:
Back to Blog