[Python數據挖掘]第7章、航空公司客戶價值分析

 1、背景和挖掘目標

2、分析方法與過程

 客戶價值識別最經常使用的是RFM模型(最近消費時間間隔Recency,消費頻率Frequency,消費金額Monetary)python

 

一、EDA(探索性數據分析)

#對數據進行基本的探索
import pandas as pd

data = pd.read_csv('data/air_data.csv', encoding = 'utf-8') #讀取原始數據,指定UTF-8編碼(須要用文本編輯器將數據裝換爲UTF-8編碼)

explore = data.describe(percentiles = [], include = 'all').T #包括對數據的基本描述,percentiles參數是指定計算多少的分位數表(如1/4分位數、中位數等);T是轉置,轉置後更方便查閱
explore['null'] = len(data)-explore['count'] #describe()函數自動計算非空值數,須要手動計算空值數

explore = explore[['null', 'max', 'min']]
explore.columns = [u'空值數', u'最大值', u'最小值'] #表頭重命名
'''這裏只選取部分探索結果。
describe()函數自動計算的字段有count(非空值數)、unique(惟一值數)、top(頻數最高者)、freq(最高頻數)、mean(平均值)、std(方差)、min(最小值)、50%(中位數)、max(最大值)'''

explore.to_excel('tmp/explore.xls') #導出結果

二、數據預處理

1.數據清洗算法

data = data[data['SUM_YR_1'].notnull()&data['SUM_YR_2'].notnull()] #票價非空值才保留

#只保留票價非零的,或者平均折扣率與總飛行千米數同時爲0的記錄。
index1 = data['SUM_YR_1'] != 0
index2 = data['SUM_YR_2'] != 0
index3 = (data['SEG_KM_SUM'] == 0) & (data['avg_discount'] == 0) #該規則是「與」
data = data[index1 | index2 | index3] #該規則是「或」

票價爲空表示該值缺失,票價爲0表示飛這一趟沒花錢,兩者概念不一樣app

2.屬性規約編輯器

原始數據屬性太多,根據以前提出的LRFMC模型,只保留6個與之相關的屬性函數

 

3.數據變換編碼

 

方法1:EXCEL手動操做(方便簡單)spa

data_select.to_excel('tmp/data_select.xls', index = False) #數據寫入

方法2:代碼操做(方便新增信息的抽取).net

from datetime import datetime

#使用匿名函數將LOAD_TIME數據轉換成datetime格式,而後才能進行日期加減(匿名函數比for循環效率高)
data_select['LOAD_TIME_convert'] = data_select['LOAD_TIME'].apply(lambda x: datetime.strptime(x, '%Y/%m/%d'))
data_select['FFP_DATE_convert'] = data_select['FFP_DATE'].apply(lambda x: datetime.strptime(x, '%Y/%m/%d'))

#構造一個Series序列接收  (LOAD_TIME-FFP_DATE)
data_select['L']=pd.Series()

#(LOAD_TIME-FFP_DATE)獲得兩個日期之間的天數間隔,而後除以30獲得月份間隔          這一步至關費時
for i in range(len(data_select)):
    data_select['L'][i] =(data_select['LOAD_TIME_convert'][i]-data_select['FFP_DATE_convert'][i]).days/30

data_select = data_select.rename(columns = {'LAST_TO_END': 'R','FLIGHT_COUNT':'F','SEG_KM_SUM':'M','avg_discount':'C'})
data_selected=data_select[['L','R','F','M','C']]
data_selected

接下來進行數據標準化3d

#標準差標準化
import pandas as pd

data = pd.read_excel('data/zscoredata.xls', index = False) 
data = (data - data.mean(axis = 0))/(data.std(axis = 0)) #簡潔的語句實現了標準化變換,相似地能夠實現任何想要的變換。
data.columns=['Z'+i for i in data.columns] #表頭重命名。

data.to_excel('tmp/zscoreddata.xls', index = False) #數據寫入

三、模型構建

 1.客戶聚類excel

#K-Means聚類算法
import pandas as pd
from sklearn.cluster import KMeans #導入K均值聚類算法

k = 5                       #須要進行的聚類類別數

#讀取數據並進行聚類分析
data = pd.read_excel('data/zscoreddata.xls') 

#調用k-means算法,進行聚類分析
kmodel = KMeans(n_clusters = k, n_jobs = 4) #n_jobs是並行數,通常等於CPU數較好
kmodel.fit(data) #訓練模型

