Python學習6之簡單實戰

eingnd發表於2020-10-18

Python學習6之簡單實戰

本系列文章用於以後編寫程式碼直接呼叫某些程式碼,也用作一個學習後的記錄,參考書籍為《Python資料分析與應用》,黃紅梅,張良均主編,張凌,施興,周東平副編,中國工信出版集團,人民郵電出版社,ISBN:9787115373045,文章附有程式碼和資料,學習起來較為容易



前言

本節講述航空公司客戶價值分析和財政收入預測分析,以及家用熱水器使用者行為分析與事件識別的實戰程式碼。不講述其具體理論,僅用作自我參考,如有興趣,自行查詢書籍進行學習。


一、航空公司客戶價值分析

1.瞭解航空公司現狀和客戶價值分析

這是程式設計之前必須考慮的東西,具體請參考書籍。

2.資料預處理

程式碼如下(示例):

import numpy as np
import pandas as pd
airline_data = pd.read_csv("air_data.csv",
    encoding="gb18030") #匯入航空資料
print('原始資料的形狀為:',airline_data.shape)

#缺失值與異常值的處理
## 去除票價為空的記錄
exp1 = airline_data["SUM_YR_1"].notnull()
exp2 = airline_data["SUM_YR_2"].notnull()
exp = exp1 & exp2
airline_notnull = airline_data.loc[exp,:]
print('刪除缺失記錄後資料的形狀為:',airline_notnull.shape)


#只保留票價非零的,或者平均折扣率不為0且總飛行公里數大於0的記錄。
index1 = airline_notnull['SUM_YR_1'] != 0
index2 = airline_notnull['SUM_YR_2'] != 0
index3 = (airline_notnull['SEG_KM_SUM']> 0) & \
    (airline_notnull['avg_discount'] != 0)  
airline = airline_notnull[(index1 | index2) & index3]
print('刪除異常記錄後資料的形狀為:',airline.shape)

#選取並構建LRFMC模型的特徵
## 選取需求特徵
airline_selection = airline[["FFP_DATE","LOAD_TIME",
    "FLIGHT_COUNT","LAST_TO_END",
    "avg_discount","SEG_KM_SUM"]]
## 構建L特徵
L = pd.to_datetime(airline_selection["LOAD_TIME"]) - \
pd.to_datetime(airline_selection["FFP_DATE"])
L = L.astype("str").str.split().str[0]
L = L.astype("int")/30
## 合併特徵
airline_features = pd.concat([L,
    airline_selection.iloc[:,2:]],axis = 1)
print('構建的LRFMC特徵前5行為:\n',airline_features.head())

# 標準化特徵
from sklearn.preprocessing import StandardScaler
data = StandardScaler().fit_transform(airline_features)
np.savez('airline_scale.npz',data)
print('標準化後LRFMC五個特徵為:\n',data[:5,:])

3.K-means演算法進行客戶分群

程式碼如下(示例):

## kmeans分類
import numpy as np
import pandas as pd
from sklearn.cluster import KMeans #匯入kmeans演算法
airline_scale = np.load('airline_scale.npz')['arr_0']
k = 5 ## 確定聚類中心數
#構建模型
kmeans_model = KMeans(n_clusters = k,n_jobs=4,random_state=123)
fit_kmeans = kmeans_model.fit(airline_scale)   #模型訓練
kmeans_model.cluster_centers_ #檢視聚類中心

kmeans_model.labels_ #檢視樣本的類別標籤

#統計不同類別樣本的數目
r1 = pd.Series(kmeans_model.labels_).value_counts()
print('最終每個類別的數目為:\n',r1)



二、財政收入預測分析

預測模型,主要是灰色預測和SVR迴歸預測。

1.瞭解背景

要對財政收入的預測背景和方法有簡單的瞭解,然後才可以尋找具體的預測方法。

2.分析資料特徵的相關性

這裡採用皮爾遜相關係數。

程式碼如下(示例):

