航空公司客户价值数据的分析

这是第一次使用CSDN记录自己的学习笔记,主要是记录代码语句的用法。

import pandas as pd
#我原本使用的是'gbk'解码方式, 但出现了报错,查询后选择了更高级的'gb18030'解码方式
air_data = pd.read_csv('air_data.csv', encoding = 'gb18030',error_bad_lines = False)
air_data.describe().T
#我使用describe()进行描述性统计,主要看min、max、mean、中位数

打印如图所示航空公司客户价值数据的分析_第1张图片

观察原始数据发现了票价存在空值,处理方法为丢弃票价为空的记录

#打印原始数据记录条数
air_data.shape[0]
#丢弃票价为空的记录
clear_air_data = air_data[air_data["SUM_YR_1"].notnull() & air_data["SUM_YR_2"].notnull()] 
#notfull()返回非空数据
clear_air_data.shape[0]

可明显观察到数据的数量变化
航空公司客户价值数据的分析_第2张图片
丢弃票价为0,平均折扣率不为0,总飞行公里数大于0的记录。

doc1 = (clear_air_data["SUM_YR_1"] !=0) | (clear_air_data["SUM_YR_2"] !=0)          #票价不为0的, '|'是或。 '&'是与。
doc2 = (clear_air_data["SEG_KM_SUM"] == 0) & (clear_air_data["avg_discount"] == 0)   #飞行公里==0,平均折扣==0的
data = clear_air_data[doc1 | doc2]
print(data.shape[0])

这是进行清洗后的数据记录条数
航空公司客户价值数据的分析_第3张图片
经过分析,通过数据选择抽离LRFMC特征。
'L’是会员入会时间到窗口结束的时间,在模型中越大越好;
'R’是客户最近一次乘坐公司飞机距观测窗口结束的月数,在模型中越小越好;
'F’是客户在观测窗口内乘坐公司飞机的次数,在模型中越大越好;
'M’是客户在观测窗口内累计的飞行里程,在模型中越大越好;
'C’客户在观测窗口内乘坐舱位所对应的折扣系数的平均值,在模型中越大越好。

#使用to_datetime()方法将数据转变成标准日期格式
import numpy as np
data["LOAD_TIME"] = pd.to_datetime(data["LOAD_TIME"])
data["FFP_DATE"] = pd.to_datetime(data["FFP_DATE"])
#转换为月份
#timedelta64(),参数'M'是将对象转变为“几个月”,参数'1'是以“一个M为单位”对对象进行处理
data["L"]= (data["LOAD_TIME"] - data["FFP_DATE"]) / np.timedelta64(1,'M')
#生成新的DataFrame
data2=data[['L','LAST_TO_END','FLIGHT_COUNT','SEG_KM_SUM','avg_discount']]
#对列索引进行重命名处理
data2.columns=['L','R','F','M','C']
print(data2)

航空公司客户价值数据的分析_第4张图片

#对数据进行Z-标准化处理
#Z-Scores的处理方式是减均值再除以方差
data3=(data2-data2.mean(axis=0))/(data2.std(axis=0))
data3

标准化后的数据如图所示
航空公司客户价值数据的分析_第5张图片
对数据预处理后进行聚类。一开始并不确定K值为多少时能达到最好的聚类效果,故先用一个迭代器进行遍历打印不同K值下的聚类效果

def distEclud(vecA, vecB):
    """
    计算两个向量的欧式距离的平方,并返回
    """
    return np.sum(np.power(vecA - vecB, 2))
 
def test_Kmeans_nclusters(data_train):
    """
    计算不同的k值时,SSE的大小变化簇内和方差
    """
    data_train = data_train.values
    nums=range(2,10)
    SSE = []
    for num in nums:
        sse = 0
        kmodel = KMeans(n_clusters=num, n_jobs=4)#初始化
        kmodel.fit(data_train)
        # 簇中心
        cluster_ceter_list = kmodel.cluster_centers_
        # 个样本属于的簇序号列表
        cluster_list = kmodel.labels_.tolist()
        for index in  range(len(data3)):
            cluster_num = cluster_list[index]
            sse += distEclud(data_train[index, :], cluster_ceter_list[cluster_num])
        print("簇数是",num , "时; SSE是", sse)
        SSE.append(sse)
    return nums, SSE
nums, SSE = test_Kmeans_nclusters(data3)

通过遍历打印不能直观的看到“肘值”,故选择画图呈现SSE随k的取值变化而变化的关系
航空公司客户价值数据的分析_第6张图片

#代码来源博客@chnhbhndchngn
#画图,通过观察SSE与k的取值尝试找出合适的k值
# 中文和负号的正常显示
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['font.size'] = 12.0
plt.rcParams['axes.unicode_minus'] = False
# 使用ggplot的绘图风格
plt.style.use('ggplot')
## 绘图观测SSE与簇个数的关系
fig=plt.figure(figsize=(10, 8))
ax=fig.add_subplot(1,1,1)
ax.plot(nums,SSE,marker="+")
ax.set_xlabel("n_clusters", fontsize=18)
ax.set_ylabel("SSE", fontsize=18)
fig.suptitle("KMeans", fontsize=20)
plt.show()

