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

使用教材:《Python数据分析与挖掘实战》
模型:RFM模型
算法:K-means聚类;其他方法还用到了层次聚类
数据:需要数据的可以评论里call。

内容:

(1)案例: 航空公司客户价值分析

(2)基于RFM客户价值理论的特征提取, 聚类结果解释

作业:

(1)使用教材 §7 航空公司客户价值聚类分析配套示例数据和代码,掌握其特征构造操作过程

(2)考虑:使用其它模型参数, 聚类算法, 并观察结果;利用数据探索分析结果, 提取有用的变量/特征, 进行聚类分析, 观察结果

  1. 背景:

企业在面向客户制定运营和营销策略,需要因人而异,给出最佳方案。因此企业的核心问题之一是客户关系管理。某航空公司正面临着:常旅客六十、竞争力下降、航空资源未得到充分利用的经营危机,建立客户价值评估模型亟待解决。

  1. 挖掘目标:

2.1 对客户进行分类,比较不同客户群体的价值,制定相应的营销策略。

2.2 对不同的客户进行特征分析,比较不同类别客户的价值。

2.3 针对不同价值的客户类别制定相应的营销策略,提供个性化服务。

1. 描述性统计分析

通过观察原始数据,发现存在票价为空值的记录,存在票价最小值为0、折扣率最小值为0但总飞行公里数大于0的记录。
查找每列属性观测值中空值个数、最大值、最小值。

#-*- coding: utf-8 -*- 

# 对数据进行基本的探索
# 返回缺失值个数以及最大最小值

import pandas as pd

datafile= r'C:/Users/lenovo/Documents/MEGA/Data Mining School/第2版CodeData/chapter7/demo/data/air_data.csv'  # 航空原始数据,第一行为属性标签
resultfile = r'C:/Users/lenovo/Documents/MEGA/Data Mining School/第2版CodeData/chapter7/demo//tmp/explore.csv'  # 数据探索结果表

# 读取原始数据,指定UTF-8编码(需要用文本编辑器将数据装换为UTF-8编码)
data = pd.read_csv(datafile, encoding = 'utf-8')

# 包括对数据的基本描述,percentiles参数是指定计算多少的分位数表(如1/4分位数、中位数等)
explore = data.describe(percentiles = [], include = 'all').T  # T是转置,转置后更方便查阅
explore['null'] = len(data)-explore['count']  # describe()函数自动计算非空值数,需要手动计算空值数

explore = explore[['null', 'max', 'min']]
explore.columns = [u'空值数', u'最大值', u'最小值']  # 表头重命名
'''
这里只选取部分探索结果。
describe()函数自动计算的字段有count(非空值数)、unique(唯一值数)、top(频数最高者)、
freq(最高频数)、mean(平均值)、std(方差)、min(最小值)、50%(中位数)、max(最大值)
'''

explore.to_csv(resultfile)  # 导出结果

explore.head()

2. 分布分析

分别从客户基本信息、乘机信息、积分信息3个角度进行数据探索,寻找客户信息的分布规律。

(1) 客户基本信息分布分析

选取客户基本信息中的入会时间、性别、会员卡级别和年龄字段进行探索分析,探索客户的基本信息分布情况。

#-*- coding: utf-8 -*- 

# 代码7-2

# 对数据的分布分析

import pandas as pd
import matplotlib.pyplot as plt 

datafile= r'C:/Users/lenovo/Documents/MEGA/Data Mining School/第2版CodeData/chapter7/demo/data/air_data.csv'  # 航空原始数据,第一行为属性标签

# 读取原始数据,指定UTF-8编码(需要用文本编辑器将数据装换为UTF-8编码)
data = pd.read_csv(datafile, encoding = 'utf-8')

# 客户信息类别
# 提取会员入会年份
from datetime import datetime
ffp = data['FFP_DATE'].apply(lambda x:datetime.strptime(x,'%Y/%m/%d'))
ffp_year = ffp.map(lambda x : x.year)
# 绘制各年份会员入会人数直方图
fig = plt.figure(figsize = (8 ,5))  # 设置画布大小
plt.rcParams['font.sans-serif'] = 'SimHei'  # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False
plt.hist(ffp_year, bins='auto', color='#0504aa')
plt.xlabel('年份')
plt.ylabel('入会人数')
plt.title('各年份会员入会人数')
plt.show()
plt.close
# 提取会员不同性别人数
male = pd.value_counts(data['GENDER'])['男']
female = pd.value_counts(data['GENDER'])['女']
# 绘制会员性别比例饼图
fig = plt.figure(figsize = (7 ,4))  # 设置画布大小
plt.pie([ male, female], labels=['男','女'], colors=['lightskyblue', 'lightcoral'],
       autopct='%1.1f%%')
