欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

程序员文章站 2024-03-22 08:01:33
...

一、背景

随着药店市场刮起了兼并重组风潮,大量资金涌入医药市场,药店行业竞争进入白热化。同时,“互联网+”及“大数据”为药店引来了新的机遇和挑战,大部分药店都在从传统零售行业转入“服务型”零售,实行“会员制”,进行会员差异化的管理和服务是其中最关键的一环。

本文主要利用药店会员销售数据,筛选出其中“已流失会员”进行挖掘分析,探索“已流失会员”的会员属性及购买行为——具有哪些特征的会员易流失?

我挑选了一家在二线城市的连锁药店进行分析,这家连锁2018年年销售额差不多在6700万,其中,会员的销售额占比在60%左右,流失会员(近6个月未到店购药的会员)占比39.10%。从会员趋势的堆积图上看流失会员的占比在逐步提升。

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

那么这些流失会员的特征是什么呢?

二、流失会员定义

首先,我们需要来定义流失会员,并按照条件对流失会员进行打标。

我们定义

  • 流失会员:年购买次数在4次(含4次)以上,近6个月未购药。

  • 留存会员:年购买次数在4次(含4次)以上,近6个月有购药。

为什么是4次,以下是2018年全年会员购物次数频率图,全年大于等于4次总计13709人,占比45.49%。鉴于药品是低频消费,我们认为一年4次的消费是比较稳定的会员。

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

三、数据集描述

该数据是收集了2018年会员年消费次数在4次以上的会员购药数据,该数据集有27个变量,6380个数据样本。具体变量的解析如下图:0~25号变量为特征变量,26号变量为标签变量。

pd.merge(dataset_column,pd.DataFrame(columns,columns=['标签']),how='inner',on='标签')

输出:

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

columns=['uid','year','sex','maxdate_diff_now','carddate_diff_now','paidmoney_one_year','paidmoney_x_one_year',
                       'paidmoney_z_one_year','paidmoney_b_one_year','paidmoney_q_one_year','paidmoney_y_one_year','paidmoney_w_one_year'
                        ,'paidmoney_s_one_year','paidmoney_r_one_year','paidmoney_t_one_year','paidmoney_4_one_year','paidmoney_5_one_year'
                        ,'paidmoney_9_one_year','quantity_one_year','count_one_year','mindatediff_one_year','maxdatediff_one_year'
                        ,'avgdatediff_one_year','paidmoney_one_order','quantity_one_order','count_one_order','label']
dataset_=dataset.loc[:,columns]
dataset_.head()

四、基本分析思路

以上研究的问题是一个二分类(流失会员or留存会员)问题,且上面的特征变量除了性别,都为数值型变量,对于二分类各个变量的比较,在统计学的分析当中,我们可以总结为以下两种:

  • 数值型数据:均值比较——方差分析检验显著性
  • 分类型数据:频数分布比较(交叉分析)——卡方分析检验显著性

各个特征变量做完分析之后,我们去研究变量对结果的权重。对于此问题,我们可以运用多个分类模型去训练数据,选出其中模型精度最高的模型,输出变量的重要程度排行。接下来我们就可以根据变量的重要程度来逐一归纳流失会员特征了。

详细的步骤,见下:

五、python库导入

可以为分析过程用到的包:

