本章的上机实验部分并没有侧重于模型,而是在于数据预处理,而文中也没有具体的代码。因此,本文主要对数据的预处理进行记录,主要包括用水事件划分、用水时长、总用水量、平均水流量等。
主要有以下过程:
- 读取数据
- 划分用水事件
- 添加事件开始时间和结束时间
- 计算用水时长和平均水流量
- 计算水流量波动
- 总结
读取数据
读取数据的代码如下:
import pandas as pd
from pandas import Series,DataFrame
import numpy as np
data=pd.read_excel("D:/ProgramData/PythonDataAnalysiscode/chapter10/demo/data/water_heater.xls")
划分用水事件
按照书中对一个用水事件的描述,相隔时间大于4分钟则是另一个用水事件的开始。因此,先选取所有水流量大于0的记录,以4分钟为阈值,得出每个用水事件,然后再将用水事件过程中的水流量为0的记录填充进来,并得出事件起始数据编号和终止数据编号。
代码如下:
#用水事件划分
threshold=pd.Timedelta(minutes=4)
data[u'发生时间']=pd.to_datetime(data[u'发生时间'],format='%Y%m%d%H%M%S')
data=data[data[u'水流量']>0]
d=data[u'发生时间'].diff()>threshold
data[u'事件编号']=d.cumsum()+1
data_1=DataFrame(columns=[u'事件编号',u'事件起始数据编号',u'事件终止数据编号'])
start_list=[]
end_list=[]
data_all=data_0[0:0]
#d.sum()+1为事件数
for i in range(1,d.sum()+2):
x=data[data[u'事件编号']==i][:1].index[0]
y=data[data[u'事件编号']==i][-1:].index[0]
#把水流量为0的数据补充进来
data_all=pd.concat([data_all,data_0[x:y+1]])
start_list.append(x)
end_list.append(y)
data_1[u'事件编号']=np.arange(1,d.sum()+2)
data_1[u'事件起始数据编号']=start_list
data_1[u'事件终止数据编号']=end_list
data_all=pd.concat([data_all,data[u'事件编号']],axis=1)
#填补缺失值
data_all=data_all.fillna(method='ffill')
data_all[u'事件编号']=data_all[u'事件编号'].astype('int64')
data_all[u'水流量']=data_all[u'水流量'].astype('float64')
添加事件开始时间和结束时间
按照书中意思,将每个事件的起始发生时间减去发送阈值的一半为事件开始时间,每个事件的终止发生时间减去发送阈值的一半为事件结束时间。
代码如下:
#添加事件开始时间和结束时间
#发送阈值为2
s=pd.Timedelta(seconds=2)
time_s_list=[]
time_e_list=[]
for j in range(1,d.sum()+2):
t1=data[data[u'事件编号']==j].iloc[0,0]-s/2
t2=data[data[u'事件编号']==j].iloc[-1,0]+s/2
time_s_list.append(t1)
time_e_list.append(t2)
data_1[u'事件开始时间']=time_s_list
data_1[u'事件结束时间']=time_e_list
计算用水时长和平均水流量
总用水时长应该等于用水时长加上停顿时长,而一次水流量不为0的总时间就是一次的用水时长,按照书中意思,指的是其与上一行的时间间隔/2+与下一行的时间间隔/2,将一次用水事件中所有水流量不为0的用水时长相加即可得出用水时长,最后可以利用总用水时长与用水时长相减求出停顿时长,并可利用水流量为0的记录数得出一次用水事件中的停顿次数。每一次的用水时长乘一次用水的水流量就是用水量,将一次用水事件中的所有水流量相加则是总用水量,总用水量/用水时长便是平均水流量。
代码如下:
#用水时长
data_1[u'总用水时长']=data_1[u'事件结束时间']-data_1[u'事件开始时间']
#将时长转化成秒数
data_1[u'总用水时长']=data_1[u'总用水时长']/np.timedelta64(1,'s')
#加一列相邻两列间的时间间隔,用来计算用水时长
data_all[u'时间间隔']=data_all[u'发生时间'].diff()
#建立一个函数用来计算用水时长
def get_time(x):
if len(x)==1:
dt=pd.Timedelta(seconds=0)
dv=((dt+s)/np.timedelta64(1,'s'))*x.iloc[0,6]
dc=0
else:
#创建一个空列表
lindex=[]
for k in range(len(x)):
#如果不为0,则将其索引存入列表
if x.iloc[k,6]!=0:
lindex.append(k)
#计算停顿次数,即一次事件中,水流量为0的次数
dc=len(x)-len(lindex)
#设置用水时长和用水量起始值
dt=pd.Timedelta(seconds=0)
dv=0
ds=0
for m in lindex:
#用水时长=(水流量不为0)与上一行的时间间隔/2+与下一行的时间间隔/2
if m==0:
dt=dt+x.iloc[m+1,10]/2
elif m==len(x)-1:
dt=dt+x.iloc[m,10]/2
else:
dt=dt+(x.iloc[m,10])/2+(x.iloc[m+1,10])/2
#总用水量=每一个用水时长*水流量
dv=dv+(dt/np.timedelta64(1,'s'))*x.iloc[m,6]
return (dt,dv,dc)
dt_list=[]
dv_list=[]
dc_list=[]
for n in range(1,d.sum()+2):
dt,dv,dc=get_time(data_all[data_all[u'事件编号']==n])
dt_list.append(dt+s)
dv_list.append(dv)
dc_list.append(dc)
data_1[u'用水时长']=dt_list
#将时长转化为秒数
data_1[u'用水时长']=data_1[u'用水时长']/np.timedelta64(1,'s')
data_1[u'停顿时长']=data_1[u'总用水时长']-data_1[u'用水时长']
data_1[u'总用水量']=dv_list
data_1[u'停顿次数']=dc_list
data_1[u'平均停顿时长']=data_1[u'停顿时长']/data_1[u'停顿次数']
data_1[u'平均水流量']=data_1[u'总用水量']/data_1[u'用水时长']
计算水流量波动
按照书中意思,水流量波动为(单次水流值-平均水流量)^2*持续时间/总的有水流量的时间。
代码如下:
#计算用水波动指标
ds_list=[]
for a in range(1,d.sum()+2):
ddata=data_all[data_all[u'事件编号']==a]
ds=0.0
if len(ddata)==1:
dt_b=s
ds=ds+((ddata.iloc[0,6]-data_1.iloc[a-1,11])**2*(dt_b/np.timedelta64(1,'s')))/(data_1.iloc[a-1,6])
else:
for b in range(len(ddata)):
if b==0:
dt_b=ddata.iloc[b+1,10]/2
elif b==len(ddata)-1:
dt_b=ddata.iloc[b,10]/2
else:
dt_b=(ddata.iloc[b,10])/2+(ddata.iloc[b+1,10])/2
#((单次水流的值-平均水流量)^2*持续时间)/总的有水流量的时间
ds_1=ddata.iloc[b,6]-data_1.iloc[a-1,11]
ds_1=ds_1*ds_1
#时间间隔要转换成秒数才能计算
ds_2=ds_1*(dt_b/np.timedelta64(1,'s'))
ds=ds+ds_2/(data_1.iloc[a-1,6])
ds_list.append(ds)
data_1[u'水流量波动']=ds_list
总结
这一章的数据转换着实费了些功夫,因为书中并没有相关代码,就只能自己理解,尝试编写。初看概念觉得并不复杂,但真的自己处理起来觉得还是挺麻烦的,但在编写过程中也对pandas、list以及函数的运用更加熟悉了,也算是小有成就感。
最后,由于上述代码均是本人通过自己对书中各概念和公式的理解自行编写,因此可能会有理解错误,或是选了较为复杂的方法。
如果有人发现错误或是有更为好的方法,希望可以一起讨论。