#皮爾遜相關係數計算
import numpy as np
import pandas as pd
inputfile = 'data.csv' ## 輸入的資料檔案
data = pd.read_csv(inputfile) ## 讀取資料
## 保留兩位小數
print('相關係數矩陣為:',np.round(data.corr(method = 'pearson'), 2))

3.Lasso迴歸選取預測的相關特徵

Lasso迴歸的具體原理自行查詢,後續可能會補充。

程式碼如下(示例):

import numpy as np
import pandas as pd
from sklearn.linear_model import Lasso
inputfile = 'data.csv' #輸入的資料檔案
data = pd.read_csv(inputfile) #讀取資料
lasso = Lasso(1000)  #呼叫Lasso()函式,設定λ的值為1000
lasso.fit(data.iloc[:,0:13],data['y'])
print('相關係數為:',np.round(lasso.coef_,5))  #輸出結果,保留五位小數

## 計算相關係數非零的個數
print('相關係數非零個數為:',np.sum(lasso.coef_ != 0))

mask = lasso.coef_ != 0  #返回一個相關係數是否為零的布林陣列
print('相關係數是否為零:',mask)

outputfile = 'new_reg_data.csv'  #輸出的資料檔案
new_reg_data = data.iloc[:, mask]  #返回相關係數非零的資料
new_reg_data.to_csv(outputfile)  #儲存資料
print('輸出資料的維度為:',new_reg_data.shape)  #檢視輸出資料的維度

4.灰色預測和SVR迴歸預測模型

注意灰色預測的包提前寫好,放在同一個資料夾裡。
首先灰色預測短期的資料特徵,然後再採用SVR迴歸預測。
程式碼如下(示例):

