little-monkey

 一、背景和挖掘目标

二、分析方法与过程

 客户价值识别最常用的是RFM模型(最近消费时间间隔Recency,消费频率Frequency,消费金额Monetary)

 

1、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\') #导出结果

2、数据预处理

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表示飞这一趟没花钱,二者概念不同

2.属性规约

原始数据属性太多,根据之前提出的LRFMC模型,只保留6个与之相关的属性

 

3.数据变换

 

方法1:EXCEL手动操作(方便简单)

data_select.to_excel(\'tmp/data_select.xls\', index = False) #数据写入

方法2:代码操作(方便新增信息的抽取)

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

接下来进行数据标准化

#标准差标准化
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) #数据写入

3、模型构建

 1.客户聚类

#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)        #调用雷达图作图函数

4、决策支持

 

三、【拓展思考】客户流失分析

1、目标

2、数据预处理

参考自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文件不同导致

 

3、模型构建

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()

 

 

 

分类:

技术点:

相关文章:

猜你喜欢
相关资源
相似解决方案