STP客户分类(Python)

一 分析背景

数据为保险公司对车险客户的调查问卷,需要对车险客户进行分类,以确定在车险市场的目标客户,并开展精准营销。

二 分析思路

使用STP分析法,即客户细分(Segmentation)、目标客户选择(Targeting)、目标客户定位(Positioning),如下图所示:
在这里插入图片描述

2.1 客户细分

分类维度:客户细分首先明确从哪个维度对客户进行分类,客户的分类维度共有5种

  • 自然属性——客户作为自然人的性别、年龄、地域等属性;
  • 社会属性——客户作为社会人的收入、职业、教育程度等属性;
  • 行为特征——客户在购买过程中的行为特征;
  • 态度偏好——客户的心理需求、购买动机、使用习惯和态度倾向等;
  • 生活状态与个性因素——客户的生活方式、价值观与个性特点等;
    前三类属于事前分类维度,即表露在外的,在未开展项目分析之前,可以从这些维度凭经验对客户进行分类,一般起验证性作用;后两类属于事后分类维度,即影藏在内的,未开展项目分析之前很难判断其类别,一般起探索性作用。不同的分类维度难易程度和竞争优势不同,与事前分类维度相比,事后分类维度实施难度更大,但也体现了客户内在本质的差异,分类效果更好,因此常用事后分类维度做客户分类,以保证分析的深入性,利用事前分类维度做细分客户的描述和检验,以保证细分客户的可接触性和差异性。

细分方法:如果在单一维度上进行细分可使用交叉分析法也即列联表来分辨其差异性,从而对客户细分;如果使用多个维度进行细分,分为是否知道细分的类别,知道细分的类别可以监督式细分,如决策树、K-menas、回归等方法,若不知道细分的类别需使用聚类分析来细分客户。
聚类常用的聚类算法有K-means和EM两种聚类算法。

2.2 目标客户选择

首先明确应该按什么标准来选择客户,即确定客户吸引力和市场竞争力指标;其次根据指标计算各细分客户的得分,最后采用矩阵分析来找到有吸引力和竞争力的细分客户作为目标客户。

2.3 目标客户定位

对目标客户开展精准营销需要解决两个问题,即相比其他细分客户有哪些显著特征,有哪些不同的需求。

三 数据分析

3.1 数据预览

