数据挖掘实战:电力窃漏电用户自动识别

案例来自《python数据分析与挖掘实战》

背景

需求:防用户窃漏电

传统方法:定期巡检、定期校验电表、用户举报窃电、计量装置故障
缺陷:对人的依赖性太强,抓窃查漏的目标不明确。

当前方法:采集电量异常、负荷异常、终端报警、线损异常等信息,建立数据分析模型。根据有关的电流、电压、负荷数据情况,构建加权的用电异常分析模型,检查客户窃漏电情况。 
缺陷:由于终端误报或漏报过 多,无法快速精确定位窃漏电嫌疑用户。建模时,模型各输入指标权重确定需要用专家的经验,有很大的主观性,实施效果不尽如人意。

目标:将终端报警信息、用电负荷数据,结合窃漏电用户,提取出窃漏电用户的关键特征,构建窃漏电用户的识别模型,自动检查判断用户是否存在窃漏电行为

 

数据分析

已知数据

某企业大用户用电负荷数据

某企业大用户终端报警信息

某企业大用户违约、窃电处理通知书(此处省略)

建模目标

  1. 归纳出窃漏电用户的关键特征,构建窃漏电用户的识别模型;
  2. 利用实时监测数据,调用窃漏电用户识别模型实现实时诊断。

建模步骤

  1. 从系统中抽取有监督数据(用户用电负荷、报警、违约处罚) :营销系统的用户基本信息、用电信息、窃漏电触发记录;计量信息系统的用电负荷、报警信息;在窃漏电开始和结束时间点的数据最能突出变化,因此要包含该时间点前后的数据,窃漏电开始时用户用电量往往会下降。
  2. 剔除白名单用户(不可能存在窃漏电行为):根据不同行业的窃漏电用户数,剔除没有数据的行业的样本
  3. 预处理,包括数据清洗、缺失值处理和数据变换; 本案例节假日用电量会比工作日小,可以过滤节假日数据;有数据缺失的特征,当缺失不多时,用拉格朗日插值法填充;仅根据用电量下降(斜率)判断窃漏电不充分,结合线损指标(窃漏电线损率上升,增长率)和告警(窃漏电相关告警次数)判断。
  4. 构建专家样本集:根据用电量下降斜率、线损增长速率、告警次数的特征,结合是否窃漏电构建样本
  5. 构建窃漏电用户识别模型: LM 神经网络、CART 决策树
  6. 在线监测用户用电负荷及终端报警,调用模型实现实时诊断。

 

 

准备工作

数据集下载:python_data_analysis_and_mining_action

代码练习平台:google colab

上传数据到google colab

from google.colab import files
files.upload()

或者直接将数据集导入到 colab中来:github数据导入colab

 

数据缺失值处理

#-*- coding: utf-8 -*-
# 拉格朗日插值代码
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)  # 读入数据

# 自定义列向量插值函数
# s为列向量,n为被插值的位置,k为取前后的数据个数,默认为5
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)

 

3、将数据划分为训练集与测试集

# -*- coding: utf-8 -*-

import pandas as pd
from random import shuffle

datafile = 'data/model.xls'
data = pd.read_excel(datafile)
data = data.as_matrix()
shuffle(data)  # 随机打乱数据

# 设置训练数据比8:2
p = 0.8
train = data[:int(len(data) * p), :]
test = data[int(len(data) * p):, :]

定义混淆矩阵可视化函数:

def cm_plot(y, yp):
    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

 

4、构建LM神经网络模型

# -*- coding: utf-8 -*-

from keras.layers.core import Activation, Dense
from keras.models import Sequential
    
# 构建LM神经网络模型
netfile = 'tmp/net.model'

net = Sequential()  # 建立神经网络
#    net.add(Dense(input_dim = 3, units = 10))
# 添加输入层(3节点)到隐藏层(10节点)的连接
net.add(Dense(10, input_shape=(3, )))
net.add(Activation('relu'))  # 隐藏层使用relu激活函数
#    net.add(Dense(input_dim = 10, units = 1))
#添加隐藏层(10节点)到输出层(1节点)的连接
net.add(Dense(1, input_shape=(10, )))
net.add(Activation('sigmoid'))  # 输出层使用sigmoid激活函数
net.compile(
    loss='binary_crossentropy',
    optimizer='adam',
    sample_weight_mode="binary")  # 编译模型,使用adam方法求解

net.fit(train[:, :3], train[:, 3], epochs=100, batch_size=1)
net.save_weights(netfile)

predict_result = net.predict_classes(train[:, :3]).reshape(
    len(train))  # 预测结果变形
'''这里要提醒的是,keras用predict给出预测概率,predict_classes才是给出预测类别,而且两者的预测结果都是n x 1维数组,而不是通常的 1 x n'''

cm_plot(train[:, 3], predict_result).show()

 

5、构建CART决策树模型

from sklearn.tree import DecisionTreeClassifier

