数据挖掘——航空公司客户价值分析(代码完整)
最近在阅读张良均、王路等人出版的书《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人, 总里程和飞行次数都是最多的,而且平均每公里票价也较高,是重点保持对象
分析完毕,结果暗合市场的二八法则的,价值不大的第二三簇的客户数最多,而价值较大的第四五簇的人数较少。
数据挖掘——航空公司客户价值分析(代码完整)相关推荐
- 数据挖掘——航空公司客户价值分析(分析+建模)
前言 本次建模项目是来自于<python数据分析与挖掘实战>的案例,是介绍航空公司客户价值的分析,书中给出了关于62988个客户的基本信息和在观测窗口内的消费积分等相关信息,其中包含了会员 ...
- 航空公司客户价值分析(附完整代码)
一.什么是客户价值分析: 客户价值分析是以客户为中心,从客户需求出发,搞清楚客户需要什么,他们有怎样的一个特征,需要什么样的产品,然后设计相应的产品,通过对客户分群从而满足客户的需求.进行客户价值 ...
- Python数据挖掘:利用聚类算法进行航空公司客户价值分析
无小意丶 个人博客地址:无小意 知乎主页:无小意丶 公众号: 数据路(shuju_lu) 刚刚开始写博客,希望能保持关注,会继续努力. 以数据相关为主,互联网为辅进行文章发布. 本文是<Pyth ...
- Python数据挖掘-RFM模型K-means聚类分析-航空公司客户价值分析
使用教材:<Python数据分析与挖掘实战> 模型:RFM模型 算法:K-means聚类:其他方法还用到了层次聚类 数据:需要数据的可以评论里call. 内容: (1)案例: 航空公司客户 ...
- 数据分析与挖掘实战-航空公司客户价值分析
航空公司客户价值分析 项目背景 信息时代的到来注定营销焦点从产品中心转到了用户中心,客户关系管理成为企业的核心问题. 客户关系管理的关键问题则是客户分类,通过分类区分客户价值的有无和高低,针对不同类别 ...
- 毕业设计之 ---基于大数据分析的航空公司客户价值分析
文章目录 0 前言 1 数据分析背景 2 分析策略 2.1 航空公司客户价值分析的LRFMC模型 2.2 数据 2.3 分析模型 3 开始分析 3.1 数据预处理 3.1.1 数据预览 3.1.2 数 ...
- 《Python数据分析与挖掘实战》第7章——航空公司客户价值分析(kmeans)
本文是基于<Python数据分析与挖掘实战>的实战部分的第七章的数据--<航空公司客户价值分析>做的分析. 旨在补充原文中的细节代码,并给出文中涉及到的内容的完整代码. 1)在 ...
- 基于机器学习的航空公司客户价值分析与流失预测
温馨提示:文末有 CSDN 平台官方提供的学长 Wechat / QQ 名片 :) 1. 项目背景 面对激烈的市场竞争,各个航空公司相继推出了更优惠的营销方式来吸引更多的客户,国内某航空公司面临着常旅 ...
- 航空公司客户价值分析python实战
航空公司客户价值分析(K_Means聚类分析) 最近在看黄红梅.张良均老师主编的<Python数据分析与应用>,拿书里的案例练练手. 本案例将使用航空公司客户数据,结合RFM模型,采用K_ ...
最新文章
- python自学第12天 模块
- AI 质检学习报告——实践篇——第三步:python利用OpenCV打开摄像头截图后实现图片识字
- 微信公众号的搭建-第五天-自定义菜单
- SpringMvc项目加载顺序及上下文小结
- OkHttp3详细使用教程(2)
- Ruby的Fiber根本不是用来做并发的~
- PHP是迄今为止最好的web平台
- java 数据类型 面试题_Java数据类型面试题目
- C# 格式化JSON的两种方式
- php拖动滑块验证原理,原生js实现拖动滑块验证
- UWB 三边测量定位原理及最小二乘法和三角形质心法的应用—通俗解析
- 计算机打表格图,怎么把电脑整个表格截屏,表格太长,还有部(怎么把excle表格截图)...
- 每日一诗词 —— 将进酒
- 设计师思维模式你get到多少个?
- ISO工具集合,好用推荐,喜欢就下载使用
- c语言一般用大写字母定义什么,c语言程序格式是什么
- python查询员工信息表
- CentOS命令行改色。
- 万年历(C语言代码实现)
- unity android服务器端,【深圳Unity3D培训】 Android客户端与PC服务器实现Socket通信