1. 程式人生 > >基於聚類(Kmeans)演算法實現客戶價值分析系統(電信運營商)

基於聚類(Kmeans)演算法實現客戶價值分析系統(電信運營商)

開發環境 jupyter notebook
一、電信運營商–客戶價值分析

從客戶需求出發,瞭解客戶需要什麼,他們有怎麼樣的特徵, 電信運營商為客戶設定不同的優惠套餐

爭取更多的使用者:推出不同的優惠套餐 降低客戶流失率 提高收入 增加 ARPU 值(average revenue per user 每個使用者平均收益) 精準的市場營銷策略定製

二、使用聚類模型—分析專案需求
由於客戶多,消費行為複雜,很難人工對客戶打標籤,這種情況下:
採用無監督學習的聚類演算法更恰當

通過對客戶的特徵,日常消費行為進行分析,瞭解其偏好, 
為降低客戶流失率和爭取新使用者提供個性化營銷依據

目標客戶:
	公眾客戶
	商業客戶
	大客戶

初步目標
	中高階使用者
	中端使用者
	離網趨勢使用者
	其它需求使用者
	
通過聚類,將公眾客戶分為多個類別
聚類完成後,對分組資料的各方面做一個觀察,年齡、性別、消費情況
三、聚類模型的原理和方法
3.1 聚類(物以類聚,人以群分)

聚類(無監督) 分類(有監督,已經知道事務類別)

3.2 聚類效果評價標準(聚成幾個類比較合適)

層次聚類(hierarchical clustering) 是一種很直觀的演算法,一層一層地進行,把小的cluster 逐步聚攏(agglomerative clustering)),也可以將大的cluster逐步分割(divisive cluster)。逐步聚攏用的多

層次聚類的 dendrogram 樹(親緣關係樹狀圖解) scipy.cluster.hierarchy.linkage進行層次聚類的時候,可以使用 scipy.cluster.hierarchy.dendrogram

畫圖,畫出一棵二叉樹,高度表示兩個後代相互之間的距離

如何切割 dendrogram 樹

四、程式碼:
4.1 資料感知
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from scipy.cluster.hierarchy import linkage,dendrogram

custinfo = pd.read_csv(r'.\\data\\custinfo.csv')
custcall = pd.read_csv(r'.\\data\\custcall.csv'
) custcall.head()
~ Customer_ID Peak_calls Peak_mins OffPeak_calls OffPeak_mins Weekend_calls Weekend_mins International_mins Nat_call_cost month
0 K100130 12 10.587465 5 4.479312 0 0.000000 4.381410 0 4
1 K100130 14 11.530076 7 4.878109 1 3.045756 4.771490 0 6
2 K100130 10 9.109470 4 3.854007 0 0.000000 3.769771 0 1
3 K100130 12 10.530956 5 4.455404 0 0.000000 4.358024 0 3
4 K100130 11 9.507319 4 4.022327 0 0.000000 3.934413 0 2
4.2 資料預處理
# 資料聚合:--對整個DataFrame數值求平均值,刪除最後一列【month】
custcall2 = custcall.groupby(custcall['Customer_ID']).mean()
custcall3 = custcall2.drop('month', 1)

# 資料合併
data = pd.merge(custinfo,custcall3,left_on='Customer_ID',right_index=True)
data.index = data['Customer_ID']
data = data.drop('Customer_ID',1) 

# 資料探索:(mean,std,min,max,25%,50%,75%)
desc = data.describe()
print(desc)   

gender_cnt = pd.value_counts(data['Gender'])
print(gender_cnt)
tariff_cnt = pd.value_counts(data['Tariff'])
print(tariff_cnt)
handset_cnt = pd.value_counts(data['Handset'])
print(handset_cnt)

for col in data.columns:
    if not col in [u'Gender',u'Tariff',u'Handset']:
        fig = plt.figure()
        ax=fig.add_subplot(1,1,1)
        data[col].hist(bins=20)
        ax.set_title(col)
        fig.show()           

這裡寫圖片描述

4.3 模型建立
data_feature = data.drop('Age',1)
data_feature = data_feature.drop('Gender',1)
data_feature = data_feature.drop('Tariff',1)
data_feature = data_feature.drop('Handset',1)
data_zs = 1.0*(data_feature - data_feature.mean())/data_feature.std()       #資料標準化  

Z = linkage(data_zs, method = 'ward', 
            metric = 'euclidean')                #譜系聚類圖(歐式距離)
P = dendrogram(Z, 0)                             #畫譜系聚類圖
plt.show()
   
k = 4                                            #聚類的類別
iteration = 500                                  #聚類最大迴圈次數

model = KMeans(n_clusters = k, 
               n_jobs = 1, 
               max_iter = iteration)             #分為k類,併發數1,數值大系統卡死
model.fit(data_zs)                               #開始聚類


r1 = pd.Series(model.labels_).value_counts()     #統計各個類別的數目
r2 = pd.DataFrame(model.cluster_centers_)        #找出聚類中心

r = pd.concat([r2, r1], axis = 1)                #橫向連線(0是縱向),得到聚類中心對應的類別下的數目
r.columns = list(data_zs.columns) + [u'class']   #重命名錶頭
print(r)

#類中心比較
# r[cols].plot(figsize=(10,10))
r2.columns = list(data_feature.columns)
r2.plot(figsize=(10,10))
plt.show()


#詳細輸出原始資料及其類別
res = pd.concat([data, 
                 pd.Series(model.labels_, index = data.index)], 
                axis = 1)                              #詳細輸出每個樣本對應的類別
res.columns = list(data.columns) + [u'class']          #重命名錶頭
res.to_excel('.\\data\\result.xls')                    #儲存結果

pd.crosstab(res['Tariff'],res['class'])
pd.crosstab(res['Handset'],res['class'])
pd.crosstab(res['Gender'],res['class'])

res[[u'Age',u'class']].hist(by='class')
res[u'Age'].groupby(res['class']).mean()

這裡寫圖片描述

4.4 概率密度圖
def density_plot(data):                        #自定義作圖函式
    plt.rcParams['axes.unicode_minus'] = False #用來正常顯示負號
    p = data.plot(kind='kde', 
                  linewidth = 2, 
                  subplots = True, 
                  sharex = False,
                  figsize=(10,15) )
    [p[i].set_ylabel(u'密度',fontproperties='SimHei') for i in range(k)]
    plt.legend()
    return plt
"""
    看密度圖的話可以看到更多的細節,但是對比效果不明顯。
    pd_: 概率密度圖檔名字首
"""
pic_output = '.\\data\\pd_'                   
for i in range(k):
    density_plot(data[res[u'class']==i]).savefig(u'%s%s.png' %(pic_output, i))

這裡寫圖片描述