plt.title('会员性别比例')
plt.show()
plt.close
# 提取不同级别会员的人数
lv_four = pd.value_counts(data['FFP_TIER'])[4]
lv_five = pd.value_counts(data['FFP_TIER'])[5]
lv_six = pd.value_counts(data['FFP_TIER'])[6]
# 绘制会员各级别人数条形图
fig = plt.figure(figsize = (8 ,5))  # 设置画布大小
plt.bar(x=range(3), height=[lv_four,lv_five,lv_six], width=0.4, alpha=0.8, color='skyblue')
plt.xticks([index for index in range(3)], ['4','5','6'])
plt.xlabel('会员等级')
plt.ylabel('会员人数')
plt.title('会员各级别人数')
plt.show()
plt.close()
# 提取会员年龄
age = data['AGE'].dropna()
age = age.astype('int64')
# 绘制会员年龄分布箱型图
fig = plt.figure(figsize = (5 ,10))
plt.boxplot(age, 
            patch_artist=True,
            labels = ['会员年龄'],  # 设置x轴标题
            boxprops = {'facecolor':'lightblue'})  # 设置填充颜色
plt.title('会员年龄分布箱线图')
# 显示y坐标轴的底线
plt.grid(axis='y')
plt.show()
plt.close

大部分会员年龄集中在30~50随之间,极少量的会员年龄小于20岁或高于60岁,且存在一个超过100岁的数据

(2)客户乘机信息分布分析

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

# 代·码7-3

# 乘机信息类别
lte = data['LAST_TO_END']       # 最后一次乘机至结束的时长
fc = data['FLIGHT_COUNT']       #  客户乘机信息中的飞行次数
sks = data['SEG_KM_SUM']        #  总飞行公里数进行探索分析
# 绘制最后乘机至结束时长箱线图
fig = plt.figure(figsize = (5 ,8))
plt.boxplot(lte, 
            patch_artist=True,
            labels = ['时长'],  # 设置x轴标题
            boxprops = {'facecolor':'lightblue'})  # 设置填充颜色
plt.title('会员最后乘机至结束时长分布箱线图')
# 显示y坐标轴的底线
plt.grid(axis='y')
plt.show()
plt.close
# 绘制客户飞行次数箱线图
fig = plt.figure(figsize = (5 ,8))
plt.boxplot(fc, 
            patch_artist=True,
            labels = ['飞行次数'],  # 设置x轴标题
            boxprops = {'facecolor':'lightblue'})  # 设置填充颜色
plt.title('会员飞行次数分布箱线图')
# 显示y坐标轴的底线
plt.grid(axis='y')
plt.show()
plt.close

客户的飞行次数明显分为两个群体,大部分客户集中在箱线图下方的箱体中,少数客户分散分布在箱体上界的上方,这部分客户很可能是高价值客户。因为其飞行次数超过箱体内的其他客户。

# 绘制客户总飞行公里数箱线图
fig = plt.figure(figsize = (5 ,10))
plt.boxplot(sks, 
            patch_artist=True,
            labels = ['总飞行公里数'],  # 设置x轴标题
            boxprops = {'facecolor':'lightblue'})  # 设置填充颜色
plt.title('客户总飞行公里数箱线图')
# 显示y坐标轴的底线
plt.grid(axis='y')
plt.show()
plt.close

客户的总飞行公里数明显分为两个群体,大部分客户集中在箱线图下方的箱体中,少数客户分散分布在箱体上界的上方,这部分客户很可能是高价值客户。因为其总飞行公里数超过箱体内的其他客户。

(3) 客户积分信息分布分析

# 代码7-4
# 积分信息类别
# 提取会员积分兑换次数
ec = data['EXCHANGE_COUNT']
# 绘制会员兑换积分次数直方图
fig = plt.figure(figsize = (8 ,5))  # 设置画布大小
plt.hist(ec, bins=5, color='#0504aa')
plt.xlabel('兑换次数')
plt.ylabel('会员人数')
plt.title('会员兑换积分次数分布直方图')
plt.show()
plt.close