#灰色預測
import numpy as np
import pandas as pd
from GM11 import GM11 ## 引入自編的灰色預測函式
inputfile = 'new_reg_data.csv' ##輸入的資料檔案
inputfile1 = 'data.csv' ## 輸入的資料檔案
new_reg_data = pd.read_csv(inputfile) ## 讀取經過特徵選擇後的資料
data = pd.read_csv(inputfile1) ##讀取總的資料
new_reg_data.index = range(1994, 2014)
new_reg_data.loc[2014] = None
new_reg_data.loc[2015] = None
l = ['x1', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x13']
for i in l:
  f = GM11(new_reg_data.loc[range(1994, 2014),i].as_matrix())[0]
  new_reg_data.loc[2014,i] = f(len(new_reg_data)-1)#2014年預測結果
  new_reg_data.loc[2015,i] = f(len(new_reg_data)) ##2015年預測結果
  new_reg_data[i] = new_reg_data[i].round(2) ## 保留兩位小數
outputfile = 'new_reg_data_GM11.xls' ## 灰色預測後儲存的路徑
y = list(data['y'].values) ## 提取財政收入列,合併至新資料框中
y.extend([np.nan,np.nan])
new_reg_data['y'] = y
new_reg_data.to_excel(outputfile) ## 結果輸出
print('預測結果為:',new_reg_data.loc[2014:2015,:]) ##預測結果展示
import pandas as pd
import numpy as np
from sklearn.svm import LinearSVR
import matplotlib.pyplot as plt
from sklearn.metrics import explained_variance_score,\
mean_absolute_error,mean_squared_error,\
median_absolute_error,r2_score
inputfile = 'new_reg_data_GM11.xls' #灰色預測後儲存的路徑
data = pd.read_excel(inputfile) #讀取資料
feature = ['x1', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x13'] 
data_train = data.loc[range(0,19)].copy()#取2014年前的資料建模
data_mean = data_train.mean()
data_std = data_train.std()
data_train = (data_train - data_mean)/data_std #資料標準化
x_train = data_train[feature].as_matrix() #特徵資料
y_train = data_train['y'].as_matrix() #標籤資料
linearsvr = LinearSVR()   #呼叫LinearSVR()函式
linearsvr.fit(x_train,y_train)
x = ((data[feature] - data_mean[feature])/ \
data_std[feature]).as_matrix()  #預測,並還原結果。
data[u'y_pred'] = linearsvr.predict(x) * \
data_std['y'] + data_mean['y']
## SVR預測後儲存的結果
outputfile = 'new_reg_data_GM11_revenue.xls'
data.to_excel(outputfile)
print('真實值與預測值分別為:',data[['y','y_pred']])
print('預測圖為:',data[['y','y_pred']].plot(style=['b-o','r-*']))

在這裡插入圖片描述

三、家用熱水器使用者行為分析與事件識別

一種比較應用於實際的資料分析吧。

1.瞭解背景

學習之前要對家用熱水器資料有一個基本的瞭解,對該問題有基本的瞭解。

2.資料預處理

刪除冗餘特徵,劃分用水事件,確定單次用水事件用水時長閾值。
程式碼如下(示例):

#刪除冗餘特徵
import pandas as pd
import numpy as np
data = pd.read_excel('original_data.xls')
print('初始狀態的資料形狀為:', data.shape)
# 刪除熱水器編號,有無水流,節能模式
data.drop(labels = ["熱水器編號","有無水流","節能模式"],
    axis = 1,inplace = True)
print('刪除冗餘特徵後的資料形狀為:', data.shape)
data.to_csv('water_heart.csv',index = False)

# 劃分用水事件
threshold = pd.Timedelta('4 min') #閾值為分鐘
data['發生時間'] = pd.to_datetime(data['發生時間'], 
    format = '%Y%m%d%H%M%S') # 轉換時間格式
data = data[data['水流量'] > 0] #只要流量大於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('sj.csv',index = False)


# 確定單次用水事件時長閾值
n = 4 #使用以後四個點的平均斜率
threshold = pd.Timedelta(minutes = 5) #專家閾值
data['發生時間'] = pd.to_datetime(data['發生時間'], 
    format = '%Y%m%d%H%M%S')
data = data[data['水流量'] > 0] #只要流量大於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)]
h = pd.DataFrame(dt, columns = ['閾值']) #轉換資料框,定義閾值列
h['事件數'] = h['閾值'].apply(event_num) #計算每個閾值對應的事件數
h['斜率'] = h['事件數'].diff()/0.25 #計算每兩個相鄰點對應的斜率
#往前取n個斜率絕對值平均作為斜率指標
h['斜率指標']= h['斜率'].abs().rolling(4).mean()
ts = h['閾值'][h['斜率指標'].idxmin() - n]
#用idxmin返回最小值的Index,由於rolling_mean()計算的是前n個斜率的絕對值平均
#所以結果要進行平移(-n)
if ts > threshold:
    ts = pd.Timedelta(minutes = 4)
print('計算出的單次用水時長的閾值為:',ts)


3.構建用水行為特徵並篩選用水事件

主要有構建用水時長與頻率特徵,用水量與波動特徵,篩選候選洗浴事件。
程式碼如下(示例):

# 構建用水時長與頻率特徵
import pandas as pd 
import numpy as np
# 讀取熱水器使用資料記錄
data = pd.read_excel('water_hearter.xlsx') 
# 讀取用水事件記錄
sj = pd.read_csv('sj.csv',encoding = 'utf8') 
data["發生時間"] = pd.to_datetime(data["發生時間"],
    format = "%Y%m%d%H%M%S") # 轉換時間格式

# 構造特徵:總用水時長
timeDel = pd.Timedelta("1 sec")
sj["事件開始時間"] = data.iloc[sj["事件起始編號"]-1,0].values- timeDel
sj["事件結束時間"] = \
data.iloc[sj["事件終止編號"]-1,0].values + timeDel
sj['洗浴時間點'] = [i.hour for i in sj["事件開始時間"]]
tmp1 = sj["事件結束時間"] - sj["事件開始時間"]
sj["總用水時長"] = np.int64(tmp1)/1000000000 

# 構造用水停頓事件
# 構造特徵“停頓開始時間”、“停頓結束時間”
# 停頓開始時間指從有水流到無水流,停頓結束時間指從無水流到有水流
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 
# 提取停頓開始時間與結束時間所對應行號,放在資料框Stop中
indStopStart = data.index[data["停頓開始時間"].notnull()]+1
indStopEnd = data.index[data["停頓結束時間"].notnull()]+1
Stop = pd.DataFrame(data = {"停頓開始編號":indStopStart[:-1],
                            "停頓結束編號":indStopEnd[1:]}) 
# 計算停頓時長,並放在資料框stop中,停頓時長=停頓結束時間-停頓結束時間
tmp2 = data.loc[indStopEnd[1:]-1,"停頓結束時間"]
tmp3 = data.loc[indStopStart[:-1]-1,"停頓開始時間"]
tmp4 = tmp2.values-tmp3.values
Stop["停頓時長"] = np.int64(tmp4)/1000000000 
# 將每次停頓與事件匹配,停頓的開始時間要大於事件的開始時間,
# 且停頓的結束時間要小於事件的結束時間
for i in range(len(sj)):
    Stop.loc[(Stop["停頓開始編號"] > sj.loc[i,"事件起始編號"]) & 
        (Stop["停頓結束編號"] < sj.loc[i,"事件終止編號"]),
            "停頓歸屬事件"] = i+1
             
# 刪除停頓次數為0的事件
Stop = Stop[Stop["停頓歸屬事件"].notnull()]

# 構造特徵 用水事件停頓總時長、停頓次數、停頓平均時長、
# 用水時長,用水/總時長
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) # 對缺失值用0插補
stopNo0 = sj["停頓次數"] != 0 # 判斷用水事件是否存在停頓
sj.loc[stopNo0,"平均停頓時長"] = \
sj.loc[stopNo0,"總停頓時長"]/sj.loc[stopNo0,"停頓次數"] 
sj.fillna(0,inplace=True) # 對缺失值用0插補
sj["用水時長"] = sj["總用水時長"] - sj["總停頓時長"] # 定義特徵用水時長
# 定義特徵 用水/總時長
sj["用水/總時長"] = sj["用水時長"] / sj["總用水時長"]
print('用水事件用水時長與頻率特徵構造完成後資料的特徵為:\n',sj.columns)
print('用水事件用水時長與頻率特徵構造完成後資料的前5行5列特徵為:\n',
      sj.iloc[:5,:5])



# 構建用水量與波動特徵
data["水流量"] = data["水流量"] / 60 # 原單位L/min,現轉換為L/sec
sj["總用水量"] = 0 # 給總用水量賦一個初始值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["用水時長"] # 定義特徵 平均水流量
# 構造特徵:水流量波動
# 水流量波動=∑(((單次水流的值-平均水流量)^2)*持續時間)/用水時長
sj["水流量波動"] = 0 # 給水流量波動賦一個初始值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,"用水時長"]

# 構造特徵:停頓時長波動
# 停頓時長波動=∑(((單次停頓時長-平均停頓時長)^2)*持續時間)/總停頓時長
sj["停頓時長波動"] = 0 # 給停頓時長波動賦一個初始值0
for i in range(len(sj)):
    # 當停頓次數為0或1時,停頓時長波動值為0,故排除
    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_bool = (sj['用水時長'] >100) & \
(sj['總用水時長'] > 120) & (sj['總用水量'] > 5)
sj_final = sj.loc[sj_bool,:]
sj_final.to_excel('sj_final.xlsx',index = False)
print('篩選出候選洗浴事件前的資料形狀為:',sj.shape)
print('篩選出候選洗浴事件後的資料形狀為:',sj_final.shape)

總結

提示:本文章簡單介紹了三個實戰專案,主要是程式碼,具體介紹如有興趣,自行檢視書籍,所用所有資料在百度網盤:
連結: https://pan.baidu.com/s/1Kj5Y2wAgDlwMGRlh9kED0Q 提取碼: 2zqm
資料連結

相關文章