#1、数据处理
import pandas as pd
import numpy as np
from scipy import stats
from scipy.stats import chi2_contingency   #卡方检验
import numpy as np
import Ipynb_importer #自定义的导入notebook函数的启动包
import mysql_connect #自定义的连接数据库的包
#2、可视化
import matplotlib.pyplot as plt
import seaborn as sns
plt.rcParams['font.family']='SimHei' 
plt.rcParams['axes.unicode_minus']=False
#3、特征工程
import sklearn
from sklearn import preprocessing                            #数据预处理模块
from sklearn.preprocessing import LabelEncoder               #标签转码
from sklearn.preprocessing import StandardScaler             #归一化
from sklearn.model_selection import train_test_split         #数据分区
from sklearn.decomposition import PCA  
#4、分类算法
from sklearn.ensemble import RandomForestClassifier     #随机森林
from sklearn.svm import SVC,LinearSVC                   #支持向量机
from sklearn.linear_model import LogisticRegression     #逻辑回归
from sklearn.neighbors import KNeighborsClassifier      #KNN算法
from sklearn.naive_bayes import GaussianNB             #朴素贝叶斯
from sklearn.tree import DecisionTreeClassifier        #决策树
#5、集成算法
import xgboost as xgb   
from xgboost import XGBClassifier 
from sklearn.ensemble import AdaBoostClassifier        
from sklearn.ensemble import GradientBoostingClassifier 
#6、模型评价
from sklearn.metrics import classification_report,precision_score,recall_score,f1_score  #分类报告
from sklearn.metrics import confusion_matrix           #混淆矩阵
from sklearn.metrics import silhouette_score           #轮廓系数
from sklearn.model_selection import GridSearchCV       #交叉验证
from sklearn.metrics import make_scorer
from sklearn.ensemble import VotingClassifier          #投票

六、数据清洗

1、缺失值处理

缺失的部分主要与销售金额相关,都为float类型。且存在大量的缺失值,部分超过90%。

根据实际的业务需求,我们暂时只对缺失值补0。都不删除,先看最后的效果,如果变量影响模型效果,可将缺失值较多的变量删除,变量的缺失值占比如下:

#缺失值占比
dataset_.isnull().sum()/len(dataset_)

输出:

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

#对缺失值补0
dataset_.fillna(0,inplace=True)
dataset_.info()

输出:

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

2、检测重复值

未检测到重复的样本。

dataset_.duplicated().sum()

输出:

0

3、检测数值类型

数值类型符合实际需求,暂时不用替换。

注意:数据要是转换为float,要重新检测缺失值。

dataset_.info()

输出:

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

4、检测标签分布

流失客户样本占比35.4%,留存客户样本占比64.6%,样本不均衡,后续进行模型训练的时候需要注意。  

解决样本不均衡有以下方法可以选择:  

  • 分层抽样  

  • 过抽样 

  • 欠抽样 

label_value=dataset_['label'].value_counts()
labels=label_value.index
plt.figure(figsize=(5,5))
plt.pie(label_value,labels=labels, explode=(0.1,0),autopct='%1.1f%%', shadow=True)
plt.title("流失客户占比高达%s%%" %((label_value['lost']/label_value.sum()*100).round(2)))
plt.show()  

输出:

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

5、检测变量编码

对于分类数据,编码的方式有很多种,归纳总结,最常见的方式基本为两种:

  • 标签编码:代表性的算法例如sklearn中的LabelEncoder()、pandas中的factorize,或者通过replace和map函数进行替换。

  • 独热编码:代表性的算法例如sklearn中的OneHotEncoder(),pandas中的get_dummies。

两者的区别在于:标签编码所产生的结果,在数据上会有大小之分,而在具体的算法,分类的数据,一般是没有数值上的差异的。 而独热编码正好填补了这个缺陷,所以当我们进行简单分析时,一般标签编码即可,但是需要模型训练的数据,一般是要进行独热编码处理的。

print(dataset_['sex'].value_counts())
print(dataset_['label'].value_counts())

输出:

女    3704
男    2676
Name: sex, dtype: int64

no_lost    4121
lost       2259
Name: label, dtype: int64

通过检测变量,我们发现需要对'sex','label'变量进行再编码,否则影响模型训练,为了直观,我们用最基础的语法map函数进行编码。

#map方法
dataset_['sex']=dataset_['sex'].map({'男':1,'女':0})
dataset_['label']=dataset_['label'].map({'lost':1,'no_lost':0})
print(dataset_['sex'].value_counts())
print(dataset_['label'].value_counts())
#pandas的方法
#dataset_['sex']=pd.factorize(dataset_['sex'])[0]
#dataset_['label']=pd.factorize(dataset_['label'],)[0]

