本次實戰項目是關於航空公司客戶價值的分析,其中用到的聚類方法是K-Means方法,屬於非監督學習。
Tools :python 3.6; jupyter
os : mac os
reference: 數據分析與挖掘實戰,csdn
數據分析或挖掘涉及的一般步驟:

數據集中共有62988個客戶的基本信息和在觀測窗口內的消費積分等相關信息,其中包含了會員卡號、入會時間、性別、年齡、會員卡級別、在觀測窗口內的飛行公里數、飛行時間等44個特征屬性。
挖掘目標
-
根據客戶信息,對客戶進行分類。
-
針對不同類型客戶進行特征提取,分析不同類型客戶的價值。
-
采取個性化服務,根據客戶類型,制定相應營銷策略。
首先導入需要用到的庫,然后概覽數據集,並進行數據預處理:
1 import pandas as pd 2 import numpy as np 3 from sklearn.cluster import KMeans 4 import matplotlib.pyplot as plt 5 6 datafile = "/Volumes/win/sj/dataset/air_data.csv" 7 data = pd.read_csv(datafile, encoding="utf-8") 8 print(data.shape) 9 print(data.info())

MEMBER_NO FFP_DATE FIRST_FLIGHT_DATE GENDER FFP_TIER WORK_CITY \ 0 54993 2006/11/02 2008/12/24 男 6 . 1 28065 2007/02/19 2007/08/03 男 6 NaN 2 55106 2007/02/01 2007/08/30 男 6 . 3 21189 2008/08/22 2008/08/23 男 5 Los Angeles 4 39546 2009/04/10 2009/04/15 男 6 貴陽 WORK_PROVINCE WORK_COUNTRY AGE LOAD_TIME ... \ 0 北京 CN 31.0 2014/03/31 ... 1 北京 CN 42.0 2014/03/31 ... 2 北京 CN 40.0 2014/03/31 ... 3 CA US 64.0 2014/03/31 ... 4 貴州 CN 48.0 2014/03/31 ... ADD_Point_SUM Eli_Add_Point_Sum L1Y_ELi_Add_Points Points_Sum \ 0 39992 114452 111100 619760 1 12000 53288 53288 415768 2 15491 55202 51711 406361 3 0 34890 34890 372204 4 22704 64969 64969 338813 L1Y_Points_Sum Ration_L1Y_Flight_Count Ration_P1Y_Flight_Count \ 0 370211 0.509524 0.490476 1 238410 0.514286 0.485714 2 233798 0.518519 0.481481 3 186100 0.434783 0.565217 4 210365 0.532895 0.467105 Ration_P1Y_BPS Ration_L1Y_BPS Point_NotFlight 0 0.487221 0.512777 50 1 0.489289 0.510708 33 2 0.481467 0.518530 26 3 0.551722 0.448275 12 4 0.469054 0.530943 39 [5 rows x 44 columns]
對原始數據進行預分析,主要排查缺失值和異常值。分析原始數據的缺失值和異常值,根據分析結果,在數據處理階段中進行相應處理。
通過觀察,清洗以下數據:
- 票價為空
- 票價為0,平均折扣率不為0,總飛行公里數大於0
處理方法:滿足清洗條件的一行數據全部丟棄。
1 data = data[data["SUM_YR_1"].notnull() & data["SUM_YR_2"].notnull()] 2 index1 = data["SUM_YR_1"] != 0 3 index2 = data["SUM_YR_2"] != 0 4 index3 = (data["SEG_KM_SUM"] == 0) & (data["avg_discount"] == 0) 5 data = data[index1 | index2| index3] 6 print(data.shape)
刪除后剩余的樣本值是62044個,可見異常樣本的比例極少,不會對分析果產生較大的影響。
特征屬性構造:
原始數據集的特征屬性太多,而且各屬性不具有降維的特征,故這里選取幾個對航空公司來說比較有價值的幾個特征進行分析,
最終選取的特征是第一年總票價、第二年總票價、觀測窗口總飛行公里數、飛行次數、平均乘機時間間隔、觀察窗口內最大乘機間隔、入會時間、觀測窗口的結束時間、平均折扣率這八個特征。
理由:
- 選取的特征是第一年總票價、第二年總票價、觀測窗口總飛行公里數是要計算平均飛行每公里的票價,因為對於航空公司來說並不是票價越高,飛行公里數越長越能創造利潤,相反而是那些近距離的高等艙的客戶創造更大的利益。
- 當然總飛行公里數、飛行次數也都是評價一個客戶價值的重要的指標
- 入會時間可以看出客戶是不是老用戶及忠誠度
- 通過平均乘機時間間隔、觀察窗口內最大乘機間隔可以判斷客戶的乘機頻率是不是固定
- 平均折扣率可以反映出客戶給公里帶來的利益,畢竟來說越是高價值的客戶享用的折扣率越高
1 data["LOAD_TIME"] = pd.to_datetime(data["LOAD_TIME"]) 2 data["FFP_DATE"] = pd.to_datetime(data["FFP_DATE"]) 3 data["入會時間"] = data["LOAD_TIME"] - data["FFP_DATE"] 4 data["平均每公里票價"] = (data["SUM_YR_1"] + data["SUM_YR_2"]) / data["SEG_KM_SUM"] 5 data["時間間隔差值"] = data["MAX_INTERVAL"] - data["AVG_INTERVAL"] 6 deal_data = data.rename( 7 columns = {"FLIGHT_COUNT" : "飛行次數", "SEG_KM_SUM" : "總里程", "avg_discount" : "平均折扣率"}, 8 inplace = False 9 ) 10 filter_data = deal_data[["入會時間", "飛行次數", "平均每公里票價", "總里程", "時間間隔差值", "平均折扣率"]] 11 print(filter_data[0:5]) 12 filter_data['入會時間'] = filter_data['入會時間'].astype(np.int64)/(60*60*24*10**9) 13 print(filter_data[0:5]) 14 print(filter_data.info())
入會時間 飛行次數 平均每公里票價 總里程 時間間隔差值 平均折扣率
0 2706 days 210 0.815798 580717 14.516746 0.961639
1 2597 days 140 1.154043 293678 11.805755 1.252314
2 2615 days 135 1.158217 283712 12.701493 1.254676
3 2047 days 23 0.859648 281336 45.136364 1.090870
4 1816 days 152 0.823617 309928 42.211921 0.970658
入會時間 飛行次數 平均每公里票價 總里程 時間間隔差值 平均折扣率
0 2706.0 210 0.815798 580717 14.516746 0.961639
1 2597.0 140 1.154043 293678 11.805755 1.252314
2 2615.0 135 1.158217 283712 12.701493 1.254676
3 2047.0 23 0.859648 281336 45.136364 1.090870
4 1816.0 152 0.823617 309928 42.211921 0.970658
<class 'pandas.core.frame.DataFrame'>
Int64Index: 62044 entries, 0 to 62978
Data columns (total 6 columns):
入會時間 62044 non-null float64
飛行次數 62044 non-null int64
平均每公里票價 62044 non-null float64
總里程 62044 non-null int64
時間間隔差值 62044 non-null float64
平均折扣率 62044 non-null float64
dtypes: float64(4), int64(2)
memory usage: 3.3 MB
None
數據標准化
為不同指標數量級不同所帶來的影響,對數據進行標准差標准化。
1 filter_zscore_data = (filter_data - filter_data.mean(axis=0))/(filter_data.std(axis=0)) 2 filter_zscore_data[0:5]

