-
数据分析与挖掘实战-家用电器用户行为分析与事件识别
2019-01-21 13:17:24在热水器用户行为分析过程中,用水事件识别最为关键。 目标 由于热水器可能用于各种事件而不仅仅是洗浴,要求根据收集到的数据,分析用户行为。 基于热水器采集到的时间序列数据,将顺...家用电器用户行为分析与事件识别
- 背景
- 居民使用家电过程中,会因为地区气候、区域不同、年龄差异,形成不同的使用习惯,若能深入了解这些习惯,针对性地开发新功能,便能开拓市场。
- 本案例以热水器为例,分析用户行为。在热水器用户行为分析过程中,用水事件识别最为关键。
- 目标
- 由于热水器可能用于各种事件而不仅仅是洗浴,要求根据收集到的数据,分析用户行为。
- 基于热水器采集到的时间序列数据,将顺序排列的离散的用水时间节点依据水流量和停顿时间间隔划分为不同大小的时间区间,每个区间是一个可理解的一次完整用水事件,并且以这一次完整的用水事件作为一个基本事件,将时间序列数据划分为独立的用水事件并且识别出洗浴事件。
- 基于此,厂商对智能操作和节能运行进行优化。
- 分析
- 用水事件划分与识别
- 对用户的历史用水数据进行选择性抽取,构建专家样本。
- 对构建的样本数据集进行数据探索和数据预处理,包括探索用水事件时间间隔的分布、规约冗余属性、识别用水数据的缺失值,并对缺失值进行处理,根据建模的需要进行属性构造等。
- 根据上述处理,对用水样本数据建立用水事件时间间隔识别模型和划分一次完整的用水事件模型,再在一次完整用水事件划分结果的基础上,剔除短暂用水事件,缩小识别范围。
- 根据上一步得到的建模样本数据,建立洗浴事件识别模型,并进行模型评价分析。
- 用水事件划分与识别
-
处理过程
- 数据获取
- 由机器自动记录,数据量大,本案例数据为无放回随机抽取200家用户从2014年1月1日到2014年12月31日的用水记录作为建模数据。
- 数据探索
- 用水停顿时间间隔为两条水流量不为0的流水记录之间的时间间隔,为了探究用户真实的用水停顿时间间隔分布情况,统计用水停顿的时间间隔并做频率分布直方图。
- 可以知道,正常的两次用水间隔在3~7分钟。
- 数据预处理
- 数据特点
- 数据涉及上万用户且每个用户的每天数据多达数万条,存在缺失值、与分析无关的属性或许未直接反映用水事件的属性。
- 数据规约
- 属性规约
- 对用户的洗浴行为的一般性分析,所以“热水器编号”属性多余,可以去除;而且,“有无水流”属性可以通过“水流量”反映,可以去除,减少特征复杂;“节能模式”都为“关”,没有意义。
- 数值规约
- 当“开关机状态”为“关”且“水流量”为0,说明热水器不在工作,记录可以规约掉。
- 属性规约
- 数据变换
- 由于目标是对洗浴事件进行识别,这就需要识别出哪些状态是完整的用水事件,继而识别出其中的洗浴事件。由于一次完整的用水事件是根据水流量和停顿时间间隔的阈值去划分的,所以本案例还建立了阈值寻优模型。同时,为了提高在大量用水事件中寻找洗浴事件的效率,本案例建立了筛选规则剔除明显不是洗浴事件的记录,得到建模数据样本。
- 一次完整用水事件的划分模型
- 哪些连续的数据是一次完整的用水事件
- 水流量不为0表示正在使用热水,水流量为0则表示用热水停顿或者结束。如果水流量为0的状态记录之间的时间间隔超过一个阈值T(也就是前后是两个不同事件),那么从该段水流量为0的记录向前找到最后一条水流量不为0的记录作为上一次用水事件的结束,向后找到水流量不为0的记录作为下一个用水事件的开始。
- 操作流程
- Step1:读取数据记录,识别到第一条水流量不为0的数据记录,记为R1按照顺序,下一条水流量不为0的记录记为R2。
- Step2:计算Ri与Ri+1之间的差值记为gapi,若gapi大于阈值T则Ri和Ri+1之间的数据记录不能认为是同一个用水事件。同时将Ri+1记录作为新的读取数据记录的开始,返回Step1;若gapi小于阈值T,则将Ri+1与Ri之间的数据记录划分到一个用水事件,并记录下一个水流量不为0的数据记录为Ri+2。
- Step3:循环Step2,直到数据记录读取完毕,结束事件划分。
- 代码实现
- 数据预处理.py
-
# -*- coding:UTF-8 -* import pandas as pd def attrStatute(): ''' 属性规约 :return: ''' rawData = pd.read_excel('data/original_data.xls').drop(columns=["热水器编号","有无水流", "节能模式"]) return rawData def valueStatute(): ''' 数值规约 :return: ''' data = pd.read_excel('data/water_heater.xls') newData = data[data['开关机状态'].isin(['关']) & data['水流量'].isin([0])] return newData def divideEvent(): ''' 事件划分 :return: ''' # 阈值设置为4分钟 threshold = pd.Timedelta('4 min') inputFile = 'data/water_heater.xls' outputFile = 'data/dividesequence.xls' data = pd.read_excel(inputFile) data['发生时间'] = pd.to_datetime(data['发生时间'], format='%Y%m%d%H%M%S') # 只保留水流量大于0的记录 data = data[data['水流量'] > 0] # 将原数据的发生时间做一阶差分,得到一个时间差值的dataframe d = data['发生时间'].diff() > threshold # 累计求和编号数据 data['事件编号'] = d.cumsum() + 1 data.to_excel(outputFile) if __name__ == '__main__': # attrStatute().to_excel("data/water_heater.xls") # valueStatute().to_excel("data/water_heater2.xls")z divideEvent()
- 哪些连续的数据是一次完整的用水事件
- 用水事件阈值寻优模型
- 原因
- 不同地区,不同季节,使用热水器停顿时长是不同的,固定一个阈值做上述处理是不合适的,所以考虑到在不同的时间段内要更新阈值,故建立阈值寻优模型来寻找最优阈值。
- 实现方法
- 指定连续的阈值尝试划分,得到的事件个数必定不太相同,绘制折线图不难发现曲线平稳处符合大多数人的要求,利用斜率来刻画寻找这个最优点。
- 代码
- 阈值寻优模型.py
-
# -*- coding: utf-8 -*- """ 在1-9分钟进行阈值寻优 """ import numpy as np import pandas as pd def event_num(ts): ''' 得到事件数目 :param ts: :return: ''' d = data[u'发生时间'].diff() > ts return d.sum() + 1 if __name__ == '__main__': inputfile = 'data/water_heater.xls' # 使用以后四个点的平均斜率 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] 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 # 采用后n个的斜率绝对值平均作为斜率指标 h[u'斜率指标'] = pd.DataFrame(h[u'斜率'].abs()[len(h)-n:]).rolling(2).mean() ts = h[u'阈值'][h[u'斜率指标'].idxmin() - n] if ts > threshold: ts = pd.Timedelta(minutes=4) print(ts)
- 结果为4分钟
- 原因
- 属性构造
- 本案例研究的是用水行为,可以构造四类指标:时长指标、频率指标、用水的量化指标以及用水的波动指标。都是为了模型服务建立特征项。
- 筛选“洗浴事件”
- 剔除短暂用水事件,剩余的为候选洗浴事件。
- 数据清洗
- 对异常值和缺失值处理,比较简单不再叙述。
- 数据特点
- 数据挖掘建模
- 经过数据预处理这一核心步骤,得到的数据已经是满足建模要求的数据了。
- 由于洗浴事件和普通用水事件在特征上不同,根据用水日志,将洗浴事件作为训练样本训练神经网络,然后根据训练好的网络检验新的数据。
- 选取11个特征作为神经网络的输入:洗浴时间点、总用水时长、总停顿时长、平均停顿时长、停顿次数、用水时长、用水时长/总用水时长、总用水量、平均用水量水流量波动和停顿时长波动。训练BP神经网络给定的输出为0或1,其中1表示为洗浴事件,0表示不是洗浴事件。
- 使用keras库训练神经网络。
- 代码
-
# -*- coding: utf-8 -*- """ 利用神经网络挖掘建模 """ import pandas as pd from keras.models import Sequential from keras.layers.core import Dense, Dropout, Activation inputFile1 = 'data/train_neural_network_data.xls' inputFile2 = 'data/test_neural_network_data.xls' testoutputfile = 'data/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() # 建模 model = Sequential() # 添加输入层、隐藏层的连接 model.add(Dense(input_dim=11, units=17)) # 以Relu函数为激活函数 model.add(Activation('relu')) # 添加隐藏层、隐藏层的连接 model.add(Dense(input_dim=17, units=10)) # 以Relu函数为激活函数 model.add(Activation('relu')) # 添加隐藏层、输出层的连接 model.add(Dense(input_dim=10, units=1)) # 以sigmoid函数为激活函数 model.add(Activation('sigmoid')) # 编译模型,损失函数为binary_crossentropy,用adam法求解 model.compile(loss='binary_crossentropy', optimizer='adam') model.fit(x_train, y_train, epochs=100, batch_size=1) model.save_weights('data/net.model') r = pd.DataFrame(model.predict_classes(x_test), columns=['预测结果']) pd.concat([data_test.iloc[:, :5], r], axis=1).to_excel(testoutputfile) rst = model.predict(x_test) print(rst)
-
- 数据获取
-
补充说明
- 案例参考书《Python数据分析与挖掘实战》
- 具体数据集代码见我的GitHub
- 与原书有借鉴,但是较大改动代码
- 修复了原书一些旧版本代码错误
- 常见错误(均是因为keras版本改动)
- 1
- TypeError:
Dense
can accept only 1 positional arguments ('units',), but you passed the following positional arguments: [23, 34] - 解决方法
- 在Dense中写好参数名称改为Dense(input_dim=23,units=34)
- TypeError:
- 2
- ValueError: ('Some keys in session_kwargs are not supported at this time: %s', dict_keys(['class_mode']))
- 解决方法
- 模型编译代码中去掉class_mode这一属性
- 3
- UserWarning: The
nb_epoch
argument infit
has been renamedepochs
- 解决方法
- 修改代码中的“nb_epoch”为“epochs”即可
- UserWarning: The
- 1
- 背景
-
家用电器用户行为分析与事件识别
2020-08-31 09:41:46其中用水事件识别时最为关键的环节。这个例子就是采集用户的用水数据,分析用户的用水行为特征。 热水器用户用水数据,如下图: 大约有1万8千条数据,我们要根据数据分出洗浴事件识别模型,对不同地区的用户的用水...看了一个例子还不错,所以自己去研究下。刚开始是先复现一遍实验,然后对实验进行一些自己的改进。
背景
居民使用家电过程,会因为地区、气候、用户年龄、性别,形成不同的使用习惯。企业通过分析用户的行为,开发新功能,拓展市场业务。
采集相关数据,以热水器为例子,分析用户的使用行为。其中用水事件识别时最为关键的环节。这个例子就是采集用户的用水数据,分析用户的用水行为特征。
热水器用户用水数据,如下图:
大约有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/家电电器用户行为分析 -
Yii Framework框架中事件和行为的区别及应用实例分析
2020-12-18 06:58:09比较难明,关键是它们的应用场景比较难明,不知道什么时候应该使用事件和行为来开发。 关于 Yii 的事件和行为的描述,可参考 http://www.yiiframework.com/doc/api/1.1/CComponent 本文参考的文章: ... -
python数据分析与挖掘实战---chapter10家用电器用户行为分析与事件识别
2020-09-14 15:18:25在热水器用户行为分析过程中,用水事件识别是最关键的环节。比如,国内某热水器生产厂商新研发的一种高端智能热水器,在状态发生改变或者有水流状态时,会采集各监控指标数据。该厂商根据其采集的用户的用水数据,...1. 项目背景与挖掘目标
1.1 背景
居民在使用家用电器过程中,会因地区气候、不同区域、用户年龄性别差异,形成不同的使用习惯。家电企业若能深入了解不同用户群的使用习惯,开发新功能,就能开拓新市场。
要了解用户使用家用电器的习惯,必须采集用户使用电器的相关数据下面以热水器为例,分析用户的使用行为。在热水器用户行为分析过程中,用水事件识别是最关键的环节。比如,国内某热水器生产厂商新研发的一种高端智能热水器,在状态发生改变或者有水流状态时,会采集各监控指标数据。该厂商根据其采集的用户的用水数据,分析用户的用水行为特征。由于用户不仅仅使用热水器来洗浴,还可能包括洗手、洗脸、刷牙、洗菜、做饭等用水行为,所以热水器采集到的数据来自各种不同的用水事件。1.2 目标
- 根据热水器采集到的数据,划分一次完整用水事件。
- 在划分好的一次完整用水事件中,识别出洗浴事件。
2. 数据预处理
热水器用户用水事件划分与识别包括以下步骤。
- 对热水用户的历史用水数据进行选择性抽取,构建专家样本。
- 对步骤1 形成的数据集进行数据探索分析与预处理,包括探索用水事件时间间隔的分布、规约冗余属性、识别用水数据的缺失值,并对缺失值进行处理,根据建模的需要进行属性构造等。根据以上处理,对用水样本数据建立用水事件时间间隔识别模型和划分一次完整的用水事件模型,再在一次完整用水事件划分结果的基础上,剔除短暂用水事件,缩小识别范围。
2.1 数据清洗
此案例中存在用水数据状态记录缺失的情况。在热水器工作态改变或处于用水阶段时,热水器每2秒(发送阈值)传输一条状态记录,而划分一次完整用水事件时,需要一个开始用水的状态记录和结束用水的状态记录。但是,在划分一次完整用水事件时,发现数据中存在没有结束用水的状态记录情况。如下图所示,热水器状态发生改变,第5条状态记录和第7条状态记录的时间间隔应该为2秒,而表中两条记录间隔为1小时27分28秒。
这可能是由于网络故障等原因导致状态记录时间间隔为几十分钟甚至几小时的情况,该类问题若用均值去填充会造成用水时间为几十分钟甚至几小时的误差。对于上述特殊情况,书中进行如下处理:在存在用水状态记录缺失的情况下,填充一条状态记录使水流量为0,发生时间加2秒,其余属性状态不变。这同样存在偏差,即上表中第5条状态记录和第7条状态记录的1小时27分28秒间隔中,也许是连续用水半个小时才停止,此时插入的第6条状态记录只加了2秒。
对于书中的填充的思路:根据一次用水事件的结束时间和结束时间下一条记录的插值判断是否需要填充,创建一个新数据框用于填充(注意长度),用concat方法在指定位置填充,把发生时间加2秒
2.2 数据规约
由于热水器采集的用水数据属性较多,本案例对建模数据做以下数据规约。
- 属性规约:因为要对热水器用户的洗浴行为的一般规律进行挖掘分析,所以“热水器编号”可以去除;因热水器采集的数据中,“有无水流”可以通过“水流量”反映出来,“节能模式”数据都只为“关”,对建模无作用,可以去除。
- 数值规约:当热水器水流量为0时,说明热水器不处于工作状态,数据记录可以规约掉。
import pandas as pd import numpy as np
data = pd.read_excel('./chapter10/demo/data/original_data.xls')
data = data.drop(['热水器编号', '有无水流', '节能模式'], axis = 1) df = data[data['水流量'] > 0] # 只要流量大于0的记录
2.3 数据变换
2.3.1 划分一次用水事件
用户的用水数据存储在数据库中,记录了各种各样的用水事件,包括洗浴、洗手、刷牙、洗脸、洗衣和洗菜等,而且一次用水事件由数条甚至数千条的状态记录组成。所以,本案例首先需要在大量的状态记录中划分出哪些连续的数据是一次完整的用水事件。
一次完整用水事件的划分步骤如下。- 读取数据记录,识别到第一条水流量不为0的数据记录记为R₁,按顺序识别接下来的一条水流量不为0数据记录为R₂。
- 若gapi > T,则Ri+1,与Ri,及之间的数据记录不能划分到同一次用水事件。同时将R;,记录作为新的读取数据记录的开始,返回步骤1 ;若gapi < T,则将Ri+1与Ri之间数据记录的划分到同一次用水事件,并记录接下来的水流量不为0数据记录为Ri+2
- 循环执行步骤2,直到数据记录读取完毕,结束事件划分。
考虑到不同地区的人们用热水器的习惯不同,以及不同季节使用热水器时停顿的时长也可能不同,固定的停顿时长阈值对于某些特殊的情况的处理是不理想的,存在把一个事件划分为两个事件或者把两个事件合为一个事件的情况。所以,考虑到在不同的时间段内要更新阈值,本案例建立了阈值寻优模型来更新寻找最优的阈值,这样可以解决因时间变化和地域不同导致阈值存在差异的问题。
于是,阈值优化的结果如下:- 当存在一个阈值的斜率指标K之l时,则取阈值最小的点A(可能存在多个阈值的斜率指标小于1)的横坐标Xa作为用水事件划分的阈值,其中K之1中的1是经过实际数据验证的一个专家阈值。
- 当不存在K之1时,则找所有阈值中斜率指标最小的阈值;如果该阈值的斜率指标小于5,则取该阈值作为用水事件划分的阈值;如果该阈值的斜率指标不小于5,则阈值取默认值的阈值为4分钟。其中,斜率指标小于5中的5是经过实际数据验证的一个专家阈值。
n = 4 threshold = pd.Timedelta(minutes = 5) def event_num(ts): d = df['发生时间'].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() / 0.25 # 计算每两个相邻点对应的斜率 h['斜率指标'] = h['斜率'].abs().rolling(4).mean() # 往前取n个斜率绝对值平均作为斜率指标 ts = h['阈值'][h['斜率指标'].idxmin() - n] # 用idxmin返回最小值的Index,由于rolling.mean()计算的是前n个斜率的绝对值平均,所以结果要进行平移(-n) print('计算出的单次用水时长的阈值为:',ts)
data['发生时间'] = pd.to_datetime(data['发生时间'], format = '%Y%m%d%H%M%S') threshold = pd.Timedelta(minutes = 4) sjks = df['发生时间'].diff() > threshold # 相邻时间向前差分,比较是否大于阈值 sjks.iloc[0] = True # 因为diff是向前取的原因,令第一个时间为第一个用水事件的开始事件 sjjs = sjks.iloc[1:] # 向后差分的结果 sjjs = pd.concat([sjjs, pd.Series(True)]) # 令最后一个时间作为最后一个用水事件的结束时间 sj = pd.DataFrame(np.arange(1, sum(sjks) +1), columns= ['事件序号']) sj["事件起始编号"] = df.index[sjks == 1] # 定义用水事件的起始编号 sj["事件终止编号"] = df.index[sjjs == 1]+1 # 定义用水事件的终止编号
2.3.2 属性构造
# 构造特征:总用水时长 timeDel = pd.Timedelta("0.5 sec") # 数据延迟时间 sj["事件开始时间"] = data.loc[sj["事件起始编号"], '发生时间'].values - timeDel # 热水事件开始发生的时间 sj["事件结束时间"] = data.loc[sj["事件终止编号"] -1, '发生时间'].values + timeDel # 热水事件结束发生的时间 sj['洗浴时间点'] = [i.hour for i in sj["事件开始时间"]] sj["总用水时长"] = np.int64(sj["事件结束时间"] - sj["事件开始时间"]) / 1000000000 # 单位秒 sj = sj[sj["总用水时长"] > 120] # 因为是为了对洗浴事件建模,加上没做数据清洗的原因,在这直接把一些短暂用水的事件去除 sj.index = (range(0, 76))
# 构造用水停顿事件 # 构造特征“停顿开始时间”、“停顿结束时间” # 停顿开始时间指从有水流到无水流,停顿结束时间指从无水流到有水流 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 # 提取停顿开始时间与结束时间所对应行号,放在数据框Stop中 indStopStart = data.index[data["停顿开始时间"].notnull()] indStopEnd = data.index[data["停顿结束时间"].notnull()] Stop = pd.DataFrame(data={"停顿开始编号":indStopStart[:-1], "停顿结束编号":indStopEnd[1:]})
# 计算停顿时长,并放在数据框stop中,停顿时长=停顿结束时间-停顿结束时间 Stop["停顿时长"] = np.int64(data.loc[indStopEnd[1:], "停顿结束时间"].values - data.loc[indStopStart[:-1],"停顿开始时间"].values) / 1000000000
# 将每次停顿与事件匹配,停顿的开始时间要大于事件的开始时间, # 且停顿的结束时间要小于事件的结束时间 for i in range(len(sj)): Stop.loc[(Stop["停顿开始编号"] > sj.loc[i, "事件起始编号"]) & (Stop["停顿结束编号"] < sj.loc[i,"事件终止编号"]), "停顿归属事件"] = i+1 # 事件排序从1开始,range排序从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["总用水时长"] # 判断用水时长占总用水时长的比重
data["水流量"] = data["水流量"] / 60 # 原单位L/min,现转换为L/sec sj["总用水量"] = 0 # 给总用水量赋一个初始值0 for i in range(len(sj)): Start = sj.loc[i, "事件起始编号"] End = sj.loc[i, "事件终止编号"] 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, "总用水量"] 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, "事件起始编号"] End = sj.loc[i, "事件终止编号"] for j in range(Start, End ): 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, "水流量波动"]
# 构造特征:停顿时长波动 # 停顿时长波动=∑(((单次停顿时长-平均停顿时长)^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: squ = (j - sj.loc[i, "平均停顿时长"]) ** 2 sj.loc[i, "停顿时长波动"] = (squ * j) / sj.loc[i, "总停顿时长"] + sj.loc[i, "停顿时长波动"] sj_bool = sj[(sj['用水时长'] > 100) & (sj['总用水量'] > 5)] # 把剩下可以判定为短暂用水的事件去除
筛选洗浴事件(书中只有一个公式,并没相关代码和思路)
3. 模型构建
from __future__ import print_function from keras.models import Sequential from keras.layers.core import Dense, Activation, Dropout
data_train = pd.read_excel('./chapter10/demo/data/train_neural_network_data.xls') data_test = pd.read_excel('./chapter10/demo/data/test_neural_network_data.xls')
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
model = Sequential() model.add(Dense(input_dim = 11, units = 17)) model.add(Activation('relu')) model.add(Dense(input_dim = 17, units = 10)) model.add(Activation('relu')) model.add(Dense(input_dim = 10, units = 1)) model.add(Activation('sigmoid')) model.compile(loss = 'binary_crossentropy', optimizer = 'adam') model.fit(x_train, y_train, epochs = 100, batch_size = 1)
r = pd.DataFrame(model.predict_classes(x_test), columns = ['预测结果']) pd.concat([data_test.iloc[:, :5], r], axis = 1) model.predict(x_test)
得到一串警告和一个结果,警告是说predict_classes()方法到2021-01-01之后就会被删除
结果predict()方法和predict_classes()方法的得到的预测相似predict_classes():
-
用户行为路径分析
2020-10-27 10:14:29移动APP产品用户行为路径分析 一、用户行为路径分析目标是什么 二、实施过程: 1.确定关键节点事件名: 2.获取事件数据: 3.可视化: 三、结论:一、用户行为路径分析目标:
展现用户访问页面、访问路径、页面跳出等使用情况,从而了解用户在产品中的真实使用情况;
定义:主要根据每位用户在App或网站中的点击(部分展现)行为日志,分析用户在App或网站中各个模块的流转规律与特点,挖掘用户的访问或点击模式;
二、作用:
1.促进App核心模块的到达率提升,查看是否有异常流失及断点;
2.特定用户群体的主流路径提取与浏览特征刻画,如观察新增用户进入后的路径特征,可以找到刺激新增留存的关键功能或页面;
3.App产品设计的优化与改版,某些功能板块对用户是否有吸引力;
三、实施过程:
1.确定关键节点事件名:
梳理选定重要的若干用户使用节点事件数据,尽量涵盖所有重要事件,若有专题模块需求,则此模块事件尽可能详细;注意筛除一些系统产生的脏事件(埋点事件是第一步,也是最重要的一步,直接决定了成品);
2.获取事件数据:
1)数据库中事件表获取选定时间段内,通常是一天,用户ID、用户事件名、事件时间戳数据;
2)对每个用户触发的埋点事件(即用户的行为)进行排序,得到用户-行为-顺序(通常取7-10步就够了,对于重复事件,可设定session去重,
即多长时间内重复出现事件只算一次,注意区分);
SQL语句:row_number() over(partition by 用户id order by 事件时间);3)形成结果表dataframe,即所有用户相同顺序的事件排成一列;
3.可视化:
笔者用的是BDP个人版,免费的,看下教程就会用了,挺小白的;
当然Python也可以,门槛高一些;
四、结论:
1.用户整体路径流向如何;
2.关键节点转化如何,流失如何;
3.模块侧重点是否符合预期,可指导运营策略;
五、用户路径而来的一些其它相关思考:
1)容易踩的坑:和运营产品等确认好事件,包括埋点,否则出来的图就会很奇怪,此外需要注意明确重复事件的数据;
2)结论延展:特定人群(如25%会直接使用搜索功能)深入挖掘,包括人群特征、画像、流失等;
3)重要路径宜结合漏斗模型(属于用户路径的子集)深入探索,对于一些关键节点进行深入挖掘;
-
用户行为分析模型
2021-02-24 18:11:05行为事件分析法一般经过事件定义与选择、下钻分析、解释与结论等环节。事件定义与选择事件描述的是,一个用户在某个时间点、某个地方、以某种方式完成了某个具体的事情。Who、When、Where、What、How是定义一个事件... -
[笔记] 用户行为分析:JavaScript注入式监听所有DOM事件
2016-03-25 00:19:05那么,如何在不破唤性修改原有源代码的基础上去分析用户行为呢?于是就想监听web UI上的所有事件,比如用户经常点一个button,那就证明这个button对应的功能是对用户非常有用的。那么关键是要解决如何hook这些事件了... -
用户行为分析的4个步骤
2020-09-25 11:35:36设定事件=确定要分析的用户关键动作事件 设定:设立确定目标 事件:用户关键动作,如用户下单购买了一台手机 用户购买了3000档手机等 三.模型分析 主要模型分析:漏斗模型 行为路径模型 热力图模型等 漏斗模型可以看出在... -
需求分析之行为和模式
2020-12-06 11:01:28关键概念:分析模式、行为模型、事件、顺序图、状态图、状态表达。需求模型由各种元素组成:基于场景(用例)、基于类(对象和类)和行为(事件和状态)。 -
幸存与否 ——泰坦尼克号沉船事件数据分析*
2018-11-07 08:27:38幸存与否 ——泰坦尼克号沉船事件数据分析 铁达尼号沉船事件发生在1912年4月。铁达尼号是当时世界上最大...本次数据分析基于真实的泰坦尼克号沉船事件数据,分析幸存与否的关键。 一、描述性分析 (1)数据变量 此次... -
深度剖析SOC高性能实时事件关联分析引擎
2012-02-16 18:55:31【引言】安全管理平台(SOC) 的一项关键技术就是事件关联分析。借助实时的事件关联分析引擎,安全管理平台能够发掘出复杂的海量安全日志和事件背后隐藏的信息,引导安全管理人员发现外 部***和内部违规行为。作为本... -
go 捕获网卡http_安天蜜网捕获“利用ElasticSearch Groovy漏洞进行门罗币(Dog)挖矿”事件分析...
2020-12-28 20:58:12概述2019年6月13日,安天蜜网捕获到利用CVE-2015-1427(ElasticSearch Groovy)远程命令执行漏洞的攻击行为。该漏洞原理是...安天对此次事件进行了详细的样本分析,并给出预防及修复建议。样本分析关键攻击载... -
论文研究-城市轨道交通智能视频分析关键技术综述.pdf
2019-09-07 03:06:46智能视频分析技术通过对监控视频流的实时分析,对场景中的各种目标进行检测、分类、跟踪,并分析和判断目标的行为,从而能在异常情况发生时可以及时报警、主动防范,提高处理突发事件的效率。主要研究了智能视频分析... -
Amazon DevOps Guru应用机器学习来分析您的操作数据以及应用程序指标和事件,以识别偏离正常操作模式的行为...
2021-02-13 18:29:00当DevOps Guru识别出关键问题时,它会自动向您发出警报,提示您相关异常,可能的根本原因以及发生问题的时间和地点的上下文。 DevOps Guru还将在可能的情况下提供有关如何解决该问题的规范性建议。 配置DevOps Guru... -
JavaScript事件总结
2020-04-29 13:16:34JavaScript 与 HTML 之间的交互是通过事件实现的,对于 Web 应用来说,有下面这些代表性的事件:单击事件、鼠标移入移出事件、键盘按下/弹起事件等,总的来说事件就是用户与浏览器的交互行为,下面我就来分析下Ja... -
基于BBS的煤矿未遂事件研究
2020-05-31 19:20:11在阐述未遂事件国内外的研究状况及我国煤炭行业未遂事件研究必要性的基础上,选取太原某煤矿采煤工作为试验对象,记录并分析未遂事件的发生,运用BBS法对通过灰色关联法对选取的关键行为进行管理。试验运用安全行为指数... -
数据分析-C端产品分析模型
2020-06-26 12:58:13产品 1、 关注产品的健康以及发展程度:整体-登录量、用户量、留存率;关键功能的点击量;关键路径-转化率 2、 产品优化 1) 路径优化:各个环节的转化率、使用时长 ...3、 功能新增: ...1、 行为事件分析 -
案例分析的报告.doc
2020-05-16 07:58:25注明案例分析的题目参与人员等等必要事项学生在写的时候还要注明班级小组日期设计要美观 (二)主题 1案例陈述 案例全盘陈述和删节陈述但是要严格保留案例的实际性要全面翔实时间地点人物事件尤其是真实情景中的关键... -
案例分析报告格式.doc
2021-01-19 11:07:46时间、地点、人物、事件,尤其是真实情景中的关键因素不可遗漏,特别要突出情境中的要素间的冲突——人物间的冲突、行为与结果的冲突、决策中的困境和困惑。 第三,案例分析/策略方法 针对第一种类型,该部分就是... -
病毒逆向分析
2018-03-18 16:32:55不出意料,检出率还挺高(60 / 66),毕竟不算是特别新的病毒了, 但是在高检出率的同时,virustotal和腾讯的哈勃分析系统都没能检测到什么关键的病毒行为(腾讯只检出了打开事件,virustotal只检出Isdebug... -
统计SDK这些事(五)路径分析
2020-06-30 15:45:17用户行为路径分析是互联网数据分析中很普遍的一种分析方法,通过分析用户在网站、APP内的行为路径能够帮助产品经理与用户运营了解用户的核心路径,发现影响用户转化的关键节点,挖掘用户的访问与点击模式,优化APP的... -
vue用户行为收集_Vue前端数据采集 埋点 追踪用户系列行为
2020-12-20 12:22:37埋点分析,是网站分析的一种常用的数据采集方法。数据埋点分为初级、中级、高级三种方式。...中级:植入多段代码,追踪用户在平台每个界面上的系列行为,事件之间相互独立(如打开商品详情页——选择... -
新颖的网络域名用户关键角色识别方法
2021-01-20 05:33:36该算法首先利用ISODATA方法对网络事件用户行为进行聚类分析;在此基础上,改进混合式蚁群(HBACA)算法,对网络事件中各用户积累行为进行持续分析,从而识别该事件执行过程中各用户所起的作用。仿真结果表明,该算法... -
日志流量分析
2019-07-17 22:01:18■无效数据:缺少访客id,会话id,订单id等关键属性的,针对不同事件有不同,的属性要求 ■解析补全数据:浏览器信息、操作系统信息、地域信息等 ■格式化数据:时间日期、客户端信息等 ■数据来源:存储在HDFS上的用户...