《Python数据分析与挖掘实战》第10章(上)——DNN

本文是基于《Python数据分析与挖掘实战》的实战部分的第10章的数据——《家用电器用户行为分析与事件识别》做的分析。

旨在补充原文中的细节代码,并给出文中涉及到的内容的完整代码;另外,原文中的数据处理部分排版先后顺序个人感觉较为凌乱,在此给出梳理。

在作者所给代码的基础上增加的内容包括:  

1)在数据规约部分: 书中提到:规约掉热水器"开关机状态"=="关"且”水流量”==0的数据,说明热水器不处于工作状态,数据记录可以规约掉。但由后文知,此条件不能进行规约 因为,"开关机状态"=="关"且”水流量”==0可能是一次用水中的停顿部分,删掉后则无法准确计算关于停顿的数据

 2)在一次完整用水事件的划分模型中: 将时间间隔列数据离散并面元,探索了不同时间间隔中,用水事件的个数; 画用水停顿时间间隔频率分布直方图; 确定一次用水事件停顿阈值,然后划分一次完整用水事件。 

3)用水事件阈值寻优模型: 通过频率分布直方图-确定阈值的变化与划分得到的事件个数关系 通过图像中斜率指标-确定阈值的变化与划分得到的事件个数关系

4)属性构造中: 原书中只给出了需要构造的属性的定义,并未给出具体代码,本文给出了具体的代码;并给出了两种方法求用水事件的时间间隔

 5)模型构造: 添加了显示混淆矩阵可视化预测结果,查看训练结果正确率

1 背景与目标分析

    根据热水器厂商提供的数据进行分析,对用户的用水事件进行分析,判断用水是否是洗浴事件,识别不同用户的用水习惯,以提供个性化的服务。

    实质:二分类问题

2 数据探索

    首先,通过频率分布直方图分析用户用水停顿时间间隔的规律性;然后,探究划分一次完整用水事件的时间间隔阈值。
data = pd.read_excel('original_data.xls',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) #7679

data[u'用水停顿时间间隔']= data[u'发生时间'].diff()/ np.timedelta64(1, 'm') #将datetime64[ns]转成 以分钟为单位(*****)
data= data.fillna(0) # 替换掉data[u'用水停顿时间间隔']的第一个空值

2.1 数据质量分析

#-----第*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

2.2 数据特征分析

2.2.1 分布分析

#----第*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'])

f1 = lambda x :'%.2f%%' %  (x*100)
dx[['f']]= dx[['fn']].applymap(f1)
dx

#-----第*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.jpg')
plt.show()

3 数据预处理

3.1 数据规约

# 规约掉"热水器编号"、"有无水流"、"节能模式"三个属性
# 注意:
#书中提到:规约掉热水器"开关机状态"=="关"且”水流量”==0的数据,说明热水器不处于工作状态,数据记录可以规约掉。但由后文知,此条件不能进行规约

# 因为,"开关机状态"=="关"且”水流量”==0可能是一次用水中的停顿部分,删掉后则无法准确计算关于停顿的数据

or_data = pd.read_excel('original_data.xls',encoding='gbk')
or_data.head()

data = or_data.drop(or_data.columns[[0,5,9]],axis=1) # 删掉不相关属性
data.to_excel('data_guiyue.xlsx')
data.head()

3.2 数据变换

3.2.1 用水事件阈值寻优模型

根据数据探索部分,初步可以推测,用水事件停顿阈值为四分钟较为合适。但是为了进一步确定是否为四分钟,为此接下来探索的是——用水事件阈值寻优模型。

#第一步:确定阈值与事件数的关系
#****************************
#@1  目标:确定阈值的变化与划分得到的事件个数关系
#    方法:通过频率分布直方图
#****************************
timedeltalist = np.arange(2.25,8.25,0.25)
# 从2.25到8.25间,以间隔为0.25,确定阈值即,阈值范围为[2.25,2.5,2.75,3,...,7.75,8]
counts = [] # 记录不同阈值下的事件个数
for i in range(len(timedeltalist)):
    threshold = pd.Timedelta(minutes = timedeltalist[i])#阈值为四分钟
    d = data[u'发生时间'].diff() > threshold #  # 相邻时间做差分,比较是否大于阈值
    data[u'事件编号'] = d.cumsum() + 1 # 通过累积求和的方式为事件编号
    temp = data[u'事件编号'].max()
    counts.append(temp)
coun = pd.Series(counts, index=timedeltalist)
# 画频率分布直方图
#将阈值与对应的事件数绘制成频率分布直方图,以确定最优阈值

plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams['axes.unicode_minus']= False
plt.rc('figure', figsize=(8,6))
np.set_printoptions(precision=4)
fig = plt.figure()
fig.set(alpha=0.2)#设置图标透明度
ax = fig.add_subplot(1,1,1)
# coun.plot(linestyle='-.',color='r',marker='<')
coun.plot(style='-.r*')#同上
ax.locator_params('x',nbins = int(len(coun)/2)+1)  # (****)
ax.set_xlabel(u'用水事件间隔阈值(分钟)')
ax.set_ylabel(u'事件数(个)')
ax.grid(axis='y',linestyle='--') # (****)
plt.savefig('threshold_numofCase.jpg')
plt.show()

    由上图可知,图像趋势平缓说明用户的停顿习惯趋于稳定,所以取该段时间开始作为阈值,既不会将短的用水时间合并,也不会将长的用水时间拆开,因此,最后选取一次用水时间间隔阈值为4分钟

