案例-基于KMeans的航空公司客户价值分析

一.背景与挖掘目标

数据源

基于一份航空公司的数据,数据包含信息有客户基本信息、乘机信息、以及积分信息等详细数据,大约6万多条数据,依据末次飞行时间LAST_FLIGHT_DATE,以2020.3.31为结束时间,选取宽度为2年的时间段作为分析观测窗口。

数据中,客户乘机信息主要重点字段包含:
LOAD_TIME-观测结束时间
FLIGHT_COUNT-观测窗口内飞行次数
SUM_YR_1-观测窗口票价收入1
SUM_YR_2-观测窗口票价收入2
SEG_KM_SUM-观测窗口的总飞行公里数
LAST_FLIGHT_DATE-末次飞行日期
AVG_FLIGHT_COUNT-平均飞行次数
BEGIN_TO_FIRST-首次飞行日期
LAST_TO_END-最后一次乘机时间至观测窗口结束时长
AVG_INTERVAL-平均乘机时间间隔
MAX_INTERVAL-最大乘机间隔

客户基本信息主要重点字段包含:
MEMBER_NO-会员卡号
FFP_DATE-入会时间
FIRST_FLIGHT_DATE-第一次飞行日期
GENDER-性别
FFP_TIER-会员卡级别
WORK_CITY-工作地城市
WORK_PROVINCE-工作地省份
WORK_COUNTRY-工作地国家
AGE-年龄

积分信息主要重点字段包含:
BP_SUM-总基本积分
EP_SUM_YR_1-总基本积分1
EP_SUM_YR_2-总基本积分2
AVG_BP_SUM-平均积分
EXCHANGE_COUNT-积分兑换次数
avg_discount-平均折扣率
PY_Flight_Count-PY飞行次数
LY_Flight_Count-LY飞行次数
PY_BP_SUM-PY基本积分
LY_BP_SUM-LY基本积分
EP_SUM-总精英积分
Points_Sum-总累计积分
Point_NotFlight-非乘机的积分变动次数

数据挖掘目标

1.借助航空公司客户数据,对客户进行分类;
2.对不同客户类别进行特征分类,比较不同客户群体的客户价值;

二.数据探索性分析

首先读取数据,观测下各字段数据类型,各自段空值情况。

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt 
from datetime import datetime

air_data = pd.read_csv('data/air_data.csv',encoding='utf-8')
explore = air_data.describe().T
explore['null'] = len(air_data)-explore['count']

探索性结果描述如下。


image.png

接下来分别从客户信息、乘机信息、积分信息等三个角度进行数据探索,寻找客户信息的分布规律。

2.1 客户信息分布

绘制各年份会员入会人数直方图,接下来的思路是用到RFM客户价值分析模型。因此需要对数据进行初步探索。

ffp = air_data['FFP_DATE'].apply(lambda x:datetime.strptime(x,'%Y/%m/%d'))
ffp_year = ffp.map(lambda x:x.year)
# 绘制各年份会员入会人数直方图
import seaborn as sns
sns.set(); np.random.seed(0)
sns.distplot(ffp_year)
image.png

提取不同级别会员的人数,画图。

lv_four = pd.value_counts(air_data['FFP_TIER'])[4]
lv_five = pd.value_counts(air_data['FFP_TIER'])[5]
lv_six = pd.value_counts(air_data['FFP_TIER'])[6]
##画图
plt.bar(x=['4','5','6'], height=[lv_four,lv_five,lv_six], width=0.4, alpha=0.8, color='#689FB0')
plt.xlabel('会员等级')
plt.ylabel('会员人数')
plt.title('会员各级别人数')
plt.show()
image.png

了解会员的年龄分布状况。绘制会员年龄分布箱型图

age = air_data['AGE'].dropna().astype(int)
import seaborn as sns
plt.figure(figsize = (4,6))
sns.set(style="whitegrid")
sns.boxplot(data=age)
image.png

2.2 乘机信息分布

选取最后一次乘机至结束的时长LAST_TO_END、客户乘机信息中的飞行次数FLIGHT_COUNT、总飞行公里数SEG_KM_SUM,进行探索分析,探索客户的乘机信息分布情况。

绘制各年份会员入会人数直方图。

lte = air_data['LAST_TO_END']#最后一次乘机时间至观测窗口结束时长,空窗时长
fc = air_data['FLIGHT_COUNT']#观测窗口内飞行次数
sks = air_data['SEG_KM_SUM']#观测窗口内总飞行公里数

# 绘制各年份会员入会人数直方图
import seaborn as sns
plt.rcParams['font.sans-serif'] = 'SimHei'  
plt.rcParams['axes.unicode_minus'] = False
sns.set(); np.random.seed(0)
sns.distplot(lte)
image.png

绘制飞行次数分布图。

import seaborn as sns
plt.rcParams['font.sans-serif'] = 'SimHei'  
plt.rcParams['axes.unicode_minus'] = False
sns.set(); np.random.seed(0)
sns.distplot(fc)
image.png

绘制飞行公里数分布箱型图。

plt.figure(figsize = (4,6))
sns.set(style="whitegrid")
sns.boxplot(data=sks)
image.png

2.3 相关性分析

客户信息属性之间存在相关性,选取入会时间、会员卡级别、客户年龄、飞行次数、总飞行公里数、最后一次乘机时间至观测窗口结束时长、积分兑换次数、总累计积分属性,通过相关系数矩阵与热力图分析各属性之间的相关性。
找出相关性系数air_dt_corr 。

