看了一个例子还不错,所以自己去研究下。刚开始是先复现一遍实验,然后对实验进行一些自己的改进。
背景
居民使用家电过程,会因为地区、气候、用户年龄、性别,形成不同的使用习惯。企业通过分析用户的行为,开发新功能,拓展市场业务。
采集相关数据,以热水器为例子,分析用户的使用行为。其中用水事件识别时最为关键的环节。这个例子就是采集用户的用水数据,分析用户的用水行为特征。
热水器用户用水数据,如下图:
大约有1万8千条数据,我们要根据数据分出洗浴事件识别模型,对不同地区的用户的用水进行识别,根据识别结果比较不同客户群的客户使用习惯。从而,可以给不同的客户提供合适的个性化产品和相应的营销策略。
目标:
1、根据数据,划分一次完整的用水事件。
2、在划分好的一次完整用水事件中,识别出洗浴事件。
分析方法与过程
本次的建模流程图为:
建模流程:
1、业务系统
2、数据抽取
3、数据探索与预处理
4、建模和应用
5、结果和反馈
数据抽取
此热水器在状态发生改变或者水流量为非零时,每两秒会采集一条状态数据。由于数据很大,所以采用无放回随机抽样法抽取一部分用水记录作为原始建模数据。
数据探索分析
以下代码在jupyter notebook里完成。
首先通过频率直方图,观察用户用水停顿时间间隔的规律性。
先查看水流量的最大值和最小值,代码:
import pandas as pd
import numpy as np
from pandas import DataFrame
import matplotlib.pyplot as plt
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']
inputfile = '../data/original_data.xls' # 输入数据路径,需要使用Excel格式
# outputfile = '../tmp/dividsequence.xls' # 输出数据路径,需要使用Excel格式
data = pd.read_excel(inputfile, encoding='gbk')
# 将该特征转成日期时间格式(***)
data[u'发生时间'] = pd.to_datetime(data[u'发生时间'], format='%Y%m%d%H%M%S')
data = data[data[u'水流量'] > 0] # 只要流量大于0的记录
# print(len(data)) # 7696
# 将datetime64[ns]转成 以分钟为单位(*****)
data[u'用水停顿时间间隔'] = data[u'发生时间'].diff() / np.timedelta64(1, 'm')
data = data.fillna(0) # 替换掉data[u'用水停顿时间间隔']的第一个空值
# -----第*1*步-----数据探索,查看各数值列的最大最小和空值情况
data_explore = data.describe().T
data_explore['null'] = len(data) - data_explore['count']
explore = data_explore[['min', 'max', 'null']]
explore.columns = [u'最小值', u'最大值', u'空值数']
explore
最小值 最大值 空值数
水流量 8.0 77.000000 0.0
用水停顿时间间隔 0.0 2093.366667 0.0
再将时间间隔划分为各个区间,计算水停顿频率,代码:
# ----第*2*步-----离散化与面元划分
# 将时间间隔列数据划分为0~0.1,0.1~0.2,0.2~0.3....13以上,由数据描述可知,
# data[u'用水停顿时间间隔']的最大值约为2094,因此取上限2100
Ti = list(data[u'用水停顿时间间隔']) # 将要面元化的数据转成一维的列表
timegaplist = [
0.0, 0.1, 0.2, 0.3, 0.5, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 2100
] # 确定划分区间
cats = pd.cut(Ti, timegaplist, right=False) # 包扩区间左端,类似"[0,0.1)",(默认为包含区间右端)
x = pd.value_counts(cats)
x.sort_index(inplace=True)
dx = DataFrame(x, columns=['num'])
dx['fn'] = dx['num'] / sum(dx['num'])
dx['cumfn'] = dx['num'].cumsum() / sum(dx['num'])
def f1(x):
return '%.2f%%' % (x * 100) # 停顿频率
dx[['f']] = dx[['fn']].applymap(f1)
dx
可以看出0到0.3分钟的停顿频率最高。
我们继续用matplotlib画出水停顿时间间隔频率分布直方图,代码:
# -----第*3*步-----画用水停顿时间间隔频率分布直方图
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
dx['fn'].plot(kind='bar')
plt.ylabel(u'频率/组距')
plt.xlabel(u'时间间隔(分钟)')
p = 1.0 * dx['fn'].cumsum() / dx['fn'].sum() # 数值等于 dx['cumfn'],但类型是列表
dx['cumfn'].plot(color='r', secondary_y=True, style='-o', linewidth=2)
plt.annotate(format((p[4]), '.4%'),
xy=(7, p[4]),
xytext=(7 * 0.9, p[4] * 0.95),
arrowprops=dict(
arrowstyle="->",
connectionstyle="arc3,rad=.2")) # 添加注释,即85%处的标记。这里包括了指定箭头样式。
plt.ylabel(u'累计频率')
plt.title(u'用水停顿时间间隔频率分布直方图')
plt.grid(axis='y', linestyle='--')
# fig.autofmt_xdate() #自动根据标签长度进行旋转
for label in ax.xaxis.get_ticklabels(): # 此语句完成功能同上,但是可以自定义旋转角度
label.set_rotation(60)
plt.savefig('Water-pause-times.png', dpi=500, bbox_inches='tight') # 解决图片不清晰,不完整的问题
plt.show()
刚开始,图像不完整,这是jupyter notebook造成的错误,用vscode 或者Pycharm运行,图像没问题,后来自己加了些代码改进了在jupyter notebook里的输出图像。
数据预处理
由于数据量大,有些数据可能存在缺失值,或者一些无关联的属性,针对这些情况应用了缺失值处理、数据规约和属性构造等来解决问题。
1、数据规约
规约掉“热水器编号”、“有无水流”、“节能模式”。
先看看原始数据,代码:
or_data = pd.read_excel(inputfile, encoding='gbk')
or_data.head()
然后删掉上面说的3个数据列,代码:
data = or_data.drop(or_data.columns[[0,5,9]],axis=1) # 删掉不相关属性
data.to_excel('data_guiyue.xlsx')
data.head()
阈值寻优代码
# -*- 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]
# 所以结果要进行平移(-n)
if ts > threshold:
ts = pd.Timedelta(minutes=4)
print(ts)
h
0 days 00:04:00
阈值 事件数 斜率 斜率指标
0 00:01:00 232 NaN NaN
1 00:01:15 227 -20.0 NaN
2 00:01:30 218 -36.0 NaN
3 00:01:45 207 -44.0 NaN
4 00:02:00 201 -24.0 31.0
5 00:02:15 197 -16.0 30.0
6 00:02:30 194 -12.0 24.0
7 00:02:45 191 -12.0 16.0
8 00:03:00 186 -20.0 15.0
9 00:03:15 181 -20.0 16.0
10 00:03:30 178 -12.0 16.0
11 00:03:45 174 -16.0 17.0
12 00:04:00 172 -8.0 14.0
13 00:04:15 172 0.0 9.0
14 00:04:30 171 -4.0 7.0
15 00:04:45 171 0.0 3.0
16 00:05:00 171 0.0 1.0
17 00:05:15 169 -8.0 3.0
18 00:05:30 164 -20.0 7.0
19 00:05:45 164 0.0 7.0
20 00:06:00 163 -4.0 8.0
21 00:06:15 161 -8.0 8.0
22 00:06:30 159 -8.0 5.0
23 00:06:45 158 -4.0 6.0
24 00:07:00 158 0.0 5.0
25 00:07:15 158 0.0 3.0
26 00:07:30 155 -12.0 4.0
27 00:07:45 154 -4.0 4.0
28 00:08:00 153 -4.0 5.0
29 00:08:15 153 0.0 5.0
30 00:08:30 152 -4.0 3.0
31 00:08:45 150 -8.0 4.0
训练多层神经网络代码
运用tf2.0
# -*- coding: utf-8 -*-
# 建立、训练多层神经网络,并完成模型的检验
from __future__ import print_function
import pandas as pd
import tensorflow as tf
from tensorflow.keras import datasets, layers, optimizers, Sequential, metrics, callbacks
inputfile1 = '../data/train_neural_network_data.xls' # 训练数据
inputfile2 = '../data/test_neural_network_data.xls' # 测试数据
testoutputfile = '../tmp/test_output_data.xls' # 测试数据模型输出文件
data_train = pd.read_excel(inputfile1) # 读入训练数据(由日志标记事件是否为洗浴)
data_test = pd.read_excel(inputfile2) # 读入测试数据(由日志标记事件是否为洗浴)
y_train = data_train.iloc[:, 4].values # 训练样本标签列
x_train = data_train.iloc[:, 5:17].values # 训练样本特征
y_test = data_test.iloc[:, 4].values # 测试样本标签列
x_test = data_test.iloc[:, 5:17].values # 测试样本特征
print(x_train.shape)
print(y_train.shape)
model = Sequential([layers.Dense(20, activation='relu', input_shape=(11,)),
layers.BatchNormalization(),
layers.Dropout(0.2),
layers.Dense(10, activation='relu'),
layers.BatchNormalization(),
layers.Dropout(0.2),
layers.Dense(1, activation='sigmoid')])
model.summary()
model.compile(optimizer=optimizers.Adam(lr=0.001),
loss=tf.losses.binary_crossentropy,
metrics=['acc'])
# 运用tensorboard可视化
log_dir = 'logs'
tensorboard_callback = callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1, write_images=True)
model.fit(x_train, y_train, epochs=1000, validation_data=[x_test, y_test], validation_freq=1,
callbacks=[tensorboard_callback])
model.save('model.h5')
test_scores = model.evaluate(x_test, y_test)
print('test loss:', test_scores[0])
print('test accuracy:', test_scores[1])
'''
载入模型
print('loaded model from file.')
network = tf.keras.models.load_model('model.h5', compile=True)
network.predict(x_test)
'''
结果:
28/28 [==============================] - 0s 5ms/sample - loss: 0.0486 - acc: 0.9643 - val_loss: 0.3131 - val_acc: 0.9048
Epoch 999/1000
28/28 [==============================] - 0s 6ms/sample - loss: 0.0129 - acc: 1.0000 - val_loss: 0.3007 - val_acc: 0.9048
Epoch 1000/1000
28/28 [==============================] - 0s 6ms/sample - loss: 0.0218 - acc: 1.0000 - val_loss: 0.2849 - val_acc: 0.9048
21/1 [===============================] - 0s 237us/sample - loss: 0.2849 - acc: 0.9048
test loss: 0.28485798835754395
test accuracy: 0.9047619
精度图像:
之所以抖动是因为加入了dropout
模型的Main Graph
网络结构是11–>20–>10–>1,是一个二分类问题,一共有21条数据,准确识别了19条数据,模型对洗浴事件的识别准确率为90.48%
由于训练数据太少,精度可能不够。
代码在我的码云里,链接https://gitee.com/rengarwang/Python_data_analysis_code/tree/master/家电电器用户行为分析
有用请点个赞!!
本站所有文章均为原创,欢迎转载,请注明文章出处:https://blog.csdn.net/weixin_45092662。百度和各类采集站皆不可信,搜索请谨慎鉴别。技术类文章一般都有时效性,本人习惯不定期对自己的博文进行修正和更新,因此请访问出处以查看本文的最新版本。