python数据分析与挖掘实战---chapter10家用电器用户行为分析与事件识别

1. 项目背景与挖掘目标

1.1 背景

居民在使用家用电器过程中,会因地区气候、不同区域、用户年龄性别差异,形成不同的使用习惯。家电企业若能深入了解不同用户群的使用习惯,开发新功能,就能开拓新市场。
要了解用户使用家用电器的习惯,必须采集用户使用电器的相关数据下面以热水器为例,分析用户的使用行为。在热水器用户行为分析过程中,用水事件识别是最关键的环节。比如,国内某热水器生产厂商新研发的一种高端智能热水器,在状态发生改变或者有水流状态时,会采集各监控指标数据。该厂商根据其采集的用户的用水数据,分析用户的用水行为特征。由于用户不仅仅使用热水器来洗浴,还可能包括洗手、洗脸、刷牙、洗菜、做饭等用水行为,所以热水器采集到的数据来自各种不同的用水事件。

1.2 目标

  1. 根据热水器采集到的数据,划分一次完整用水事件。
  2. 在划分好的一次完整用水事件中,识别出洗浴事件。

2. 数据预处理

热水器用户用水事件划分与识别包括以下步骤。

  1. 对热水用户的历史用水数据进行选择性抽取,构建专家样本。
  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 划分一次用水事件

用户的用水数据存储在数据库中,记录了各种各样的用水事件,包括洗浴、洗手、刷牙、洗脸、洗衣和洗菜等,而且一次用水事件由数条甚至数千条的状态记录组成。所以,本案例首先需要在大量的状态记录中划分出哪些连续的数据是一次完整的用水事件。
一次完整用水事件的划分步骤如下。

  1. 读取数据记录,识别到第一条水流量不为0的数据记录记为R₁,按顺序识别接下来的一条水流量不为0数据记录为R₂。
  2. 若gapi > T,则Ri+1,与Ri,及之间的数据记录不能划分到同一次用水事件。同时将R;,记录作为新的读取数据记录的开始,返回步骤1 ;若gapi < T,则将Ri+1与Ri之间数据记录的划分到同一次用水事件,并记录接下来的水流量不为0数据记录为Ri+2
  3. 循环执行步骤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 =<
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值