数据挖掘实战1-电力窃漏电用户识别
本次学习我们将使用“什么是数据挖掘”中的挖掘过程:根据实际问题定义挖掘目标、取什么样的原始数据、对原始数据的探索分析、如何对数据进行处理、建立合适的模型完成目标、评估模型完成的好不好。
问题背景:实际生活中,有很多人可能会偷别人的电用,或者计量电量的设备坏了,造成无法根据实际用电情况计价,可能导致用户多交或少交了钱。我们可以使用自动化设备实现对用户用电负荷等数据进行采集,通过从这个数据中找到异常的情况。
一、挖掘目标
1、归纳出窃漏电用户的关键特征,构建窃漏电用户的识别模型 2、利用实时监测数据,调用窃漏电用户识别模型实现实时判断是否是窃漏电用户。 根据目标可以知道这类问题属于分类预测问题,根据数据预测这个用户属于哪一类用户,到底是正常用户,还是偷电用户?所以我们后面会考虑用分类和预测的算法模型进行建模。
二、数据抽取:
1、从营销系统抽取用户信息 2、从计量自动化系统采集电量、负荷等 如下图,你可以看到能实际采集到的数据如下:

三、数据探索:分布分析+周期性分析
探索与预测无关的数据,缩小数据集范围,达到精准预测
1、分布分析
统计5年内所有窃漏用户进行分布分析,统计出各个用电类别的窃漏电用户分布情况,如下图所示,可以发现非居民类别不存在窃漏电情况,故在接下来的分析中不考虑非居民类别的用电数据。

2、周期性分析
随机抽取一个正常用电用户和一个窃漏电用户,周期性对电量进行探索。 (1)正常用电,如下图所示,总体来说用电量比较平稳,没有太大的波动。
 (2)窃漏电用户用电量出现明显下降的趋势,如下图所示。

分析结论:窃漏电的过程就是用电量持续下降的过程。
四、数据预处理
数据本身的样子可能并不适合我们处理,比如跟预测结论没有关系的数据,我们可以过滤掉。比如存在一些缺失值,样本很多的情况下我们就大方的删了,但样本很少的时候,我们就需要把它补上。比如好多个数据之间有明显的关系,我们可以把他们合并为一个数据作为特征明显指标。总之,我们对于数据的预处理目的就是:用尽可能少的数据探索出尽可能精准的结果。 1、缺失值可以删除也可以插补,插补的方法很多,我们这里使用“拉格朗日插值法”进行数据的补充,(该数学推导请点击这里查看)。 我们这里调用python库中已经实现的拉格朗日函数对样本数据进行插值,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
import pandas as pd from scipy.interpolate import lagrange
inputfile = '../data/missing_data.xls' outputfile= '../tmp/missing_data_processed.xls'
data=pd.read_excel(inputfile,header=None)
def ployinterp_column(s,n,k=5): y=s[list(range(n-k,n)) + list(range(n+1,n+1+k))] y=y[y.notnull()] return lagrange(y.index,list(y))(n)
for i in data.columns: for j in range(len(data)): if(data[i].isnull())[j]: data[i][j]=ployinterp_column(data[i],j)
data.to_excel(outputfile,header=None,index=False)
|
如下图,拉格朗日插补法的效果如下:
 我们就使用这样的方法处理所有的数据,这里就不再赘述,紧接着我们从大量数据中抽取291个专家样本数据(使用这291个数据进行模型构建)。从原始数据开始到现在为止,其实我们已经做了三件事情了: 1、过滤掉无关的属性,例如用户编号。 2、缺失值处理。 3、从大量数据中选取291个样本数据。 现在,我们要对291个样本数据进行降维处理(也就是将相关属性合并为一个属性) 我们构建三个指标(新属性,由旧属性变换而来): (1)电量趋势下降指标。对每天的前后5天(总共11天)计算电量的下降趋势(即斜率) (2)线损指标。若第L天的线路供电为S,线路上各个用户用电总量为W,则线损率T=(S-W)/S * 100% (3)告警类指标。计算终端报警的次数总和。 这里只展示最终数据,数据变换的过程根据实际意义可以进行修改。最终数据如下,最后一列给定结果是为了模型的学习和模型的评估,最终是为了这个模型可以预测其他的不可知漏电行为。

五、模型构建
我们已经完成的数据的处理,现在的数据可以用来训练和测试模型。 重点来了,由于我们是分类问题,所以我们从分类模型中选择模型。这里实际上是一个二分类问题,结果只有0或者1,此时我们就不会选择回归分析(因为回归分析是分析连续性结果);并且我们的属性并不多,所以我们会更倾向选择决策树算法或者神经网络算法。这里在决策树算法中选择CART算法(算法详解请点击这里),在神经网络算法中选择LM算法(算法详解请点击这里)。实际上其他一些算法也能应用于该问题,感兴趣的读者也可以尝试效果,我们在这里只选择两个常见的进行比较。 我们的整体过程如下图,
 这里稍微简单的说一下机器学习的过程,首先切分数据集为训练集和测试集(也可以是独立的两个数据集),然后我们选择合适的算法,每种算法模型都可以有多种参数,但是确定什么样的参数才能解决我们当前的问题,这就需要训练集一个一个的带入到算法模型里然后一步一步的调整到合适的参数获得模型。最后我们需要把测试集代入到训练好的模型里,评估一下这个模型是不是在任何该问题的数据下都能够准确的得到预测结果。评估分类模型的指标也有很多(参考什么是数据挖掘),本章我们选择ROC曲线(什么是ROC曲线)。
