python航空公司客户价值分析

项目背景

信息时代的到来注定营销焦点从产品中心转到了用户中心,客户关系管理成为企业的核心问题。
客户关系管理的关键问题则是客户分类,通过分类区分客户价值的有无和高低,针对不同类别的用户制定个性化服务方案,采取不同营销方案,集中营销资源于高价值用户。
对于航空公司而言,建立合理的客户价值评估模型,对客户进行分群,进行分价值处理是必要的。

挖掘目标

借助航空公司数据,进行客户分类。
对不同客户类别进行特征分析,比较不同类别的客户价值。
对不同价值的客户提供个性化服务,制定相应的营销策略。

分析过程

目标是用户价值识别,一般客户价值识别最广泛的模型是三个指标(最近消费时间间隔(Recency)、消费频率(Frequency)和消费金额(Monetary))来进行客户细分,识别高价值用户,这就是RFM模型。
这里需要注意,显然使用消费金额是不合理的(一个长距离的普通舱用户和一个短距离的高级舱用户价值显然不一样)。所以采用累积里程M和乘坐舱位对应的折扣系数的平均值C两个指标代替消费金额。
此外,这种会员制的公司一般入会时间也影响价值判断,所以引入客户关系长度L,作为另一指标。
最终这个模型的指标5个分别是客户关系长度L、消费时间间隔R、消费频率F、飞行里程M、折扣系数平均数C。

处理过程

显然,得到了需要的处理指标了,我们就要进行分析,如果使用传统的RFM的属性分箱方法(依据平均值分析),可以识别,但是分类结果群太多,企业不可能花费这么多的针对性营销成本。
所以很明显,这是一个聚类分析的案例,不妨使用K-Means聚类分析。

数据获取

数据给出。
数据探索 - 主要目标发现缺失值和异常值(如票价为0,可能原因是兑换或者0折)

数据预处理

数据清洗

显然,通过探索已经得知数据存在异常值、缺失值,但是原始数据量太大,这类异常数据占比太少,直接丢弃即可。

属性规约

显然,原始数据中的数据属性太多了,根据需求指标,删除不相干的或者弱相关或者冗余的属性列。
数据变换

属性构造

构造出不存在的指标
L = LOAD_TIME - FFP_DATE
R = LAST_TO_END
F = FLIGHT_COUNT
M = SEG_KM_SUM
C = AVG_DISCOUNT

数据标准化

不同属性之间取值范围差距过大,理论上影响程度应该一致。
数据挖掘建模
聚类分群(根据5个指标)
采用K-Means聚类分群
特征分析每个群,进行群组排序(依据价值)
不难看出,基本上每一类都有明显的优势和弱势特征,企业需要做的就是根据这些修改营销方案。

import numpy as np
import pandas as pd
path = 'D:/python_data_file/Python_data_analysis_and_meaning_fighting/chapter7/demo/data/air_data.csv'
data = pd.read_csv(path)
data.head()

describe()函数自动计算的字段有count(非空值数)、unique(唯一值数)、top(频数最高者)、freq(最高频数)、mean(平均值)、
std(方差)、min(最小值)、50%(中位数)、max(最大值)
percentiles: 是一个列表,分位数,默认是[.25, .5, .75]
include: 包含在结果中的数据类型白名单
.T转置的目的是为了便于查阅
describe()函数的返回类型:Series/DataFrame of summary statistics

des_df = data.describe(percentiles=[],include='all').T#图太大了,自己运行看吧
des_df['null'] = len(data)-des_df['count']#descible函数自动计算非空值数,用数据长度减去非空值数就是空值数
des_df = des_df[['null','max','min']]#这里只取空值数,最大值,最小值
des_df.columns = [u'空值数',u'最大值',u'最小值']#表头重命名
des_df

图没有截完整,太大了

数据清洗

通过数据探索发现,发现数据中存在缺失值,票价最小值为0、折扣率最小值为0、总飞行公里数大于0的记录。由于原始数据量大。这类数据所占比例较小,对于问题影响不大,因此对其进行丢弃处理。具体的处理方法: 1)丢弃票价位空的记录(SUM_YR) 2)丢弃票价为0、平均折扣率不为0、总飞行公里数大于0的记录(我觉得以上数据清洗的结果不能看出这个结论) 使用pandas对满足清洗条件的数据进行丢弃,处理方法:满足清洗条件的一行数据全部丢弃。

属性规约

原始数据中属性太多,根据航空公司客户价值LRFMC模型,选择与LRFMC指标相关的6个属性:FFP_DATE、LOAD_TIME、FLIGHT_COUNT、AVG_DISCOUNT、SEG_KM_SUM、LAST_TO_END。删除与其不相关、弱相关或冗余的属性。经过属性选择后的数据集如下形式:

clean_data = data[data['SUM_YR_1'].notnull() & data['SUM_YR_2'].notnull()] #两个窗口的票价同时非空值才保留?
#只保留票价非0的,或者平均折扣率与总飞行公里数同时为0的记录
index1 = data['SUM_YR_1']!=0  #返回一个Series,元素类型是布尔类型
index2 = data['SUM_YR_2']!=0
index3 = (data['SEG_KM_SUM'] == 0)&(data['avg_discount'] ==0)#返回一个Series,元素类型是布尔类型
clean_data = clean_data[index1 | index2 | index3].reset_index(drop=True)
clean_data.head()
#属性规约----------
clean_data = clean_data[['LOAD_TIME','FFP_DATE','LAST_TO_END','FLIGHT_COUNT','SEG_KM_SUM','avg_discount']]
clean_data

数据变换

数据变换是将数据转换成“适当的”格式,以适应挖掘任务及算法的需求。本案例中主要采用的数据变换方式为属性构造和数据标准化。

由于原始数据中并没有直接给出LRFMC五个指标,需要通过原始数据提取这五个指标,具体的计算方式如下。

(1)L=LOAD_TIME - FFP_DATE (L代表客户关系长度,表征会员入会时间的长短) 会员入会时间距观测窗口结束的月数=观测窗口的结束时间-入会时间 (单位:月) (2)R=LAST_TO_END (R代表消费时间间隔) 客户最近一次乘坐公司飞机距观测窗口结束的月数 = 最后一次乘机时间至观测窗口末端时长 (单位:月) (3)F =FLIGHT_COUNT (F代表消费频率) 客户在观测窗口内乘坐公司飞机的次数 = 观测窗口的飞行次数 (单位:月) (4)M =SEG_KM_SUM (M飞行里程数) 客户在观测时间内在公司累计飞行里程 = 观测窗口的总飞行公里数 (单位:月) (5)C=avg_discount (C代表折扣系数的平均值) 客户在观测时间内乘坐舱位所对应的折扣系数的平均值 = 平均折扣率 (单位:月)

d_ffp=pd.to_datetime(clean_data['FFP_DATE'])  #转换成为DateTime数据类型
d_load=pd.to_datetime(clean_data['LOAD_TIME']) #由于输入是series,返回Series of datetime64 dtype[ns]
res=d_load-d_ffp #这个单位是’天‘,下面还得转换成’月‘  看一下res的数据类型,发现是timedelta64[ns]
clean_data['L']=res.map(lambda x:x/np.timedelta64(30*24*60,'m'))#L代表客户关系长度,表征会员入会时间的长短
#map()是Python内置的高阶函数,它接收一个函数和一个序列,根据提供的函数对指定序列做映射。
#lambda  行内自定义的函数,用来映射序列res
#np.timedelta64(30*24*60,'m'):   创建时间间隔Timedelta对象(一个月有多少分钟)
#x/np.timedelta64(30*24*60,'m') 得出总会员入会时间长度有多少个月
 
clean_data['R'] = clean_data['LAST_TO_END'] # R代表消费时间间隔(单位:月)
clean_data['F'] = clean_data['FLIGHT_COUNT'] # F代表消费频率(单位:月)
clean_data['M'] = clean_data['SEG_KM_SUM'] # M飞行里程数(单位:月)
clean_data['C'] = clean_data['avg_discount'] # C代表折扣系数的平均值(单位:月)
clean_data = clean_data[['L','R','F','M','C']]
clean_data
#同样我们看下描述性统计量
clean_data.describe(percentiles=[],include='all')
#标准化处理
zscore_data = (clean_data - clean_data.mean(axis = 0))/(clean_data.std(axis = 0)) #简洁的语句实现了标准化变换,类似地可以实现任何想要的变换。
zscore_data.columns=['Z'+i for i in clean_data.columns] #表头重命名。
zscore_data

模型构建
客户价值分析模型构建主要由两个部分构成,第一部分根据航空公司客户5个指标的数据,对客户进行聚类分群。第二部分结合业务对每个客户进行特征分析,分析其客户价值,并对每个客户群进行排名。

  1. 客户聚类
    采用K-Means聚类算法对客户数据进行客户群分类,聚成5类(需要结合业务的理解与分析来确定客户的类别数量),代码如下
from sklearn.cluster import KMeans #导入K均值聚类算法
k = 5                       #需要进行的聚类类别数

