最近在阅读张良均、王路等人出版的书《python数据分析与挖掘实战》,其中有个案例是介绍航空公司客户价值的分析,其中用到的聚类方法是K-Means方法,我一直把学习的重心放在监督学习上,今天就用这个案例练习一下非监督学习。由于书上将这个案例介绍的比较详细,导致网上的好多博客都是直接将代码复制到博客上甚至是直接截图粘贴,还都说是自己原创, 真好笑。本文只是部分参考,不喜勿喷。

书中给出了关于62988个客户的基本信息和在观测窗口内的消费积分等相关信息,其中包含了会员卡号、入会时间、性别、年龄、会员卡级别、在观测窗口内的飞行公里数、飞行时间等44个特征属性。

为了便于观察数据,采用anaconda的notebook进行分析及可视化

首先导入分析中用到的各种第三方工具包

import pandas as pd
import numpy as np
from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

接着将数据读取到程序中,并查看每个特征属性的相关信息,以便对“脏”数据进行处理

datafile = "air_data.csv"
data = pd.read_csv(datafile, encoding="utf-8")
print(data.shape)
print(data.info())
(62988, 44)
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 62988 entries, 0 to 62987
Data columns (total 44 columns):
MEMBER_NO                  62988 non-null int64
FFP_DATE                   62988 non-null object
FIRST_FLIGHT_DATE          62988 non-null object
GENDER                     62985 non-null object
FFP_TIER                   62988 non-null int64
WORK_CITY                  60719 non-null object
WORK_PROVINCE              59743 non-null object
WORK_COUNTRY               62962 non-null object
AGE                        62568 non-null float64
LOAD_TIME                  62988 non-null object
FLIGHT_COUNT               62988 non-null int64
BP_SUM                     62988 non-null int64
EP_SUM_YR_1                62988 non-null int64
EP_SUM_YR_2                62988 non-null int64
SUM_YR_1                   62437 non-null float64
SUM_YR_2                   62850 non-null float64
SEG_KM_SUM                 62988 non-null int64
WEIGHTED_SEG_KM            62988 non-null float64
LAST_FLIGHT_DATE           62988 non-null object
AVG_FLIGHT_COUNT           62988 non-null float64
AVG_BP_SUM                 62988 non-null float64
BEGIN_TO_FIRST             62988 non-null int64
LAST_TO_END                62988 non-null int64
AVG_INTERVAL               62988 non-null float64
MAX_INTERVAL               62988 non-null int64
ADD_POINTS_SUM_YR_1        62988 non-null int64
ADD_POINTS_SUM_YR_2        62988 non-null int64
EXCHANGE_COUNT             62988 non-null int64
avg_discount               62988 non-null float64
P1Y_Flight_Count           62988 non-null int64
L1Y_Flight_Count           62988 non-null int64
P1Y_BP_SUM                 62988 non-null int64
L1Y_BP_SUM                 62988 non-null int64
EP_SUM                     62988 non-null int64
ADD_Point_SUM              62988 non-null int64
Eli_Add_Point_Sum          62988 non-null int64
L1Y_ELi_Add_Points         62988 non-null int64
Points_Sum                 62988 non-null int64
L1Y_Points_Sum             62988 non-null int64
Ration_L1Y_Flight_Count    62988 non-null float64
Ration_P1Y_Flight_Count    62988 non-null float64
Ration_P1Y_BPS             62988 non-null float64
Ration_L1Y_BPS             62988 non-null float64
Point_NotFlight            62988 non-null int64
dtypes: float64(12), int64(24), object(8)
memory usage: 21.1+ MB
None
print(data[0:5])
   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]

通过观测可知,数据集中存在票价为零但是飞行公里大于零的不合理值,但是所占比例较小,这里直接删去

data = data[data["SUM_YR_1"].notnull() & data["SUM_YR_2"].notnull()]
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]
print(data.shape)
(62044, 44)

删除后剩余的样本值是62044个,可见异常样本的比例不足1.5%,因此不会对分析结果产生较大的影响。