利用阈值的斜率指标来作为某点的斜率指标

# 第二步:阈值优化
#****************************
#@2  目标:确定阈值的变化与划分得到的事件个数关系
#    方法:通过图像中斜率指标
#****************************

# 当存在阈值的斜率指标 k<KS :
#     取阈值最小的点A(可能存在多个阈值的斜率指标小于1)的横坐标x作为用水事件划分的阈值(该值是经过实验数据验证的专家阈值)
# 当不存在阈值的斜率指标 k<KS:
#     找所有阈值中“斜率指标最小”的阈值t1:
#     若:该阈值t1对应的斜率指标小于KS2:
#         则取该阈值作为用水事件划分的阈值
#     若:该阈值t1对应的斜率指标不小于KS2
#         则阈值取默认值——4分钟
# 备注:
# KS是评价斜率指标用的专家阈值1
# KS是评价斜率指标用的专家阈值2
data = pd.read_excel('dataExchange_divideEvent.xlsx')
n = 4 #使用以后四个点的平均斜率
KS = 1 # 专家阈值1
KS2 = 5 # 专家阈值2
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 = DataFrame(dt,columns = [u'阈值']) # 定义阈值列(**)
h[u'事件数'] = h[u'阈值'].apply(event_num) # 计算每个阈值对应的事件数(*****)
h[u'斜率'] = h[u'事件数'].diff()/0.25 # 计算每个相邻点对应的斜率(****)
h[u'斜率指标偏移前'] = pd.rolling_mean(h[u'斜率'].abs(), n)# 采用当前指标和后n个指标斜率的绝对值的平均作为当前指标的斜率

h[u'斜率指标'] = np.nan
h[u'斜率指标'][:-4] = h[u'斜率指标偏移前'][4:]


mink = h[u'斜率指标'][h[u'斜率指标'] < KS]# 斜率指标小于1的值的集合
mink1 = h[u'斜率指标'][h[u'斜率指标'] < KS2]# 斜率指标小于5的值的集合

if list(mink): # 斜率指标值小于1不为空时,即,存在斜率指标值小于1时
    minky = [h[u'阈值'][i] for i in mink.index]# 取“阈值最小”的点A所对应的间隔时间作为ts
    ts = min(minky) #取最小时间为ts
elif list(mink1):# 当不存在斜率指标值小于1时,找所有阈值中“斜率指标最小”的阈值
    t1 = h[u'阈值'][h[u'斜率指标'].idxmin()] #“斜率指标最小”的阈值t1
    # ts = h[u'阈值'][h[u'斜率指标偏移前'].idxmin() - n] #等价于前一行作用(*****)
    # 备注:用idxmin返回最小值的Index,由于rolling_mean自动计算的是前n个斜率的绝对值的平均,所以结果要平移-n,得到偏移后的各个值的斜率指标,注意:最后四个值没有斜率指标因为找不出在它以后的四个更长的值
    if h[u'斜率指标'].min()<5:
        ts = t1#当该阈值的斜率指标小于5,则取该阈值作为用水事件划分的阈值
    else:
        ts = pd.Timedelta(minutes = 4)# 当该阈值的斜率指标不小于5,则阈值取默认值——4分钟

tm = ts/np.timedelta64(1, 'm')

print "当前时间最优时间间隔为%s分钟" % tm
至此,确定了一次用水停顿阈值为4分钟,开始划分一次完整事件。
threshold = pd.Timedelta(minutes=4)#阈值为四分钟
d = data[u'发生时间'].diff() > threshold # 相邻时间做差分,比较是否大于阈值(*****)
data[u'事件编号'] = d.cumsum() + 1 # 通过累积求和的方式为事件编号(*****)

data.to_excel('dataExchange_divideEvent.xlsx')

3.2.3 属性构造

   由于属性构造内容较多,因此,本小节单独放在下一篇博客中进行分享。


备注:本章节完整代码详见点击打开链接

  • 2
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Python DNN模型在训练据上出现欠拟合时,意味着该模型不能很好地捕捉到训练据中的相关模式和特征。常见的欠拟合表现为训练损失无法被有效地降低,模型无法获得足够低的训练误差。 欠拟合可能由以下原因导致: 1. 模型复杂度不足:DNN模型的层和宽度不足以提取据中的复杂模式。此时,可以尝试增加模型的层、神经元个或者使用更复杂的模型架构来提高模型的表达能力。 2. 据量不足:如果训练据量过小,模型可能无法学习到据中的全部模式。增加训练据量可以提高模型的泛化性能。 3. 特征选择或提取不当:如果输入特征无法很好地表示据的信息,模型可能无法学习到准确的规律。此时可以尝试使用更具表征性的特征或者进行特征工程来提升模型性能。 4. 学习率过低:如果模型的学习率设置过低,模型可能需要更长的时间才能适应据中的特征。增加学习率可以加速模型的训练过程。 针对DNN模型欠拟合问题,可以采取以下解决方法: 1. 增加模型复杂度,增加网络层和神经元个。 2. 收集更多的训练据以提高模型的泛化能力。 3. 使用更具表征性的特征或进行特征工程。 4. 调整学习率以提高模型的学习效率。 通过适当地调整模型架构、优化算法和据集,可以解决DNN模型在训练据上的欠拟合问题,提高模型的性能和准确度。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值