Python數據分析難?手把手教你處理上萬條京東訂單數據(附源碼)
數據集簡介
本數據集共收集了發生在一個月內的28010條數據,包含以下字段:
- ['訂單編號', '總金額', '買家實際支付金額', '收貨地址', '訂單創建時間', '訂單付款時間 ', '退款金額']
7個字段說明:
- 訂單編號:訂單編號;
- 總金額:訂單總金額;
- 買家實際支付金額:總金額 - 退款金額(在已付款的情況下)。金額為0(在未付款的情況下);
- 收貨地址:各個省份;
- 訂單創建時間:下單時間;
- 訂單付款時間:付款時間;
- 退款金額:付款后申請退款的金額。如無付過款,退款金額為0。
數據概覽:

相關庫、函數和數據的導入
- # 相關庫和函數的導入
- import numpy as np
- from sklearn import metrics
- import math
- import copy
- import pandas as pd
- import scipy as sp導入常用的基本庫
- import datetime as date # 導入datetime庫
- import seaborn as sns # 導入seaborn庫,用于數據可視化
- from IPython.display import display # 載入數據查看時需要使用的函數
- from sklearn.model_selection import train_test_split # 導入數據集劃分時需要使用的函數
- from sklearn.metrics import confusion_matrix # 導入生成混淆矩陣的函數
- from sklearn.preprocessing import LabelEncoder # 導入分類變量編碼時需要使用的函數
- from sklearn.metrics import classification_report # 導入分類結果評價時要用到的函數
- from sklearn.discriminant_analysis import LinearDiscriminantAnalysis # 導入LDA判別時需要使用的函數
- from sklearn.naive_bayes import MultinomialNB # 導入樸素貝葉斯時需要使用的額函數
- from sklearn.neighbors import KNeighborsClassifier # 導入KNN判別時需要使用的函數
- from sklearn.tree import DecisionTreeClassifier # 導入決策樹函數
- from sklearn.neural_network import MLPClassifier # 導入神經網絡函數
- from sklearn import svm # 導入支持向量機函數
- from sklearn.model_selection import GridSearchCV # 導入模型優化方法中的網格搜索法需要用到的函數
- from sklearn.cross_validation import KFold # 導入模型評估時使用的函數
- # 數據導入
- dt = pd.read_csv('D:資料/數據分析/數據分析與數據挖掘/實戰演練/5(tmall_order_report)/tmall_order_report.csv',encoding='gbk',engine='python')
數據檢查與清洗
首先,查看一下數據集的變量類型:
- dt.dtypes # 查看數據集有哪些變量

然后,將變量中右側的空格去除(以免影響后續調用變量),并進行重復值和缺失值檢查:
- dt.columns = dt.columns.str.rstrip() # 去除列名右側的空格
- dt.duplicated().sum() # 檢查數據是否有重復值,發現并沒有重復值
- display(sum(dt.isnull().sum())) # 檢查數據集是否有缺失值

檢查出來有缺失值(約占數據總量的12-15%),考慮原數據中是否是因為“訂單付款時間”這一列存在缺失值而導致產生這樣的檢查結果:
- col = dt.columns.values.tolist() # 提取數據集中的所有列變量的名稱
- col.remove('訂單付款時間') # 將訂單付款時間這一列去除
- display(sum(dt[col].isnull().sum())) # 再次檢查是否有缺失值,發現并沒有缺失值,也就是缺失值均來自“訂單付款時間”這一列

結果表明,缺失值僅來自“訂單付款時間”這一列。接下來就是處理缺失值,處理的思路可以是先計算出各訂單付款時間和下單時間的平均值,然后用下單時間 + 平均值,作為缺失值的填補對象:
- c = np.array(['訂單創建時間','訂單付款時間']) # 提取訂單創建時間、付款時間這兩列的列名
- for i in c:
- dt[i] = pd.to_datetime(dt[i]) # 將訂單創建時間、付款時間由object類型轉為datetime類型,方便運算
- for i in range(0,dt.shape[0]):
- if (dt['訂單付款時間'].iloc[i] < dt['訂單創建時間'].iloc[i]) == True:
- dt['訂單付款時間'].iloc[i] = dt['訂單付款時間'].iloc[i] + date.timedelta(days=1) # 將訂單付款時間 < 訂單創建時間的時間數據往后加1天(因為原數據中沒有考慮日期差異情況)
- mu = np.mean(dt['訂單付款時間']-dt['訂單創建時間']) # 計算時間差的均值,用于之后進行缺失值替換
- for i in range(0,dt.shape[0]):
- if pd.isnull(dt['訂單付款時間'].iloc[i]) == True: # 進行缺失值填補
- dt['訂單付款時間'].iloc[i] = dt['訂單創建時間'].iloc[i] + mu
在填補完成之后,再次檢查缺失值和重復值的情況:
- display(sum(dt.isnull().sum())) # 再次檢查數據集是否有缺失值,發現已經處理完了,但是還要檢查是否增加正確
- display(dt.duplicated().sum()) # 再次檢查數據是否有重復值,發現并沒有重復值,發現也沒有重復值

