这两天在看张良均、王路等人出版的书《python数据分析与挖掘实战》,前面整理了一篇笔记,现在就实战一下吧。
数据量:62988,
共有44个客户属性,其中包含了会员卡号、入会时间、性别、年龄、会员卡级别、在观测窗口内的飞行公里数、飞行时间等
拿到数据集,先进行整体上的观察
import pandas as pd
import numpy as np
df = pd.read_csv( 'air_data.csv', encoding = 'utf-8') #读取原始数据,指定UTF-8编码(需要用文本编辑器将数据装换为UTF-8编码)
print(df.shape)
print(df.info)
df.head()
ex = df.describe(percentiles = [], include = 'all').T #包括对数据的基本描述,percentiles参数是指定计算多少的分位数表(如1/4分位数、中位数等);T是转置,转置后更方便查阅
ex['null'] = len(df)- ex['count'] #describe()函数自动计算非空值数,需要手动计算空值数
ex = ex[['null', 'max', 'min']]
ex.columns = [u'空值数', u'最大值', u'最小值'] #表头重命名
ex
具体处理方法如下:
删去票价为0的数据
及票价为0,折扣率不为0,总飞行公里数大于0的记录
df= df[df['SUM_YR_1'].notnull()&df['SUM_YR_2'].notnull()] #票价非空值才保留
#只保留票价非零的,或者平均折扣率与总飞行公里数同时为0的记录。
index1 = df['SUM_YR_1'] != 0
index2 = df['SUM_YR_2'] != 0
index3 = (df['SEG_KM_SUM'] == 0) & (df['avg_discount'] == 0) #该规则是删去脏数据,要么有飞有折扣有公里数,要么就都是折扣为0且里程数为0
df= df[index1 | index2 | index3] #该规则是“或”
df.T
在客户分类中,RFM模型是一个经典的分类模型,——最近消费(Recency)、消费频率(Frequency)、消费金额(Monetary)细分客户群体,从而分析不同群体的客户价值。
同样金额的不同旅客对公司的价值是不同的,比如买长航线低等级舱位的旅客与短途高等级舱位旅客相比,后者相对价值更高。因此,M消费金额不能用于衡量客户价值的一个因素,我们将其分解成两个。一定时间内的飞行里程M,一定时间内所坐舱位的平均折扣系数C
最终我们通过L(客户入会时间距今月数)、R(客户最近一次乘机时间距今月数)、F(客户在样本时间内乘机次数)、M(客户在样本时间内的飞行里程)、C(客户在样本时间内乘坐舱位的平均折扣系数)
因此,我们选择入会时间(FFP_date)、样本观测结束时间LOAD TIME、飞行次数(flight count)、平均折扣率(AVG DISCOUNT)、总飞行公里数(seg-km-sum)、最后一次乘机时间距今时长(last to end)这6个属性进行分析,删去其他弱相关属性
L=LOAD_TIME-FFP_DATE(入会时长)
R=LAST_TO_END(最近一次乘机时间)
F=FLIGHT_COUNT(频次)
M=SEG_KM_SUM(飞行公里数)
C=AVG_DISCOUNT(平均折扣率)
df['LOAD_TIME']=pd.to_datetime(df['LOAD_TIME']) #将字符串格式转换为日期格式
df['FFP_DATE'] = pd.to_datetime(df['FFP_DATE'])
df['L']=df['LOAD_TIME']- df['FFP_DATE']
#特征提取,只选L,LAST_TO_END,SEG_KM_SUM,avg_discount
#即入会时间离观测结束的天数,最近一次乘机的月数,观测窗口的飞行次数,总飞行里程,折扣平均
df1 = df[['L','LAST_TO_END','SEG_KM_SUM','FLIGHT_COUNT','avg_discount']]
df1=df1.rename(
columns = {'L':'L入会时间','LAST_TO_END':'R最近消费距今时间','SEG_KM_SUM':'M总里程','FLIGHT_COUNT':'F飞行次数','avg_discount':'C平均折扣率'},
inplace = False
)
df1.head()
观察一下各组数据间的最大最小值
由于不同的属性相差范围较大,这里进行标准化处理
#数据标准化
df1 = (df1 - df1.mean(axis = 0))/(df1.std(axis = 0)) #简洁的语句实现了标准化变换,类似地可以实现任何想要的变换。
df1.columns=['Z'+i for i in df1.columns] #表头重命名。
df1.head()
首先采用无监督学习中K-means 进行模型构建,首先根据上述5个客户指标进行聚类,其次根据结果结合业务针对不同客户群进行分析,并进行排名。
K-Means算法主要就在于k=n_cluster参数的确定上面,到底是将k确定为几能进行更好的分类.因为是无监督的聚类分析问题,所以不寻在绝对正确的值,需要进行研究试探。这里采用计算SSE的方法,尝试找到最好的K数值。
SSE值代表了每一个数据点离聚类中心的距离的评分,即为误差平方。当k小于真实聚类数时,随着k增大数据之间的聚合度会增加,SSE下降幅度会很大,当k快达到真实聚类值时,SSE下降幅度会变平稳,就想一个手肘的形状,肘点即为最优k值,相关代码如下:
#模型构建
from sklearn.cluster import KMeans #导入K均值聚类算法
import matplotlib.pyplot as plt
SSE = []
for k in range(1,9):
estimator = KMeans(n_clusters=k)
estimator.fit(df1)
SSE.append(estimator.inertia_)#样本到最近的聚类中心的距离平方之和
X = range(1,9)
plt.xlabel('k')
plt.ylabel('SSE')
plt.plot(X,SSE,'o-')
plt.show()
观察图像,并没有的所谓的“肘”点出现,基本上是随k值的增大逐渐减小的。在k=4,5这段区间变化放缓
设K=5
#取k=5进行的类别聚类
k = 5
#df1中读取数据并进行聚类分析
#调用k-means算法#
kmodel = KMeans(n_clusters = k, n_jobs = 4) #n_jobs是并行数,一般等于CPU数较好
kmodel.fit(df1) #训练模型
kmodel.cluster_centers_ #查看聚类中心
kmodel.labels_ #查看各样本对应的类别
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(df1.columns) + [u'各类别人数'] #重命名表头
r
注:此部分代码主要参考传送门
# 绘图
# 中文和负号的正常显示
plt.rcParams['font.sans-serif'] = 'SimHei'
plt.rcParams['font.size'] = 12.0
plt.rcParams['axes.unicode_minus'] = False
fig=plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, polar=True)
center_num = r.values
feature = ["L入会时间", "R最近消费距今时间", "M总里程", "F飞行次数程", "C平均折扣率"]
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()
基于LRFMC模型的具体含义,我们可以对这5个客户群进行价值排名。同时,将这5个客户群重新定义为五个等级的客户类别:重要保持客户,重要挽留客户,重要发展客户,一般发展客户,低价值客户。
对应客户群1:
重要保持客户:这类客户乘坐的次数(F)和飞行的里程数(M)都很高。折扣率(C)和入会时间(L)也不低,说明他们经常乘坐飞机,且有一定经济实力,是航空公司的高价值客户。
对应客户群2
最大的特点是时间间隔差值(R)最大,其他指标表现一般,分析可能是“季节型客户”,一年中可能有一段时间需要乘坐飞机进行旅行等等,属于一般发展客户
对应客户群3
重要挽留客户:这类客户最大的特点就是入会的时间(L)较长,入会时间长(L),平均折扣率较低,而且总里程和总次数都不高,分析可能是流失的客户,需要在争取一下,尽量让他们“回心转意”;
对应客户群4:
重要发展客户:这类客户平均折扣率很高(C),最近乘坐过本航班时间间隔(R)短,但是乘坐的次数(F)和(M)都很低,成为会员的时间也很近(L),说明这些乘客刚入会员不久,所以乘坐飞机次数少,是未来重要发展客户。
对应客户群5
低价值客户:这类客户乘坐时间间隔长(R)或乘坐次数(F)和总里程(M)低,平均折扣也很低,各方面的数据都是比较低的
最终将表格的客户进行分类
df1['聚类类别'] = kmodel.labels_
#划分客户类别
for i in range(len(df1)):
if df1['聚类类别'][i] == 0:
df1['聚类类别'][i] = '重要保持客户'
elif df1['聚类类别'][i] == 1:
df1['聚类类别'][i] = '一般发展客户'
elif df1['聚类类别'][i] == 2:
df1['聚类类别'][i] = '重要挽留客户'
elif df1['聚类类别'][i] == 3:
df1['聚类类别'][i] = '重要发展客户'
else:
df1['聚类类别'][i] = '低价值客户'
#重命名属性
df1.columns = ['L','R','M','F','C','客户类别']
参考文章:
1
2
3