利用K-Means聚類算法對客戶數據進行客戶分群
對於K-Means方法,k的取值是一個難點,因為是無監督的聚類分析問題,所以不尋在絕對正確的值,需要進行研究試探。這里采用計算SSE的方法,嘗試找到最好的K數值。
1 def distEclud(vecA, vecB): 2 """ 3 計算兩個向量的歐式距離的平方,並返回 4 """ 5 return np.sum(np.power(vecA - vecB, 2)) 6 7 def test_Kmeans_nclusters(data_train): 8 """ 9 計算不同的k值時,SSE的大小變化 10 """ 11 # print(data_train) 12 data_train = data_train.values 13 # print(data_train) 14 nums=range(2,10) 15 SSE = [] 16 for num in nums: 17 sse = 0 18 kmodel = KMeans(n_clusters=num, n_jobs=4) 19 kmodel.fit(data_train) 20 # 簇中心 21 cluster_ceter_list = kmodel.cluster_centers_ 22 # print("1.ceter_list:",cluster_ceter_list) 23 # 個樣本屬於的簇序號列表 24 cluster_list = kmodel.labels_.tolist() 25 # print("2.cluster_list:",len(cluster_list),cluster_list[-20:]) 26 for index in range(len(data)):#計算殘差平方和 27 cluster_num = cluster_list[index] 28 29 sse += distEclud(data_train[index, :], cluster_ceter_list[cluster_num]) 30 print("簇數是",num , "時; SSE是", sse) 31 # print("3.dt_index:",data_train[index, :]) 32 # print("4.c_c_l:",cluster_ceter_list[cluster_num]) 33 SSE.append(sse) 34 return nums, SSE 35 36 nums, SSE = test_Kmeans_nclusters(filter_zscore_data)
簇數是 2 時; SSE是 296587.67961 簇數是 3 時; SSE是 245317.603158 簇數是 4 時; SSE是 209300.127424 簇數是 5 時; SSE是 183885.854183 簇數是 6 時; SSE是 167465.312745 簇數是 7 時; SSE是 151869.231702 簇數是 8 時; SSE是 142922.664126 簇數是 9 時; SSE是 135004.037996
1 #畫圖,通過觀察SSE與k的取值嘗試找出合適的k值 2 # 中文和負號的正常顯示 3 plt.rcParams['font.sans-serif'] = 'STHeiti'#mac字體替換 4 plt.rcParams['font.size'] = 12.0 5 plt.rcParams['axes.unicode_minus'] = False 6 # 使用ggplot的繪圖風格 7 plt.style.use('ggplot') 8 ## 繪圖觀測SSE與簇個數的關系 9 fig=plt.figure(figsize=(10, 10)) 10 ax=fig.add_subplot(1,1,1) 11 ax.plot(nums,SSE,marker="+") 12 ax.set_xlabel("n_clusters", fontsize=18) 13 ax.set_ylabel("SSE", fontsize=18) 14 fig.suptitle("KMeans", fontsize=20) 15 plt.show()