結果顯示已經沒有缺失值和重復值了。
描述性分析
首先,對訂單總金額進行描述性分析:
- display(dt['總金額'].describe()) # 查看訂單總金額的情況,發現最大的訂單價格達到了188320元,最小的則只有1元,平均訂單價為107元左右

從描述統計的結果中可以看到,最大的訂單價格達到了188320元,最小的只有1元,平均訂單價在107元左右,中位數為1元,說明應該是一個左偏分布,即大部分訂單的價格應該都不高。然后查看買家實際支付金額為0(支付未完成)的訂單比例:
- sum(dt['買家實際支付金額']==0) / dt.shape[0] # 查看買家實際支付金額為0(也就是支付未完成)的訂單比例,占比約為32.3%

從結果中可以看到,大概有32.3%的買家未完成支付,這一比例還是比較高的。再看看訂單付款時間相比于訂單創建時間的延遲情況:
- display((dt['訂單付款時間']-dt['訂單創建時間']).describe()) # 查看訂單付款時間相比于訂單創建時間的延遲情況,發現最慢的支付延遲了接近1天,而大部分訂單在10分鐘內就完成了支付
從中可以看到,最慢的支付延遲了接近1天,而大部分訂單在10分鐘內就完成了支付。最后,來對收貨地址情況進行描述性分析:
- siz = dt.groupby(dt['收貨地址']).size() # 對收貨地址進行分組統計
- idx_sort = np.argsort(-siz) # 對分組統計的結果進行降序排序
- display(siz[idx_sort].head()) # 查看降序排序的結果的前5名,發現收貨地址選擇上海、廣東、江蘇、浙江、北京的最多
- siz[idx_sort].tail() # 查看降序排序的結果的最后5名,發現收貨地址選擇湖北、新疆、寧夏、青海和西藏的最少,其中湖北可能受疫情影響所致

從結果中可以看到,收貨地址選擇上海、廣東、江蘇、浙江、北京的最多,而選湖北、新疆、寧夏、青海和西藏的最少,其中湖北可能受疫情影響所致。
建模預處理
首先,進行特征構建,并生成用于建模的數據集,處理過程如下:
- d1 = (dt['訂單付款時間']-dt['訂單創建時間']) # 輸出訂單付款和創建之間的時間差,作為一個新變量
- d1 = (d1 / np.timedelta64(1, 's')).astype(int) # 將時間差的格式進行轉換,轉換為按秒計數,并把格式變為int類型
- le_train = LabelEncoder() # 使用從sklearn.preprocessing中import的LabelEncoder對分類數據進行編碼,以便于后續使用交叉驗證建模
- le_train.fit(dt['收貨地址'].tolist()) # 對模型進行訓練
- d2 = le_train.transform(dt['收貨地址'].tolist()) # 轉化數據,作為第2個變量
- d3 = np.zeros(dt.shape[0]) # 構建一個全為0的數組
- for i in range(0,dt.shape[0]):
- if (dt['總金額'].iloc[i]-dt['買家實際支付金額'].iloc[i]) == dt['退款金額'].iloc[i]:
- d3[i] = 1 # 生成一個新變量(類別變量),當買家有支付(無論退不退款)時為1,沒有支付時為0(無支付時上述等式不成立,實際支付金額和退款金額均為0),表明支付的情況
- dt_use = np.vstack((d1,d2,d3)).T # 生成用于建模分析的數據集,np.vstack用于數組的垂直連接
然后是對數據集進行劃分,形成訓練集和測試集,為之后的建模做準備:
- x_train,x_test, y_train, y_test = train_test_split(dt_use[:,0:2],dt_use[:,2:3],test_size=0.25, random_state=0) # 使用從sklearn.model_selection中import的train_test_split函數進行訓練集、測試集的劃分
- print('訓練集的自變量數據的維度',x_train.shape)
- print('訓練集的因變量量數據的維度',x_test.shape)
- print('測試集的自變量數據的維度',y_train.shape)
- print('測試集的因變量數據的維度',y_test.shape) # 查看數據集劃分后的維度情況

