☰
基于Python和SQLite的轻量级资源能效评估工具实践
2026/10/20 12:43:01 网站建设 项目流程

我见过不少团队在月度复盘时翻开能源台账,一张张 Excel 表里面堆满电量、水量、气量,却说不清楚哪条产线在哪个时段出了问题。真正的问题往往不是没有数据,而是缺少一套能把数据变成判断的评估体系。REA(Resource Efficiency Analysis,资源能效评估)就是我做的一个轻量工具,用来把分散的能耗数据整理成可比较、可下钻、可评分的分析结果。这篇文章里,我会完整讲讲这个工具从需求梳理、数据处理、建模到落地页面和试点踩坑的全过程,适合手里同样握着台账但想往上走一步的运营、设备管理员和刚接数据分析的同学参考。

需要先说明,这个项目没有用重型平台,也没有复杂算法,核心是几段可复用的 Python 脚本加一份 SQLite 数据库。如果你熟悉 pandas 和一点统计学常识,应该能直接照着思路搭出一版自己的 REA。

1. 从Excel台账到REA:这个评估工具到底想解决什么问题

1.1 台账复盘里的三个老大难

我在做这个工具之前,先在自己负责的模拟项目X里做了一次需求梳理。当时每个月底要把车间电耗、水耗、天然气和产量加总,再算单耗,用 Excel 透视表能出结果,但速度慢,口径也乱。更麻烦的是三个方面:

  • 第一,各部门用了不同版本的单耗公式。有的按“总能耗/总产量”,有的按“生产时段能耗/入库数量”,口径不统一导致横向对比基本没有意义。
  • 第二,发现异常的时间太晚。比如某台设备在凌晨空转,要到月底看曲线才发现,这期间的损耗早就发生了。
  • 第三,复盘会上的争论都是“感觉这个月好像耗电多了”,缺少客观的定位链条,说不清是多耗在哪一层。

REA 要解决的,就是把“月底找原因”变成“每天自动看趋势”,并且用同一套规则给所有区域和资源类型打分。目标很明确:每周生成一份能效评估报告,列出总评分、异常清单和疑似问题位置。

1.2 为什么没选现成平台和复杂仿真

市场上商用能源管理系统很多,但当时我们评估后放弃了导入平台,原因有几点:一是对接成本和每年的授权费用不划算,二是很多功能我们用不到,三是最重要的——我们想完全掌控评分的口径,不希望它是黑盒。

REA 的第一版定位非常收敛:不建仿真模型,不做设备级实时控制,只聚焦在日维度的电、水、气数据分析。这样可以避开接入成本极高的实时点位建设,用现有计量表加人工录入就能跑起来。

边界拉到这个程度,项目从启动到第一版可用的周期就压得很短。我认为这是做评估类工具一个很实际的经验:先明确“我要回答什么问题”,再决定收集什么数据。如果一开始就上全套物联网,大概率会在设备对接上耗尽精力。

1.3 REA 的总体结构

整个 REA 的脉络分四层:数据采集层、清洗存储层、评分分析层、展示通知层。

数据采集层解决“数据怎么进来”,前期允许人工上传 Excel,随后接了一路自动采集定时任务。清洗存储层把多来源的表计读数统一成一套表结构,落到 SQLite。评分分析层计算每个区域和维度的能效指标,比对基线,定位异常。展示通知层把结果渲染成网页报告,推送给相关同事。

每一层之间只通过数据表交互,上层不会直接读原始文件。这个解耦的好处是:后期某一路数据源从人工 Excel 改成自动采集,完全不影响上层模型和页面。

如果你也要做类似工具,建议别贪心,按这个四层结构去拆分,第一版先跑通最细的链路,而不是把界面做得非常复杂。

2. 数据接入与清洗:REA最关键的隐形工作量

2.1 把所有用能数据归成一张表

不少人会低估清洗的工作量。台账里的电费单、设备运行记录、产量报表各有各的时间粒度和命名方式,直接拼起来根本没法算。REA 的做法是先定义一张统一事实表的表结构,再把不同数据源往里灌。

这张统一表包含这些核心字段:

字段名说明示例
record_date数据日期2025-03-11
site_id区域/车间编码A01
energy_type资源类型electricity/gas/water
meter_point表计/工位编码M-A01-03
usage_value当日用量236.5
unit计量单位kWh/m³
cost当日费用(可选)127.4
temperature当日平均温度(辅助)18.2
output_value当日产量(若适用)805

这样设计的一个直接好处是,后边无论做评分、画趋势还是异常定位,都只需要对着一张表操作,不用到处 join 十几张原始账。数据上报逻辑也简单:每路子数据做好映射,统一插入这张事实表即可。

2.2 清洗时最容易踩的四个坑