绝大部分客户的兑换次数在0~1 的区间内,表示大部分客户都很少进行积分兑换。

# 提取会员总累计积分
ps = data['Points_Sum']
# 绘制会员总累计积分箱线图
fig = plt.figure(figsize = (5 ,8))
plt.boxplot(ps, 
            patch_artist=True,
            labels = ['总累计积分'],  # 设置x轴标题
            boxprops = {'facecolor':'lightblue'})  # 设置填充颜色
plt.title('客户总累计积分箱线图')
# 显示y坐标轴的底线
plt.grid(axis='y')
plt.show()
plt.close

一部分客户集中在箱体中,少部分客户分散分布在箱体上方,这部分客户的积分要明显高于箱体内的客户的积分。

相关系数矩阵与热力图

# 代码7-5
import sys
# 提取属性并合并为新数据集
data_corr = data[['FFP_TIER','FLIGHT_COUNT','LAST_TO_END',
                  'SEG_KM_SUM','EXCHANGE_COUNT','Points_Sum']]

age1 = data['AGE'].fillna(0)
#data_corr['AGE'] = age1.astype('int64')
data_corr.loc["AGE",:] = age1.astype('int64')
#data_corr['ffp_year'] = ffp_yearage1.astype('int64')
data_corr.loc["ffp_year",:] = ffp_year

# 计算相关性矩阵
dt_corr = data_corr.corr(method = 'pearson')
print('相关性矩阵为:\n',dt_corr)

# 绘制热力图
import seaborn as sns
plt.subplots(figsize=(10, 10)) # 设置画面大小 
sns.heatmap(dt_corr, annot=True, vmax=1, square=True, cmap='Blues') 
plt.show()
plt.close

3. 数据预处理

1. 数据清洗

数据预处理方法有:数据清洗、属性规约、数据变换。

1) 丢弃票价为空的记录;

2) 保留票价部位0的,或者平均折扣率不为0且飞行公里数大于0的记录;

3) 丢弃年龄大于100的记录。

数据预处理方法有:数据清洗、属性规约、数据变换。

1) 丢弃票价为空的记录;

2) 保留票价部位0的,或者平均折扣率不为0且飞行公里数大于0的记录;

3) 丢弃年龄大于100的记录。

# 去除票价为空的记录
airline_notnull = airline_data.loc[airline_data['SUM_YR_1'].notnull() & 
                                   airline_data['SUM_YR_2'].notnull(),:]
print('删除缺失记录后数据的形状为:',airline_notnull.shape)

# 只保留票价非零的,或者平均折扣率不为0且总飞行公里数大于0的记录。
index1 = airline_notnull['SUM_YR_1'] != 0
index2 = airline_notnull['SUM_YR_2'] != 0
index3 = (airline_notnull['SEG_KM_SUM']> 0) & (airline_notnull['avg_discount'] != 0)
index4 = airline_notnull['AGE'] > 100  # 去除年龄大于100的记录
airline = airline_notnull[(index1 | index2) & index3 & ~index4]
print('数据清洗后数据的形状为:',airline.shape)

airline.to_csv(cleanedfile)  # 保存清洗后的数据

2. 属性规约

RFM模型:

(1)R(Recency):最近一次消费时间与截止时间的间隔。通常情况下,客户最近一次消费时间与截止时间的间隔越短,对及时提供的商品或是服务也最有可能感兴趣。且若最近一次消费时间很近的客户数量增加,说明公司在稳步上升。

(2)F(Frequency):客户在某段时间内所消费的次数。消费品类越高,客户的满意度和对产品的忠诚度,价值越大。商家需要通过各种营销方式,去刺激客户消费,提高他们的消费频率,提升店铺的复购率。

(3)M(Monetary):客户在某段时间内所消费的金额。

建模:

RFM模型包括三个特征,R,F,M特征,映射在三维坐标系上。一般的,每个坐标轴一般用5级表示程度,1:最小;5:最大。

在RFM模型中,消费金额表示在一段时间内购买该企业产品的金额的综合。由于航空票价收到运输距离、舱位等级等多种因素的影响,同样消费金额的不同旅客对航空公司的价值不同。eg:一位购买长航线、低等级舱位票的旅客与一位购买短航线、高等级舱位的旅客相比,后者的价值更大。因此,本案利的消费金额用客户在一段时间内累计的飞行里程M和客户在一定时间内乘坐舱位所对应的折扣系数的平均值C两个特征代替。