输出:

0    3704
1    2676
Name: sex, dtype: int64

0    4121
1    2259
Name: label, dtype: int64

'sex'字段:1代表男性,0代表女性;'label'字段:1代表lost(流失客户),0代表no_lost(留存客户)。

6、根据业务的变量转换

paidmoney_x_one_year,paidmoney_z_one_year ,paidmoney_b_one_year,paidmoney_q_one_year ,paidmoney_y_one_year, paidmoney_w_one_year ,paidmoney_s_one_year ,paidmoney_r_one_year,paidmoney_4_one_year ,paidmoney_5_one_year ,paidmoney_9_one_year这11个字段现在是数值型,其表示的是细分领域(西药、中成药、保健品、医疗器械、)的销售金额,根据实际的业务场景,此处我们建议将其转换为百分比,各字段同除于paidmoney_one_year。

dataset_1=dataset_.copy()
list_paidmoney=['paidmoney_x_one_year','paidmoney_z_one_year','paidmoney_b_one_year','paidmoney_q_one_year','paidmoney_y_one_year','paidmoney_w_one_year','paidmoney_s_one_year','paidmoney_r_one_year','paidmoney_4_one_year','paidmoney_5_one_year','paidmoney_9_one_year']
for i in list_paidmoney:
    dataset_1[i]=dataset_1[i]/dataset_1['paidmoney_one_year']
dataset_1.head()

输出:

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

 因为篇幅过长,仅展示部分数据。

七、筛选特征

1、相关系数矩阵

去掉uid,计算相关系数矩阵:

#提取特征
feature=dataset_1.iloc[:,1:27]
#相关系数矩阵
corr=feature.corr().round(2)

#相关系数矩阵的可视化
plt.figure(figsize=(15,12))
ax = sns.heatmap(corr, xticklabels=corr.columns, yticklabels=corr.columns, 
                 linewidths=0.2, cmap="RdYlGn",annot=True)
plt.title("相关系数矩阵")

输出:

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

2、查看label和特征变量之间的相关性  

plt.figure(figsize=(15,6))
corr['label'].sort_values(ascending=False).plot(kind='bar')
plt.title('label(用户是否流失)与变量间的相关性 ')

输出: 

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

从相关系数排行上看,我们看到maxdate_diff_now(最晚购药日期距今时间)和label存在高度相关,从业务上考虑,最后一次购药时间距今越长,越容易流失,所以我们将其删除。另外,我们也将paidmoney_4_one_year删除。

feature=feature.drop(['maxdate_diff_now','paidmoney_4_one_year'],axis=1)
feature.info()

输出:

数据挖掘案例——药店流失会员预测:什么样的会员容易流失?

另外,从“图——label(用户是否流失)与变量间的相关性”看:mindatediff_one_year(最小时间间隔)、quantity_one_year(年销售量)相关性几乎为0,故这两个维度可以忽略。   

[‘paidmoney_t_one_year’,‘carddate_diff_now’,‘maxdatediff_one_year’,'avgdatediff_one_year',‘year’, ‘paidmoney_r_one_year’, ‘paidmoney_s_one_year’, ‘sex’,
‘paidmoney_q_one_year’, ‘paidmoney_y_one_year’, ‘paidmoney_4_one_year’, ‘paidmoney_w_one_year’,
‘paidmoney_5_one_year’,‘paidmoney_b_one_year’,‘paidmoney_one_year’,‘paidmoney_9_one_year’,‘count_one_year’,‘quantity_one_order’,‘paidmoney_z_one_year’,‘paidmoney_x_one_year’]等都有较高的相关性,将以上维度合并成一个列表list_columns,然后进行频数。

 

 

 

相关标签: 案例