我在清洗阶段做过不少返工,核心坑有四个:

第一是时间对齐问题。电费系统按自然日 0 点到 24 点统计,生产系统按早班 8 点到晚班 8 点统计,两者直接比对必然失真。REA 的处理是给每条记录增加时间段标签,先对齐到“是否覆盖完整 24 小时”,不完整的记录先剔除。

第二是零值。节假日停产时,用量天然为零,这个零不是异常。可如果设备处于待机状态但表计抄错成 0,那就是数据质量问题。单纯把零值当异常会误报,把零值当正常又会漏报,我的处理是叠加一个“生产状态”标志位,只有生产状态下出现零值才进入异常候选。

第三是单位混乱。有的数据源是千瓦时,有的传上来是兆瓦时;水表可能报吨也可能报立方米。如果不在接入层统一换算,后面所有指标全都白算。REA 里我强制每个接入文件带 unit 字段,并在导入函数里做了对照字典。

第四是重复统计。曾经在开发测试中把同一个日期的抄表数据导入了两次,评分直接从正常跳到了红色告警。后来我在事实表上为 record_date、site_id、energy_type、meter_point 建立了唯一索引,重复插入只保留最新一次。

处理数据时不要只写“去除异常值”这种模糊逻辑。每一条清洗规则都要对应一个可解释的业务理由,比如“生产状态下连续两小时用量为 0 判定为抄表异常”,这样后面追溯时才能说清楚。

2.3 一段可复用的清洗骨架

REA 的数据清洗模块用 Python 实现,核心逻辑是加载数据、统一列名、处理缺失值和标记异常候选。以下是一段简化后的核心代码,保留了我实际使用时的处理顺序:

import pandas as pd from datetime import datetime def clean_energy_records(df): # 1. 统一列名 df = df.rename(columns={ '日期': 'record_date', '区域': 'site_id', '类型': 'energy_type', '表计': 'meter_point', '用量': 'usage_value', '单位': 'unit' }) # 2. 日期标准化 df['record_date'] = pd.to_datetime(df['record_date']) # 3. 数值异常过滤:小于0或明显超过量程上限直接剔除 df = df[(df['usage_value'] >= 0) & (df['usage_value'] < 100000)] # 4. 单位换算:统一折算为kWh或m³ unit_map = {'kWh': 1, 'MWh': 1000, 'm3': 1, 't': 1} df['usage_std'] = df.apply( lambda row: row['usage_value'] * unit_map.get(row['unit'], 1), axis=1 ) # 5. 按唯一键去重,保留最新记录 df = df.sort_values('record_date') df.drop_duplicates( subset=['record_date', 'site_id', 'energy_type', 'meter_point'], keep='last', inplace=True ) return df[['record_date', 'site_id', 'energy_type', 'meter_point', 'usage_std']]

这些逻辑看似简单,但每一条都是来自真实数据反复打磨的产物。清洗工作占了整个 REA 大概一半的开发时间,但只有前面干净,后面的模型才不会跑偏。

3. 能效评分模型与逐级下钻:让数字自己说出问题

3.1 指标不是越多越好

很多人在设计评价体系时喜欢堆指标,好像维度越全越专业。实际结果往往是指标之间互相矛盾,评分没法解释。REA 第一版只用了三个核心指标:单位产量能耗、单位面积能耗和峰谷时段占比。

  • 单位产量能耗:总能耗除以合格产出量,这是制造业最常用的“单耗”指标。
  • 单位面积能耗:总能耗除以用能区域建筑面积,适合没有稳定产出量的辅助车间。
  • 峰谷时段占比:峰段用量在当日总用量中的比例,帮助发现那些把高耗能工序安排在电价峰段的排程问题。

三个指标在最终评分中按不同权重组合。制造车间更看重单位产量能耗,办公区域更看重单位面积能耗,动力站房则重点关注峰谷占比。REA 的设计是允许每个区域配置自己的一组权重,而不是全公司一把尺子。

3.2 评分公式和基线漂移问题

评分的计算思路并不复杂。先为每个区域和能源类型建立基线,再用当日实际值与基线比较,映射到 0 到 100 的分数区间。基线不能是固定常数,因为生产负荷会波动,天气也会影响空调用能。

我用的是滚动时间窗口基线:取过去 90 天同类型日的数据,剔除明显异常后计算中位数和离散度。这里特别注意要用中位数而不是均值,因为能耗数据右偏且偶尔有尖峰,均值会被极端值拉得很高。

具体到评分,以电量为例:

当日分数 = 100 - 100 * (当日单耗 / 基线单耗 - 1) * 灵敏度系数