# 构建CART决策树模型
treefile = 'tmp/tree.pkl'
tree = DecisionTreeClassifier()
tree.fit(train[:, :3], train[:, 3])

joblib.dump(tree, treefile)

cm_plot(train[:, 3], tree.predict(train[:, :3])).show()  # 显示混淆矩阵可视化结果
# 注意到Scikit-Learn使用predict方法直接给出预测结果。

fpr, tpr, thresholds = roc_curve(
    test[:, 3], tree.predict_proba(test[:, :3])[:, 1], pos_label=1)
plt.plot(fpr, tpr, linewidth=2, label='ROC of CART', color='green')
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()
print(thresholds)

完整代码如下:

#-*- coding: utf-8 -*-
# pylint: disable=E1101
from random import shuffle

import matplotlib.pyplot as plt
import pandas as pd
from keras.layers.core import Activation, Dense
from keras.models import Sequential
from scipy.interpolate import lagrange
from sklearn.externals import joblib
from sklearn.metrics import confusion_matrix, roc_curve
from sklearn.tree import DecisionTreeClassifier
"""
cm_plot-->自定义混淆矩阵可视化
programmer_1-->使用拉格朗日插值法进行插值
programmer_2-->构建CART决策树模型,进行预测给出训练结果,并且绘制ROC曲线
programmer_3-->使用神经网络模型,进行预测给出训练结果,并且绘制ROC曲线
"""


def cm_plot(y, yp):
    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


def programmer_1():
    inputfile = 'data/missing_data.xls'
    outputfile = 'tmp/missing_data_processed.xls'

    data = pd.read_excel(inputfile, header=None)  # 读入数据

    # 自定义列向量插值函数
    # s为列向量,n为被插值的位置,k为取前后的数据个数,默认为5
    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)


def programmer_2():
    datafile = 'data/model.xls'
    data = pd.read_excel(datafile)
    data = data.as_matrix()
    shuffle(data)  # 随机打乱数据

    # 设置训练数据比8:2
    p = 0.8
    train = data[:int(len(data) * p), :]
    test = data[int(len(data) * p):, :]

    # 构建CART决策树模型
    treefile = 'tmp/tree.pkl'
    tree = DecisionTreeClassifier()
    tree.fit(train[:, :3], train[:, 3])

    joblib.dump(tree, treefile)

    cm_plot(train[:, 3], tree.predict(train[:, :3])).show()  # 显示混淆矩阵可视化结果
    # 注意到Scikit-Learn使用predict方法直接给出预测结果。

    fpr, tpr, thresholds = roc_curve(
        test[:, 3], tree.predict_proba(test[:, :3])[:, 1], pos_label=1)
    plt.plot(fpr, tpr, linewidth=2, label='ROC of CART', color='green')
    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()
    print(thresholds)


def programmer_3():
    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神经网络模型
    netfile = 'tmp/net.model'

    net = Sequential()  # 建立神经网络
    #    net.add(Dense(input_dim = 3, units = 10))
    # 添加输入层(3节点)到隐藏层(10节点)的连接
    net.add(Dense(10, input_shape=(3, )))
    net.add(Activation('relu'))  # 隐藏层使用relu激活函数
    #    net.add(Dense(input_dim = 10, units = 1))
    #添加隐藏层(10节点)到输出层(1节点)的连接
    net.add(Dense(1, input_shape=(10, )))
    net.add(Activation('sigmoid'))  # 输出层使用sigmoid激活函数
    net.compile(
        loss='binary_crossentropy',
        optimizer='adam',
        sample_weight_mode="binary")  # 编译模型,使用adam方法求解

    net.fit(train[:, :3], train[:, 3], epochs=100, batch_size=1)
    net.save_weights(netfile)

    predict_result = net.predict_classes(train[:, :3]).reshape(
        len(train))  # 预测结果变形
    '''这里要提醒的是,keras用predict给出预测概率,predict_classes才是给出预测类别,而且两者的预测结果都是n x 1维数组,而不是通常的 1 x n'''

    cm_plot(train[:, 3], predict_result).show()

    predict_result = net.predict(test[:, :3]).reshape(len(test))
    fpr, tpr, thresholds = roc_curve(test[:, 3], predict_result, pos_label=1)
    plt.plot(fpr, tpr, linewidth=2, label='ROC of LM')
    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()
    print(thresholds)


if __name__ == "__main__":
    programmer_1()
    programmer_2()
    programmer_3()

 

扩展应用

问题:汽车销售行业存在少开发票、少计收入、不及时确认保修索赔款等偷漏税行为

目标:对汽车销售业纳税人属性建模,识别偷漏税的人

参考:数据挖掘实战:汽车销售业偷漏税识别

 

参考:

Python数据挖掘—电力窃漏电用户自动识别

python数据分析与挖掘实战

部分问题解决参考:
https://blog.csdn.net/braveheartm/article/details/81591833

 

 

  • 6
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值