# kmodel.cluster_centers_ #查看聚類中心
# kmodel.labels_ #查看各樣本對應的類別

#簡單打印結果
s = pd.Series(['客戶羣1','客戶羣2','客戶羣3','客戶羣4','客戶羣5'], index=[0,1,2,3,4]) #建立一個序列s
r1 = pd.Series(kmodel.labels_).value_counts() #統計各個類別的數目
r2 = pd.DataFrame(kmodel.cluster_centers_) #找出聚類中心
r = pd.concat([s,r1,r2], axis = 1) #橫向鏈接(0是縱向),獲得聚類中心對應的類別下的數目
r.columns =[u'聚類名稱'] +[u'聚類個數'] + list(data.columns) #重命名錶頭
print(r)

2.客戶價值分析

#雷達圖代碼摘自  https://blog.csdn.net/Just_youHG/article/details/83904618
def plot_radar(data):
    '''
    the first column of the data is the cluster name;
    the second column is the number of each cluster;
    the last are those to describe the center of each cluster.
    '''
    kinds = data.iloc[:, 0]
    labels = data.iloc[:, 2:].columns
    centers = pd.concat([data.iloc[:, 2:], data.iloc[:,2]], axis=1)
    centers = np.array(centers)
    n = len(labels)
    angles = np.linspace(0, 2*np.pi, n, endpoint=False)
    angles = np.concatenate((angles, [angles[0]]))
    
    fig = plt.figure()
    ax = fig.add_subplot(111, polar=True) # 設置座標爲極座標
    
    # 畫若干個五邊形
    floor = np.floor(centers.min())     # 大於最小值的最大整數
    ceil = np.ceil(centers.max())       # 小於最大值的最小整數
    for i in np.arange(floor, ceil + 0.5, 0.5):
        ax.plot(angles, [i] * (n + 1), '--', lw=0.5 , color='black')
    
    # 畫不一樣客戶羣的分割線
    for i in range(n):
        ax.plot([angles[i], angles[i]], [floor, ceil], '--', lw=0.5, color='black')
    
    # 畫不一樣的客戶羣所佔的大小
    for i in range(len(kinds)):
        ax.plot(angles, centers[i], lw=2, label=kinds[i])
        #ax.fill(angles, centers[i])
    
    ax.set_thetagrids(angles * 180 / np.pi, labels) # 設置顯示的角度,將弧度轉換爲角度
    plt.legend(loc='lower right', bbox_to_anchor=(1.5, 0.0)) # 設置圖例的位置,在畫布外
    
    ax.set_theta_zero_location('N')        # 設置極座標的起點(即0°)在正北方向,即至關於座標軸逆時針旋轉90°
    ax.spines['polar'].set_visible(False)  # 不顯示極座標最外圈的圓
    ax.grid(False)                         # 不顯示默認的分割線
    ax.set_yticks([])                      # 不顯示座標間隔
    
    plt.show()

plot_radar(r)        #調用雷達圖做圖函數

四、決策支持

 

3、【拓展思考】客戶流失分析

一、目標

二、數據預處理

參考自https://blog.csdn.net/zhouchen1998/article/details/85113535

import pandas as pd
from datetime import datetime

def clean(data):
    '''
    數據清洗,去除空記錄
    '''
    data = data[data['SUM_YR_1'].notnull() & data['SUM_YR_2'].notnull()]  # 票價非空值才保留

    # 只保留票價非零的,或者平均折扣率與總飛行千米數同時爲0的記錄。
    index1 = data['SUM_YR_1'] != 0
    index2 = data['SUM_YR_2'] != 0
    index3 = (data['SEG_KM_SUM'] == 0) & (data['avg_discount'] == 0)  # 該規則是「與」
    data = data[index1 | index2 | index3]  # 該規則是「或」

    #取出須要的屬性列
    data = data[['LOAD_TIME', 'FFP_DATE', 'LAST_TO_END', 'FLIGHT_COUNT', 'avg_discount', 'SEG_KM_SUM', 'LAST_TO_END',
                 'P1Y_Flight_Count', 'L1Y_Flight_Count']]
    return data

