本文共 5467 字,大约阅读时间需要 18 分钟。
学习资料:
参考图书:《Python数据分析与挖掘实战》(机械工业出版社)第7章
参考博文:
书中介绍了背景,要求根据背景通过数据挖掘实现如下目标:
FRM模型(Frequency Recency Monetary)
F:消费频率
R:最近消费时间间隔
M:消费金额
传统的RFM模型分析的属性分箱方法,如图所示。它是依据属性的平均值进行划分,其中大于平均值得表示为向上的箭头(↑),小于平均值的表示为向下的箭头(↓),虽然也能够识别出最具有价值的客户,但是细分的客户群太多,提高了针对性营销的成本。
书中采用了LRFMC模型的五个指标进行K-Means聚类,识别出最有价值的客户。
L:客户关系长度
R:消费时间间隔
F:消费频率
M:飞行里程
C:平均值
为什么选择这五个指标呢?
指标含义:
模型 | L | R | F | M | C |
---|---|---|---|---|---|
LRFMC模型 | 会员入会时间距观测窗口结束的月数 | 客户最近一次乘坐公司飞机距观测窗口结束的月数 | 客户在观测窗口内乘坐公司飞机的次数 | 客户在观测窗口内累计的飞行里程 | 客户在观测窗口内乘坐舱位所对应的折扣系统的平均值 |
分析步骤:
(1)从航空公司的数据源中进行选择性抽取与新增数据抽取分别形成历史数据和增量数据;
(2)对步骤(1)中形成的两个数据集进行数据探索分析(EDA)与预处理,包括数据缺失值与异常值的探索分析,数据的属性规约、清洗和变换。
(3)对步骤(2)中形成的已完成数据预处理的建模数据,基于旅客价值LRFMC模型进行客户分群,对各个客户群进行特征分析,识别出有价值的客户;
(4)针对模型结果得到不同价值的客户,采用不同的营销手段,提供定制化的服务。
省略一些代码……
explore = data.describe(percentiles = [], include = 'all').T #包括对数据的基本描述,percentiles参数是指定计算多少的分位数表(如1/4分位数、中位数等);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_excel(resultfile) #导出结果
接下来进行数据预处理。
采用数据清洗、属性规约与数据变换等预处理方法。
通过EDA分析,发现数据中存在缺失值,票价最小值为0,折扣率最小值为0,总飞行公里数大于0的记录。
由于原始数据量大,这类数据所占据比例较小,对于问题影响不大,因此对其进行丢弃处理。
具体方法如下:
通过观测可知,数据集中存在票价为零但是飞行公里大于零的不合理值,但是所占比例较小,这里直接删去
只保留票价非零的,或者平均折扣率与总飞行公里数同时为0的记录。
删除后剩余的样本值是62044个,可见异常样本的比例不足1.5%,因此不会对分析结果产生较大的影响。
属性规约
选择与LRFMC指标相关的6个属性。删除与其不相关、弱相关或者冗余的属性。
原始数据集的特征属性太多,而且各属性不具有降维的特征,故这里选取几个对航空公司来说比较有价值的几个特征进行分析,这里并没有完全按照书中的做法选取特征,最终选取的特征是第一年总票价、第二年总票价、观测窗口总飞行公里数、飞行次数、平均乘机时间间隔、观察窗口内最大乘机间隔、入会时间、观测窗口的结束时间、平均折扣率这八个特征。下面说明这么选的理由:
对特征进行变换:
由于不同的属性相差范围较大,这里进行标准化处理
对于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.values nums=range(2,10) SSE = [] for num in nums: sse = 0 kmodel = 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, SSE nums, SSE = test_Kmeans_nclusters(filter_zscore_data)
画图
#画图,通过观察SSE与k的取值尝试找出合适的k值# 中文和负号的正常显示plt.rcParams['font.sans-serif'] = 'SimHei'plt.rcParams['font.size'] = 12.0plt.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.valuesfeature = ["入会时间", "飞行次数", "平均每公里票价", "总里程", "时间间隔差值", "平均折扣率"]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;k=6时,做出雷达图进行分析。
通过观察可知:
综上,当k取值为5时,得到最好的聚类效果,将所有的客户分成5个人群,再进一步分析可以得到以下结论:
持续更新
转载地址:http://ppvdi.baihongyu.com/