ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

电力窃漏电识别实战:从数据清洗到模型评估的完整链路

电力窃漏电识别实战:从数据清洗到模型评估的完整链路 简介这是一份面向具备一定Python基础读者的数据挖掘实战资源以电力窃漏电用户自动识别为案例完整演示从数据预处理、特征工程到模型训练与评估的流程适合希望将机器学习落地到业务场景的初学者。压缩包内共13个文件体积仅23KB包含4个Python脚本、3个Excel数据表以及npy、pkl和model等模型持久化文件代码与数据配套齐全。资源中提供拉格朗日插值处理缺失值、决策树与LM模型构建、混淆矩阵可视化等关键实现均可直接对照学习也可以替换数据迁移到其他分类任务。目前已有443人学习或下载说明其内容具有一定的实用参考价值。通过跟随完整案例学习能够理解电力窃漏电识别中的特征构造思路和模型调优技巧快速建立自己的数据挖掘项目框架。1. python数据挖掘机器学习实战先搞清楚这份电力窃漏电识别资源里到底有什么做数据挖掘的人应该都见过那个经典案例——电力窃漏电用户自动识别。它出现在《Python数据分析与挖掘实战》第6章也是很多高校数据挖掘课的大作业原型。我这次拆的这份资源就是把该案例完整落地的一套代码和数据集三个核心Python脚本、原始数据表和中间结果文件覆盖从缺失值处理到模型训练再到结果评估的完整链路。别看文件不多里头的坑不少尤其是missing_data.xls这个故意造出来的脏数据不处理直接建模模型效果会惨到怀疑人生。适合正在啃数据挖掘、机器学习想照着真实业务数据走一遍完整流程的人也适合准备期末项目但不想从零搓代码的同学。2. 缺失值处理拉格朗日插值为什么能救回电量曲线2.1 先看清数据长什么样解压后你会看到data model.xls和missing_data.xls两个Excel文件前者是构建好的专家样本后者是被人为挖掉一部分数据的原始表。missing_data.xls里的每一列代表一个监测点在不同时刻的用电量行是按时间排列的采集点。实际电力数据在传输过程中经常丢包采集终端离线、信道干扰都会造成数据缺失。如果直接把缺失行删掉时间序列的连续性就被打断了——窃漏电分析要看的是用电趋势的下降幅度中间少几个点斜率计算就偏了。用均值填充又会把曲线抹平丢失波动信息。所以这个案例选择了拉格朗日插值利用缺失点前后若干已知点的数值构造一个多项式来逼近缺失位置的合理值。2.2 拉格朗日插值的原理和边界拉格朗日插值的核心思想很简单对给定的n1个点构造一个不超过n次的多项式让这个多项式恰好穿过这些点。公式长这样# 拉格朗日插值核心实现对应6-1_Lagrange_interpolation.py import pandas as pd from scipy.interpolate import lagrange def ployinterp_column(s, n, k5): # s: 某列数据序列, n: 缺失位置下标, k: 前后各取k个点 # 拼接缺失位置前k个点和后k个点的下标 idx list(range(n - k, n)) list(range(n 1, n 1 k)) y s[idx] # 去掉取点范围内的空值避免参与拟合的点本身是NaN y y[y.notnull()] # 用scipy的lagrange构造插值函数再代入缺失位置n求值 return lagrange(y.index, list(y))(n)这里lagrange(y.index, list(y))返回一个多项式函数直接调用它传入缺失位置的下标n得到的就是插值结果。参数k5表示每个缺失点取前后各5个点参与拟合k越大参与拟合的点越多曲线越平滑但计算量也越大。注意一个关键边界如果缺失位置太靠前或太靠后比如n2时n-k会变成-3Pandas的列表切片会从尾部取数取到的根本不是缺失点前面的数据。这在业务上没有任何意义甚至会把后面的数据硬塞进来。常见做法是把越界的下标过滤掉或者限制只处理中间区域的数据。2.3 完整的缺失值处理脚本主脚本6-1_Lagrange_interpolation.py的逻辑是把整张表逐列逐行扫描发现NaN就用插值补上最后写回新的Excelimport pandas as pd from scipy.interpolate import lagrange inputfile data/missing_data.xls outputfile tmp/missing_data_processed.xls data pd.read_excel(inputfile, headerNone) def ployinterp_column(s, n, k5): # 取缺失位置前后各k个点的下标过滤掉负数下标和越界下标 idx [] for i in range(n - k, n 1 k): if i 0 and i len(s): idx.append(i) y s[idx] # 如果取到的有效点不足2个直接返回原值或0 y y[y.notnull()] if len(y) 2: return s[n] return lagrange(y.index, list(y))(n) for i in data.columns: for j in range(len(data)): if data.loc[j, i] is None or pd.isnull(data.loc[j, i]): data.loc[j, i] ployinterp_column(data[i], j) data.to_excel(outputfile, headerNone, indexFalse)逐列逐行扫描的逻辑很朴素但这里有个性能问题如果数据量大双重循环会非常慢。我一般会先用data.isnull().sum()统计每列缺失数量确认缺失率在可接受范围再决定是逐点插值还是直接丢弃该行。注意拉格朗日插值适合缺失点数量少、前后数据趋势稳定的场景。如果某个监测点连续缺失十几个采集点插值结果就是一条硬拉出来的直线这种数据建议整段标记为异常不参与后续建模。3. 专家样本构造与特征工程从原始表到能训练的模型输入3.1 专家样本到底是怎么构建出来的data model.xls不是直接从采集系统里导出的原始日志而是业务专家结合窃漏电行为模式筛选出来的标记样本。这个案例里使用的核心判别逻辑是三条用户在窃漏电后用电量通常会呈现持续下降趋势计量设备前后的线损会出现异常偏差用电信息采集终端会频繁发出告警。所以专家样本就是围绕这三个维度构造特征再打上是否窃漏电的标签。我把这套特征整理成了一张参数表方便你要在自己数据上复现时对照特征名称计算方式业务含义电量趋势下降指标连续5天用电量做线性回归取斜率并归一化用电量是否持续下滑线损指标供电量-售电量/供电量与理论线损差值线路损耗是否异常告警类指标统计终端告警次数按等级加权采集终端是否频繁报警标签1为窃漏电0为正常用户业务稽查结果的二分类标注3.2 为什么说特征工程往往比模型选择更影响结果很多初学者拿到data model.xls就直接开始调模型忽略了前面构造特征这一步。本质上模型能学到什么上限是由特征决定的——决策树再强也学不到输入里没有的信息。我验证过这个案例把告警类指标去掉决策树识别率大概掉5到8个百分点。原因在于窃漏电用户在计量异常发生前后终端告警行为有明显变化这几乎是强信号。做特征工程时不要只盯着单一特征要关注特征之间的交互比如电量趋势下降线损偏高的组合比单独看任何一个特征都更能区分窃漏电用户。脚本里已经帮你把特征和标签准备好但强烈建议你打开Excel自己看一眼数据的分布比如有没有可疑的极端值、标签比例是否失衡。这几列特征的量纲差异明显逻辑回归模型对特征尺度敏感而决策树模型则无所谓这也是后面为什么两种模型表现差异大的原因之一。3.3 训练集和测试集划分不要忘了随机种子import pandas as pd from sklearn.model_selection import train_test_split data pd.read_excel(data model.xls) # 假设前3列是特征最后一列是标签 X data.iloc[:, :3].values y data.iloc[:, 3].values # stratify保证训练集和测试集中正负样本比例一致 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42, stratifyy )test_size0.3表示拿30%的样本做测试集random_state42固定随机种子保证每次运行结果一致。stratifyy是容易被忽略的参数——如果窃漏电用户本来就少不按标签分层抽样测试集里可能一个正样本都没有模型评估结果会失真。这里X是特征矩阵y是标签向量两份数据必须按相同索引对应否则混掉会导致评估结果完全错误。4. 建模与调参决策树、逻辑回归和tree.pkl_01~04的秘密4.1 逻辑回归基线模型先跑一个最简单的结果脚本6-3_lm_model.py对应逻辑回归模型。它的优势是可解释性强每个特征的权重直接对应业务含义——比如线损指标的系数为正说明线损率越高窃漏电概率越大。# 6-3_lm_model.py 核心逻辑 from sklearn.linear_model import LogisticRegression from sklearn.metrics import confusion_matrix, roc_auc_score import joblib model LogisticRegression(C100, solverliblinear, max_iter1000) model.fit(X_train, y_train) y_pred model.predict(X_test) # 打印混淆矩阵和AUC cm confusion_matrix(y_test, y_pred) auc_score roc_auc_score(y_test, model.predict_proba(X_test)[:, 1]) print(AUC:, auc_score)solverliblinear在小数据集上表现稳定且收敛快C100表示正则化强度较弱让模型更贴合训练数据。如果你直接用默认的lbfgs也会收敛但在我复现时后者在这个数据规模下偶尔不收敛输出警告liblinear则没有这个问题。predict_proba得到的是每个样本属于正类的概率AUC指标计算必须用概率而非直接使用预测标签。这个案例里逻辑回归的识别正确率大概在78%上下。78%听起来不低但对电力稽查场景来说这意味着每排查100个用户就有22个被误判现场稽查成本很高。4.2 决策树模型为什么CART在这个场景下更占优6-2_dt_model.py实现的是CART决策树。业务上它有一个逻辑回归给不了的好处——模型可以直接输出判断规则比如“线损指标大于0.3且电量趋势下降指标小于-0.1则判定为窃漏电”。稽查人员拿到规则后可以直接去现场核查不需要理解任何数学公式。# 6-2_dt_model.py 核心逻辑 from sklearn.tree import DecisionTreeClassifier, export_graphviz model DecisionTreeClassifier( max_depth4, # 限制树深度防止过拟合 min_samples_leaf5, # 叶子节点最少样本数 criterionentropy # 用信息增益比还是基尼系数 ) model.fit(X_train, y_train) score model.score(X_test, y_test) print(决策树正确率:, score) # 导出树结构可视化规则 export_graphviz(model, out_filetree.dot, feature_names[电量趋势下降, 线损指标, 告警指标], class_names[正常, 窃漏电], filledTrue)max_depth4和min_samples_leaf5是控制过拟合的关键参数。不设这两项决策树能长到每个叶子节点只剩一个样本训练集正确率接近100%测试集直接崩掉。我在复现时把max_depth从3试到10发现4到5层时测试集表现最好继续加深正确率反而下降——这就是典型的过拟合信号。criterionentropy表示用信息增益选分裂特征默认的gini基尼系数在这个案例上两者差别不大。4.3tree.pkl系列文件的谜底阈值调优与模型序列化压缩包里那堆tree.pkl_01.npy到tree.pkl_04.npy、tree.pkl和net.model其实对应两种东西一种是不同参数下训练好的决策树模型序列化保存另一种是神经网络模型权重。pkl后缀是Python的pickle序列化格式joblib.dump也能产出类似文件。这些文件是调参过程中每个阶段的中间产物。为什么要存下来因为调参不是一次就能拿到最优结果。常见做法是循环多组参数每训练一个模型就用joblib.dump保存一份方便回溯对比。我当时复现时就犯过蠢——不存中间模型跑到第8组参数时想对比第3组的结果只能重跑一遍。从那以后我每次调参都强制保存模型快照命名严格带上参数标识避免“哪个pkl对应哪组参数”这种混乱。import joblib # 反复调参的标准套路 for depth in range(3, 7): model DecisionTreeClassifier(max_depthdepth, min_samples_leaf5) model.fit(X_train, y_train) joblib.dump(model, ftmp/tree_depth{depth}.pkl) print(fdepth{depth}, test_score{model.score(X_test, y_test):.4f})注意joblib.dump和pickle.dump的区别joblib对包含大量numpy数组的模型更高效net.model这种神经网络权重文件是model.save()的产物加载方式也不同。从tmp目录里的文件数量看原作者至少尝试了4组以上的参数组合。5. 避坑记录五条踩出来的血泪经验5.1 拉格朗日插值在数据首尾位置直接翻车现象脚本报IndexError: index -3 is out of bounds或者跑通后插补出来的值完全是负数明显不合理。原因缺失点位于数据序列首尾附近时取前后各5个点的逻辑会取到负下标Python负索引从尾部取值拿到的根本不是“前面”的数据甚至会把后文数据硬接上来。解决取点前显式过滤所有小于0或大于等于序列长度的下标参考我前面改写过的ployinterp_column版本。数据首尾确实不适合用拉格朗日插值建议直接丢弃首尾缺失行。5.2 缺失点前后全是NaN时插值函数直接报错现象lagrange抛出ValueError: x and y arrays must have same length脚本中断。原因如果缺失点连续出现取出的前后点里全是空值过滤后y为空数组无法构造多项式。解决捕获这种情况并做降级处理——要么取更远的非空点要么直接返回该列的均值。我一般会在ployinterp_column里加一个判断有效点不足2个时放弃插值用前后最近的非空值填充。5.3 决策树不限制深度训练集近乎满分测试集一塌糊涂现象model.score(X_train)高达0.99model.score(X_test)只有0.7左右两者差距巨大。原因决策树默认会无限生长直到叶子节点纯净数据量又小模型把训练集彻底背下来了。解决设置max_depth4、min_samples_leaf5训练集和测试集分数差距明显缩小。判断过拟合还有个实用技巧用cross_val_score做5折交叉验证看各折分数方差是否过大。5.4 不设随机种子同一个脚本两次运行结果不一样现象什么代码都没改两次运行决策树正确率分别是0.88和0.92。原因train_test_split和决策树训练过程都有随机性不固定随机种子每次划分的训练集不同——广义上讲这算模型稳定性问题严格来说是复现性问题。解决在train_test_split和DecisionTreeClassifier里都传入random_state42。固定种子后结果可复现调参时才能确定分数变化来自参数而不是随机波动。这是新手最容易忽略的坑也是项目交付时必须处理好的细节。5.5 混淆矩阵的方向看反了现象把confusion_matrix的输出直接解读发现“把正常用户误判为窃漏电”的数量和“把窃漏电误判为正常”的数量反了。原因sklearn的confusion_matrix返回的是[[tn, fp], [fn, tp]]行代表真实标签列代表预测标签这和很多教材里的图示方向不一致。解决评估时使用带标签的classification_report或者用ConfusionMatrixDisplay可视化输出不要靠记忆去读矩阵下标。6. 模型评估与落地ROC曲线和那份稽查名单怎么出模型训练完不等于结束最后一步是评估和落地。评估环节这个案例用到了cm_plot.py它负责绘制混淆矩阵和ROC曲线ROC曲线下的面积AUC是衡量模型区分能力的关键指标比单纯看正确率更可靠——即使类别不平衡AUC也能反映模型真实排序能力。from sklearn.metrics import roc_curve, auc import matplotlib.pyplot as plt # y_test为真实标签, proba为模型输出的正类概率 fpr, tpr, thresholds roc_curve(y_test, proba) roc_auc auc(fpr, tpr) print(AUC:, roc_auc) # 找最优点对应的阈值 optimal_idx (tpr - fpr).argmax() optimal_threshold thresholds[optimal_idx] print(最优阈值:, optimal_threshold)roc_curve返回的thresholds是模型在不同阈值下的分类边界。默认情况下模型用0.5作为阈值但如果你更看重查出窃漏电用户的召回率——宁愿多派几次稽查也不愿漏掉一个——可以把阈值往下调。比如最优阈值算出来是0.3意味着概率超过0.3就标记为窃漏电。调阈值在cm_plot.py里有体现实际操作就是对比不同阈值下的混淆矩阵选一个业务上可接受的点。落地到稽查流程时我会把预测概率按降序排列输出前100个用户作为嫌疑名单附上每个用户的特征值和模型概率供现场稽查按优先级处理。这就是从数据挖掘到业务决策的完整闭环。这份资源的实用价值就在这里它不教你调一个完美的模型而是带你走完一个真实业务问题的全链路。从missing_data.xls的脏数据到拉格朗日插值修复到专家样本特征构造再到决策树与逻辑回归建模对比最后到net.model这样的模型文件落地。如果你想快速拿到一套能跑的电力窃漏电识别基线方案或者想通过这个经典案例打通数据挖掘的完整流程这份代码和数据集值得下载后仔细过一遍。我每次做类似的时间序列分类项目都会翻出这个案例重新对照一遍流程确认自己没有跳过该有的步骤——希望帮到你。本文还有配套的精品资源点击获取
返回列表