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