另外,航空公司会员入会时间的长短在一定程度也能够影响客户价值,所以在模型中增加客户关系长度L,作为区分客户的另一特征。

因此,LRFMC模型的5个特征:客户关系长度L、消费时间间隔R、消费频率F、飞行里程M、折扣系数的平均值C。

#-*- coding: utf-8 -*- 

# 代码7-7

# 属性选择、构造与数据标准化

import pandas as pd
import numpy as np

# 读取数据清洗后的数据
cleanedfile = r'C:/Users/lenovo/Documents/MEGA/Data Mining School/第2版CodeData/chapter7/demo/tmp/data_cleaned.csv'  # 数据清洗后保存的文件路径
airline = pd.read_csv(cleanedfile, encoding = 'utf-8')
# 选取需求属性
airline_selection = airline[['FFP_DATE','LOAD_TIME','LAST_TO_END',
                                     'FLIGHT_COUNT','SEG_KM_SUM','avg_discount']]
print('筛选的属性前5行为:\n',airline_selection.head())

3. 数据变换

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

# 代码7-8

# 构造属性L
L = pd.to_datetime(airline_selection['LOAD_TIME']) - \
pd.to_datetime(airline_selection['FFP_DATE'])
L = L.astype('str').str.split().str[0]
L = L.astype('int')/30

# 合并属性
airline_features = pd.concat([L,airline_selection.iloc[:,2:]],axis = 1)
airline_features.columns = ['L','R','F','M','C']
print('构建的LRFMC属性前5行为:\n',airline_features.head())

# 数据标准化
from sklearn.preprocessing import StandardScaler
data = StandardScaler().fit_transform(airline_features)
np.savez(r'C:/Users/lenovo/Documents/MEGA/Data Mining School/第2版CodeData/chapter7/demo/tmp/airline_scale.npz',data)
print('标准化后LRFMC五个属性为:\n',data[:5,:])

4. 模型构建

客户价值分析模型构建主要由两个部分构成:

(1)根据航空公司客户5个指标的数据,对客户作据类分群;

(2)结合业务对每个客户群进行特征分析,分析其客户价值,并对每个客户群进行排名。

1. 客户聚类

采用K-Means聚类算法对客户数据进行客户分群,聚成5类。

使用scikit-learn库下的聚类子库(sklearn.cluster)可以实现K-Means聚类算法。使用标准化后的数据进行聚类。

#-*- coding: utf-8 -*- 

# 代码7-9

# K-means聚类

import pandas as pd
import numpy as np
from sklearn.cluster import KMeans  # 导入kmeans算法

# 读取标准化后的数据
airline_scale = np.load(r'C:/Users/lenovo/Documents/MEGA/Data Mining School/第2版CodeData/chapter7/demo/tmp/airline_scale.npz')['arr_0']
k = 5  # 确定聚类中心数

# 构建模型,随机种子设为123
kmeans_model = KMeans(n_clusters = k,n_jobs=4,random_state=123)
fit_kmeans = kmeans_model.fit(airline_scale)  # 模型训练

# 查看聚类结果
kmeans_cc = kmeans_model.cluster_centers_  # 聚类中心
print('各类聚类中心为:\n',kmeans_cc)
kmeans_labels = kmeans_model.labels_  # 样本的类别标签
print('各样本的类别标签为:\n',kmeans_labels)
r1 = pd.Series(kmeans_model.labels_).value_counts()  # 统计不同类别样本的数目
print('最终每个类别的数目为:\n',r1)
# 输出聚类分群的结果
cluster_center = pd.DataFrame(kmeans_model.cluster_centers_,\
             columns = ['ZL','ZR','ZF','ZM','ZC'])   # 将聚类中心放在数据框中
cluster_center.index = pd.DataFrame(kmeans_model.labels_ ).\
                  drop_duplicates().iloc[:,0]  # 将样本类别作为数据框索引
print(cluster_center)

2. 客户价值分析

针对聚类结果进行特征分析,绘制客户分散雷达图

# 代码7-10

%matplotlib inline
import matplotlib.pyplot as plt 
# 客户分群雷达图
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()
plt.close