原始数据集的特征属性太多,而且各属性不具有降维的特征,故这里选取几个对航空公司来说比较有价值的几个特征进行分析,这里并没有完全按照书中的做法选取特征,最终选取的特征是第一年总票价、第二年总票价、观测窗口总飞行公里数、飞行次数、平均乘机时间间隔、观察窗口内最大乘机间隔、入会时间、观测窗口的结束时间、平均折扣率这八个特征。下面说明这么选的理由:

  • 选取的特征是第一年总票价、第二年总票价、观测窗口总飞行公里数是要计算平均飞行每公里的票价,因为对于航空公司来说并不是票价越高,飞行公里数越长越能创造利润,相反而是那些近距离的高等舱的客户创造更大的利益。
  • 当然总飞行公里数、飞行次数也都是评价一个客户价值的重要的指标
  • 入会时间可以看出客户是不是老用户及忠诚度
  • 通过平均乘机时间间隔、观察窗口内最大乘机间隔可以判断客户的乘机频率是不是固定
  • 平均折扣率可以反映出客户给公里带来的利益,毕竟来说越是高价值的客户享用的折扣率越高
filter_data = data[[ "FFP_DATE", "LOAD_TIME", "FLIGHT_COUNT", "SUM_YR_1", "SUM_YR_2", "SEG_KM_SUM", "AVG_INTERVAL" , "MAX_INTERVAL", "avg_discount"]]
filter_data[0:5]
  FFP_DATE LOAD_TIME FLIGHT_COUNT SUM_YR_1 SUM_YR_2 SEG_KM_SUM AVG_INTERVAL MAX_INTERVAL avg_discount
0 2006/11/02 2014/03/31 210 239560.0 234188.0 580717 3.483254 18 0.961639
1 2007/02/19 2014/03/31 140 171483.0 167434.0 293678 5.194245 17 1.252314
2 2007/02/01 2014/03/31 135 163618.0 164982.0 283712 5.298507 18 1.254676
3 2008/08/22 2014/03/31 23 116350.0 125500.0 281336 27.863636 73 1.090870
4 2009/04/10 2014/03/31 152 124560.0 130702.0 309928 4.788079 47 0.970658

对特征进行变换:

data["LOAD_TIME"] = pd.to_datetime(data["LOAD_TIME"])
data["FFP_DATE"] = pd.to_datetime(data["FFP_DATE"])
data["入会时间"] = data["LOAD_TIME"] - data["FFP_DATE"]
data["平均每公里票价"] = (data["SUM_YR_1"] + data["SUM_YR_2"]) / data["SEG_KM_SUM"]
data["时间间隔差值"] = data["MAX_INTERVAL"] - data["AVG_INTERVAL"]
deal_data = data.rename(columns = {"FLIGHT_COUNT" : "飞行次数", "SEG_KM_SUM" : "总里程", "avg_discount" : "平均折扣率"},inplace = False
)
filter_data = deal_data[["入会时间", "飞行次数", "平均每公里票价", "总里程", "时间间隔差值", "平均折扣率"]]
print(filter_data[0:5])
filter_data['入会时间'] = filter_data['入会时间'].astype(np.int64)/(60*60*24*10**9)
print(filter_data[0:5])
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'>
RangeIndex: 62988 entries, 0 to 62987
Data columns (total 6 columns):
入会时间       62988 non-null float64
飞行次数       62988 non-null int64
平均每公里票价    62299 non-null float64
总里程        62988 non-null int64
时间间隔差值     62988 non-null float64
平均折扣率      62988 non-null float64
dtypes: float64(4), int64(2)
memory usage: 2.9 MB
None

没找到更好的处理timedatle的方法,这里自己用笨方法找了一下规律,暂且这样处理吧。

由于不同的属性相差范围较大,这里进行标准化处理

filter_zscore_data = (filter_data - filter_data.mean(axis=0))/(filter_data.std(axis=0))
filter_zscore_data[0:5]
  入会时间 飞行次数 平均每公里票价 总里程 时间间隔差值 平均折扣率
0 1.441178 14.104488 0.609218 26.887901 -0.975255 1.294751
1 1.312523 9.122093 1.806504 13.193844 -1.006818 2.862354
2 1.333768 8.766208 1.821278 12.718386 -0.996389 2.875087
3 0.663343 0.794378 0.764434 12.605032 -0.618769 1.991687
4 0.390687 9.976218 0.636894 13.969099 -0.652816 1.343389