灵敏度系数默认取 2,意思是当日单耗超过基线 50%,分数就降到 0 分。分数低于 60 记为预警,低于 40 记为严重告警。这个阈值不是拍脑袋,我根据试点区域的历史数据反推过:用这个阈值能覆盖绝大多数真实异常,同时把误报控制在每周两三条以内。

离散度的判断上,我推荐使用绝对中位差(MAD)而不是标准差。标准差对异常值敏感,真实能耗序列里的极端尖峰会让标准差膨胀,导致后来真正的异常反而不突出。MAD 把偏离稳健中心的情况放大,更适合这类工程数据。

3.3 异常逐级下钻的链条设计

光给一个分数还不够,REA 最有价值的部分是异常定位。我的思路是设计一个“总区域到细分表计”的下钻链条:当 A01 区域电量评分为 20 时,系统自动列出该区域下各分表计的前日用量与各自基线的偏差,并按偏差率排序。

下钻逻辑的伪代码如下:

electric_daily = load_fact_table('electricity', target_date='2025-03-11') for site in electric_daily['site_id'].unique(): site_data = electric_daily[electric_daily['site_id'] == site] score = calc_site_score(site_data) if score < 60: # 逐级下钻:site -> meter_point meter_details = site_data.groupby('meter_point')['usage_std'] \ .sum().reset_index() for _, row in meter_details.iterrows(): baseline = rolling_baseline(row['meter_point']) deviation = (row['usage_std'] - baseline) / baseline if deviation > 0.3: alert.append({ 'site': site, 'meter_point': row['meter_point'], 'deviation': round(deviation, 2) })

实际试点中,这套链条让我快速定位过一个空压机房的连续空载问题:前一天整个 A01 的大楼电耗评分掉到 35,下钻后看到动力站房分表的偏差率达到 +62%,再对比该表计的小时曲线,确认是储气罐压力达到上限后空压机没有卸载,持续带载空转了近五个小时。如果没有下钻逻辑,光看到一个区域总分偏低,排查范围会大得多。

3.4 为什么不能只看同比环比

有的团队习惯用同期对比判断异常,比如“本月比上月多了 5% 就很异常”。REA 的经验是:同比环比只能做参考,不能做判定主依据。生产排程、节假日、天气变化本身就会造成大范围波动,单纯的同比环比会把正常波动当异常,也会掩盖同比例上升背后隐藏的衰退式异常。

正确做法是同时参照三个参考系:滚动基线、周期模板(同期同星期几)、区域横向对比。比如 A02 区域自报用电量 2800 kWh,滚动基线是 2600,偏差 7.7%,这个幅度可能还在正常范围;但如果同一天产能相近的 A03 区域只有 2200,那 A02 就值得复查设备运行策略。

4. 把REA结果做成人能看懂的页面:报告与仪表板

4.1 轻量报告方案的选择

模型算完之后必须有出口。REA 的报告方案我先后试过两种:第一种是挂一套在线仪表板,功能强但对服务器的要求高;第二种是每天生成静态 HTML 报告,用脚本发到团队共享目录。最终我选了第二种作为主线,只在月底生成一份更完整的月报。

选择静态 HTML 有几个实际考虑:部署简单,一个 cron 任务就能跑完;打开速度快,不用等接口加载;也不容易被内网权限策略挡住。真正的在线看板我留着后续有更高频的查看需求时再做。对大多数分析场景,一天看一次已经足够了。

4.2 报告页面的组成和生成代码

页面的布局我按“总分概览、区域排行、异常清单、趋势图、明细表”五块来设计。顶部是一张评分总表,用颜色区分正常、预警和严重;中部放异常清单,按照偏差率排序;下部放一张近 60 天的用电趋势图,方便抓整体走向。

用 Python 生成 HTML 报告时,我不引入大型前端框架,直接拼接 HTML 字符串再保存。下面是报告生成部分的核心代码:

def render_report(score_df, alerts, trend_series): trend_div = trend_series.to_frame().plot(kind='line').get_figure() trend_div.savefig('trend_temp.png', dpi=120) rows = '' for _, row in score_df.iterrows(): color = '#c62828' if row['score'] < 40 else \ '#f9a825' if row['score'] < 60 else '#2e7d32' rows += ( '<tr>' f"<td>{row['site_id']}</td>" f"<td style='color:{color}'>{row['score']:.0f}</td>" f"<td>{row['energy_type']}</td>" f"<td>{row['usage_std']:.1f}</td>" '</tr>' ) alert_html = ''.join( f"<li>{a['site']} / {a['meter_point']} " f"偏差 {a['deviation'] * 100:.0f}%</li>" for a in alerts ) html = f""" <html><head><meta charset='utf-8'> <style>body{{font-family:'Microsoft YaHei',sans-serif;margin:24px}} table{{border-collapse:collapse;width:100%}} td,th{{border:1px solid #ddd;padding:8px}}</style></head> <body> <h2>REA 日报 {datetime.today():%Y-%m-%d}</h2> <h3>区域评分</h3><table>{rows}</table> <h3>异常清单</h3><ul>{alert_html}</ul> <h3>近60天趋势</h3><img src='trend_temp.png' style='max-width:640px'/> </body></html> """ with open('rea_report.html', 'w', encoding='utf-8') as f: f.write(html)