數據建模
首先,構建初始的模型,這里完成的是分類預測任務,選擇經典的幾個模型,分別是 SVM、LDA、樸素貝葉斯NB、KNN判別、決策樹Detree和神經網絡Network:
- models = {} # 構建一個models集合
- models['SVM'] = svm.SVC() # 支持向量機模型
- models['LDA'] = LinearDiscriminantAnalysis() # LDA判別模型
- models['NB'] = MultinomialNB() # 樸素貝葉斯模型
- models['KNN'] = KNeighborsClassifier() # KNN判別模型
- models['Detree'] = DecisionTreeClassifier() # 決策樹模型
- models['Network'] = MLPClassifier() # 神經網絡模型
然后,對模型進行訓練和分析:
- target_names = ['有支付','沒有支付'] # 生成類別的名稱
- for key in models:
- models[key].fit(x_train,y_train) # 模型訓練
- display(confusion_matrix(y_test,models[key].predict(x_test))) # 對y_test進行預測,輸出混淆矩陣
- print(classification_report(y_test,models[key].predict(x_test),target_names=target_names)) # 對y_test進行預測,輸出預測的分類評價
- print('\n')
SVM模型的混淆矩陣和準確率:

LDA模型的混淆矩陣和準確率:

樸素貝葉斯NB模型的混淆矩陣和準確率:

KNN判別模型的混淆矩陣和準確率:

決策樹Detree模型的混淆矩陣和準確率:

神經網絡Network模型的混淆矩陣和準確率:

從上述結果中可以看到,SVM和KNN的準確率較高。下面對樸素貝葉斯NB模型進行調參,看是否能改善其預測準確率:
- param_grid_nb = {'alpha':[0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]} # 設定貝葉斯模型中不同alpha值
- model_nb_ty = MultinomialNB() # 設定貝葉斯的模型
- kfold = KFold(10, 6) # 采用10折交叉驗證,初始隨機起點為6
- grid = GridSearchCV(estimator=model_nb_ty,
- param_grid=param_grid_nb, scoring='neg_mean_squared_error', cv=kfold) # 設置網格搜索的模型
- grid_result = grid.fit(x_train, y_train) # 利用構建好的模型對數據集進行訓練,搜索最優的k值
- print('最優:%s 使用%s' % (grid_result.best_score_, grid_result.best_params_)) # 輸出最優的參數情況
- nb_model = MultinomialNB(alpha=0) # 根據模型調參的結果,重新設定樸素貝葉斯模型
- nb_model.fit(x_train,y_train) # 模型訓練
- display(confusion_matrix(y_test,nb_model.predict(x_test))) # 對y_test進行預測,輸出混淆矩陣
- print(classification_report(y_test,nb_model.predict(x_test),target_names=target_names)) # 對y_test進行預測,輸出預測的分類評價,發現并沒有什么改進

