家用热水器用户行为分析与事件识别

发布时间 2023-03-26 21:20:05作者: 仰望半月的夜

一、数据探索
1、探索分析热水器的水流量状况
`import pandas as pd
import matplotlib.pyplot as plt
inputfile = 'D:/a/第十章/original_data.xls'
data = pd.read_excel(inputfile)

查看水流分布

lv_non = pd.value_counts(data['有无水流'])['无']
lv_move = pd.value_counts(data['有无水流'])['有']

条形图

fig = plt.figure(figsize=(6,5))
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['axes.unicode_minus']=False
plt.bar(x = range(2),height=[lv_non,lv_move],width=0.4,alpha = 0.8,
color = 'skyblue')
plt.xticks([index for index in range(2)],['无','有'])
plt.xlabel('水流状态')
plt.ylabel('记录数')
plt.title('不同水流状态记录数(3130)')
plt.show()
plt.close()
得到:

#查看水流量分布 water = data['水流量'] fig= plt.figure(figsize=(5,8)) plt.boxplot(water, patch_artist=True, labels=['水流量'], boxprops={'facecolor':'lightblue'}) plt.title('水流量分布箱型图(3130)') plt.grid(axis='y') plt.show()
得到:

二、数据预处理
1、属性归约

删除冗余

import pandas as pd import numpy as np data = pd.read_excel('D:/a/第十章/original_data.xls') print('初始状态的数据状态:',data.shape) data.drop(labels=["热水器编号","有无水流","节能模式"],axis=1,inplace=True) print('删除冗余属性后的数据形状为:',data.shape) data.to_csv('D:/a/第十章/water_heart.csv',index=False)

划分用水事件

data = pd.read_csv('D:/a/第十章/water_heart.csv') threshold = pd.Timedelta('4 min') data['发生时间'] = pd.to_datetime(data['发生时间'],format='%Y%m%d%H%M%S') data = data[data['水流量']>0] sjKs = data['发生时间'].diff()>threshold sjKs.iloc[0] = True sjJs = sjKs.iloc[1:] sjJs = pd.concat([sjJs,pd.Series(True)]) sj = pd.DataFrame(np.arange(1,sum(sjKs)+1),columns=["事件序号"]) sj["事件起始编号"] = data.index[sjKs==1]+1 sj["事件终止编号"] = data.index[sjJs==1]+1 print('当阈值为4分钟的时候事件数目为:',sj.shape[0]) sj.to_csv('D:/a/第十章/sj.csv',index=False)

确定单次用水事件时长阈值

n=4 threshold = pd.Timedelta(minutes=5) data['发生时间'] = pd.to_datetime(data['发生时间'],format='%Y%m%d%H%M%S') data = data[data['水流量']>0] def event_num(ts): d = data['发生时间'].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=['阈值']) h['事件数'] = h['阈值'].apply(event_num) h['斜率'] = h['事件数'].diff()/25 h['斜率指标'] = h['斜率'].abs().rolling(4).mean() ts = h['阈值'][h['斜率指标'].idxmin()-n] if ts > threshold: ts = pd.Timedelta(minutes=4) print('计算出的单次用水时长的阈值为:',ts)

`data = pd.read_excel('D:/a/第十章/water_hearter.xlsx') # 读取热水器使用数据记录
sj = pd.read_csv('D:/a/第十章/sj.csv') # 读取用水事件记录

转换时间格式

data["发生时间"] = pd.to_datetime(data["发生时间"],format="%Y%m%d%H%M%S")

构造特征:总用水时长

timeDel = pd.Timedelta("0.5 sec")
sj["事件开始时间"] = data.iloc[sj["事件起始编号"]-1,0].values- timeDel
sj["事件结束时间"] = data.iloc[sj["事件终止编号"]-1,0].values + timeDel
sj['洗浴时间点'] = [i.hour for i in sj["事件开始时间"]]
sj["总用水时长"] = np.int64(sj["事件结束时间"] - sj["事件开始时间"])/1000000000 + 1

构造用水停顿事件

构造特征“停顿开始时间”、“停顿结束时间”

停顿开始时间指从有水流到无水流,停顿结束时间指从无水流到有水流