import numpy as np
import pandas as pd
from  factor_analyzer import calculate_kmo,calculate_bartlett_sphericity,FactorAnalyzer,factor_analyzer
import pandas as pd
#数据加载
data=pd.read_csv(r'F:\python_for_dataanalysis\客户分类数据.csv',encoding='GB2312',index_col=0)
print(data.info())
print('\n{:-^60}'.format('data preview'))
print(data.head())
print('\n{:-^60}'.format('是否重复'))
print(data.duplicated('问卷编号').sum())
Data columns (total 30 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   问卷编号           712 non-null    int64  
 1   是否购买车险         712 non-null    object 
 2   性别             712 non-null    object 
 3   年龄             712 non-null    object 
 4   城市             712 non-null    object 
 5   学历             712 non-null    object 
 6   家庭月收入          712 non-null    object 
 7   职业             711 non-null    object 
 8   汽车价格           712 non-null    object 
 9   决策时间           712 non-null    object 
 10  是否收集信息         712 non-null    object 
 11  从什么渠道收集信息      335 non-null    object 
 12  投保渠道           712 non-null    object 
 13  保险公司的选择        712 non-null    object 
 14  保费金额           712 non-null    float64
 15  索赔经历           712 non-null    object 
 16  一站式服务考虑程度      712 non-null    int64  
 17  网上投保考虑程度       712 non-null    int64  
 18  产品个性化考虑程度      712 non-null    int64  
 19  选择保险公司的考虑因素    712 non-null    object 
 20  满意度            712 non-null    object 
 21  对自己的生活很满意      712 non-null    int64  
 22  为享受而产生的浪费是必要的  712 non-null    int64  
 23  买房子前要先有车       712 non-null    int64  
 24  不惜金钱和时间装修房子    712 non-null    int64  
 25  买衣服都买便宜的       712 non-null    int64  
 26  休息时经常进行户外活动    712 non-null    int64  
 27  尝试生活充满变化       712 non-null    int64  
 28  喜欢独自享受安静的生活    712 non-null    int64  
 29  下班后尽快回家        712 non-null    int64  
dtypes: float64(1), int64(13), object(16)
memory usage: 172.4+ KB
None
------------------------data preview------------------------
   问卷编号 是否购买车险 性别      年龄  城市       学历   家庭月收入              职业    汽车价格  决策时间  \
0     1      是  男  18-30岁  西安     大学学历   <0.7万      工业/生产型企业职工   10万以下  一周以内   
1     2      是  女  18-30岁  成都     大学学历   <0.7万          党政机关干部   10万以下  一周以内   
2     3      是  男  18-30岁  西安  高中及以下学历  0.7-1万  私营业主/个体户/自由职业者   10万以下  一周以内   
3     4      是  男  41-55岁  广州     大学学历  0.7-1万  私营业主/个体户/自由职业者  20-30万  一周以内   
4     5      是  男  41-55岁  广州     大学学历   <0.7万       专业人员/技术人员  20-30万  三周以内   
...息时经常进行户外活动  尝试生活充满变化  喜欢独自享受安静的生活 下班后尽快回家  
0     ...     7         7            7       7  
1     ...     4         5            4       3  
2     ...     5         4            5       7  
3     ...     5         5            5       4  
4     ...     5         5            4       5  
[5 rows x 30 columns]
----------------------------是否重复----------------------------
0

如上图所示,问卷共有29个问题,712条记录,其中“从什么渠道收集信息”存在缺失值,此题根据上一题是否收集信息有关,缺失为正常,以问卷编号为主键,数据无重复值。从21至29列为事后分类维度表达了客户的心理活动和生活状态,其余问题为事前分类维度。

3.2 确定分类维度

事后分类维度优于事前分类维度,因此选择事后分类维度作为第一维度进行细分客户,其次只有接触到细分客户企业精准营销才能落地,而能否接触到用户反映在用户购买行为上,因此显著区分用户购买行为的保费金额作为第二分类维度。

3.3 分类维度的降维

事后分类维度有9个语句,语句间可能存在相关性,而相关性会造成信息重叠的扩大化,增加分类偏差,而本案例对客户进行细分所用聚类模型便是将相似数据划分一类的思想,而特征间的相关性便会影响最后客户细分的结果。对特征进行降维能很好的解决此类问题,降维分为以下两种:

  • 基于特征选择的降维:指的是根据一定的规则经验,直接选取原有特征维度的一部分参与后续的建模和计算,用选择的维度代替所有维度,一般分为4种方法,即经验法、测算法、基于统计分析的方法、机器学习算法;
  • 基于特征转换的降维:指按照一定的数学变换方法,把给定的一组相关特征通过数字模型将高维空间的数据点映射到低维度空间,利用映射后的变量特征来表示原变量的总体特征,常用的方法有ICA、PCA、FA、LDA、LEA;

本案例使用 FA因子分析来削减数据维度,因为希望降维后的特征能很好的对原始的特征进行描述,从而有助于业务部门的理解,推动数据分析的结果业务落地。因子分析的过程是从原始变量相关矩阵内部的依赖性出发,去寻找共性因子和个性因子并得到最优解释的过程,相比于其他基于特征的降维方法,因子分析能更好的描述原始变量。因子分析的步骤如下

3.3.1 相关性检验

因子分析的前提是原有变量之间存在较强相关关系,一般使用KMO取样适当性量数和Bartlette球度检验来检验原有变量之间是否有相关性(也可用方差齐性检验),关于检验的取值及效果如下:
在这里插入图片描述

from factor_analyzer import calculate_kmo,calculate_bartlett_sphericity,FactorAnalyzer,factor_analyzer
#适用性检验
df=data.iloc[:,-9:]     #筛选出事后分类维度
kmo=calculate_kmo(df)   #得到kmo值
bartlett=calculate_bartlett_sphericity(df)  #可得到显著性水平
print('kmo: {:.3f} \nbartlett: {:.3f}'.format(kmo[1],bartlett[1]))
kmo: 0.716 
bartlett: 0.000

kmo=0.716>0.7,显著性水平sig<0.05,通过分析说明本案例适合因子分析。

3.3.2 因子分析(FA)

#因子分析
fa=FactorAnalyzer(n_factors=4,method='principal',rotation='varimax') #公共因子个数为4,因子提取方法为主成分法,因子旋转为最大方差法
fa.fit(df)
communality=fa.get_communalities()                     #公因子方差(提取的因子对这几个变量的解释度)
contribute=fa.get_factor_variance()                   #解释的总方差(贡献率)
matrix=fa.loadings_                                          #旋转成分矩阵(各个因子对维度的解释程度)
component_matrix=pd.DataFrame(matrix,index=df.columns)
print('公因子方差:\n',pd.DataFrame(communality,index=df.columns,columns=['communalities']))                 
print('\n解释的总方差:\n',pd.DataFrame(contribute,index=['SS Loadings','Proportion Var','Cumulative Var']))                        
print('\n旋转成分矩阵:\n',component_matrix)
---------------------------公因子方差----------------------------
                              communalities
对自己的生活很满意              0.747973
为享受而产生的浪费是必要的       0.671108
买房子前要先有车               0.742333
不惜金钱和时间装修房子         0.738087
买衣服都买便宜的               0.665677
休息时经常进行户外活动         0.776893
尝试生活充满变化              0.696074
喜欢独自享受安静的生活         0.615239
下班后尽快回家                0.620690

---------------------------解释的总方差---------------------------
                        0         1         2         3
SS Loadings     1.759476  1.644054  1.562519  1.308026
Proportion Var  0.195497  0.182673  0.173613  0.145336
Cumulative Var  0.195497  0.378170  0.551783  0.697119

---------------------------旋转成分矩阵---------------------------
                              0         1         2         3
对自己的生活很满意        -0.068532  0.152857  0.203330  0.823752
为享受而产生的浪费是必要的  0.491223  0.004481  0.059112  0.652912
买房子前要先有车           0.830760  0.149003  0.164485 -0.053973
不惜金钱和时间装修房子     0.834636  0.071595  0.131777  0.137766
买衣服都买便宜的           0.214852  0.670634  0.319035 -0.260733
休息时经常进行户外活动     0.084896  0.090240  0.870070  0.067232
尝试生活充满变化          0.208076  0.062736  0.782726  0.190219
喜欢独自享受安静的生活    0.159632  0.763682 -0.014676  0.079574
下班后尽快回家           -0.067478  0.740474  0.039754  0.257401

公因子方差即提取的因子对原始维度的解释程度,由上可知提取的因子对9个语句的解释程度均超过60%,说明提取的因子对原始维度具有一定的解释力。
解释的总方差中SS Loadings为各因子的特征值,Proportion Var为各因子对总体所贡献的方差,Cumulative Var为各因子的累计方差贡献,由上可知这些因子能解释总体信息量的69.7%。
旋转成分矩阵即每个因子对维度信息的解释量,由上可知每个维度上只有一个因子对该维度的信息解释量较大达到60%以上,即4个因子在原始维度上的特征被明显分开,具有差异性。

fa=FactorAnalyzer(n_factors=4,method=‘principal’,rotation=‘varimax’)中n_factors为指定公因子数量,公因子数量越多对原始信息的解释量越大,但数量太多又无法起到降低维度的作用;method='principal’因子提取方法为主成分法,还可以指定其他的如最小二乘法(’minres‘)和极大似然法(’ml‘),rotation='varimax’为使用最大方差法对提取因子进行旋转,使得因子特征的差异化明显,也可以选择其他如最小/最大斜交旋转法(’oblimin‘,‘oblimax’)、幂极小法/极大法(‘quartimin’,‘quartimax’)等。

3.3.3 因子命名及因子得分

根据提取的各因子对各个维度的解释情况,将因子0命名为“享受型因子”,因子1、2、3依次为“居家型因子”、“外向型因子”、“自信型因子”,计算各维度在各因子上的得分

score=fa.transform(df)                        #计算因子得分
print('因子得分:\n',score)
category=[list(np.where(pd.Series(x)>=pd.Series(x).max())[0]) for x in score]
data_pro=pd.concat([data,pd.DataFrame(category,columns=['因子类别'])],axis=1)   #将因子类别合并到初始数据
因子得分:
 [[-0.01125898  1.61676426  1.39820364  0.93845075]
 [-0.17980536 -0.85583289 -0.06137993 -2.97879831]
 [-1.10583292  0.72918341 -0.66475733 -0.66457342]
 ...
 [-0.36497343  0.30366095 -0.14586793 -0.5062061 ]
 [ 0.81396785  1.44768401  1.01653858  0.01330543]
 [ 0.2984315  -1.40768948  0.32201535  0.45824759]]

将因子得分最高的作为因子类别,np.where(condition)可以得到满足条件的索引,使用列表推导式得到因子得分最高的索引作为因子类别,使用pd.concat()合并至初始数据。

3.4 细分维度的数据转化

数据标准化的目的是处理不同规模和量纲的数据,使其缩放到相同的数据区间和范围,以减少规模、特征、分布差异等对模型的影响,此外标准化后的数据还具有直接计算并生成复合指标的意义是加权指标的必要步骤,其主要常用的有以下几种:

  • Z-Score标准化:基于原始数据的均值和方差进行标准化,标准化后的数据符合标准正态分布,但其为一种中心化方法,会改变原有数据分布结构,不适合对稀疏数据处理;
  • Min-Max标准化:对数据进行线性变换,使其完全落入[0,1]区间内,同时还能较好的保持数据原有的结构

本案例中细分维度有“因子类别”和“保费金额”,因子类别为分类型数据,取值0,1,2,3,而保费金额为数值型数据取值范围为[350,8500],为了量纲统一,使数据具有可比性,对其进行Z-score标准化,代码如下:

#数据转换
from sklearn.preprocessing import StandardScaler
x=data_pro[['保费金额','因子类别']]
x=MinMaxScaler().fit_transform(x)

用sklearn库的MinMaxScaler建立模型,然后使用fit_transform进行转换。

3.5 聚类分析(层次聚类)

聚类分析的基本思想是“物以类聚,人以群分”,其中经典的算法包括K-means,层次聚类,DBSCAN、两步聚类、谱聚类等等,聚类分析分层次聚类和迭代聚类(典型的K-means聚类),其两种的比较如下:

层次聚类迭代聚类
思路逐层合并不断迭代,已确定类别中心点和类别的构成
类别数事先未知,输出树状图可以从中选择最优方案在建模时需要指定类别数,可以迭代类别数量,选择最优类别数
计算速度由于需要反复计算距离,当样本太大或变量太多非常耗时计算量小,内存占用低,运行速度快,可处理多变量、大样本数据

本案例使用层次聚类,层次聚类的思想是逐层合并,根据样本距离,将距离最近的样本合为一类,然后计算所形成的类别与其它样本的距离,对距离最近的再做合并,层次聚类对连续变量和分类变量均可进行聚类,对数据类型要求不高。本案例代码如下:

#聚类
import scipy.cluster.hierarchy as sch
import matplotlib.pyplot as plt
dismat=sch.distance.pdist(x,'euclidean')   #生成点与点之间的距离矩阵,这里用的欧氏距离
z=sch.linkage(dismat,method='average')     #进行层次聚类,method为计算类间距的方法,single:最近邻,complete:最远邻,average:平均距离
P=sch.dendrogram(z)                         #将层次聚类结果以树状图结果表示出来
plt.savefig(r'F:\python_for_dataanalysis\plot.png')  #保存树状图
best_label=sch.fcluster(z,t=5,criterion='maxclust')    #最大聚类个数为5

在这里插入图片描述
其中sch.fcluster返回最终聚类结果,参数z为关联函数生成的矩阵,t的含义与criterion相关,如下:

  • 当criterion为’inconsistent’时,t值应该在0-1之间,t越接近1代表两个数据之间的相关性越大,t越趋于0表明两个数据的相关性越小。这种相关性可以用来比较两个向量之间的相关性,可用于高维空间的聚类 ;
  • 当criterion为’distance’时,t值代表了绝对的差值,如果小于这个差值,两个数据将会被合并,当大于这个差值,两个数据将会被分开;
  • 当criterion为’maxclust’时,t代表了最大的聚类的个数,设置4则最大聚类数量为4类,当聚类满足4类的时候,迭代停止 ;
  • 当criterion为’monocrit’时,t的选择不是固定的,而是根据一个函数monocrit[j]来确定;

本案例将聚类数设置为5,此时各类样本数量相对较均匀

3.6 聚类效果检验

对聚类细分后的数据进行检验,判断其是否具有显著性差异,一般使用统计学中的假设检验对其进行分析,基于本案例的数据特点,即因子类别为多分类变量,保费金额为连续数值型变量,细分类别为多分类变量,使用单因素方差分析和卡方分析来进行检验

  • 单因素方差分析:检验三个样本以上的均值是否有显著性差异,一般用于单个分类变量与连续性变量的关系,其对数据要求较高,需要数据满足正态性、独立性和方差齐性的要求,数据为连续数据,H0原假设为样本均值相等
  • 卡方分析:主要用于分析定类数据与定类数据之间的关系情况,其对数据要求不高

使用单因素方差分析对数据要求较高,对保费金额进行方差齐性检验发现其并不满足条件,由于样本数据量并不大,其保费金额可近似看做分类变量,使用卡方分析来检验不同的因子类别下以及不同的保费金额下,其细分类别是否具有显著性差异,其代码如下:

#对聚类效果检验
from scipy import stats
result=pd.concat([data_pro,pd.DataFrame(best_label,columns=['细分类别'])],axis=1)
mm=pd.crosstab(result['细分类别'],result['保费金额'])
print('{:-^60}'.format('保费金额卡方检验'))
print('P-value:',stats.chi2_contingency(mm)[1])
cross_tab=pd.crosstab(result['细分类别'],result['因子类别'])  #卡方检验
print('\n{:-^60}'.format('因子类别卡方检验'))
print('P-value:',stats.chi2_contingency(cross_tab)[1])
--------------------------保费金额卡方检验--------------------------
P-value: 1.171024438397459e-58

--------------------------因子类别卡方检验--------------------------
P-value: 7.499919871972058e-299

卡方检验的P值均小于显著性水平0.05,表明细分的客户在保费金额和因子类别(生活状态)方面具有差异性,聚类效果良好。

3.7 细分客户的命名

根据细分的客户在因子类别以及保费金额上的差异性表现,对细分客户进行命名,因子类别为分类型数据,保费金额为数值型数据,分别使用交叉表和均值来刻画不同客户的差异化表现,代码如下:

#细分客户命名
cross=pd.crosstab(result['细分类别'],result['因子类别'],margins=True)
avg_money=result.groupby('细分类别')['保费金额'].mean()
print('{:-^60}\n{}\n{:-^60}\n{}'.format('交叉表',cross,'不同类别下保费金额的均值',avg_money))
----------------------------交叉表-----------------------------
因子类别    0    1    2    3  All
细分类别                         
1          0    0    0  179  179
2          0    0    0    1    1
3          0    3    0    0    3
4         185   0    0    0  185
5          0  188  156    0  344
All   185  191  156  180  712
------------------------不同类别下保费金额的均值------------------------
             保费金额
细分类别             
1     2188.273743
2     5100.000000
3     6499.600000
4     2274.750811
5     2144.634593

根据以上分析,细分类别1,2,3,4、5可分别命名为低端自信型客户、高端自信客户、高端居家型客户、中端享受型客户、低端居家型客户

3.8 目标客户的选择

客户划分后需要对目标客户进行选择,如何对目标市场进行选择,需要从两个方面来进行判断,即市场吸引力和企业竞争力。

  • 市场吸引力需要分析宏观环境和市场环境,宏观环境即影响市场的各种宏观因素,这些因素可归纳为PEST,市场环境指的是市场的具体现状,衡量指标如市场规模、利润水平、增长速度、成长潜力等等;
  • 企业竞争力的分析可归纳为影响企业生存状态的波特五力,其包括供应商、购买者、直接竞争对手、替代品和潜在进入者,分析竞争环境就是讲企业与影响生存状态的波特五力进行对比;
3.8.1 确定衡量指标及计算指标

具体选择那些指标来判断市场吸引力和企业竞争力需根据实际情况来进行确定,本案例使用客户规模和保费金额来衡量市场吸引力,市场份额来衡量企业的竞争力,如下:

#市场吸引力
counts=result.groupby('细分类别')[['因子类别']].count()
counts['占比']=(counts.div(counts.sum())*100).round(2)
concat=pd.concat([counts,avg_money],axis=1)
scaler=StandardScaler().fit_transform(concat.iloc[:,1:])
attraction=pd.concat([concat,pd.DataFrame(scaler,index=[1,2,3,4,5])],axis=1)
attraction['score']=attraction.iloc[:,-2]*0.6+attraction.iloc[:,-1]*0.4
attraction.columns=['人数','人数占比','平均保费金额','Z-人数占比','Z-平均金额','吸引力评分']
attraction.index.name='客户类别'
print(attraction)
        人数   人数占比       平均保费金额      Z-人数占比    Z-平均金额     吸引力评分
客户类别
1       179    25.14        2188.273743      0.283796     -0.799550      -0.149543
2        1     0.14         5100.000000     -1.095996      0.802505      -0.336596
3        3     0.42         6499.600000     -1.080543      1.572576      -0.019295
4       185    25.98        2274.750811      0.330157     -0.751970      -0.102694
5       344    48.31        2144.634593      1.562587     -0.823561       0.608128

计算出各个类别客户的人数占比以及平均保费金额,对人数和平均金额进行加权评分作为客户吸引力的评分,计算前由于人数和平均金额的量纲不同使用sklearn库的StandardScaler来进行标准化处理,根据企业对人数规模和金额的重要性选择权重,这里对人数和金额使用60%和40%的权重;

#企业竞争力
competitive=pd.crosstab(result['细分类别'],result['保险公司的选择'],normalize=0,margins=True)*100
competitive['ALL']=competitive.sum(axis=1)
competitive.columns.name='人数占比'
competitive
人数占比      丁保险公司       丙保险公司      乙保险公司      甲保险公司    ALL
细分类别                                                    
1            22.346369      22.905028      25.698324      29.050279     100.0
2            0.000000       100.000000     0.000000       0.000000      100.0
3            0.000000       33.333333      33.333333      33.333333     100.0
4           28.108108       24.864865      25.945946      21.081081     100.0
5           25.000000       26.162791      23.837209      25.000000     100.0
All         25.000000       25.140449      24.859551      25.000000     100.0

使用pd.crosstab()画出客户类别与所选择保险公司的人数占比的交叉表,从表中可得知甲保险公司在各类客户上的人数占比

3.8.2 绘制矩阵图选择目标客户

以企业竞争力为横坐标,客户吸引力为纵坐标,做出矩阵图,可得出各类客户在矩阵图中的不同象限,在第一象限企业竞争力和客户吸引力均高,为首选目标客户,在第二、第四象限要么竞争力高要么吸引力好,可积极争取,在第三象限竞争力和吸引力均低,可为舍弃对象,在资源充足的情况下可兼顾该类客户。绘制矩阵图如下:

#矩阵图
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['Simhei']      #设置中文字体为黑体
plt.rcParams['axes.unicode_minus']=False        #不设置坐标轴无法显示负号
plt.rcParams['font.sans-serif']=['Simhei']
plt.rcParams['axes.unicode_minus']=False
fig,ax=plt.subplots(1,1,figsize=(8,5))
ax.scatter(competitive.iloc[:-1,-2],attraction['吸引力评分'],s=40)
plt.xlim(-5,50)
plt.xticks(list(np.arange(0,60,10)),list(pd.Series(np.arange(0,60,10)).map(lambda x:str(x)+'%')))
plt.xlabel('企业竞争力')
plt.ylim(-1,1)
plt.ylabel('客户吸引力')
plt.hlines(0,-5,50,color='b',alpha=0.5)
plt.vlines(25,-1,1,color='b',alpha=0.5)
text=['低端自信型客户','高端自信客户','高端居家型客户','中端享受型客户','低端居家型客户']
for i in range(len(text)):
    plt.annotate(text[i],xy=(competitive.iloc[i,-2],attraction.iloc[i,-1]),xytext=(competitive.iloc[i,-2]-4,(attraction.iloc[i,-1]-0.1)))
plt.savefig('F:\python_for_dataanalysis\p.png')
plt.show()

在这里插入图片描述
从图中可以看出低端居家客户(编号5)是首选客户,其次是高端居家和低端自信客户。

3.9 目标客户定位

3.9.1 特征描述

先判断目标客户与其它细分类客户在哪些方面存在显著性差异,然后再具体分析其显著性差异是什么。反映客户特征均为分类变量,这里使用卡方检验对其显著性进行检验,并输出列联表,如下:

for i in range(2,9):
    cross_table=pd.crosstab(result['细分类别'],result.iloc[:,i])  #卡方检验
    print('\n{:-^60}'.format(result.columns[i]))
    print(cross_table)
    print('P-value:',stats.chi2_contingency(cross_table)[1])
-----------------------------性别-----------------------------
性别      女    男
细分类别          
1      35  144
2       1    0
3       1    2
4      41  144
5     159  185
P-value: 6.917180816479783e-11

-----------------------------年龄-----------------------------
年龄    18-3031-4041-55岁
细分类别                        
1          0      62     117
2          0       1       0
3          0       3       0
4          0      66     119
5        148     104      92
P-value: 1.0582376952219347e-43

-----------------------------城市-----------------------------
城市    上海  北京   广州  成都  武汉  沈阳   西安
细分类别                              
1     12  44   26   0   0   0   97
2      0   1    0   0   0   0    0
3      2   1    0   0   0   0    0
4     34   9  115   0   0  27    0
5     38  72   63  31   1  11  128
P-value: 2.640597996918795e-52

-----------------------------学历-----------------------------
学历    大学学历  研究生及以上学历  高中及以下学历
细分类别                         
1      103         7       69
2        1         0        0
3        1         0        2
4      111         5       69
5      210         6      128
P-value: 0.8303382831977886

---------------------------家庭月收入----------------------------
家庭月收入  0.7-11-1.51.5-2<0.7>2万
细分类别                                     
1          39      59      24     48    9
2           0       0       0      0    1
3           0       0       0      0    3
4          39      62      29     43   12
5          94     117      39     80   14
P-value: 9.42913843735742e-10

-----------------------------职业-----------------------------
职业    下岗/退休人员  专业人员/技术人员  党政机关干部  其他  军人  商业/服务性企业职工  在校学生  工业/生产型企业职工  无工作  \
细分类别                                                                          
1           7         20      21   5   1          26     3          17    5   
2           0          1       0   0   0           0     0           0    0   
3           0          1       0   0   1           0     0           0    0   
4           2         24      11   3   1          30     3          10    6   
5          11         72      22   3   0          73     8          30   10   

职业    私营业主/个体户/自由职业者  经理/企业经营管理干部  
细分类别                               
1                 45           29  
2                  0            0  
3                  1            0  
4                 67           27  
5                 77           38  
P-value: 2.0158727495726078e-10

----------------------------汽车价格----------------------------
汽车价格  10-2010万以下  20-3030万以上
细分类别                              
1        102      0      70      7
2          0      0       0      1
3          0      0       1      2
4         23      0     129     33
5         41    134     125     44
P-value: 2.0807852716854927e-61

由上可知目标客户具有以下特征:在性别上男女均有其男性占比较高,大部分年龄在18-30岁,聚集在武汉地区,学历与其它细分客户相比无显著性差异,家庭月收入大部分在1-1.5万,其职业大部分为个体户、自由职业者和私营业主,汽车价格在10万以下。
在SPSS中可以使用多重对应分析来分析其特征之间的联系和差异,多重对应分析实质上是将交叉表中的频数数据变换(降维)后,利用图示化(散点图)的方式,从而将抽象的交叉表形象化,直观解释变量的不同类别之间的联系

3.9.1 客户偏好需求

同理可用卡方检验,判断其是否具有显著性差异,然后根据列联表得出其特征需求,代码如下:

for i in range(9,21):
    cross_table=pd.crosstab(result['细分类别'],result.iloc[:,i])  #卡方检验
    print('\n{:-^60}'.format(result.columns[i]))
    print(cross_table)
    print('P-value:',stats.chi2_contingency(cross_table)[1])

输出太长这里不做展示,目标客户在投保渠道,保费金额,满意度这些方面具有显著性差异,在投保渠道上目标客户主要使用电话投保,在满意度方面大部分都是满意

四 总结

此处的总结并不是对整个案例分析结果的总结,而是在做的时候出现的问题以及自己的思考

4.1 缺陷以及思考

整体而言,在聚类时并没有得到一个比较满意的结果

1聚类所细分出来的类别在因子类别上存在明显的差异性,虽在保费金额上存在差异,但各个细分类别的在不同的保费金额上的数量差距很大,改变层次聚类的类数可以使在保费金额上数量均衡但是在因子类别以及保费金额上没有体现显著性差异;
原因可能在于样本在保费金额上存在离群值,在特征工程时并没有对数据进行很好的处理,尝试过使用Z-score对数据进行转化但并没有得到很好的结果,使用K-means聚类可以得到较好的因子类别上的差异,但是各个因子类别上的人数差别很大,Z-score标准化会使得保费金额的均值一样,如果再使用k均值聚类,其各个类别在保费金额上的均值相差不大
2聚类后是否应该检验不同类别在保费金额上的差异性,保费金额其数据量小并不是严格正态分布,在确定细分类别后不存在方差齐性,不适合用单因素方差分析判断差异性
3使用K-means聚类,并使用轮廓系数进行聚类效果评价,能得到较高的分数,但是保费金额上各类客户的均值是相差不多的,可能在特征工程时保费金额不能作为连续的数值变量进行处理?

以下是我刚开始使用K-means聚类的过程,内容仅供参考

3.4 细分维度的数据转化
在数据建模中很多算法无法直接处理非数值型变量,如Kmeans算法为基于距离的相识度来进行计算,这时就需要将其转化为数值型数据,一般将此类数据进行真值转换,即用0和1来表示,这是因为在用数字直接表示分类和顺序变量的过程中,无法准确还原不同类别信息之间的差异性和关联性

  • 针对分类数据:分类数据指某些数据属性只能归于某一类别的非数值型数据。例如男和女,无论用1和2来区分,那么1和2本身就已经带有距离的差异,但实际二者是不具有这种差异性的;
  • 针对顺序数据:顺序数据一般为某一有序类别的数据,例如高、中、低,他们的差异为什么用3,2,1来表示而不是30,20,10来表示呢,因此任何一个有序数字的排序也都无法准确表达出顺序数据的差异性;

数据标准化的目的是处理不同规模和量纲的数据,使其缩放到相同的数据区间和范围,以减少规模、特征、分布差异等对模型的影响,此外标准化后的数据还具有直接计算并生成复合指标的意义是加权指标的必要步骤,其主要常用的有以下几种:

  • Z-Score标准化:基于原始数据的均值和方差进行标准化,标准化后的数据符合标准正态分布,但其为一种中心化方法,会改变原有数据分布结构,不适合对稀疏数据处理
  • Min-Max标准化:对数据进行线性变换,使其完全落入[0,1]区间内,同时还能较好的保持数据原有的结构

本案例中细分维度有“因子类别”和“保费金额”,因子类别为分类型数据,取值0,1,2,3,而保费金额为数值型数据取值范围为[350,8500],先将分类数据热独编码(也叫真值转换、标志转换)发现对于层次聚类,使用真值转换和不使用真值转换对聚类结果并没有影响,再将两个维度进行标准化,统一量纲,使数据具有可比性,代码如下:

#数据转换
from sklearn.preprocessing import StandardScaler
x=data_pro[['保费金额','因子类别']]
x=pd.get_dummies(x,columns=['因子类别'])
x=StandardScaler().fit_transform(x)

热独编码的实现有两种方式:
使用sklearn库中的OneHotEncoder,返回的为转换后的矩阵,代码如下:

from sklearn.preprocessing import OneHotEncoder
model_enc=OneHotEncoder(sparse=False)
x=model_enc.fit_transform(x)

使用pandas的get_dummies,即本案例所用
数据标准化使用sklearn库的StandardScaler建立模型,然后使用fit_transform进行转换,此外还可直接使用sklearn.preprocessing.sacler()进行标准化处理。
3.5 聚类分析
聚类分析的基本思想是“物以类聚,人以群分”,其中经典的算法包括K-means,DBSCAN、两步聚类、谱聚类等等,本案例使用K-means进行聚类,K均值在算法稳定性、效率和准确率表现都很好,是聚类中最常用的算法之一,它是基于点与点之间的距离的相似度来计算最佳类别归属。本案例代码如下:

#聚类
from sklearn.metrics import silhouette_score
from sklearn.cluster import KMeans
score_list=[]
score1_int=-1
for i in range(3,7):
    model=KMeans(n_clusters=i,random_state=1)
    label=model.fit_predict(x)
    score1=silhouette_score(x,label)     #轮廓系数
    score_list.append([i,score1])
    if score1>score1_int:
        best_label=label
        score1_int=score1
        best_clusters=i
print(score_list)
[[3, 0.5266676334298763], [4, 0.6958125386169491], [5, 0.6768238619732173], [6, 0.6346985745830507]]

使用轮廓系数来判断聚类的效果,循环找到最佳聚类数量 n_clusters,并将最佳聚类结果保存进best_label,通过score_list可知,当聚类数量为4时轮廓系数(silhouette)最大,sil=0.696>0.5,说明聚类效果较优,不同类别间具有显著的区分效应。对非监督式聚类效果的评估一般使用以下两个指标:

  • silhouette_score:轮廓系数,他用来计算所有样本的平均轮廓系数,使用平均群内距离和每个样本的平均最近簇来计算,其最高值为1,最差为-1,0附近的值表示重叠的聚类,负值通常表示样本被错分;
  • calinski_harabasz_score:Calinski_Harabaz(CH)该参数定义为群内离散与簇间离散的比值,CH值越大类内越紧密,类间越分散,其聚类效果越好

3.6 聚类效果检验
对聚类细分后的数据进行检验,判断其是否具有显著性差异,一般使用统计学中的假设检验对其进行分析,基于本案例的数据特点,即因子类别为多分类变量,保费金额为连续数值型变量,细分类别为多分类变量,使用单因素方差分析(这是错误的判断,使用单因素方差前应对数据进行判断)和卡方分析来进行检验
使用单因素方差分析来检验不同的保费金额下,其细分类别是否具有显著差异,使用卡方分析来检验不同的因子类别下其细分类别是否具有显著性差异,其代码如下:

#对聚类效果检验
import statsmodels.api as sm
from statsmodels.formula.api import ols
from scipy import stats
result=pd.concat([data_pro,pd.DataFrame(best_label,columns=['细分类别'])],axis=1)
model_lm=ols('细分类别 ~ (保费金额)',data=rusult).fit()                   #训练最小二乘回归模型
print('{:-^60}'.format('单因素方差分析'))
print(sm.stats.anova_lm(model_lm,typ=1))                                 #输出单因素方差分析,typ=1表示为单因素
cross_tab=pd.crosstab(result['细分类别'],result['因子类别'])               #生成交叉表(列联表)
print('\n{:-^60}'.format('卡方检验'))
print('P-value:',stats.chi2_contingency(cross_tab)[1])                   #卡方检验
--------------------------单因素方差分析---------------------------
             df      sum_sq    mean_sq          F        PR(>F)
保费金额        1.0   67.935746  67.935746  48.387852  7.952113e-12
Residual  710.0  996.828298   1.403984        NaN           NaN
----------------------------卡方检验----------------------------
P-value: 0.0

结果表明具有显著性差异

-----------------------------------以上,本文存在很多不足,欢迎各位指正-----------------------------------------------------

案例完整代码:

import pandas as pd
import numpy as np
from factor_analyzer import calculate_kmo,calculate_bartlett_sphericity,FactorAnalyzer,factor_analyzer
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler,MinMaxScaler
from scipy import stats
import scipy.cluster.hierarchy as sch
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['Simhei']
plt.rcParams['axes.unicode_minus']=False
#数据加载
data=pd.read_csv(r'F:\python_for_dataanalysis\客户分类数据.csv',encoding='GB2312',index_col=0)
print(data.info())
print('\n{:-^60}'.format('data preview'))
print(data.head())
print('\n{:-^60}'.format('是否重复'))
print(data.duplicated('问卷编号').sum())
#适用性检验
df=data.iloc[:,-9:]     #筛选出事后分类维度
kmo=calculate_kmo(df)   #得到kmo值
bartlett=calculate_bartlett_sphericity(df)  #可得到显著性水平
print('kmo: {:.3f} \nbartlett: {:.3f}'.format(kmo[1],bartlett[1]))
#因子分析
fa=FactorAnalyzer(n_factors=4,method='principal',rotation='varimax') #公共因子个数为4,因子提取方法为主成分法,因子旋转为最大方差法
fa.fit(df)
communality=fa.get_communalities()                     #公因子方差(提取的因子对这几个变量的解释度)
contribute=fa.get_factor_variance()                   #解释的总方差(贡献率)
matrix=fa.loadings_                                          #旋转成分矩阵(各个因子对维度的解释程度)
component_matrix=pd.DataFrame(matrix,index=df.columns)
print('{:-^60}\n'.format('公因子方差'),pd.DataFrame(communality,index=df.columns,columns=['communalities']))                 
print('{:-^60}\n'.format('解释的总方差'),pd.DataFrame(contribute,index=['SS Loadings','Proportion Var','Cumulative Var']))                        
print('{:-^60}\n'.format('旋转成分矩阵'),component_matrix)
#计算因子得分
score=fa.transform(df)                        
print('因子得分:\n',score)   
category=[list(np.where(pd.Series(x)>=pd.Series(x).max())[0]) for x in score]
data_pro=pd.concat([data,pd.DataFrame(category,columns=['因子类别'])],axis=1)
#数据转换
x=data_pro[['保费金额','因子类别']]
#x=pd.get_dummies(x,columns=['因子类别'])  #对层次聚类无影响
x=MinMaxScaler().fit_transform(x)
#聚类
dismat=sch.distance.pdist(x,'euclidean')   #生成点与点之间的距离矩阵,这里用的欧氏距离
z=sch.linkage(dismat,method='average')     #进行层次聚类,method为计算类间距的方法,single:最近邻,complete:最远邻,average:平均距离
P=sch.dendrogram(z)                         #将层次聚类结果以树状图结果表示出来
plt.savefig(r'F:\python_for_dataanalysis\plot.png')  #保存树状图
best_label=sch.fcluster(z,t=5,criterion='maxclust')    #最大聚类个数为5
#对聚类效果检验
result=pd.concat([data_pro,pd.DataFrame(best_label,columns=['细分类别'])],axis=1)
mm=pd.crosstab(result['细分类别'],result['保费金额'])
print('{:-^60}'.format('保费金额卡方检验'))
print('P-value:',stats.chi2_contingency(mm)[1])
cross_tab=pd.crosstab(result['细分类别'],result['因子类别'])  #卡方检验
print('\n{:-^60}'.format('因子类别卡方检验'))
print('P-value:',stats.chi2_contingency(cross_tab)[1])
#细分客户命名
cross=pd.crosstab(result['细分类别'],result['因子类别'],margins=True)
avg_money=result.groupby('细分类别')[['保费金额']].mean()
print('{:-^60}\n{}\n{:-^60}\n{}'.format('交叉表',cross,'不同类别下保费金额的均值',avg_money))
#市场吸引力
counts=result.groupby('细分类别')[['因子类别']].count()
counts['占比']=(counts.div(counts.sum())*100).round(2)
concat=pd.concat([counts,avg_money],axis=1)
scaler=StandardScaler().fit_transform(concat.iloc[:,1:])
attraction=pd.concat([concat,pd.DataFrame(scaler,index=[1,2,3,4,5])],axis=1)
attraction['score']=attraction.iloc[:,-2]*0.6+attraction.iloc[:,-1]*0.4
attraction.columns=['人数','人数占比','平均保费金额','Z-占比','Z-平均金额','吸引力评分']
attraction.index.name='客户类别'
#企业竞争力
competitive=pd.crosstab(result['细分类别'],result['保险公司的选择'],normalize=0,margins=True)*100
competitive['ALL']=competitive.sum(axis=1)
competitive.columns.name='人数占比'
#矩阵图
fig,ax=plt.subplots(1,1,figsize=(8,5))
ax.scatter(competitive.iloc[:-1,-2],attraction['吸引力评分'],s=40)
plt.xlim(-5,50)
plt.xticks(list(np.arange(0,60,10)),list(pd.Series(np.arange(0,60,10)).map(lambda x:str(x)+'%')))
plt.xlabel('企业竞争力')
plt.ylim(-1,1)
plt.ylabel('客户吸引力')
plt.hlines(0,-5,50,color='b',alpha=0.5)
plt.vlines(25,-1,1,color='b',alpha=0.5)
text=['低端自信型客户','高端自信客户','高端居家型客户','中端享受型客户','低端居家型客户']
for i in range(len(text)):
    plt.annotate(text[i],xy=(competitive.iloc[i,-2],attraction.iloc[i,-1]),xytext=(competitive.iloc[i,-2]-4,(attraction.iloc[i,-1]-0.1)))
plt.savefig('F:\python_for_dataanalysis\p.png')
plt.show()
#特征描述
for i in range(2,9):
    cross_table=pd.crosstab(result['细分类别'],result.iloc[:,i])  #卡方检验
    print('\n{:-^60}'.format(result.columns[i]))
    print(cross_table)
    print('P-value:',stats.chi2_contingency(cross_table)[1])
#偏好需求
for i in range(9,21):
    cross_table=pd.crosstab(result['细分类别'],result.iloc[:,i])  #卡方检验
    print('\n{:-^60}'.format(result.columns[i]))
    print(cross_table)
    print('P-value:',stats.chi2_contingency(cross_table)[1])
  • 0
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值