样式不需要太精致,重点是信息层级清楚。实际操作中,我在表格里加入了按评分排序和按区域名称排序两种视图,并用参数控制,避免每次想换顺序都要改代码。

4.3 自动产出与通知

静态报告生成后,我用系统计划任务每天早上 8 点跑一次完整链路:拉取前一日数据、清洗、计算评分、渲染 HTML。跑完以后把报告文件复制到共享目录,同时给关注列表发一条只有评分和异常条数的摘要。

注意一点:自动任务一定要加日志输出和失败重试。REA 早期发生过一次源头表数据凌晨才到,任务 8 点跑的时候数据不完整,产出的报告评分偏低。后来我在任务里加了“数据完整性检查”,统计当日应到达的表计数量,少于预期时直接跳过计算,并发出告警邮件而不是报告。

5. 试点里踩过的坑、边界条件与还能怎么扩展

5.1 试点期最头疼的三种偏差

第一版 REA 在一个模拟车间试运行四周,效果达到了预期,但中间的偏差也很有代表性。

第一种是节假日误报。放假前后一天的用能量大起大落,90 天基线没有及时切换节日模板,导致大量假异常。后来我在基线的计算中加入了一个节假日标志,只有同类型的日期才参与比较。这个改动并不复杂,却把误报数量降了一大截。

第二种是产量口径不一致。同一车间某些天用“入库数”当产量,某些天用“生产计划数”当产量,单耗指标随之失真。最终我要求所有接入REA的产量数据必须统一为“合格品入库数”,并且在字段校验里做了范围合理性检查。

第三种是用电曲线形态相似但总量异常。比如区域整体电量没超基线,但夜间 2 点到 5 点的负荷明显高于历史同期。这类异常用日维度指标根本看不出来。REA 的应对是增加了一个小时级扫描任务,专门分析低谷时段的平均功率,一旦超过基线窗口的 1.4 倍就提示夜间异常运转。

5.2 模型边界:什么时候 REA 会失效

我需要坦诚地讲,REA 不是万能的。它更适合生产节奏相对稳定的连续生产场景,如果车间按订单频繁启停,每天产量差异十倍,那所有日维度指标都会失真。

遇到这种场景,我的建议是把指标从“单耗”改成“待机能耗占比”和“启停频次分析”。方向不再是判断一天省不省,而是判断每次生产准备和收尾的待机时间有没有压缩空间。

REA 另一个局限是没有把天气变量做校正。制冷和空压系统用量与温度高度耦合,单纯看历史同期会误伤。后续我计划引入平均温度和湿度作为协变量,用回归残差替代原始偏差,这样基线会更贴合真实用能需求。

5.3 扩展方向:从评估到预测

评估只能回答现状,不能回答未来。REA 的下一阶段我想做两件事。

第一件事是能耗预测。用最近 60 天的数据和生产计划预测明天用量,把预测值和实际值放一起展示,偏差大的直接升级为异常。这比单纯历史基线更直观,也能提早发现计量仪表本身的问题。

第二件事是多区域横向对标。当接入的区域数量变多后,评分逻辑和数据量都会趋于稳定,我可以把同行业、同类型区域的能效分布画出来,让每个区域都能看到自己在整体分布中的位置。这会比单区域评分有意思得多,也会暴露一些生产管理层面的隐性浪费。

5.4 给想自己搭一套REA的朋友几句实话

如果你也想做类似的评估系统,我的建议是先找三个以内的区域试点,把数据源理清楚再谈建模。数据都没拉全的时候,任何好看的评分都只是自我安慰。

另外,评分分值不用追求绝对精确,能区分出正常和异常就够。REA 最大的价值是它把模糊的“感觉这个月用电多”变成了具体的“动力站房电量偏差 62%”。我们真正需要的不是复杂算法,而是一条可靠的问题定位链路。

我自己的体会是,评估工具做得越轻、越能说明白每条规则的理由,就越容易坚持用下去。REA 现在每周还在自动跑,已经帮同事发现过压缩机空载、水表故障和排程峰段错置三类问题。工具本身不复杂,复杂的是肯不肯把口径统一、基础清洗和规则解释这些脏活累活干完。如果你手里也有一堆用能台账,从一张统一事实表开始,大概率能走出和 REA 一样的路。

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

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

立即咨询