通過觀察,並未發現明顯的轉折點,因此嘗試選取k為4,5,6的聚類,看能否得到合適的客戶聚類:
1 kmodel = KMeans(n_clusters=4, n_jobs=4) 2 kmodel.fit(filter_zscore_data) 3 # 簡單打印結果 4 r1 = pd.Series(kmodel.labels_).value_counts() #統計各個類別的數目 5 r2 = pd.DataFrame(kmodel.cluster_centers_) #找出聚類中心 6 # print(r1,r2) 7 # 所有簇中心坐標值中最大值和最小值 8 max = r2.values.max() 9 min = r2.values.min() 10 # print(max,min) 11 r = pd.concat([r2, r1], axis = 1) #橫向連接(0是縱向),得到聚類中心對應的類別下的數目 12 r.columns = list(filter_zscore_data.columns) + [u'類別數目'] #重命名表頭 13 # print(r) 14 # 繪圖 15 fig=plt.figure(figsize=(10, 8)) 16 ax = fig.add_subplot(111, polar=True) 17 center_num = r.values 18 feature = ["入會時間", "飛行次數", "平均每公里票價", "總里程", "時間間隔差值", "平均折扣率"] 19 N =len(feature) 20 for i, v in enumerate(center_num): 21 print(i,v,"#") 22 # 設置雷達圖的角度,用於平分切開一個圓面 23 angles=np.linspace(0, 2*np.pi, N, endpoint=False) 24 # 為了使雷達圖一圈封閉起來,需要下面的步驟 25 center = np.concatenate((v[:-1],[v[0]]))#-1:倒數第二個。把v拼接最后一個數 26 angles=np.concatenate((angles,[angles[0]])) 27 # 繪制折線圖 28 ax.plot(angles, center, 'o-', linewidth=2, label = "第%d簇人群,%d人"% (i+1,v[-1])) 29 # 填充顏色 30 ax.fill(angles, center, alpha=0.25) 31 # 添加每個特征的標簽 32 ax.set_thetagrids(angles * 180/np.pi, feature, fontsize=15) 33 # 設置雷達圖的范圍 34 ax.set_ylim(min-0.1, max+0.1) 35 # 添加標題 36 plt.title('客戶群特征分析圖', fontsize=20) 37 # 添加網格線 38 ax.grid(True) 39 # 設置圖例 40 plt.legend(loc='upper right', bbox_to_anchor=(1.3,1.0),ncol=1,fancybox=True,shadow=True) 41 42 # 顯示圖形 43 plt.show()