調參后的結果和調參前并沒有多少改善,說明NB模型在本次預測中受限于數據情況,而不是模型參數。
最后放上全過程的代碼,供大家學習使用:
- ### 數據分析前期準備工作 ###
- # 相關庫和函數的導入
- import numpy as np
- from sklearn import metrics
- import math
- import copy
- import pandas as pd
- import scipy as sp
- import matplotlib.pyplot as plt # 導入常用的基本庫
- import datetime as date # 導入datetime庫
- import seaborn as sns # 導入seaborn庫,用于數據可視化
- from IPython.display import display # 載入數據查看時需要使用的函數
- from sklearn.model_selection import train_test_split # 導入數據集劃分時需要使用的函數
- from sklearn.metrics import confusion_matrix # 導入生成混淆矩陣的函數
- from sklearn.preprocessing import LabelEncoder # 導入分類變量編碼時需要使用的函數
- from sklearn.metrics import classification_report # 導入分類結果評價時要用到的函數
- from sklearn.discriminant_analysis import LinearDiscriminantAnalysis # 導入LDA判別時需要使用的函數
- from sklearn.naive_bayes import MultinomialNB # 導入樸素貝葉斯時需要使用的額函數
- from sklearn.neighbors import KNeighborsClassifier # 導入KNN判別時需要使用的函數
- from sklearn.tree import DecisionTreeClassifier # 導入決策樹函數
- from sklearn.neural_network import MLPClassifier # 導入神經網絡函數
- from sklearn import svm # 導入支持向量機函數
- from sklearn.model_selection import GridSearchCV # 導入模型優化方法中的網格搜索法需要用到的函數
- from sklearn.cross_validation import KFold # 導入模型評估時使用的函數
- # 全局設定
- plt.rcParams['font.sans-serif']=['SimHei'] # 設定中文字符的顯示設定
- # 數據導入
- dt = pd.read_csv('D:資料/數據分析/數據分析與數據挖掘/實戰演練/5(tmall_order_report)/tmall_order_report.csv',encoding='gbk',engine='python')
- ### 數據整理與查看 ###
- # 數據檢查與清洗
- dt.dtypes # 查看數據集有哪些變量
- dt.columns = dt.columns.str.rstrip() # 去除列名右側的空格
- dt.duplicated().sum() # 檢查數據是否有重復值,發現并沒有重復值
- display(sum(dt.isnull().sum())) # 檢查數據集是否有缺失值
- col = dt.columns.values.tolist() # 提取數據集中的所有列變量的名稱
- col.remove('訂單付款時間') # 將訂單付款時間這一列去除
- display(sum(dt[col].isnull().sum())) # 再次檢查是否有缺失值,發現并沒有缺失值,也就是缺失值均來自“訂單付款時間”這一列
- c = np.array(['訂單創建時間','訂單付款時間']) # 提取訂單創建時間、付款時間這兩列的列名
- for i in c:
- dt[i] = pd.to_datetime(dt[i]) # 將訂單創建時間、付款時間由object類型轉為datetime類型,方便運算
- for i in range(0,dt.shape[0]):
- if (dt['訂單付款時間'].iloc[i] < dt['訂單創建時間'].iloc[i]) == True:
- dt['訂單付款時間'].iloc[i] = dt['訂單付款時間'].iloc[i] + date.timedelta(days=1) # 將訂單付款時間 < 訂單創建時間的時間數據往后加1天(因為原數據中沒有考慮日期差異情況)
- mu = np.mean(dt['訂單付款時間']-dt['訂單創建時間']) # 計算時間差的均值,用于之后進行缺失值替換
- for i in range(0,dt.shape[0]):
- if pd.isnull(dt['訂單付款時間'].iloc[i]) == True: # 進行缺失值填補
- dt['訂單付款時間'].iloc[i] = dt['訂單創建時間'].iloc[i] + mu
- display(sum(dt.isnull().sum())) # 再次檢查數據集是否有缺失值,發現已經處理完了,但是還要檢查是否增加正確
- display(dt.duplicated().sum()) # 再次檢查數據是否有重復值,發現并沒有重復值,發現也沒有重復值
- # 描述性分析
- display(dt['總金額'].describe()) # 查看訂單總金額的情況,發現最大的訂單價格達到了188320元,最小的則只有1元,平均訂單價為107元左右
- sum(dt['買家實際支付金額']==0) / dt.shape[0] # 查看買家實際支付金額為0(也就是支付未完成)的訂單比例,占比約為32.3%
- display((dt['訂單付款時間']-dt['訂單創建時間']).describe()) # 查看訂單付款時間相比于訂單創建時間的延遲情況,發現最慢的支付延遲了接近1天,而大部分訂單在10分鐘內就完成了支付
- siz = dt.groupby(dt['收貨地址']).size() # 對收貨地址進行分組統計
- idx_sort = np.argsort(-siz) # 對分組統計的結果進行降序排序
- display(siz[idx_sort].head()) # 查看降序排序的結果的前5名,發現收貨地址選擇上海、廣東、江蘇、浙江、北京的最多
- siz[idx_sort].tail() # 查看降序排序的結果的最后5名,發現收貨地址選擇湖北、新疆、寧夏、青海和西藏的最少,其中湖北可能受疫情影響所致
- ### 建模預處理 ###
- # 特征構建
- d1 = (dt['訂單付款時間']-dt['訂單創建時間']) # 輸出訂單付款和創建之間的時間差,作為一個新變量
- d1 = (d1 / np.timedelta64(1, 's')).astype(int) # 將時間差的格式進行轉換,轉換為按秒計數,并把格式變為int類型
- le_train = LabelEncoder() # 使用從sklearn.preprocessing中import的LabelEncoder對分類數據進行編碼,以便于后續使用交叉驗證建模
- le_train.fit(dt['收貨地址'].tolist()) # 對模型進行訓練
- d2 = le_train.transform(dt['收貨地址'].tolist()) # 轉化數據,作為第2個變量
- d3 = np.zeros(dt.shape[0]) # 構建一個全為0的數組
- for i in range(0,dt.shape[0]):
- if (dt['總金額'].iloc[i]-dt['買家實際支付金額'].iloc[i]) == dt['退款金額'].iloc[i]:
- d3[i] = 1 # 生成一個新變量(類別變量),當買家有支付(無論退不退款)時為1,沒有支付時為0(無支付時上述等式不成立,實際支付金額和退款金額均為0),表明支付的情況
- dt_use = np.vstack((d1,d2,d3)).T # 生成用于建模分析的數據集,np.vstack用于數組的垂直連接
- # 數據集劃分
- x_train,x_test, y_train, y_test = train_test_split(dt_use[:,0:2],dt_use[:,2:3],test_size=0.25, random_state=0) # 使用從sklearn.model_selection中import的train_test_split函數進行訓練集、測試集的劃分
- print('訓練集的自變量數據的維度',x_train.shape)
- print('訓練集的因變量量數據的維度',x_test.shape)
- print('測試集的自變量數據的維度',y_train.shape)
- print('測試集的因變量數據的維度',y_test.shape) # 查看數據集劃分后的維度情況
- ### 數據建模 ###
- # 初始模型構建
- models = {} # 構建一個models集合
- models['SVM'] = svm.SVC() # 支持向量機模型
- models['LDA'] = LinearDiscriminantAnalysis() # LDA判別模型
- models['NB'] = MultinomialNB() # 樸素貝葉斯模型
- models['KNN'] = KNeighborsClassifier() # KNN判別模型
- models['Detree'] = DecisionTreeClassifier() # 決策樹模型
- models['Network'] = MLPClassifier() # 神經網絡模型
- # 模型訓練與分析
- target_names = ['有支付','沒有支付'] # 生成類別的名稱
- for key in models:
- models[key].fit(x_train,y_train) # 模型訓練
- display(confusion_matrix(y_test,models[key].predict(x_test))) # 對y_test進行預測,輸出混淆矩陣
- print(classification_report(y_test,models[key].predict(x_test),target_names=target_names)) # 對y_test進行預測,輸出預測的分類評價
- print('\n')
- # 模型調參(NB模型)
- param_grid_nb = {'alpha':[0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1.0]} # 設定貝葉斯模型中不同alpha值
- model_nb_ty = MultinomialNB() # 設定貝葉斯的模型
- kfold = KFold(10, 6) # 采用10折交叉驗證,初始隨機起點為6
- grid = GridSearchCV(estimator=model_nb_ty,
- param_grid=param_grid_nb, scoring='neg_mean_squared_error', cv=kfold) # 設置網格搜索的模型
- grid_result = grid.fit(x_train, y_train) # 利用構建好的模型對數據集進行訓練,搜索最優的k值
- print('最優:%s 使用%s' % (grid_result.best_score_, grid_result.best_params_)) # 輸出最優的參數情況
- nb_model = MultinomialNB(alpha=0) # 根據模型調參的結果,重新設定樸素貝葉斯模型
- nb_model.fit(x_train,y_train) # 模型訓練
- display(confusion_matrix(y_test,nb_model.predict(x_test))) # 對y_test進行預測,輸出混淆矩陣
- print(classification_report(y_test,nb_model.predict(x_test),target_names=target_names)) # 對y_test進行預測,輸出預測的分類評價,發現并沒有什么改進