判别方法SSE又被称作“手肘法”,其核心思想是:随着聚类数k的增大,样本划分会更加精细,每个簇的聚合程度会逐渐提高,那么误差平方和SSE自然会逐渐变小。并且,当k小于真实聚类数时,由于k的增大会大幅增加每个簇的聚合程度,故SSE的下降幅度会很大,而当k到达真实聚类数时,再增加k所得到的聚合程度回报会迅速变小,所以SSE的下降幅度会骤减,然后随着k值的继续增大而趋于平缓,也就是说SSE和k的关系图是一个手肘的形状,而这个肘部对应的k值就是数据的真实聚类数。当然,这也是该方法被称为手肘法的原因。

通过观察曲线并没有发现一个明显的“肘点”,所以我们要尝试通过代入K值,能不能找到比较好的聚类类别
航空公司客户价值数据的分析_第7张图片

#雷达图代码来源博客@chnhbhndchngn

kmodel = KMeans(n_clusters=4, n_jobs=4)
kmodel.fit(data3)
# 简单打印结果
r1 = pd.Series(kmodel.labels_).value_counts() #统计各个类别的数目
r2 = pd.DataFrame(kmodel.cluster_centers_) #找出聚类中心
# 所有簇中心坐标值中最大值和最小值
max = r2.values.max()
min = r2.values.min()
r = pd.concat([r2, r1], axis = 1) #横向连接(0是纵向),得到聚类中心对应的类别下的数目
r.columns = list(data3.columns) + [u'类别数目'] #重命名表头
 
# 绘图
fig=plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, polar=True)
center_num = r.values
feature =["会员入会时长","最后一次飞行时间与窗口期间隔","飞行次数","总公里数","平均折扣率"]
N =len(feature)
for i, v in enumerate(center_num):
    # 设置雷达图的角度,用于平分切开一个圆面
    angles=np.linspace(0, 2*np.pi, N, endpoint=False)
    # 为了使雷达图一圈封闭起来,需要下面的步骤
    center = np.concatenate((v[:-1],[v[0]]))
    angles=np.concatenate((angles,[angles[0]]))
    # 绘制折线图
    ax.plot(angles, center, 'o-', linewidth=2, label = "第%d簇人群,%d人"% (i+1,v[-1]))
    # 填充颜色
    ax.fill(angles, center, alpha=0.25)
    # 添加每个特征的标签
    ax.set_thetagrids(angles * 180/np.pi, feature, fontsize=15)
    # 设置雷达图的范围
    ax.set_ylim(min-0.1, max+0.1)
    # 添加标题
    plt.title('客户群特征分析图', fontsize=20)
    # 添加网格线
    ax.grid(True)
    # 设置图例
    plt.legend(loc='upper right', bbox_to_anchor=(1.3,1.0),ncol=1,fancybox=True,shadow=True)
    
# 显示图形
plt.show()

图为k = 4的聚类模型
航空公司客户价值数据的分析_第8张图片

图为k = 5时的聚类模型
航空公司客户价值数据的分析_第9张图片

图为k = 6时的聚类模型
航空公司客户价值数据的分析_第10张图片

打印出k = 4, k = 5时的客户类群

#打印客户类群代码来源博客@爱吃牛肉的拉面

#K-Means聚类算法
from sklearn.cluster import KMeans #导入K均值聚类算法

k = 5                       #需要进行的聚类类别数
#调用k-means算法,进行聚类分析
kmodel = KMeans(n_clusters = 4, n_jobs = 4) #n_jobs是并行数,一般等于CPU数较好
kmodel.fit(data3) #训练模型

#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 =['聚类名称'] +['聚类个数'] + list(data3.columns)#重命名表头
#r.columns = ['聚类名称', '聚类个数', '会员入会时长', '最后一次飞行与窗口期间隔', '飞行次数', '总公里数', '平均折扣率']
print(r)

k = 4时的客户类群
航空公司客户价值数据的分析_第11张图片

k = 5时的客户类群
在这里插入图片描述

结合雷达图和客户类群分布 可以看出来k = 4时,每个人群包含的信息比较复杂,且特征不明显

当k = 5时,分析的结果比较合理,分出的五种类型人群都有自己的特点又不相互重复

当k取值6时,各种人群也都有自己的特点,但是第4簇人群完全在第5簇人群特征中包含了,没有必要多分这么一类

综上,当k = 5时能到到较好的分类结果。
分析如下:
第一簇用户:12125人。最大的特点是飞行次数多,飞行间隔时间短,不太看重折扣率,可以发展

第二簇用户:5336人。入会时间长,飞行总里程大,是公司的“老客户”,应当争取

第三簇用户:24663人。各方面数据都比较低,属于一般用户

第四簇用户:4179人。该类客户折扣率最高,应该是乘坐商务舱的人员,是重点发展对象

第五簇用户:15741人。入会时间长但其他数据偏低,需要推出一系列优惠措施吸引他们的目光。

你可能感兴趣的:(python,聚类)