对于K-Means方法,k的取值是一个难点,因为是无监督的聚类分析问题,所以不寻在绝对正确的值,需要进行研究试探。这里采用计算SSE的方法,尝试找到最好的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.valuesnums=range(2,10)SSE = []for num in nums:sse = 0kmodel = 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(data)):cluster_num = cluster_list[index]sse += distEclud(data_train[index, :], cluster_ceter_list[cluster_num])print("簇数是",num , "时; SSE是", sse)SSE.append(sse)return nums, SSEnums, SSE = test_Kmeans_nclusters(filter_zscore_data)
簇数是 2 时; SSE是 296587.688611
簇数是 3 时; SSE是 245317.292202
簇数是 4 时; SSE是 209299.798194
簇数是 5 时; SSE是 183885.938906
簇数是 6 时; SSE是 167465.10385
簇数是 7 时; SSE是 151869.163041
簇数是 8 时; SSE是 142922.824005
簇数是 9 时; SSE是 135003.92238
#画图,通过观察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()


观察图像,并没有的所谓的“肘”点出现,是随k值的增大逐渐减小的,这里选取当k分别取4, 5, 6时进行,看能不能通过分析结果来反向选取更合适的值,k取值4时的代码如下

kmodel = KMeans(n_clusters=4, n_jobs=4)
kmodel.fit(filter_zscore_data)
# 简单打印结果
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(filter_zscore_data.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取值5,6时的代码与上述类似,不再给出,直接给出结果图:

通过观察可知:

当k取值4时,每个人群包含的信息比较复杂,且特征不明显

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

当k取值6时,各种人群也都有自己的特点,但是第4簇人群完全在第5簇人群特征中包含了,有点冗余的意思

综上,当k取值为5时,得到最好的聚类效果,将所有的客户分成5个人群,再进一步分析可以得到以下结论:

  • 1.第一簇人群,10957人,最大的特点是时间间隔差值最大,分析可能是“季节型客户”,一年中在某个时间段需要多次乘坐飞机进行旅行,其他的时间则出行的不多,这类客户我们需要在保持的前提下,进行一定的发展;
  • 2.第二簇人群,14732人,最大的特点就是入会的时间较长,属于老客户按理说平均折扣率应该较高才对,但是观察窗口的平均折扣率较低,而且总里程和总次数都不高,分析可能是流失的客户,需要在争取一下,尽量让他们“回心转意”;
  • 3.第三簇人群,22188人,各方面的数据都是比较低的,属于一般或低价值用户
  • 4.第三簇人群,8724人,最大的特点就是平均每公里票价和平均折扣率都是最高的,应该是属于乘坐高等舱的商务人员,应该重点保持的对象,也是需要重点发展的对象,另外应该积极采取相关的优惠政策是他们的乘坐次数增加
  • 5.第五簇人群,5443人, 总里程和飞行次数都是最多的,而且平均每公里票价也较高,是重点保持对象

分析完毕,结果暗合市场的二八法则的,价值不大的第二三簇的客户数最多,而价值较大的第四五簇的人数较少。

数据挖掘——航空公司客户价值分析(代码完整)相关推荐

  1. 数据挖掘——航空公司客户价值分析(分析+建模)

    前言 本次建模项目是来自于<python数据分析与挖掘实战>的案例,是介绍航空公司客户价值的分析,书中给出了关于62988个客户的基本信息和在观测窗口内的消费积分等相关信息,其中包含了会员 ...

  2. 航空公司客户价值分析(附完整代码)

    一.什么是客户价值分析:   客户价值分析是以客户为中心,从客户需求出发,搞清楚客户需要什么,他们有怎样的一个特征,需要什么样的产品,然后设计相应的产品,通过对客户分群从而满足客户的需求.进行客户价值 ...

  3. Python数据挖掘:利用聚类算法进行航空公司客户价值分析

    无小意丶 个人博客地址:无小意 知乎主页:无小意丶 公众号: 数据路(shuju_lu) 刚刚开始写博客,希望能保持关注,会继续努力. 以数据相关为主,互联网为辅进行文章发布. 本文是<Pyth ...

  4. Python数据挖掘-RFM模型K-means聚类分析-航空公司客户价值分析

    使用教材:<Python数据分析与挖掘实战> 模型:RFM模型 算法:K-means聚类:其他方法还用到了层次聚类 数据:需要数据的可以评论里call. 内容: (1)案例: 航空公司客户 ...

  5. 数据分析与挖掘实战-航空公司客户价值分析

    航空公司客户价值分析 项目背景 信息时代的到来注定营销焦点从产品中心转到了用户中心,客户关系管理成为企业的核心问题. 客户关系管理的关键问题则是客户分类,通过分类区分客户价值的有无和高低,针对不同类别 ...

  6. 毕业设计之 ---基于大数据分析的航空公司客户价值分析

    文章目录 0 前言 1 数据分析背景 2 分析策略 2.1 航空公司客户价值分析的LRFMC模型 2.2 数据 2.3 分析模型 3 开始分析 3.1 数据预处理 3.1.1 数据预览 3.1.2 数 ...

  7. 《Python数据分析与挖掘实战》第7章——航空公司客户价值分析(kmeans)

    本文是基于<Python数据分析与挖掘实战>的实战部分的第七章的数据--<航空公司客户价值分析>做的分析. 旨在补充原文中的细节代码,并给出文中涉及到的内容的完整代码. 1)在 ...

  8. 基于机器学习的航空公司客户价值分析与流失预测

    温馨提示:文末有 CSDN 平台官方提供的学长 Wechat / QQ 名片 :) 1. 项目背景 面对激烈的市场竞争,各个航空公司相继推出了更优惠的营销方式来吸引更多的客户,国内某航空公司面临着常旅 ...

  9. 航空公司客户价值分析python实战

    航空公司客户价值分析(K_Means聚类分析) 最近在看黄红梅.张良均老师主编的<Python数据分析与应用>,拿书里的案例练练手. 本案例将使用航空公司客户数据,结合RFM模型,采用K_ ...