類似的,k為5,6時:


客戶價值分析
根據客戶群體特征雷達圖,
- 當k取值4時,每個人群包含的信息比較復雜,且特征不明顯
- 當k取值5時,分析的結果比較合理,分出的五種類型人群都有自己的特點又不相互重復
- 當k取值6時,各種人群也都有自己的特點,但是第4簇人群完全在第5簇人群特征中包含了,有點冗余的意思
客戶價值排名:重要保持客戶(5)>重要發展客戶(4)>重要挽留客戶(3)>一般客戶(1),第(2)群體是低價值客戶。
綜上,當k取值為5時,得到最好的聚類效果,將所有的客戶分成5個人群,再進一步分析可以得到以下結論:
- 第一簇人群,5443人, 總里程和飛行次數都是最多的,而且平均每公里票價也較高,是需要保持對象
- 第二簇人群,22188人,群體數量最大,但各方面數據都極低,乘坐次數很少、乘坐里程很小、很長時間沒有乘坐公司航班,屬於低價值客戶。
- 第三簇人群,14733人,該數量較大,最大的特點就是入會的時間較長,屬於老客戶按理說平均折扣率應該較高才對,但是觀察窗口的平均折扣率較低,但乘坐頻率變小,總里程也不高,分析可能是流失的客戶,屬於重要挽留客戶;
-
第四簇人群,10957人,最大的特點是時間間隔差值最大,分析可能是“季節型客戶”,一年中在某個時間段需要多次乘坐飛機進行旅行,其他的時間則出行的不多,這類客戶我們需要在保持的前提下,進行一定的發展;
-
第五簇人群,8723人,乘坐次數很多,乘坐里程很大,所乘航班折扣率較高,應該是屬於乘坐高等艙的商務人員,屬於重要保持客戶。也是需要重點發展的對象,另外應該積極采取相關的優惠 政策是他們的乘坐次數增加
模型應用
- 對於數量極少的客戶群5,進行一對一精准營銷。
- 對於數量極少的客戶群4,實行里程數兌換機票。
- 對於數量較大的客戶群3,提供會員升級提醒服務。
- 積極和非航空類企業合作,顧客在合作企業消費也可獲得本航空公司獎勵,增加客戶與公司的聯系。
綜上,結果符合市場的二八法則的,價值不大的第二三簇的客戶數最多,而價值較大的第四五簇的人數較少。
完整代碼及數據請到git主頁下載!
https://github.com/nashgame/DataScience/tree/master/notebook
