一、背景与目标
家电企业要了解使用家用电器的习惯,必须采集用户使用电器的相关数据,本文以热水器为例,分析用户的使用行为。在热水器用户行为分析中,用水事件识别是最关键的环节。
热水器厂商根据洗浴事件识别模型,对不同地区的用户进行识别,比较不同客户群客户的使用习惯。从而厂商可给不同的客户群提供个性化的产品、制定相应的营销策略。
本次数据挖掘的建模目标如下:
- 根据热水器采集的数据,划分一次完整的用水事件。
- 在划分好的一次完整用水事件中,识别出洗浴事件。
二、方法步骤
- 对热水用户的历史数据进行选择抽取,构建专家样本。
- 对步骤1形成的数据集进行数据探索分析与预处理。
- 在步骤2得到的数据的基础上,建立洗浴事件的识别模型,对洗浴事件识别模型进行分析评价。
- 对步骤3形成的模型结果应用并对洗浴事件划分进行优化。
- 调用洗浴事件识别模型,对实时监控的热水器水流数据进行洗浴事件的自动识别。
三、过程
3.1 数据抽取
由于热水器采集频率较高,并且数据来自大量用户,数据总量大,因此,本案例对原数据采用无放回随机抽样法抽取200家热水器用户从2014年1月1日到2014年12月31日的用水记录作为原始建模数据。
数据情况如下:
3.2 数据探索分析
3.3 数据预处理
3.31 数据规约
- 属性规约:去除属性“热水器编号”;因为“有无水流”可以通过“水流量”反映,所以去除属性“有无水流”;“节能模式”数据都是“关”,对建模无用,去除。
- 数值规约:当“开关机状态”为“关”且水流量为0时,热水器不处于工作状态,数据记录可以规约掉。
处理后数据如下:
3.32 数据变换
(1)一次完整用水事件的划分模型
用户的用水数据记录了各种用水事件,如:洗脸、洗手、刷牙、洗头、洗菜、洗浴等,而且一次性用水事件由数条状态记录组成。所以首先需要在大量的状态中划分出哪些连续的数据时一次完整的用水事件。
水流量不为0时,表明用户正在使用热水;水流量为0时,用户热水发生停顿或者用热水结束。如果水流量为0的状态记录之间的时间间隔超过一个阈值T,则归属于一次完整的事件。
#-*- coding: utf-8 -*-
#用水事件划分
import pandas as pd
threshold = pd.Timedelta('4 min') #阈值为分钟
inputfile = '.../data/water_heater.xls' #输入数据路径,需要使用Excel格式
outputfile = 'C:/Users/Cong Ma/Desktop/exercise/case_10_神经网络/tmp/dividsequence.xls' #输出数据路径,需要使用Excel格式
data = pd.read_excel(inputfile)
data[u'发生时间'] = pd.to_datetime(data[u'发生时间'], format = '%Y%m%d%H%M%S')
data = data[data[u'水流量'] > 0] #只要流量大于0的记录
d = data[u'发生时间'].diff() > threshold #相邻时间作差分,比较是否大于阈值
data[u'事件编号'] = d.cumsum() + 1 #通过累积求和的方式为事件编号
data.to_excel(outputfile)
(2)用水事件阈值寻优模型
#-*- coding: utf-8 -*-
#阈值寻优
import numpy as np
import pandas as pd
inputfile = '.../data/water_heater.xls' #输入数据路径,需要使用Excel格式
n = 4 #使用以后四个点的平均斜率
threshold = pd.Timedelta(minutes = 5) #专家阈值
data = pd.read_excel(inputfile)
data[u'发生时间'] = pd.to_datetime(data[u'发生时间'], format = '%Y%m%d%H%M%S')
data = data[data[u'水流量'] > 0] #只要流量大于0的记录
def event_num(ts):
d = data[u'发生时间'].diff() > ts #相邻时间作差分,比较是否大于阈值
return d.sum() + 1 #这样直接返回事件数
dt = [pd.Timedelta(minutes = i) for i in np.arange(1, 9, 0.25)]
h = pd.DataFrame(dt, columns = [u'阈值']) #定义阈值列
h[u'事件数'] = h[u'阈值'].apply(event_num) #计算每个阈值对应的事件数
h[u'斜率'] = h[u'事件数'].diff()/0.25 #计算每两个相邻点对应的斜率
h[u'斜率指标'] = h[u'斜率'].abs().rolling(n).mean() #采用后n个的斜率绝对值平均作为斜率指标
ts = h[u'阈值'][h[u'斜率指标'].idxmin() - n]
#注:用idxmin返回最小值的Index,由于rolling_mean()自动计算的是前n个斜率的绝对值平均
#所以结果要进行平移(-n)
if ts > threshold:
ts = pd.Timedelta(minutes = 4)
print(ts)
(3)属性构造
构造出以下4类指标:
- 时长指标:用水开始事件、用水结束时间、总用水时长、停顿时长、总停顿时长、用水时长、平均停顿时长、用水时长/总用水时长
- 频率指标:停顿次数
- 用水量指标:总用水量、平均水流量
- 用水波动指标:水流量波动、停顿时长波动
处理后数据如下:
3.4 模型构建
#-*- coding: utf-8 -*-
#建立、训练多层神经网络,并完成模型的检验
import pandas as pd
inputfile1='../input/train_neural_network_data.xls' #训练数据
inputfile2='../input/test_neural_network_data.xls' #测试数据
testoutputfile = '../output/test_output_data.xls' #测试数据模型输出文件
data_train = pd.read_excel(inputfile1) #读入训练数据(由日志标记事件是否为洗浴)
data_test = pd.read_excel(inputfile2) #读入测试数据(由日志标记事件是否为洗浴)
y_train = data_train.iloc[:,4].as_matrix() #训练样本标签列
x_train = data_train.iloc[:,5:17].as_matrix() #训练样本特征
y_test = data_test.iloc[:,4].as_matrix() #测试样本标签列
x_test = data_test.iloc[:,5:17].as_matrix() #测试样本特征
x_train.head():
y_train.head():
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
model = Sequential() # 建立模型
model.add(Dense(17, activation='relu', input_dim=11)) # 添加输入层->隐藏层的连接,以Relu函数为激活函数
model.add(Dense(10, activation='relu')) # 添加隐藏层->隐藏层的连接,以Relu函数为激活函数
model.add(Dense(1, activation='sigmoid')) # 添加隐藏层->输出层的连接,以sigmoid函数为激活函数
print(model.summary()) # 显示模型形态
# 编译模型,损失函数为binary_crossentropy,用adam法求解
model.compile(loss='binary_crossentropy', optimizer='adam')
model.fit(x_train, y_train, nb_epoch = 100, batch_size = 1) # 训练模型
model.save_weights('../output/net.model') # 保存模型参数
yp = pd.DataFrame(model.predict_classes(x_test), columns = [u'预测结果'])
y_probability = pd.DataFrame(model.predict(x_test), columns = [u'预测概率'])
out_1 = pd.concat([y_probability, yp], axis=1)
out_2 = pd.concat([data_test.iloc[:,:5], yp], axis = 1)
out_2.to_excel(testoutputfile)
model:
out_1:
out_2:
3.5 模型评价
import seaborn as sns
from sklearn.metrics import accuracy_score, confusion_matrix
import matplotlib.pyplot as plt
# 1 正确率
accuracy_score(y_test, yp)
正确率:
# 2 混淆矩阵
# 2.1 得到混淆矩阵
C2=confusion_matrix(y_test, yp) # 注意到Scikit-Learn使用predict方法直接给出预测结果。
print(C2) #打印出来看看
# 2.2 根据混淆矩阵画热力图
sns.set()
f,ax=plt.subplots(figsize=(5, 5))
sns.heatmap(C2, annot=True, fmt=".3g",ax=ax) # 画热力图,注意:默认fmt=".2g",此时图中的数据"144"会显示成"1.4e+02"
ax.set_title('confusion matrix') # 标题
ax.set_xlabel('predict') # x轴
ax.set_ylabel('true') # y轴
C2:
热力图:
# 3 ROC曲线
from sklearn.metrics import roc_curve, auc # 导入ROC曲线函数和AUC计算函数
from matplotlib import pyplot as plt
fpr, tpr, thresholds = roc_curve(y_test, model.predict_proba(x_test), pos_label=1)
roc_auc = auc(fpr,tpr) # 计算auc的值
plt.plot(fpr, tpr, linewidth=2, label = 'ROC of ANN (area = {:.2})'.format(roc_auc), color = 'green') # 作出ROC曲线
plt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--') # 辅助对角线
plt.title('Receiver operating characteristic')
plt.xlabel('False Positive Rate') # 坐标轴标签
plt.ylabel('True Positive Rate') # 坐标轴标签
plt.ylim(0,1.05) # 边界范围
plt.xlim(0,1.05) # 边界范围
plt.legend(loc=4) # 图例