data_corr = air_data[['FFP_TIER','FLIGHT_COUNT','LAST_TO_END','SEG_KM_SUM','EXCHANGE_COUNT','Points_Sum']]

data_corr['AGE'] = air_data['AGE'].fillna(0).astype(int)
data_corr['ffp_year'] = ffp_year #会员入会年份
air_dt_corr = data_corr.corr(method='pearson')
image.png

画出相关性系数热力图。

plt.figure(figsize=(10,10))
mask = np.zeros_like(air_dt_corr,dtype=np.bool)
mask[np.triu_indices_from(mask)] = True
cmap = sns.diverging_palette(220,10,as_cmap=True)

g = sns.heatmap(air_dt_corr,mask=mask,cmap=cmap,square=True,annot=True,fmt='0.2f')
plt.xticks(fontsize=10)
plt.yticks(fontsize=10)
plt.show
image.png

三.基于RFM模型的客户价值分析

在衡量客户价值的过程中,以广泛应用的RFM模型为基础,进行新的模型搭建。
从以上相关性关系图中可以看出客户属性之间存在相关性。因此,选取客户关系长度L、消费时间间隔R、消费频率F、飞行里程M、折扣系数C五个特征作为衡量客户价值的特征。

客户关系长度L:会员入会时间距离观测窗口结束的月数,数据表没有直接给出,需要用(LOAD_TIME-FFP_DATE);
消费时间间隔R:LAST_TO_END,客户最近一次乘坐飞机距观测窗口结束的月数;
消费频率F:FLIGHT_COUNT,客户在观测窗口内乘坐飞机的次数;
飞行里程M:SEG_KM_SUM,客户在观测窗口内累计飞行的公里数;
折扣系数C:avg_discount,客户在观测窗口内乘坐舱位所对应的折扣系数的平均值;

3.1 数据标准化

在原有数据基础上进行属性选取。

# 选取需求属性
airline_selection = air_data[['FFP_DATE','LOAD_TIME','LAST_TO_END','FLIGHT_COUNT','SEG_KM_SUM','avg_discount']]
#构造属性L
airline_selection['L'] = \
(pd.to_datetime(air_data['LOAD_TIME'])-pd.to_datetime(air_data['FFP_DATE'])).astype(str).str.split().str[0]
airline_selection = airline_selection.iloc[:,2:]
image.png

属性选取之后,发现这些指标不在同一量级,为了接下来进行模型训练,需要先做数据标准化处理。

##数据标准化处理
from sklearn.preprocessing import StandardScaler
stand_data = StandardScaler().fit_transform(airline_selection)

pd.DataFrame(stand_data)

标准化数据的结果。


image.png

3.2 K-Means模型构建

将上述数据进行K-Means聚类。找到各个聚集点的质心cluster_center。

from sklearn.cluster import KMeans
#构建模型
kmeans_model = KMeans(n_clusters=5,n_jobs=4,random_state=123)
fit_kmeans = kmeans_model.fit(stand_data)
kmeans_cc = kmeans_model.cluster_centers_ #聚类中心
kmeans_labels = kmeans_model.labels_ #样本的类别标签
#输出聚类分群结果
cluster_center = pd.DataFrame(kmeans_cc,columns=['ZL','ZR','ZF','ZM','ZC'])
cluster_center.index = pd.DataFrame(kmeans_model.labels_ ).drop_duplicates().iloc[:,0]

最终质心如图。


image.png

3.3 客户价值分群雷达图

# 客户分群雷达图
labels = ['ZL','ZR','ZF','ZM','ZC']
legen = ['客户群' + str(i + 1) for i in cluster_center.index]  # 客户群命名,作为雷达图的图例
lstype = ['-','--',(0, (3, 5, 1, 5, 1, 5)),':','-.']
kinds = list(cluster_center.iloc[:, 0])

# 由于雷达图要保证数据闭合,因此再添加L列,并转换为 np.ndarray
cluster_center = pd.concat([cluster_center, cluster_center[['ZL']]], axis=1)
centers = np.array(cluster_center.iloc[:, 0:])

# 分割圆周长,并让其闭合
n = len(labels)
angle = np.linspace(0, 2 * np.pi, n, endpoint=False)
angle = np.concatenate((angle, [angle[0]]))

# 绘图
fig = plt.figure(figsize = (8,6))
ax = fig.add_subplot(111, polar=True)  # 以极坐标的形式绘制图形
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号 
# 画线
for i in range(len(kinds)):
    ax.plot(angle, centers[i], linestyle=lstype[i], linewidth=2, label=kinds[i])
# 添加属性标签
ax.set_thetagrids(angle * 180 / np.pi, labels)
plt.title('客户特征分析雷达图')
plt.legend(legen)
plt.show()

图像结果呈现。


image.png

质心的分布状态。


image.png

3.4 总结

画出雷达图也就是为各个客户群体划分界限。接下来需要根据业务特征下定义,评估每项指标的高中低界限在哪里,给每个客户贴标签。
例如:
客户群体1在特征C处的值最大,在特征F,M处的值最小,说明客户群体1偏好乘坐高级舱位。
客户群体2在特征F、M上的值最大,且在特征R上的值最小,说明客户群体2的会员频繁乘机,近期都有乘机记录。
客户群体3在R处的值相对最大,在特征L、F、M和C处的值都较小,说明客户群体3已经很久没有乘机记录,是入会时间短的低价值客户群。
客户群体4在所有特征值上都较小,且在特征L处最小,说明客户群体4属于新入会员较多的群体。
客户群体5在L处最大,在R处值最小,其他特征比较适中,说明客户群体5入会时间较长,飞行频率也较高,是有高价值的客户群。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容