数据分析与挖掘实战-家用电器用户行为分析与事件识别
生活随笔
收集整理的這篇文章主要介紹了
数据分析与挖掘实战-家用电器用户行为分析与事件识别
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
家用電器用戶(hù)行為分析與事件識(shí)別
- 背景
- 居民使用家電過(guò)程中,會(huì)因?yàn)榈貐^(qū)氣候、區(qū)域不同、年齡差異,形成不同的使用習(xí)慣,若能深入了解這些習(xí)慣,針對(duì)性地開(kāi)發(fā)新功能,便能開(kāi)拓市場(chǎng)。
- 本案例以熱水器為例,分析用戶(hù)行為。在熱水器用戶(hù)行為分析過(guò)程中,用水事件識(shí)別最為關(guān)鍵。
- 目標(biāo)
- 由于熱水器可能用于各種事件而不僅僅是洗浴,要求根據(jù)收集到的數(shù)據(jù),分析用戶(hù)行為。
- 基于熱水器采集到的時(shí)間序列數(shù)據(jù),將順序排列的離散的用水時(shí)間節(jié)點(diǎn)依據(jù)水流量和停頓時(shí)間間隔劃分為不同大小的時(shí)間區(qū)間,每個(gè)區(qū)間是一個(gè)可理解的一次完整用水事件,并且以這一次完整的用水事件作為一個(gè)基本事件,將時(shí)間序列數(shù)據(jù)劃分為獨(dú)立的用水事件并且識(shí)別出洗浴事件。
- 基于此,廠(chǎng)商對(duì)智能操作和節(jié)能運(yùn)行進(jìn)行優(yōu)化。
- 分析
- 用水事件劃分與識(shí)別
- 對(duì)用戶(hù)的歷史用水?dāng)?shù)據(jù)進(jìn)行選擇性抽取,構(gòu)建專(zhuān)家樣本。
- 對(duì)構(gòu)建的樣本數(shù)據(jù)集進(jìn)行數(shù)據(jù)探索和數(shù)據(jù)預(yù)處理,包括探索用水事件時(shí)間間隔的分布、規(guī)約冗余屬性、識(shí)別用水?dāng)?shù)據(jù)的缺失值,并對(duì)缺失值進(jìn)行處理,根據(jù)建模的需要進(jìn)行屬性構(gòu)造等。
- 根據(jù)上述處理,對(duì)用水樣本數(shù)據(jù)建立用水事件時(shí)間間隔識(shí)別模型和劃分一次完整的用水事件模型,再在一次完整用水事件劃分結(jié)果的基礎(chǔ)上,剔除短暫用水事件,縮小識(shí)別范圍。
- 根據(jù)上一步得到的建模樣本數(shù)據(jù),建立洗浴事件識(shí)別模型,并進(jìn)行模型評(píng)價(jià)分析。
- 用水事件劃分與識(shí)別
-
處理過(guò)程
- 數(shù)據(jù)獲取
- 由機(jī)器自動(dòng)記錄,數(shù)據(jù)量大,本案例數(shù)據(jù)為無(wú)放回隨機(jī)抽取200家用戶(hù)從2014年1月1日到2014年12月31日的用水記錄作為建模數(shù)據(jù)。
- 數(shù)據(jù)探索
- 用水停頓時(shí)間間隔為兩條水流量不為0的流水記錄之間的時(shí)間間隔,為了探究用戶(hù)真實(shí)的用水停頓時(shí)間間隔分布情況,統(tǒng)計(jì)用水停頓的時(shí)間間隔并做頻率分布直方圖。
- 可以知道,正常的兩次用水間隔在3~7分鐘。
- 數(shù)據(jù)預(yù)處理
- 數(shù)據(jù)特點(diǎn)
- 數(shù)據(jù)涉及上萬(wàn)用戶(hù)且每個(gè)用戶(hù)的每天數(shù)據(jù)多達(dá)數(shù)萬(wàn)條,存在缺失值、與分析無(wú)關(guān)的屬性或許未直接反映用水事件的屬性。
- 數(shù)據(jù)規(guī)約
- 屬性規(guī)約
- 對(duì)用戶(hù)的洗浴行為的一般性分析,所以“熱水器編號(hào)”屬性多余,可以去除;而且,“有無(wú)水流”屬性可以通過(guò)“水流量”反映,可以去除,減少特征復(fù)雜;“節(jié)能模式”都為“關(guān)”,沒(méi)有意義。
- 數(shù)值規(guī)約
- 當(dāng)“開(kāi)關(guān)機(jī)狀態(tài)”為“關(guān)”且“水流量”為0,說(shuō)明熱水器不在工作,記錄可以規(guī)約掉。
- 屬性規(guī)約
- 數(shù)據(jù)變換
- 由于目標(biāo)是對(duì)洗浴事件進(jìn)行識(shí)別,這就需要識(shí)別出哪些狀態(tài)是完整的用水事件,繼而識(shí)別出其中的洗浴事件。由于一次完整的用水事件是根據(jù)水流量和停頓時(shí)間間隔的閾值去劃分的,所以本案例還建立了閾值尋優(yōu)模型。同時(shí),為了提高在大量用水事件中尋找洗浴事件的效率,本案例建立了篩選規(guī)則剔除明顯不是洗浴事件的記錄,得到建模數(shù)據(jù)樣本。
- 一次完整用水事件的劃分模型
- 哪些連續(xù)的數(shù)據(jù)是一次完整的用水事件
- 水流量不為0表示正在使用熱水,水流量為0則表示用熱水停頓或者結(jié)束。如果水流量為0的狀態(tài)記錄之間的時(shí)間間隔超過(guò)一個(gè)閾值T(也就是前后是兩個(gè)不同事件),那么從該段水流量為0的記錄向前找到最后一條水流量不為0的記錄作為上一次用水事件的結(jié)束,向后找到水流量不為0的記錄作為下一個(gè)用水事件的開(kāi)始。
- 操作流程
- Step1:讀取數(shù)據(jù)記錄,識(shí)別到第一條水流量不為0的數(shù)據(jù)記錄,記為R1按照順序,下一條水流量不為0的記錄記為R2。
- Step2:計(jì)算Ri與Ri+1之間的差值記為gapi,若gapi大于閾值T則Ri和Ri+1之間的數(shù)據(jù)記錄不能認(rèn)為是同一個(gè)用水事件。同時(shí)將Ri+1記錄作為新的讀取數(shù)據(jù)記錄的開(kāi)始,返回Step1;若gapi小于閾值T,則將Ri+1與Ri之間的數(shù)據(jù)記錄劃分到一個(gè)用水事件,并記錄下一個(gè)水流量不為0的數(shù)據(jù)記錄為Ri+2。
- Step3:循環(huán)Step2,直到數(shù)據(jù)記錄讀取完畢,結(jié)束事件劃分。
- 代碼實(shí)現(xiàn)
- 數(shù)據(jù)預(yù)處理.py
- # -*- coding:UTF-8 -*
import pandas as pddef attrStatute():'''屬性規(guī)約:return:'''rawData = pd.read_excel('data/original_data.xls').drop(columns=["熱水器編號(hào)","有無(wú)水流", "節(jié)能模式"])return rawDatadef valueStatute():'''數(shù)值規(guī)約:return:'''data = pd.read_excel('data/water_heater.xls')newData = data[data['開(kāi)關(guān)機(jī)狀態(tài)'].isin(['關(guān)']) & data['水流量'].isin([0])]return newDatadef divideEvent():'''事件劃分:return:'''# 閾值設(shè)置為4分鐘threshold = pd.Timedelta('4 min')inputFile = 'data/water_heater.xls'outputFile = 'data/dividesequence.xls'data = pd.read_excel(inputFile)data['發(fā)生時(shí)間'] = pd.to_datetime(data['發(fā)生時(shí)間'], format='%Y%m%d%H%M%S')# 只保留水流量大于0的記錄data = data[data['水流量'] > 0]# 將原數(shù)據(jù)的發(fā)生時(shí)間做一階差分,得到一個(gè)時(shí)間差值的dataframed = data['發(fā)生時(shí)間'].diff() > threshold# 累計(jì)求和編號(hào)數(shù)據(jù)data['事件編號(hào)'] = d.cumsum() + 1data.to_excel(outputFile)if __name__ == '__main__':# attrStatute().to_excel("data/water_heater.xls")# valueStatute().to_excel("data/water_heater2.xls")zdivideEvent()
?
- 哪些連續(xù)的數(shù)據(jù)是一次完整的用水事件
- 用水事件閾值尋優(yōu)模型
- 原因
- 不同地區(qū),不同季節(jié),使用熱水器停頓時(shí)長(zhǎng)是不同的,固定一個(gè)閾值做上述處理是不合適的,所以考慮到在不同的時(shí)間段內(nèi)要更新閾值,故建立閾值尋優(yōu)模型來(lái)尋找最優(yōu)閾值。
- 實(shí)現(xiàn)方法
- 指定連續(xù)的閾值嘗試劃分,得到的事件個(gè)數(shù)必定不太相同,繪制折線(xiàn)圖不難發(fā)現(xiàn)曲線(xiàn)平穩(wěn)處符合大多數(shù)人的要求,利用斜率來(lái)刻畫(huà)尋找這個(gè)最優(yōu)點(diǎn)。
- 代碼
- 閾值尋優(yōu)模型.py
- # -*- coding: utf-8 -*-
"""
在1-9分鐘進(jìn)行閾值尋優(yōu)
"""
import numpy as np
import pandas as pddef event_num(ts):'''得到事件數(shù)目:param ts::return:'''d = data[u'發(fā)生時(shí)間'].diff() > tsreturn d.sum() + 1if __name__ == '__main__':inputfile = 'data/water_heater.xls'# 使用以后四個(gè)點(diǎn)的平均斜率n = 4threshold = pd.Timedelta(minutes=5)data = pd.read_excel(inputfile)data[u'發(fā)生時(shí)間'] = pd.to_datetime(data[u'發(fā)生時(shí)間'], 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'閾值'])# 計(jì)算每個(gè)閾值對(duì)應(yīng)的事件數(shù)h[u'事件數(shù)'] = h[u'閾值'].apply(event_num)# 計(jì)算每?jī)蓚€(gè)相鄰點(diǎn)對(duì)應(yīng)的斜率h[u'斜率'] = h[u'事件數(shù)'].diff()/0.25# 采用后n個(gè)的斜率絕對(duì)值平均作為斜率指標(biāo)h[u'斜率指標(biāo)'] = pd.DataFrame(h[u'斜率'].abs()[len(h)-n:]).rolling(2).mean()ts = h[u'閾值'][h[u'斜率指標(biāo)'].idxmin() - n]if ts > threshold:ts = pd.Timedelta(minutes=4)print(ts)
?
- 結(jié)果為4分鐘
- 原因
- 屬性構(gòu)造
- 本案例研究的是用水行為,可以構(gòu)造四類(lèi)指標(biāo):時(shí)長(zhǎng)指標(biāo)、頻率指標(biāo)、用水的量化指標(biāo)以及用水的波動(dòng)指標(biāo)。都是為了模型服務(wù)建立特征項(xiàng)。
- 篩選“洗浴事件”
- 剔除短暫用水事件,剩余的為候選洗浴事件。
- 數(shù)據(jù)清洗
- 對(duì)異常值和缺失值處理,比較簡(jiǎn)單不再敘述。
- 數(shù)據(jù)特點(diǎn)
- 數(shù)據(jù)挖掘建模
- 經(jīng)過(guò)數(shù)據(jù)預(yù)處理這一核心步驟,得到的數(shù)據(jù)已經(jīng)是滿(mǎn)足建模要求的數(shù)據(jù)了。
- 由于洗浴事件和普通用水事件在特征上不同,根據(jù)用水日志,將洗浴事件作為訓(xùn)練樣本訓(xùn)練神經(jīng)網(wǎng)絡(luò),然后根據(jù)訓(xùn)練好的網(wǎng)絡(luò)檢驗(yàn)新的數(shù)據(jù)。
- 選取11個(gè)特征作為神經(jīng)網(wǎng)絡(luò)的輸入:洗浴時(shí)間點(diǎn)、總用水時(shí)長(zhǎng)、總停頓時(shí)長(zhǎng)、平均停頓時(shí)長(zhǎng)、停頓次數(shù)、用水時(shí)長(zhǎng)、用水時(shí)長(zhǎng)/總用水時(shí)長(zhǎng)、總用水量、平均用水量水流量波動(dòng)和停頓時(shí)長(zhǎng)波動(dòng)。訓(xùn)練BP神經(jīng)網(wǎng)絡(luò)給定的輸出為0或1,其中1表示為洗浴事件,0表示不是洗浴事件。
- 使用keras庫(kù)訓(xùn)練神經(jīng)網(wǎng)絡(luò)。
- 代碼
- # -*- coding: utf-8 -*-
"""
利用神經(jīng)網(wǎng)絡(luò)挖掘建模
"""
import pandas as pd
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, ActivationinputFile1 = '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函數(shù)為激活函數(shù)
model.add(Activation('relu'))
# 添加隱藏層、隱藏層的連接
model.add(Dense(input_dim=17, units=10))
# 以Relu函數(shù)為激活函數(shù)
model.add(Activation('relu'))
# 添加隱藏層、輸出層的連接
model.add(Dense(input_dim=10, units=1))
# 以sigmoid函數(shù)為激活函數(shù)
model.add(Activation('sigmoid'))
# 編譯模型,損失函數(shù)為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=['預(yù)測(cè)結(jié)果'])
pd.concat([data_test.iloc[:, :5], r], axis=1).to_excel(testoutputfile)
rst = model.predict(x_test)
print(rst)
?
- # -*- coding: utf-8 -*-
"""
利用神經(jīng)網(wǎng)絡(luò)挖掘建模
"""
import pandas as pd
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, ActivationinputFile1 = '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函數(shù)為激活函數(shù)
model.add(Activation('relu'))
# 添加隱藏層、隱藏層的連接
model.add(Dense(input_dim=17, units=10))
# 以Relu函數(shù)為激活函數(shù)
model.add(Activation('relu'))
# 添加隱藏層、輸出層的連接
model.add(Dense(input_dim=10, units=1))
# 以sigmoid函數(shù)為激活函數(shù)
model.add(Activation('sigmoid'))
# 編譯模型,損失函數(shù)為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=['預(yù)測(cè)結(jié)果'])
pd.concat([data_test.iloc[:, :5], r], axis=1).to_excel(testoutputfile)
rst = model.predict(x_test)
print(rst)
- 數(shù)據(jù)獲取
-
補(bǔ)充說(shuō)明
- 案例參考書(shū)《Python數(shù)據(jù)分析與挖掘?qū)崙?zhàn)》
- 具體數(shù)據(jù)集代碼見(jiàn)我的GitHub
- 與原書(shū)有借鑒,但是較大改動(dòng)代碼
- 修復(fù)了原書(shū)一些舊版本代碼錯(cuò)誤
- 常見(jiàn)錯(cuò)誤(均是因?yàn)閗eras版本改動(dòng))
- 1
- TypeError:?Dense?can accept only 1 positional arguments ('units',), but you passed the following positional arguments: [23, 34]
- 解決方法
- 在Dense中寫(xiě)好參數(shù)名稱(chēng)改為Dense(input_dim=23,units=34)
- 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 in?fit?has been renamed?epochs
- 解決方法
- 修改代碼中的“nb_epoch”為“epochs”即可
- 1
總結(jié)
以上是生活随笔為你收集整理的数据分析与挖掘实战-家用电器用户行为分析与事件识别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Linux服务-NFS服务部署
- 下一篇: 机器学习-机器学习概论(入门机器学习基础