客户群1在特征C处的值最大,在特征F、M处的值较小,说明客户群1是偏好乘坐高级舱位的客户群;客户群2频繁乘机且近期都有乘机记录;客户群3在特征R处的值最小,在L、F、M、C的值都较小,说明客户群3已经很久没有乘机,是入会时间较短的低价值的客户群;客户群4在所有特征至上的值都很小,且在L处的值最小,说明客户群4属于新入会员且较多的客户群;客户群5入会时间较长,飞行频率也较高,是有较高价值的客户群。

基于特征描述,定义5个等级的客户类别:

重要保持客户、重要发展客户、重要挽留客户、一般客户、低价值客户。

(1)重要保持客户:

该类客户的平均折扣系数(C)较高,最近乘机距今的时间长度(R)低,飞行次数(F)或总飞行历程(M)较高,他们属于航空公司的高价值客户,是最为理想的客户类型。航空公司应该优先将资源投放到他们身上,进行差异化管理和一对一营销。

(2)重要发展客户:

这类客户的平均折扣系数较高,R低,F或M低,入会时间短,是航空公司的潜在价值客户。航空公司要努力促使这类客户增加在本公司的乘机消费和合作伙伴的消费,增加客户满意度,使他们成为公司的忠诚客户。

(3) 重要挽留客户:

这类客户在过去的F,F,较高,但是最近乘机距今的时间长度高或者说称作频率较小,客户价值变化的不确定性很高。由于客户价值衰退原因各有不同,因此航空公司需要掌握客户的最新信息,维持与客户的互动,重点联系采取一定的营销手段,延长客户的生命周期。

(4) 一般客户与低价值客户:

这类客户的C很低,最近乘机距今的时间长度R高,飞行次数F或总飞行里程M较低,入会时间L短,他们是航空公司的一般客户与低价值客户,可能是在航空公司机票打折促销时,才会乘坐本公司航班。

其中,重要发展客户、重要保持客户、重要挽留客户分别可以归入客户生命周期管理的发展期、稳定器、衰退期3个阶段。

除了上述的K-means聚类算法,我们还可以用层次聚类算法。由于数据量过大,我们仅仅使用前1000条作为测试聚类。

from sklearn.cluster import AgglomerativeClustering

from sklearn.manifold import TSNE
# 读取标准化后的数据
airline_scale = np.load(r'C:/Users/lenovo/Documents/MEGA/Data Mining School/第2版CodeData/chapter7/demo/tmp/airline_scale.npz')['arr_0']
airline_scale 
airline_scale = pd.DataFrame(airline_scale,columns = ["L","R","F","M","C"])
data_zs = 1*(airline_scale -airline_scale.mean())/airline_scale.std()
ac = AgglomerativeClustering(n_clusters=5,linkage='ward')
ac.fit(data_zs[:1000])
# 读取数据清洗后的数据
cleanedfile = r'C:/Users/lenovo/Documents/MEGA/Data Mining School/第2版CodeData/chapter7/demo/tmp/data_cleaned.csv'  # 数据清洗后保存的文件路径
airline = pd.read_csv(cleanedfile, encoding = 'utf-8')
# 选取需求属性
airline_selection = airline[['FFP_DATE','LOAD_TIME','LAST_TO_END',
                                     'FLIGHT_COUNT','SEG_KM_SUM','avg_discount']]
airline_selection
airline_features
data = airline_features
r = pd.concat([data[:1000],pd.Series(ac.labels_[:1000],index=data[:1000].index)],axis=1)
r.columns = list(data.columns) +['聚类类别']

tsne = TSNE(random_state = 105)
tsne.fit_transform(data_zs[:1000])
tsne = pd.DataFrame(tsne.embedding_, index=data_zs[:1000].index)
d = tsne[r['聚类类别'] == 0]
plt.plot(d[0], d[1], 'ro')
d = tsne[r['聚类类别'] == 1]
plt.plot(d[0], d[1], 'go')
d = tsne[r['聚类类别'] == 2]
plt.plot(d[0], d[1], 'b*')
d = tsne[r['聚类类别'] == 3]
plt.plot(d[0], d[1], 'yo')
d = tsne[r['聚类类别'] == 4]
plt.plot(d[0],d[1],marker = "o")
plt.show()

你可能感兴趣的:(数据挖掘,python,机器学习,数据分析,聚类,数据挖掘)