Python数据分析与应用----家用热水器用户分析与事件识别

网友投稿 763 2022-10-01

Python数据分析与应用----家用热水器用户分析与事件识别

Python数据分析与应用----家用热水器用户分析与事件识别

Python数据分析与应用----家用热水器用户分析与事件识别

目录:

​​一、预处理热水器用户用水数据​​​

​​1.删除冗余特征​​

​​2.划分用水事件​​​

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

​​​二、构建用水行为特征并筛选用水事件​​​

​​1.构建用水时长与频率特征​​​

​​2.构建水量与波动特征​​​

​​3.筛选候选洗浴事件​​​

​​三、构建行为事件分析的BP神经网络模型​​​

​​1.构建神经网络模型​​​

​​2.评价神经网络模型​​

一、预处理热水器用户用水数据

1.删除冗余特征

import pandas as pd import numpy as npdata = pd.read_excel('data/original_data.xls')print('初始状态的数据形状为:',data.shape)# 删除冗余数据data.drop(labels=['热水器编号','有无水流','节能模式'],axis=1,inplace=True)print('删除冗余数据后的数据形状为:',data.shape)data.to_csv('tmp/water_heart.csv',index=False)初始状态的数据形状为: (18840, 12)删除冗余数据后的数据形状为: (18840, 9)

由于本案例主要针对目标为用户,分析器洗浴行为规律,所以可以对一些无关特征进行删减。‘热水器编号‘并不涉及用户信息,可以剔除;‘有无流水‘可以通过‘水流量’体现;‘节能模式’也与分析用户无关,因此将这几个特征删去。

2.划分用水事件

所谓用水事件,就是指用水所做的某一件事。比如:用水洗浴、用水洗手、用水刷牙、用水洗菜等等,这样独立的单一的与用水有关的事情称作用水事件。在本案例中,一件完整的用水事件可能包含有大量连续的数据记录。比如洗衣服来来回回洗了好几遍,自然就会出现多条用水记录,而这多条的用水记录组成的就是这一次的洗衣用水事件。这里面涉及到一个阈值的问题。

符号

解释

t1

所有水流量不为0的用水行为发生的时间

T

时间间隔阈值

对于序列t1构建其向前时差列和向后时差列,并将每一组时差与阈值T比较。若向前超出T,则记为新用水事件开始编号;若向后超出T,则记为本次用水事件结束编号。

用水停顿时间间隔,就是同一用水事件中用水停顿的间隔时长,平时手洗衣物,不会只洗一遍,与此同时用水不会一次用完,只会洗一次用一次水,这样就产生了用水停顿间隔。当然,不同的用水事件的用水停顿间隔也会有所不同,比如洗一次衣服中间的间隔与刷一次牙中间的间隔,显然后者会更短。根据原始数据给出的特征,用水量为0表示此时用户用热水发生停顿或结束,用水量不为0表示用户正在使用热水。

// 划分用水事件threshold = pd.Timedelta('4 min') // 阈值为分钟data['发生时间'] = pd.to_datetime(data['发生时间'],format='%Y%m%d%H%M%S') // 转换时间格式data = data[data['水流量']>0] // 提取水流量大于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('tmp/sj.csv',index=False)

在定义实践编号时加1是因为:计算差值都是与相邻后一个连续时间数据比对的,在这里2与3的差值比对为True,说明2-3的间隔时段大于T,3应当为下一事件的起点。

diff函数是从数学上来说,是将数据与平移后的数据进行比较得出的差异数据。从操作的意义上来说,是两条临近记录的差值,也就是一阶差分。

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

// 确定单次用水时长阈值n = 4threshold = pd.Timedelta(minutes = 5) // 专家阈值data['发生时间'] = pd.to_datetime(data['发生时间'],format='%Y%m%d%H%M%S') // 转换时间格式// 自定义函数:输入划分时间的时间阈值,得到划分的事件数def event_nums(ts): d = data['发生时间'].diff()>ts // 相邻时间向前差分,比较是否大于阈值 return d.sum() #直接返回事件数//转换数据框,定义阈值列dt = [pd.Timedelta(minutes=i) for i in np.arange(1,9,0.25)]h = pd.DataFrame(dt,columns=['阈值']) h['事件数'] = h['阈值'].apply(event_nums) // 计算每个阈值对应的事件数,调用了上面的event_num函数h['斜率'] = h['事件数'].diff()/0.25 // 计算每两个相邻点对应的斜率// 往前取n个斜率绝对值平均作为斜率指标h['斜率指标'] = h['斜率'].abs().rolling(4).mean()ts = h['阈值'][h['斜率指标'].idxmin()-n] //#这里ts得到的是4min,利用修正过后的索引对h[u'阈值']取值,idxmin()返回数组中最小值的索引.//注:用idxmin返回最小值的Index,由于rolling_mean()自动计算的是前n个斜率的绝对值平均(根据下方列表可以通透地理解这个意思),所以结果要进行平移(-n).if ts>threshold: //这里的意思是,如果上面计算得到的ts小鱼上面设定的专家阈值,就以ts为准,否则就降低为4. ts = pd.Timedelta(minutes=4) print('计算出的单次用水时长的阈值为:',ts) 计算出的单次用水时长的阈值为: 0 days 00:04:00

dt = [pd.Timedelta(minutes=i) for i in np.arange(1,9,0.25)]h = pd.DataFrame(dt,columns=['阈值'])

这里用到的语法是列表推导,也叫作列表解析。这里arange用于创建等差数组,这里的数据最终会变化成时间数据格式,也就是说,这里的0.25代表每分钟的四分之一,也就是15秒,按照15秒为步长,进行取数据。再由h将其转化为DataFrame类型数据。