#调用k-means算法,进行聚类分析
kmodel = KMeans(n_clusters = k, n_jobs = 4) #n_jobs是并行数,一般等于CPU数较好
kmodel.fit(zscore_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(zscore_data.columns) #重命名表头
r
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['font.sans-serif']=['SimHei']#显示中文

#画出雷达特征图
labels = data.columns #标签
k = 5 #数据个数
plot_data = kmodel.cluster_centers_
color = ['b', 'g', 'r', 'c', 'y'] #指定颜色

angles = np.linspace(0, 2*np.pi, k, endpoint=False)
plot_data = np.concatenate((plot_data, plot_data[:,[0]]), axis=1) # 闭合
angles = np.concatenate((angles, [angles[0]])) # 闭合

fig = plt.figure(figsize=(6,6))
ax = fig.add_subplot(111, polar=True) #polar参数!!
for i in range(len(plot_data)):
    ax.plot(angles, plot_data[i], 'o-', color = color[i], label = u'客户群'+str(i), linewidth=2)# 画线

ax.set_rgrids(np.arange(0.01, 3.5, 0.5), np.arange(-1, 2.5, 0.5), fontproperties="SimHei")
ax.set_thetagrids(angles * 180/np.pi, labels, fontproperties="SimHei")
plt.legend(loc = 4)
plt.show()

2.客户价值分析
针对聚类结果进行特征分析,如下图所示,其中:

客户群1:在F、M属性上最大,在R属性上最小。

客户群2:在L属性上最大。

客户群3:在R属性上最大,在F、M属性上最小。

客户群4:在L、C属性上最小。

客户群5:在C属性上最大。

结合业务分析,通过比较各个指标在群间的大小对某一个群的特征进行评价分析。例如客户群1在F、M属性最大,在R指标最小,因此可以说F、M、R是客户群1的优势特征。以此类推,F、M、R是客户群3上的劣势特征,从而总结出每个群的优势和劣势特征。

根据数据和图片发现,每个客户群都有显著不同的表现特征:客户群1在R属性上最大,在F、M属性上最小;客户群2在F、M属性上最大,在R属性上最小;客户群3在L、C属性上最小;客户群4在L属性上最大;客户群5在C属性上最大。

基于特征描述,本案例定义5个等级的客户案例:重要保持客户、重要发展客户、重要挽留客户、一般客户、低价值客户,每种客户类型特征如下:
1、重要保持客户:平均折扣率(C)较高(一般航班舱位的等级较高),最近乘坐公司航班(R)较低,乘坐的次数(F)或里程数(M)较高。这类客户对航空公司贡献最高,应尽可能延长这类客户的高消费水平。
2、重要发展客户:平均折扣率(C)较高,最近乘坐公司航班(R)较低,乘坐的次数(F)或里程数(M)较低。这类客户是航空公司的潜在价值客户,需要努力促使增加他们的乘机消费。
3、重要挽留客户:平均折扣率(C)较高、乘坐的次数(F)或里程数(M)较高,但最近乘坐公司航班(R)较低,很久没有乘坐本公司的航班,原因各不相同,需要采取一定的营销手段,延长客户的生命周期。
4、一般与低价值客户:平均折扣率(C)很低、乘坐的次数(F)或里程数(M)较低,最近乘坐公司航班(R)很高,入会时长(L)短,他们是公司的低价值客户,可能只在航空公司打折的时候才会乘坐航班。

可以看出重要保持客户、重要发展客户、重要挽留客户是最具价值的前三名客户类型,为了深度挖掘航空公司各类型客户的价值,需要提升重要发展客户的价值、稳定和延长重要保持客户的高水平消费、对重要挽留客户积极进行关系恢复,并策划相应的营销策略加强巩固客户关系。

客户流失分析

在航空公司这个特殊的领域,国内竞争还是很严峻的,一个顾客的流失造成的损失是4-5个新顾客的流入所不能弥补的,分析用户(特别是会员用户)相关信息,建立模型,发现流失用户特征,制定针对性营销策略,挽留用户是企业生存的重要一环。本项目基于大数据样本进行挖掘建模,根据用户特征建立模型发现用户流失特征及其原因分析。
我们不妨这样定义:
已流失客户:次年飞行次数与第一年飞行次数小于50%
准流失客户:次年飞行次数与第一年飞行次数在[50%,90%)内
未流失客户:第二年飞行次数与第一年飞行次数比例大于90%

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'
clean_data['K'] = temp
clean_data.head()
data_standard = pd.concat([zscore_data,clean_data['K']],axis=1)
data_standard
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 =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_standard)
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()


参考:https://www.cnblogs.com/little-monkey/p/10472250.html
https://blog.csdn.net/zhouchen1998/article/details/85113535

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