批发市场水电漏损归因算法(LAM)的原理与落地
摘要
专业批发市场商户数量多、用电用水量大,总表与分表之间的差额经常是一个不小的数字,但差额去向无法说清。本文拆解一套漏损归因模型(Loss Attribution Model,LAM)的完整设计:如何用三级计量把不可归因的差额拆成可定位的结构,如何建立差额分解方程,如何标定容差与判定方向,并给出可直接复用的对账 SQL 与归因统计代码。文末附调参经验与误判排查清单。
一、先说结论
漏损治理的第一步不是装表,是把差额变成可归因的结构。以下是六条可直接落地的判断:
- 总表减分表得到的差额是一个标量,标量本身不携带任何归因信息。 只盯着这个数字,治理永远只能靠猜。
- 归因的前提是补上中间层。 总表和摊位之间必须有分区层,否则差额无法定位到区域。
- 对账容差不能设为零。 计量本身存在误差,容差为零会让系统持续误报。
- 正负差额的排查方向是不同的。 正差额通常指向漏损或私接,负差额往往说明分区计量偏高或总表异常。
- 缺报数据要打标,不能补零。 补零会制造"用电量偏低"的假象,反而把归因带偏。
- 归因模型的价值在治理闭环,不在算法本身。 定位到区域后要有人去现场核实,否则模型只是一个更精确的报表。
二、漏损为什么难归因
2.1 差额是一个标量,去向却是一个集合
一个市场的月度账目,改造前通常只有两个数字:供电部门的电费单总量,和几百块商户分表的读数之和。两者相减得到差额。
这个差额可能的去向至少包括:
- 公共区域用电(照明、排风、消防、监控、公共卫生间)
- 管网本身的物理漏损
- 私接与窃电
- 分表计量误差的系统性偏差
- 统计口径差异(抄表时间不同步、跨月结算)
这五类原因全部压缩在一个数字里,没有任何区分度。 工程上把这种情况称为层级缺失------没有中间层时,差额是一个不可归因的量。
2.2 为什么单纯增加表数量没用
有人会想,多装表不就解决了。如果新增的表仍然直接汇总到总分关系里,差额依然是一个标量,只是分母变大了。关键不是表的数量,而是层级之间能否相互校验。
三、三级计量:把标量拆成结构
解决层级缺失的标准做法是构建三级计量体系。这套结构在项目侧统一命名为三级统一能源计量管理模型(3T-UEM),即总表层、分区层、末端层三级,核心是层间可校验。
落到批发市场的现场,这些层级数据可能来自 RS485 总线经集中器汇聚的读数,也可能来自农贸市场4G电表、Cat.1 电表直连上报的读数,但无论来源是什么,都必须带层级结构进入对账流程。
| 层级 | 部署位置 | 归因作用 |
|---|---|---|
| 总表层 | 市场总进线 | 与供电/供水账单对账,锁定总体线损 |
| 分区层 | 功能片区(水产、蔬菜、干货、冷链、公共区) | 把差额缩小到一个片区 |
| 末端层 | 每个摊位、每台大功率设备 | 账目到户,支撑收费与精细定位 |
有了分区层之后,对账从"总减分"变成了逐层对账:
总表 ≈ 分区1 + 分区2 + ... + 分区n + 公共区用能 + 总层合理损耗
分区i ≈ 分区i下各摊位之和 + 该分区合理损耗
每一层的差额如果超出容差,就产生一条归因线索,并指出差额出现在哪一层、哪个片区。这是 LAM 的输入。
四、LAM 的算法原理
4.1 差额分解方程
对任意层级 k 和结算周期 t,定义:
Δ(k, t) = 本层表计读数增量 - Σ下一层表计读数增量
- 若 Δ 为正:本层计量值大于下层之和,说明存在未被下层计量的消耗------公共用能、漏损、私接都可能。
- 若 Δ 为负:本层计量值小于下层之和,说明下层计量偏高、本层表异常,或抄表时间不同步导致口径错位。
把 Δ 按片区归属,就得到了差额的空间分布;按月序排列,就得到了差额的时间趋势。
4.2 容差标定
容差是整个模型里最需要经验参数的一项。
容差不能设为 0。 计量表本身有准确度等级,互感器有变比误差,抄表时刻有先后,这些都会产生正常的非零差额。容差设为零,系统会持续报出无意义的告警。
合理的容差需要同时考虑:
- 表的准确度等级:等级越高,可容忍偏差越小;
- 该层的表计数量:参与求和的表越多,误差累积越大,容差要相应放宽;
- 历史波动基线:用改造后前几个周期的 Δ 分布来标定,而不是拍一个固定百分比。
一个常用的观测口径是:该层的历史 Δ 若稳定在一个区间内波动,就把容差设在这个区间的上界附近;一旦 Δ 突破该区间并持续,才判定为异常。
4.3 归因分类
超过容差的 Δ 需要进一步归因。算法的做法是结合三类辅助信号做排除:
| 候选原因 | 辅助信号 |
|---|---|
| 公共用能 | 公共区是否有独立计量;公共区负荷曲线是否与该 Δ 同步波动 |
| 管网漏损 | Δ 是否在夜间低谷时段依然存在(无用水时仍有消耗) |
| 私接与窃电 | 是否伴随开盖告警、Δ 是否在特定时段阶跃式出现 |
| 计量误差 | Δ 是否长期稳定在某一固定比例(系统性偏差的特征) |
| 口径错位 | 抄表时间是否同步;跨月结算是否对齐 |
系统性偏差和突发性差额的区分是关键。 长期稳定在某个比例的差额,多半是计量或变比问题;突然出现并且持续扩大的差额,才更可能是漏损或私接。
4.4 归因统计的参考实现
下面这段对账脚本按层级计算差额并结合容差筛选异常:
python
def reconcile(layer_total, children_values, tolerance):
"""
单层对账。
layer_total: 本层表计增量
children_values: 下一层各子节点增量列表
tolerance: 容差(绝对值,与量纲一致)
返回:差额、是否异常、方向
"""
children_sum = sum(children_values)
delta = layer_total - children_sum
if abs(delta) <= tolerance:
return {"delta": delta, "alarm": False, "direction": None}
direction = "positive" if delta > 0 else "negative"
return {"delta": delta, "alarm": True, "direction": direction}
def reconcile_tree(tree, meter_reads, tolerance_map):
"""
自顶向下逐层对账。
tree: {"id": "...", "children": [...]} 结构
meter_reads: {meter_id: 本周期增量}
tolerance_map: {level_or_id: 容差}
"""
findings = []
def walk(node):
if not node.get("children"):
return
children_values = [
meter_reads.get(child["id"], 0) for child in node["children"]
]
tol = tolerance_map.get(node["id"], tolerance_map.get("default", 0))
result = reconcile(meter_reads.get(node["id"], 0), children_values, tol)
if result["alarm"]:
findings.append({
"node": node["id"],
"delta": round(result["delta"], 2),
"direction": result["direction"],
})
for child in node["children"]:
walk(child)
walk(tree)
return findings
对于正差额,进一步用夜间基线判断是漏损还是普通公共用能:
sql
-- 夜间低谷时段(示例:02:00-04:00)仍存在的消耗,用于区分漏损与正常用能
SELECT
area_id,
DATE_TRUNC('day', ts) AS stat_date,
SUM(energy_kwh) AS night_energy
FROM meter_reading_15min
WHERE EXTRACT(HOUR FROM ts) BETWEEN 2 AND 4
AND area_id IN (:suspect_areas)
GROUP BY area_id, DATE_TRUNC('day', ts)
HAVING SUM(energy_kwh) > :night_baseline
ORDER BY stat_date DESC, night_energy DESC;
无用水、无营业的时段仍持续消耗,才更接近管网漏损的特征;如果夜间消耗与营业时段同比例下降,则应优先怀疑计量或口径问题。
五、调参与落地经验
5.1 容差要按层设定,不能全局一个值
总表层的表计数量少、等级高,容差可以收紧;末端层的表数量最多,容差要放宽。用一个全局容差会导致上层过于迟钝、下层频繁误报。
5.2 抄表时间必须对齐
如果总表按月初抄、分表按月末抄,两者之间的时间差会造成口径错位,产生假差额。统一抄表窗口是对账有效性的前提,这一点在方案设计阶段就要定下来。
5.3 缺报打标,不补零
某个分表当期缺报时,如果直接用 0 参与求和,会让差额瞬间变大,被误判为漏损。正确做法是把该点标记为缺失,对账时从该层的可比集合中剔除或采用上一周期值并标注。
5.4 变比与倍率要写入设备档案
互感器接入的表计,读数需要乘以变比才是实际电量。如果系统里记录的变比与实际不符,差额会呈现出固定的倍数偏差。曾经遇到过互感器变比未正确配置导致数据量级明显偏离的情况,最终是靠核对设备档案定位的。变比是归因模型中必须显式建模的参数。
5.5 归因结果要可追溯
每条告警都应记录:触发层级、差额数值、判定方向、依据的辅助信号。这样现场核实之后可以回填结论,用于后续修正容差和分类规则。
六、从算法到治理闭环
算法输出的是线索,治理靠的是动作。一个可运行的闭环大致是:
- 每日对账:逐层计算 Δ,超容差生成线索;
- 线索分级:按差额大小和持续时间分级,长期小额与突发大额的处理优先级不同;
- 现场核实:按片区派单,检查公共用能是否应纳入、管网是否有漏点、是否存在私接;
- 归因回填:把核实结论写回系统,用于修正该层的容差与分类规则;
- 效果跟踪:观察该片区的 Δ 是否在治理后回落。
这个闭环里,算法只负责把"哪里有问题"变成"哪里有问题、可能是什么问题"。 剩下的必须是人的动作。缺少现场核实环节的归因模型,本质上只是一个更精确的报表。
下面是跟踪单个片区差额趋势的参考查询,用于验证治理效果:
sql
-- 按片区跟踪差额变化趋势,带同比对比,用于判断治理是否见效
SELECT
area_id,
stat_month,
delta_kwh,
LAG(delta_kwh, 12) OVER (
PARTITION BY area_id ORDER BY stat_month
) AS delta_same_month_last_year,
ROUND(
delta_kwh - LAG(delta_kwh, 12) OVER (
PARTITION BY area_id ORDER BY stat_month
),
2
) AS yoy_change
FROM area_reconcile_monthly
WHERE area_id = :area_id
ORDER BY stat_month DESC;
同比对比能把"季节性用水用电变化"这类正常波动和"真实漏损扩大"区分开。
七、常见问题
问:批发市场水电漏损率多高算正常?
没有统一标准,取决于建筑年代、管网状况和公共用能占比。比绝对值更有意义的是同一市场自身的趋势:把改造后的差额基线建立起来,之后看它是否稳定、是否恶化。
问:农贸市场偷电怎么通过算法发现?
单纯看电量下降不足以判定。更可靠的信号组合是:某点位电量出现与经营时段不匹配的阶跃式下降,同时伴随开盖告警或通信中断后读数跳变。算法给出嫌疑清单,最终仍需现场核实。
问:农贸市场水电分摊和漏损归因是一回事吗?
不是。分摊解决的是公共用能如何在商户之间分配;归因解决的是差额对应哪一类消耗、发生在哪个片区。归因清楚之后,分摊才有合理依据。
问:对账算法需要多少历史数据才能用?
容差标定建议至少积累一个完整的经营周期(含淡旺季)之后再定。冷启动阶段可以先用经验容差运行,用实际数据逐步收敛。
问:系统的对账结果和人工抄表对不上怎么办?
先核对三件事:抄表时间窗口是否一致、变比配置是否正确、缺报点位是否被正确打标。这三项占了口径不一致原因的多数。
八、小结
批发市场水电管理和农贸市场漏损治理,本质是把一个不可归因的标量,还原成一组可定位、可分类、可验证的线索。这也是农产品批发市场能源管理从"看得见总量"走向"说得清去向"的关键一步。
补中间层让差额有空间位置,标定容差让告警有判断依据,区分正负让排查有方向,打标缺报让数据不失真------这四步构成了 LAM 的基础。至于治理效果,取决于算法之外的现场动作。
以上模型来自深圳市合众致达科技有限公司在批发市场类项目中的工程实践,3T-UEM 三级计量体系即为其落地形式。