​​返回顶部​​

二、构建用水行为特征并筛选用水事件

1.构建用水时长与频率特征

发送阈值是指热水器传输数据的频率大小。如下图,在20:00:10时,还未显示用水,而在20:00:12时,热水器记录到用水行为,所以用水时间应当在10min-12min之间,考虑到网络不稳定会导致网络数据传输延时数分钟或数小时因素,,求平均值会导致误差较大,综合分析,“用水开始时间”为起始数据的时间减去“发送阈值”的一半。

// 构建用水时长与频率特征// 读取热水器使用数据记录data = pd.read_csv('tmp/water_heart.csv')// 读取用水事件记录sj = pd.read_csv('tmp/sj.csv')data['发生时间'] = pd.to_datetime(data['发生时间'],format='%Y%m%d%H%M%S') # 转换时间格式print(data.head())// 构造特征---总用水时长timeDel = pd.Timedelta('1 sec')sj['事件开始时间'] = data.iloc[sj['事件起始编号']-1,0].values-timeDelsj['事件结束时间'] = data.iloc[sj['事件终止编号']-1,0].values+timeDeltmp1 = sj["事件结束时间"] - sj["事件开始时间"]// print(tmp1)sj["总用水时长"] = np.int64(tmp1)/1000000000 // print(sj["总用水时长"])// 构造用水停顿事件// 构造特征:停顿开始时间、停顿结束时间// 停顿开始时间是从有水到无水流的时间,停顿结束时间是从无水到有水流的时间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+1,'停顿结束时间'] = data.loc[i+1,'发生时间'] + timeDel print(data.head()) // 提取停顿开始时间与结束时间所对应行号,放在数据框stop中indStopStart = data.index[data['停顿开始时间'].notnull()]+1indStopEnd = data.index[data['停顿结束时间'].notnull()]+1Stop = pd.DataFrame(data={'停顿开始编号':indStopStart[:-1],'停顿结束编号':indStopEnd[1:]})print(Stop)// 计算停顿时长,并放在数据框stop中,停顿时长=停顿结束时间-停顿开始时间tmp2 = data.loc[indStopEnd[1:]-1,"停顿结束时间"]print(tmp2)tmp3 = data.loc[indStopStart[:-1]-1,"停顿开始时间"]print(tmp3)tmp4 = tmp2.values-tmp3.valuesStop["停顿时长"] = np.int64(tmp4)/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[:,"停顿开始编号"].valuessj.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])

2.构建水量与波动特征

用水量也是识别改事件是否为洗浴事件的重要特征。例如,用水时间中的洗漱事件比洗浴事件有停顿次数多、用水总量少、平均用水少的特点。手洗大量衣物的事件比洗浴事件有停顿次数多、用水总量多、平均用水量多的特点。

同时,用水波动也是区分不同用水事件的关键。一般在洗漱事件中,刷牙和洗脸的用水量完全不同;在一次手洗衣物事件中,。每次用水的量和停顿时间相差却不大。

data["水流量"] = data["水流量"] / 60 // 原单位L/min,现转换为L/secsj["总用水量"] = 0 // 给总用水量赋一个初始值0for 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 //给水流量波动赋一个初始值0for 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 // 给停顿时长波动赋一个初始值0for i in range(len(sj)): // 当停顿次数为0或1时,停顿时长波动值为0,故排除 if sj.loc[i,"停顿次数"] > 1: 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])

3.筛选候选洗浴事件

sj_bool = (sj['用水时长'] >100) & (sj['总用水时长'] > 120) & (sj['总用水量'] > 5)sj_final = sj.loc[sj_bool,:]sj_final.to_excel('tmp/sj_final.xlsx',index = False)print('筛选出候选洗浴事件前的数据形状为:',sj.shape)print('筛选出候选洗浴事件后的数据形状为:',sj_final.shape)//筛选出候选洗浴事件前的数据形状为: (172, 15)//筛选出候选洗浴事件后的数据形状为: (76, 15)

​​返回顶部​​

三、构建行为事件分析的BP神经网络模型

1.构建神经网络模型

import numpy as npimport pandas as pdfrom sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import StandardScalerfrom sklearn.neural_network import MLPClassifierfrom sklearn.externals import joblib// 读取数据Xtrain = pd.read_excel('tmp/sj_final.xlsx')ytrain = pd.read_excel('data/water_heater_log.xlsx')test = pd.read_excel('data/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_test)// 建立模型bpnn = MLPClassifier(hidden_layer_sizes = (17,10), max_iter = 200, solver = 'lbfgs',random_state=45)bpnn.fit(x_stdtrain, y_train)// 保存模型joblib.dump(bpnn,'water_heater_nnet.m')print('构建的模型为:\n',bpnn)

2.评价神经网络模型

from sklearn.metrics import classification_reportfrom sklearn.metrics import roc_curvefrom sklearn.metrics import accuracy_scoreimport matplotlib.pyplot as pltbpnn = joblib.load('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和FPRplt.figure(figsize=(6,4)) // 创建画布plt.plot(fpr,tpr) // 绘制曲线plt.title('用户用水事件识别ROC曲线')//标题plt.xlabel('FPR') // x轴标签plt.ylabel('TPR') // y轴标签plt.savefig('用户用水事件识别ROC曲线.png') // 保存图片plt.show()## 显示图形

​​返回顶部​​

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:小程序如何引入外部css(微信小程序引入外部css)
下一篇:小程序调用百度云接口实现人脸识别(人脸识别小程序源码)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~