for i in range(len(data)-1):
if (data.loc[i,"水流量"] != 0) & (data.loc[i + 1,"水流量"] == 0):
data.loc[i + 1,"停顿开始时间"] = data.loc[i +1, "发生时间"] - timeDel
if (data.loc[i,"水流量"] == 0) & (data.loc[i + 1,"水流量"] != 0) :
data.loc[i,"停顿结束时间"] = data.loc[i , "发生时间"] + timeDel

提取停顿开始时间与结束时间所对应行号,放在数据框Stop中

indStopStart = data.index[data["停顿开始时间"].notnull()]+1
indStopEnd = data.index[data["停顿结束时间"].notnull()]+1
Stop = pd.DataFrame(data={"停顿开始编号":indStopStart[:-1],
"停顿结束编号":indStopEnd[1:]})

计算停顿时长,并放在数据框stop中,停顿时长=停顿结束时间-停顿结束时间

Stop["停顿时长"] = np.int64(data.loc[indStopEnd[1:]-1,"停顿结束时间"].values-
data.loc[indStopStart[:-1]-1,"停顿开始时间"].values)/1000000000

将每次停顿与事件匹配,停顿的开始时间要大于事件的开始时间,

且停顿的结束时间要小于事件的结束时间

for i in range(len(sj)):
Stop.loc[(Stop["停顿开始编号"] > sj.loc[i,"事件起始编号"]) &
(Stop["停顿结束编号"] < sj.loc[i,"事件终止编号"]),"停顿归属事件"]=i+1

删除停顿次数为0的事件

Stop = Stop[Stop["停顿归属事件"].notnull()]

构造特征 用水事件停顿总时长、停顿次数、停顿平均时长、

用水时长,用水/总时长

