四、数据预处理
1. 属性归约
因为分析主要对象热水器用户,分析的主要目的为热水器用户洗浴行为的一般规律,所以“热水器编号”属性可以除去;因为“有无水流”可以通过“水流量”属性反映出来,“节能模式”属性取值相同均为“关”,对分析无用,可以去除。
删除“热水器编号”、“有无水流”、“节能模式”后数据如下所示:
import pandas as pd
import numpy as np
import matplotlib. pyplot as plt
% matplotlib inline
data = pd. read_excel( '../data/original_data.xls' )
print ( '初始状态的数据形状为:' , data. shape)
data. drop( labels= [ "热水器编号" , "有无水流" , "节能模式" ] , axis= 1 , inplace= True )
print ( '删除冗余属性后的数据形状为:' , data. shape)
data. to_csv( '../tmp/water_heart.csv' , index= False )
初始状态的数据形状为: (18840, 12)
删除冗余属性后的数据形状为: (18840, 9)
2. 划分用水事件
在用水状态记录中水流量不为0,表明热水器用户正在使用热水;而水流量为0时,表明热水器用户用热水时发生了停顿或者用热水结束。对于任何一个用水记录,如果它的向前时差超过了阈值T,则将它记为事件的开始编号;如果它的向后时差超过阈值T,则将其记为时间的结束编号。划分模型的符号说明如下表所示。
符号 释义
t
1
t_1
t 1 所有水流量不为0的用水行为的发生时间 时间间隔阈值
T
T
T
data = pd. read_csv( '../tmp/water_heart.csv' )
threshold = pd. Timedelta( '1 min' )
data[ '发生时间' ] = pd. to_datetime( data[ '发生时间' ] , format = '%Y%m%d%H%M%S' )
data = data[ data[ '水流量' ] > 0 ]
sjKs = data[ '发生时间' ] . diff( ) > threshold
sjKs. iloc[ 0 ] = True
sjJs = sjKs. iloc[ 1 : ]
sjJs = pd. concat( [ sjJs, pd. Series( True ) ] )
sj = pd. DataFrame( np. arange( 1 , sum ( sjKs) + 1 ) , columns = [ "事件序号" ] )
sj[ "事件起始编号" ] = data. index[ sjKs == 1 ] + 1
sj[ "事件终止编号" ] = data. index[ sjJs == 1 ] + 1
print ( '当阈值为4分钟的时候事件数目为:' , sj. shape[ 0 ] )
sj. to_csv( '../tmp/sj.csv' , index = False )
当阈值为4分钟的时候事件数目为: 232
3. 确定单次用水事件时长阈值
n = 4
threshold = pd. Timedelta( minutes= 5 )
data[ '发生时间' ] = pd. to_datetime( data[ '发生时间' ] , format = '%Y%m%d%H%M%S' )
data = data[ data[ '水流量' ] > 0 ]
def event_num ( ts) :
d = data[ '发生时间' ] . diff( ) > ts
return d. sum ( ) + 1
dt = [ pd. Timedelta( minutes= i) for i in np. arange( 1 , 9 , 0.25 ) ]
x = 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( )
h[ 'x' ] = x
ts = h[ '阈值' ] [ h[ '斜率指标' ] . idxmin( ) - n]
if ts > threshold:
ts = pd. Timedelta( minutes= 4 )
print ( '计算出的单次用水时长的阈值为:' , ts)
plt. rcParams[ 'font.sans-serif' ] = 'SimHei'
plt. rcParams[ 'axes.unicode_minus' ] = False
plt. plot( h[ 'x' ] , h[ '事件数' ] )
计算出的单次用水时长的阈值为: 0 days 00:04:00
4. 属性构造
(1)构建用水时长与频率属性
data = pd. read_excel( '../data/water_hearter.xlsx' , encoding= 'gbk' )
sj = pd. read_csv( '../tmp/sj.csv' )
data[ "发生时间" ] = pd. to_datetime( data[ "发生时间" ] , format = "%Y%m%d%H%M%S" )
timeDel = pd. Timedelta( "0.5 sec" )
sj[ "事件开始时间" ] = data. iloc[ sj[ "事件起始编号" ] - 1 , 0 ] . values- timeDel
sj[ "事件结束时间" ] = data. iloc[ sj[ "事件终止编号" ] - 1 , 0 ] . values + timeDel
sj[ '洗浴时间点' ] = [ i. hour for i in sj[ "事件开始时间" ] ]
sj[ "总用水时长" ] = np. int64( sj[ "事件结束时间" ] - sj[ "事件开始时间" ] ) / 1000000000 + 1
sj. head( 10 )
事件序号 事件起始编号 事件终止编号 事件开始时间 事件结束时间 洗浴时间点 总用水时长 0 1 3 3 2014-10-19 07:01:55.500 2014-10-19 07:01:56.500 7 2.0 1 2 57 57 2014-10-19 07:38:15.500 2014-10-19 07:38:16.500 7 2.0 2 3 382 385 2014-10-19 09:46:37.500 2014-10-19 09:47:15.500 9 39.0 3 4 405 405 2014-10-19 11:50:16.500 2014-10-19 11:50:17.500 11 2.0 4 5 408 408 2014-10-19 13:56:20.500 2014-10-19 13:56:21.500 13 2.0 5 6 411 594 2014-10-19 15:34:38.500 2014-10-19 15:48:46.500 15 849.0 6 7 613 619 2014-10-19 15:55:14.500 2014-10-19 15:55:29.500 15 16.0 7 8 660 666 2014-10-19 17:23:20.500 2014-10-19 17:23:41.500 17 22.0 8 9 668 763 2014-10-19 17:24:57.500 2014-10-19 17:31:32.500 17 396.0 9 10 766 766 2014-10-19 17:34:35.500 2014-10-19 17:34:36.500 17 2.0
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, "停顿结束时间" ] = data. loc[ i, "发生时间" ] + timeDel
data. head( 10 )
发生时间 开关机状态 加热中 保温中 实际温度 热水量 水流量 加热剩余时间 当前设置温度 停顿结束时间 停顿开始时间 0 2014-10-19 06:39:17 关 关 关 30°C 0% 0 0分钟 50°C NaT NaT 1 2014-10-19 07:01:54 关 关 关 30°C 0% 0 0分钟 50°C 2014-10-19 07:01:54.500 NaT 2 2014-10-19 07:01:56 关 关 关 30°C 0% 8 0分钟 50°C NaT NaT 3 2014-10-19 07:12:30 关 关 关 30°C 0% 0 0分钟 50°C NaT 2014-10-19 07:12:29.500 4 2014-10-19 07:12:36 关 关 关 29°C 0% 0 0分钟 50°C NaT NaT 5 2014-10-19 07:16:02 关 关 关 30°C 0% 0 0分钟 50°C NaT NaT 6 2014-10-19 07:16:08 关 关 关 29°C 0% 0 0分钟 50°C NaT NaT 7 2014-10-19 07:20:05 关 关 关 30°C 0% 0 0分钟 50°C NaT NaT 8 2014-10-19 07:20:10 关 关 关 29°C 0% 0 0分钟 50°C NaT NaT 9 2014-10-19 07:21:53 关 关 关 30°C 0% 0 0分钟 50°C NaT NaT
indStopStart = data. index[ data[ "停顿开始时间" ] . notnull( ) ] + 1
indStopEnd = data. index[ data[ "停顿结束时间" ] . notnull( ) ] + 1
Stop = pd. DataFrame( data= { "停顿开始编号" : indStopStart[ : - 1 ] ,
"停顿结束编号" : indStopEnd[ 1 : ] } )
Stop[ "停顿时长" ] = np. int64( data. loc[ indStopEnd[ 1 : ] - 1 , "停顿结束时间" ] . values-
data. loc[ indStopStart[ : - 1 ] - 1 , "停顿开始时间" ] . values) / 1000000000
Stop. head( 10 )
停顿开始编号 停顿结束编号 停顿时长 0 4 56 1545.0 1 58 381 7682.0 2 384 384 1.0 3 386 404 2130.0 4 406 407 1679.0 5 409 410 5007.0 6 453 454 11.0 7 595 612 286.0 8 620 659 5259.0 9 667 667 1.0
for i in range ( len ( sj) ) :
Stop. loc[ ( Stop[ "停顿开始编号" ] > sj. loc[ i, "事件起始编号" ] ) &
( Stop[ "停顿结束编号" ] < sj. loc[ i, "事件终止编号" ] ) , "停顿归属事件" ] = i+ 1
Stop = Stop[ Stop[ "停顿归属事件" ] . notnull( ) ]
Stop. head( 10 )
停顿开始编号 停顿结束编号 停顿时长 停顿归属事件 2 384 384 1.0 3.0 6 453 454 11.0 6.0 16 1143 1143 1.0 15.0 20 1350 1350 1.0 18.0 21 1353 1353 1.0 18.0 23 1368 1368 1.0 19.0 24 1371 1371 1.0 19.0 25 1377 1377 1.0 19.0 32 1578 1578 1.0 25.0 41 2166 2166 1.0 33.0
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 )
stopNo0 = sj[ "停顿次数" ] != 0
sj. loc[ stopNo0, "平均停顿时长" ] = sj. loc[ stopNo0, "总停顿时长" ] / sj. loc[ stopNo0, "停顿次数" ]
sj. fillna( 0 , inplace= True )
sj[ "用水时长" ] = sj[ "总用水时长" ] - sj[ "总停顿时长" ]
sj[ "用水/总时长" ] = sj[ "用水时长" ] / sj[ "总用水时长" ]
print ( '用水事件用水时长与频率特征构造完成后数据的特征为:\n' , sj. columns)
print ( '用水事件用水时长与频率特征构造完成后数据的前5行5列特征为:\n' ,
sj. iloc[ : 5 , : 5 ] )
sj. head( 10 )
用水事件用水时长与频率特征构造完成后数据的特征为:
Index(['事件序号', '事件起始编号', '事件终止编号', '事件开始时间', '事件结束时间', '洗浴时间点', '总用水时长',
'总停顿时长', '停顿次数', '平均停顿时长', '用水时长', '用水/总时长'],
dtype='object')
用水事件用水时长与频率特征构造完成后数据的前5行5列特征为:
事件序号 事件起始编号 事件终止编号 事件开始时间 事件结束时间
0 1 3 3 2014-10-19 07:01:55.500 2014-10-19 07:01:56.500
1 2 57 57 2014-10-19 07:38:15.500 2014-10-19 07:38:16.500
2 3 382 385 2014-10-19 09:46:37.500 2014-10-19 09:47:15.500
3 4 405 405 2014-10-19 11:50:16.500 2014-10-19 11:50:17.500
4 5 408 408 2014-10-19 13:56:20.500 2014-10-19 13:56:21.500
事件序号 事件起始编号 事件终止编号 事件开始时间 事件结束时间 洗浴时间点 总用水时长 总停顿时长 停顿次数 平均停顿时长 用水时长 用水/总时长 0 1 3 3 2014-10-19 07:01:55.500 2014-10-19 07:01:56.500 7 2.0 0.0 0.0 0.0 2.0 1.000000 1 2 57 57 2014-10-19 07:38:15.500 2014-10-19 07:38:16.500 7 2.0 0.0 0.0 0.0 2.0 1.000000 2 3 382 385 2014-10-19 09:46:37.500 2014-10-19 09:47:15.500 9 39.0 1.0 1.0 1.0 38.0 0.974359 3 4 405 405 2014-10-19 11:50:16.500 2014-10-19 11:50:17.500 11 2.0 0.0 0.0 0.0 2.0 1.000000 4 5 408 408 2014-10-19 13:56:20.500 2014-10-19 13:56:21.500 13 2.0 0.0 0.0 0.0 2.0 1.000000 5 6 411 594 2014-10-19 15:34:38.500 2014-10-19 15:48:46.500 15 849.0 11.0 1.0 11.0 838.0 0.987044 6 7 613 619 2014-10-19 15:55:14.500 2014-10-19 15:55:29.500 15 16.0 0.0 0.0 0.0 16.0 1.000000 7 8 660 666 2014-10-19 17:23:20.500 2014-10-19 17:23:41.500 17 22.0 0.0 0.0 0.0 22.0 1.000000 8 9 668 763 2014-10-19 17:24:57.500 2014-10-19 17:31:32.500 17 396.0 0.0 0.0 0.0 396.0 1.000000 9 10 766 766 2014-10-19 17:34:35.500 2014-10-19 17:34:36.500 17 2.0 0.0 0.0 0.0 2.0 1.000000
** (2 )构建用水量与波动属性**
data[ "水流量" ] = data[ "水流量" ] / 60
sj[ "总用水量" ] = 0
for i in range ( len ( sj) ) :
Start = sj. loc[ i, "事件起始编号" ] - 1
End = sj. loc[ i, "事件终止编号" ] - 1
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, "总用水量" ]
sj. loc[ i, "总用水量" ] = sj. loc[ i, "总用水量" ] + data. loc[ End, "水流量" ] * 2
else :
sj. loc[ i, "总用水量" ] = data. loc[ Start, "水流量" ] * 2
sj[ "平均水流量" ] = sj[ "总用水量" ] / sj[ "用水时长" ]
sj. head( 10 )
事件序号 事件起始编号 事件终止编号 事件开始时间 事件结束时间 洗浴时间点 总用水时长 总停顿时长 停顿次数 平均停顿时长 用水时长 用水/总时长 总用水量 平均水流量 0 1 3 3 2014-10-19 07:01:55.500 2014-10-19 07:01:56.500 7 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222 1 2 57 57 2014-10-19 07:38:15.500 2014-10-19 07:38:16.500 7 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222 2 3 382 385 2014-10-19 09:46:37.500 2014-10-19 09:47:15.500 9 39.0 1.0 1.0 1.0 38.0 0.974359 0.085000 0.002237 3 4 405 405 2014-10-19 11:50:16.500 2014-10-19 11:50:17.500 11 2.0 0.0 0.0 0.0 2.0 1.000000 0.012222 0.006111 4 5 408 408 2014-10-19 13:56:20.500 2014-10-19 13:56:21.500 13 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222 5 6 411 594 2014-10-19 15:34:38.500 2014-10-19 15:48:46.500 15 849.0 11.0 1.0 11.0 838.0 0.987044 9.415833 0.011236 6 7 613 619 2014-10-19 15:55:14.500 2014-10-19 15:55:29.500 15 16.0 0.0 0.0 0.0 16.0 1.000000 0.242222 0.015139 7 8 660 666 2014-10-19 17:23:20.500 2014-10-19 17:23:41.500 17 22.0 0.0 0.0 0.0 22.0 1.000000 0.379444 0.017247 8 9 668 763 2014-10-19 17:24:57.500 2014-10-19 17:31:32.500 17 396.0 0.0 0.0 0.0 396.0 1.000000 2.899167 0.007321 9 10 766 766 2014-10-19 17:34:35.500 2014-10-19 17:34:36.500 17 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222
sj[ "水流量波动" ] = 0
for i in range ( len ( sj) ) :
Start = sj. loc[ i, "事件起始编号" ] - 1
End = sj. loc[ i, "事件终止编号" ] - 1
for j in range ( Start, End + 1 ) :
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, "水流量波动" ] = sj. loc[ i, "水流量波动" ] / sj. loc[ i, "用水时长" ]
sj. head( 10 )
事件序号 事件起始编号 事件终止编号 事件开始时间 事件结束时间 洗浴时间点 总用水时长 总停顿时长 停顿次数 平均停顿时长 用水时长 用水/总时长 总用水量 平均水流量 水流量波动 0 1 3 3 2014-10-19 07:01:55.500 2014-10-19 07:01:56.500 7 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222 0.000000e+00 1 2 57 57 2014-10-19 07:38:15.500 2014-10-19 07:38:16.500 7 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222 0.000000e+00 2 3 382 385 2014-10-19 09:46:37.500 2014-10-19 09:47:15.500 9 39.0 1.0 1.0 1.0 38.0 0.974359 0.085000 0.002237 1.522223e-03 3 4 405 405 2014-10-19 11:50:16.500 2014-10-19 11:50:17.500 11 2.0 0.0 0.0 0.0 2.0 1.000000 0.012222 0.006111 0.000000e+00 4 5 408 408 2014-10-19 13:56:20.500 2014-10-19 13:56:21.500 13 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222 0.000000e+00 5 6 411 594 2014-10-19 15:34:38.500 2014-10-19 15:48:46.500 15 849.0 11.0 1.0 11.0 838.0 0.987044 9.415833 0.011236 4.975237e-06 6 7 613 619 2014-10-19 15:55:14.500 2014-10-19 15:55:29.500 15 16.0 0.0 0.0 0.0 16.0 1.000000 0.242222 0.015139 1.681375e-05 7 8 660 666 2014-10-19 17:23:20.500 2014-10-19 17:23:41.500 17 22.0 0.0 0.0 0.0 22.0 1.000000 0.379444 0.017247 2.600616e-07 8 9 668 763 2014-10-19 17:24:57.500 2014-10-19 17:31:32.500 17 396.0 0.0 0.0 0.0 396.0 1.000000 2.899167 0.007321 4.286956e-06 9 10 766 766 2014-10-19 17:34:35.500 2014-10-19 17:34:36.500 17 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222 0.000000e+00
sj[ "停顿时长波动" ] = 0
for i in range ( len ( sj) ) :
if sj. loc[ i, "停顿次数" ] > 1 :
for j in Stop. loc[ Stop[ "停顿归属事件" ] == ( i+ 1 ) , "停顿时长" ] . values:
sj. loc[ i, "停顿时长波动" ] = ( ( j - sj. loc[ i, "平均停顿时长" ] ) ** 2 ) * j + \
sj. loc[ i, "停顿时长波动" ]
sj. loc[ i, "停顿时长波动" ] = sj. loc[ i, "停顿时长波动" ] / sj. loc[ i, "总停顿时长" ]
print ( '用水量和波动特征构造完成后数据的特征为:\n' , sj. columns)
print ( '用水量和波动特征构造完成后数据的前5行5列特征为:\n' , sj. iloc[ : 5 , : 5 ] )
sj. head( 10 )
用水量和波动特征构造完成后数据的特征为:
Index(['事件序号', '事件起始编号', '事件终止编号', '事件开始时间', '事件结束时间', '洗浴时间点', '总用水时长',
'总停顿时长', '停顿次数', '平均停顿时长', '用水时长', '用水/总时长', '总用水量', '平均水流量', '水流量波动',
'停顿时长波动'],
dtype='object')
用水量和波动特征构造完成后数据的前5行5列特征为:
事件序号 事件起始编号 事件终止编号 事件开始时间 事件结束时间
0 1 3 3 2014-10-19 07:01:55.500 2014-10-19 07:01:56.500
1 2 57 57 2014-10-19 07:38:15.500 2014-10-19 07:38:16.500
2 3 382 385 2014-10-19 09:46:37.500 2014-10-19 09:47:15.500
3 4 405 405 2014-10-19 11:50:16.500 2014-10-19 11:50:17.500
4 5 408 408 2014-10-19 13:56:20.500 2014-10-19 13:56:21.500
事件序号 事件起始编号 事件终止编号 事件开始时间 事件结束时间 洗浴时间点 总用水时长 总停顿时长 停顿次数 平均停顿时长 用水时长 用水/总时长 总用水量 平均水流量 水流量波动 停顿时长波动 0 1 3 3 2014-10-19 07:01:55.500 2014-10-19 07:01:56.500 7 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222 0.000000e+00 0.0 1 2 57 57 2014-10-19 07:38:15.500 2014-10-19 07:38:16.500 7 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222 0.000000e+00 0.0 2 3 382 385 2014-10-19 09:46:37.500 2014-10-19 09:47:15.500 9 39.0 1.0 1.0 1.0 38.0 0.974359 0.085000 0.002237 1.522223e-03 0.0 3 4 405 405 2014-10-19 11:50:16.500 2014-10-19 11:50:17.500 11 2.0 0.0 0.0 0.0 2.0 1.000000 0.012222 0.006111 0.000000e+00 0.0 4 5 408 408 2014-10-19 13:56:20.500 2014-10-19 13:56:21.500 13 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222 0.000000e+00 0.0 5 6 411 594 2014-10-19 15:34:38.500 2014-10-19 15:48:46.500 15 849.0 11.0 1.0 11.0 838.0 0.987044 9.415833 0.011236 4.975237e-06 0.0 6 7 613 619 2014-10-19 15:55:14.500 2014-10-19 15:55:29.500 15 16.0 0.0 0.0 0.0 16.0 1.000000 0.242222 0.015139 1.681375e-05 0.0 7 8 660 666 2014-10-19 17:23:20.500 2014-10-19 17:23:41.500 17 22.0 0.0 0.0 0.0 22.0 1.000000 0.379444 0.017247 2.600616e-07 0.0 8 9 668 763 2014-10-19 17:24:57.500 2014-10-19 17:31:32.500 17 396.0 0.0 0.0 0.0 396.0 1.000000 2.899167 0.007321 4.286956e-06 0.0 9 10 766 766 2014-10-19 17:34:35.500 2014-10-19 17:34:36.500 17 2.0 0.0 0.0 0.0 2.0 1.000000 0.004444 0.002222 0.000000e+00 0.0