最新文章

  1. python自学第12天 模块
  2. AI 质检学习报告——实践篇——第三步:python利用OpenCV打开摄像头截图后实现图片识字
  3. 微信公众号的搭建-第五天-自定义菜单
  4. SpringMvc项目加载顺序及上下文小结
  5. OkHttp3详细使用教程(2)
  6. Ruby的Fiber根本不是用来做并发的~
  7. PHP是迄今为止最好的web平台
  8. java 数据类型 面试题_Java数据类型面试题目
  9. C# 格式化JSON的两种方式
  10. php拖动滑块验证原理,原生js实现拖动滑块验证
  11. UWB 三边测量定位原理及最小二乘法和三角形质心法的应用—通俗解析
  12. 计算机打表格图,怎么把电脑整个表格截屏,表格太长,还有部(怎么把excle表格截图)...
  13. 每日一诗词 —— 将进酒
  14. 设计师思维模式你get到多少个?
  15. ISO工具集合,好用推荐,喜欢就下载使用
  16. c语言一般用大写字母定义什么,c语言程序格式是什么
  17. python查询员工信息表
  18. CentOS命令行改色。
  19. 万年历(C语言代码实现)
  20. unity android服务器端,【深圳Unity3D培训】 Android客户端与PC服务器实现Socket通信

热门文章

  1. 【软件相关】SolidWorks 2020 安装拾遗
  2. 关系型数据之分区分表分库
  3. MBA工商管理-管理类联考怎么复习?
  4. 数据库审计系统-数据库安全审计工具
  5. nginx防护规则,拦截非法字符,防止SQL注入、防XSS,nginx过滤url访问,屏蔽垃圾蜘蛛,WordPress安全代码篇
  6. 姚舜:你曾经拼命努力过,哪怕一次吗?
  7. 卡方分布Chi-squared Distribution
  8. 文档与文件比较工具简单汇总
  9. js遍历json的key和value
  10. [Openwrt 项目开发笔记]:Openwrt平台搭建(一)