def LRFMCK(data):
    '''
    通過計算獲得個人指標數據
    '''
    # 其中K爲標籤標示用戶類型
    data2 = pd.DataFrame(columns=['L', 'R', 'F', 'M', 'C', 'K'])
    time_list = []
    for i in range(len(data['LOAD_TIME'])):
        str1 = data['LOAD_TIME'][i].split('/')
        str2 = data['FFP_DATE'][i].split('/')
        temp = datetime(int(str1[0]), int(str1[1]), int(str1[2])) - datetime(int(str2[0]), int(str2[1]), int(str2[2]))
        time_list.append(temp.days)
    data2['L'] = pd.Series(time_list)
    data2['R'] = data['LAST_TO_END']
    data2['F'] = data['FLIGHT_COUNT']
    data2['M'] = data['SEG_KM_SUM']
    data2['C'] = data['avg_discount']
    temp = data['L1Y_Flight_Count'] / data['P1Y_Flight_Count']
    for i in range(len(temp)):
        if temp[i] >=0.9:
            # 未流失客戶
            temp[i] = 'A'
        elif 0.5 < temp[i] < 0.9:
            # 準流失客戶
            temp[i] = 'B'
        else:
            temp[i] = 'C'
    data2['K'] = temp
    data2.to_csv('data/data_changed.csv', encoding='utf-8')

def standard():
    '''
    標準差標準化
    '''
    data = pd.read_csv('data/data_changed.csv', encoding='utf-8').iloc[:, 1:6]
    # 簡潔的語句實現了標準化變換,相似地能夠實現任何想要的變換
    data = (data - data.mean(axis=0)) / (data.std(axis=0))
    data.columns = ['Z' + i for i in data.columns]
    data2 = pd.read_csv('data/data_changed.csv', encoding='utf-8')
    data['K'] = data2['K']
    data.to_csv('data/data_standard.csv', index=False)

if __name__ == '__main__':
    data = pd.read_csv('data/air.csv', encoding='utf-8', engine='python')
    data=clean(data)
    data.to_csv('data/data_filter.csv', index = False, encoding='utf-8')
    data = pd.read_csv('data/data_filter.csv', encoding='utf-8')   #不從新讀取的話,調用LRFMCK會報錯,我也不知道爲何
    LRFMCK(data)
    standard()

不知道爲何,老是要反覆寫入文件和讀取文件,否則會莫名其妙的報錯。猜想多是csv文件與xls文件不一樣致使

 

三、模型構建

import pandas as pd
from sklearn import tree
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix #導入混淆矩陣函數
import pydotplus

# 讀取數據
def getDataSet(fileName):
    data = pd.read_csv(fileName)
    dataSet = []
    for item in data.values:
        dataSet.append(list(item[:5]))
    label = list(data['K'])
    return dataSet, label
    
# 做圖評估
def cm_plot(y, yp):
  cm = confusion_matrix(y, yp) #混淆矩陣
  plt.matshow(cm, cmap=plt.cm.Greens) #畫混淆矩陣圖,配色風格使用cm.Greens,更多風格請參考官網。
  plt.colorbar() #顏色標籤
  for x in range(len(cm)): #數據標籤
    for y in range(len(cm)):
      plt.annotate(cm[x,y], xy=(x, y), horizontalalignment='center', verticalalignment='center')  
  plt.ylabel('True label') #座標軸標籤
  plt.xlabel('Predicted label') #座標軸標籤
  return plt

data, label = getDataSet('data/data_standard.csv')
train_data, test_data, train_label, test_label = train_test_split(data, label, test_size=0.2)

#使用決策樹
clf = tree.DecisionTreeClassifier(max_depth=5)
clf = clf.fit(train_data, train_label)

# 可視化
dataLabels = ['ZL', 'ZR', 'ZF', 'ZM', 'ZC', ]
data_list = []
data_dict = {}
for each_label in dataLabels:
    for each in data:
        data_list.append(each[dataLabels.index(each_label)])
    data_dict[each_label] = data_list
    data_list = []
lenses_pd = pd.DataFrame(data_dict)
#print(lenses_pd.keys())

#畫決策樹的決策流程
dot_data = StringIO()
tree.export_graphviz(clf, out_file=dot_data, feature_names=lenses_pd.keys(),
                         class_names=clf.classes_, filled=True, rounded=True, special_characters=True)
graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
graph.write_pdf("tree.pdf")

cm_plot(test_label, clf.predict(test_data)).show()

相關文章
相關標籤/搜索