stopAgg = Stop.groupby("停顿归属事件").agg({"停顿时长":sum,"停顿开始编号":len})
sj.loc[stopAgg.index - 1,"总停顿时长"] = stopAgg.loc[:,"停顿时长"].values
sj.loc[stopAgg.index-1,"停顿次数"] = stopAgg.loc[:,"停顿开始编号"].values
sj.fillna(0,inplace=True) # 对缺失值用0插补
stopNo0 = sj["停顿次数"] != 0 # 判断用水事件是否存在停顿
sj.loc[stopNo0,"平均停顿时长"] = sj.loc[stopNo0,"总停顿时长"]/sj.loc[stopNo0,"停顿次数"]
sj.fillna(0,inplace=True) # 对缺失值用0插补
sj["用水时长"] = sj["总用水时长"] - sj["总停顿时长"] # 定义特征用水时长
sj["用水/总时长"] = sj["用水时长"] / sj["总用水时长"] # 定义特征 用水/总时长
print('用水事件用水时长与频率特征构造完成后数据的特征为:\n',sj.columns)
print('用水事件用水时长与频率特征构造完成后数据的前5行5列特征为:\n',
sj.iloc[:5,:5])
![](https://img2023.cnblogs.com/blog/2524815/202303/2524815-20230326210931333-153603262.png)data["水流量"] = data["水流量"] / 60 # 原单位L/min,现转换为L/sec
sj["总用水量"] = 0 # 给总用水量赋一个初始值0
for i in range(len(sj)):
Start = sj.loc[i,"事件起始编号"]-1
End = sj.loc[i,"事件终止编号"]-1
if Start != End:
for j in range(Start,End):
if data.loc[j,"水流量"] != 0:
sj.loc[i,"总用水量"] = (data.loc[j + 1,"发生时间"] -
data.loc[j,"发生时间"]).seconds*
data.loc[j,"水流量"] + sj.loc[i,"总用水量"]
sj.loc[i,"总用水量"] = sj.loc[i,"总用水量"] + data.loc[End,"水流量"] * 2
else:
sj.loc[i,"总用水量"] = data.loc[Start,"水流量"] * 2

sj["平均水流量"] = sj["总用水量"] / sj["用水时长"] # 定义特征 平均水流量

构造特征:水流量波动

水流量波动=∑(((单次水流的值-平均水流量)^2)*持续时间)/用水时长

sj["水流量波动"] = 0 # 给水流量波动赋一个初始值0
for i in range(len(sj)):
Start = sj.loc[i,"事件起始编号"] - 1
End = sj.loc[i,"事件终止编号"] - 1
for j in range(Start,End + 1):
if data.loc[j,"水流量"] != 0:
slbd = (data.loc[j,"水流量"] - sj.loc[i,"平均水流量"])**2
slsj = (data.loc[j + 1,"发生时间"] - data.loc[j,"发生时间"]).seconds
sj.loc[i,"水流量波动"] = slbd * slsj + sj.loc[i,"水流量波动"]
sj.loc[i,"水流量波动"] = sj.loc[i,"水流量波动"] / sj.loc[i,"用水时长"]

构造特征:停顿时长波动

停顿时长波动=∑(((单次停顿时长-平均停顿时长)^2)*持续时间)/总停顿时长

sj["停顿时长波动"] = 0 # 给停顿时长波动赋一个初始值0
for i in range(len(sj)):
if sj.loc[i,"停顿次数"] > 1: # 当停顿次数为0或1时,停顿时长波动值为0,故排除
for j in Stop.loc[Stop["停顿归属事件"] == (i+1),"停顿时长"].values:
sj.loc[i,"停顿时长波动"] = ((j - sj.loc[i,"平均停顿时长"])**2) * j +
sj.loc[i,"停顿时长波动"]
sj.loc[i,"停顿时长波动"] = sj.loc[i,"停顿时长波动"] / sj.loc[i,"总停顿时长"]

print('用水量和波动特征构造完成后数据的特征为:\n',sj.columns)
print('用水量和波动特征构造完成后数据的前5行5列特征为:\n',sj.iloc[:5,:5])
![](https://img2023.cnblogs.com/blog/2524815/202303/2524815-20230326211004662-2110880926.png)sj_bool = (sj['用水时长']>100)&(sj['总用水时长']>120)&(sj['总用水量']>5)
sj_final = sj.loc[sj_bool,:]
sj_final.to_excel('D:/a/第十章/sj_final.xlsx',index=False)
print('筛选出候选洗浴事件前的数据形状为:',sj.shape)
print('筛选出候选洗浴事件后的数据形状为:',sj_final.shape)`

`#构建BP神经网络
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.neural_network import MLPClassifier
import joblib

读取数据

Xtrain = pd.read_excel('D:/a/第十章/sj_final.xlsx')
ytrain = pd.read_excel('D:/a/第十章/water_heater_log.xlsx')
test = pd.read_excel('D:/a/第十章/test_data.xlsx')

训练集测试集区分。

x_train, x_test, y_train, y_test = Xtrain.iloc[:,5:],test.iloc[:,4:-1],
ytrain.iloc[:,-1],test.iloc[:,-1]

标准化

stdScaler = StandardScaler().fit(x_train)
x_stdtrain = stdScaler.transform(x_train)
x_stdtest = stdScaler.transform(x_train)

建立模型

bpnn = MLPClassifier(hidden_layer_sizes = (17,10), max_iter = 200, solver = 'lbfgs',random_state=50)
bpnn.fit(x_stdtrain, y_train)

保存模型

joblib.dump(bpnn,'D:/a/第十章/water_heater_nnet.m')
print('构建的模型为:\n',bpnn)![](https://img2023.cnblogs.com/blog/2524815/202303/2524815-20230326211055583-720416409.png)# 模型评价
from sklearn.metrics import classification_report
from sklearn.metrics import roc_curve
import joblib
import matplotlib.pyplot as plt
bpnn = joblib.load('D:/a/第十章/water_heater_nnet.m') # 加载模型
y_pred = bpnn.predict(x_stdtest) # 返回预测结果
print('神经网络预测结果评价报告:\n',classification_report(y_test,y_pred))

绘制roc曲线图

plt.rcParams['font.sans-serif'] = 'SimHei' # 显示中文
plt.rcParams['axes.unicode_minus'] = False # 显示负号
fpr, tpr, thresholds = roc_curve(y_pred,y_test) # 求出TPR和FPR
plt.figure(figsize=(6,4)) # 创建画布
plt.plot(fpr,tpr) # 绘制曲线
plt.title('用户用水事件识别ROC曲线') # 标题
plt.xlabel('FPR') # x轴标签
plt.ylabel('TPR') # y轴标签
plt.savefig('D:/a/第十章/用户用水事件识别ROC曲线(3130).png') # 保存图片
plt.show() # 显示图形`