按照上图的步骤我们进行代码的编写:
1、数据划分代码: 导入291个样本,将数据分为训练集和测试集
1 2 3 4 5 6 7 8 9 10 11
|
import pandas as pd from random import shuffle
datafile='../data/model.xls' data=pd.read_excel(datafile) data=data.as_matrix() shuffle(data)
p = 0.8 train=data[:int(len(data)*p),:] test=data[int(len(data)*p):,:]
|
2、LM神经网络算法: 这里用到了python的神经网络库keras,构建三层网络模型,训练模型并且保存模型。这里的ROC曲线代码在评估阶段提供
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
from keras.models import Sequential from keras.layers.core import Dense,Activation from keras.models import load_model
netfile='../tmp/net.model'
net = Sequential() net.add(Dense(input_dim=3,output_dim=10)) net.add(Activation('relu')) net.add(Dense(input_dim=10,output_dim=1)) net.add(Activation("sigmoid"))
net.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])
net.fit(train[:,:3],train[:,3],nb_epoch=1000,batch_size=1) net.save_weights(netfile) predict_result = net.predict_classes(train[:,:3]).reshape(len(train)) '''这里要提醒的是,keras用predict给出预测概率,predict_class才是给出预测类别, 而且两者的预测结果都是n x 1维数组,而不是通常的1 下n'''
from cm_plot import * cm_plot(train[:,3],predict_result).show()
predict_result_test=net.predict(test[:,:3]).reshape(len(test)) roc_plot(test[:,3],predict_result_test ,'ROC of LM')
|
3、CART决策树 这里使用python的sklearn库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
from sklearn.tree import DecisionTreeClassifier
treefile='../tmp/tree.pk1' tree = DecisionTreeClassifier() tree.fit(train[:,:3],train[:,3])
from sklearn.externals import joblib joblib.dump(tree,treefile)
from cm_plot import * cm_plot(train[:,3],tree.predict(train[:,:3])).show()
roc_plot(test[:,3],tree.predict_proba(test[:,:3])[:,1],'ROC of CART')
|
4、ROC曲线
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
import matplotlib.pyplot as plt
'''画混淆矩阵图''' def cm_plot(y, yp): from sklearn.metrics import confusion_matrix cm = confusion_matrix(y, yp)
plt.matshow(cm, cmap=plt.cm.Greens) plt.colorbar() for x in range(len(cm)): for y in range(len(cm)): plt.annotate(cm[x,y], xy=(x, y), horizontalalignment='center', verticalalignment='center') plt.ylabel('True label') plt.xlabel('Predicted label') return plt
'''画ROC曲线图'''
from sklearn.metrics import roc_curve def roc_plot(x,xp,l): fpr,tpr,thresholds=roc_curve(x,xp, pos_label=1) plt.plot(fpr,tpr,linewidth=2,label=l) plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.ylim(0,1.05) plt.xlim(0,1.05) plt.legend(loc=4) plt.show() return plt
|
在这里补充一下,每一次随机分的训练集和测试集并不完全相同,代码运行一次,分一次数据集,就不一样一次,所以每一次的训练得出的模型并不是完全相同的,但我们希望比较两个算法的优缺点时,我们希望他们要用同一个训练集,同一个测试集,所以这里补充一下,把两个算法写在一起训练并且比较ROC曲线更有说服力。(ps:上面的训练集划分和两个算法的代码可以等同于如下:)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
|
import sys reload(sys) sys.setdefaultencoding('ISO-8859-1')
'''读取数据,设置数据''' import pandas as pd from random import shuffle
datafile='../data/model.xls' data=pd.read_excel(datafile) data=data.as_matrix() shuffle(data)
p = 0.8 train=data[:int(len(data)*p),:] test=data[int(len(data)*p):,:]
'''LM预测'''
from keras.models import Sequential from keras.layers.core import Dense,Activation from keras.models import load_model
netfile='../tmp/net.model'
net = Sequential() net.add(Dense(input_dim=3,output_dim=10)) net.add(Activation('relu')) net.add(Dense(input_dim=10,output_dim=1)) net.add(Activation("sigmoid"))
net.compile(loss='binary_crossentropy',optimizer='adam',metrics=['accuracy'])
net.fit(train[:,:3],train[:,3],nb_epoch=1000,batch_size=1) net.save_weights(netfile)
predict_result = net.predict_classes(train[:,:3]).reshape(len(train)) '''这里要提醒的是,keras用predict给出预测概率,predict_class才是给出预测类别, 而且两者的预测结果都是n x 1维数组,而不是通常的1 下n'''
'''CART预测'''
from sklearn.tree import DecisionTreeClassifier tree = DecisionTreeClassifier() tree.fit(train[:,:3],train[:,3])
'''画混淆矩阵和ROC曲线''' from cm_plot import * cm_plot(train[:,3],predict_result).show() cm_plot(train[:,3],tree.predict(train[:,:3])).show()
predict_result_test=net.predict(test[:,:3]).reshape(len(test)) roc_plot(test[:,3],predict_result_test ,'ROC of LM') roc_plot(test[:,3],tree.predict_proba(test[:,:3])[:,1],'ROC of CART')
|
六、模型评价
根据上述两个算法同时比较的代码,可得到如下ROC曲线:

|