【数据分析项目实战】手把手教学(Pyecharts+PCA+KMeans)经典数据集:客户性格分析(customer personality analysis)

分享kaggle上面经典数据集customer personality analysis实战情况
数据集基本情况:
在这里插入图片描述

一、确定研究问题

研究问题:什么样的客户喜欢什么样的商品。旨在更精准地把握客户需求,优化营销策略

二、清洗数据

1. 准备工作

导入基本库:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

为了程序更高效地运行,需要做两个准备工作:屏蔽非错误警告和更改工作路径:

import warnings
import sys
if not sys.warnoptions:
    warnings.simplefilter("ignore")
###
import os
os.chdir(r'现在的工作路径')
os.getcwd()

2. 处理缺失值

通过df.info()查看基本信息,isnull().sum()查看缺失值

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2240 entries, 0 to 2239
Data columns (total 29 columns):
 #   Column               Non-Null Count  Dtype  
---  ------               --------------  -----  
 0   ID                   2240 non-null   int64  
 1   Year_Birth           2240 non-null   int64  
 2   Education            2240 non-null   object 
 3   Marital_Status       2240 non-null   object 
 4   Income               2216 non-null   float64
 5   Kidhome              2240 non-null   int64  
 6   Teenhome             2240 non-null   int64  

仅有24个缺失值,直接dropna()

3. 调整数据形式

(1)时点变时期

出生年→年龄
注册日期→注册天数
技巧:生成副本列,避免对数据集造成不可撤销的更改

#年龄
df['age']= 2021-df['Year_Birth']
#注册天数
df['Dt_Customer_']=pd.to_datetime(df['Dt_Customer'],dayfirst=True)
#需要提示‘天’在第一位
df['Dt_Customer_']=max(df.Dt_Customer_)-df['Dt_Customer_']
df['days']=df['Dt_Customer_'].dt.days
del df['Dt_Customer_']
(2)非数值数据编码

对教育水平和婚姻状况先了解填入内容:

print(df.Education.value_counts())
print(df.Marital_Status.value_counts())
结果:
Education
Graduation    1116
PhD            481
Master         365
2n Cycle       200
Basic           54
Name: count, dtype: int64
Marital_Status
Married     857
Together    573
Single      471
Divorced    232
Widow        76
Alone         3
Absurd        2
YOLO          2
Name: count, dtype: int64```

先合并同类项:本科以下,本科,本科以上;独居,同居;在进行编码

注意:为了体现教育水平的高低,应当采取顺序编码(OrdinalEncoder)是标签一一对应。

#教育水平
df['edu']=df['Education'].apply(lambda x: 'post' if x in ['PhD','Mastr']
                               else 'graduate'if x== 'Graduation'
                               else 'under')
from sklearn.preprocessing import OrdinalEncoder
ordinal=OrdinalEncoder(categories=[['under','graduate','post']])
df['edu']=ordinal.fit_transform(df[['edu']])
#婚姻状况
from sklearn.preprocessing import LabelEncoder
leb=LabelEncoder()
df['together']=df['Marital_Status'].apply(lambda x:'together' if x in ['Married','Together']
                                         else 'alone')
df['together']=leb.fit_transform(df['together'])
(3)数值型合并
#孩子
df['children']=df.Kidhome+df.Teenhome #不可以sum
#总消费
df['amount_sum']=df.MntFishProducts+df.MntFruits+df.MntGoldProds+df.MntMeatProducts+df.MntSweetProducts+df.MntWines

最后去掉已经被处理过的数据:

to_drop=['Year_Birth','Education','Marital_Status','Kidhome','Teenhome','Dt_Customer']
df_drop=df.drop(df[to_drop],axis=1)

4.处理异常值

.describe()初步观察后

uni=['Income','Recency','age','days','amount_sum']
sns.pairplot(df_drop[uni],diag_kind='kde')

在这里插入图片描述

df_clean=df_drop[(df_drop['age']<90) & (df_drop['Income']<300000)]
sns.pairplot(df_clean[uni],diag_kind='kde')

在这里插入图片描述
此外,还应当通过相关性查看有无十分高度相关一般来说0.9以上。
在这里插入图片描述

三、PCA主成分分析

注意:只要是能体现客户特征的变量都应当加入,不能局限于attribute,排除售后和非性格因素即可

1.归一化

#归一化std
#对方法的讨论:MMX要求标准差小,且不好控制平滑项;Robust缩小力度较小;还是std最好
#PCA也需要中心化,一举两得
from sklearn.preprocessing import StandardScaler
stdsc = StandardScaler()
std_sp=['Income', 'Recency', 'MntWines', 'MntFruits', 'MntMeatProducts',
       'MntFishProducts', 'MntSweetProducts', 'MntGoldProds',
       'NumWebPurchases', 'NumCatalogPurchases','edu','together',
       'NumStorePurchases', 'NumWebVisitsMonth', 'age', 'days',
       'children', 'amount_sum']
#删除表示售后反应,以及promotion因素
std_fit=stdsc.fit(df_clean[std_sp])
std_num=stdsc.fit_transform(df_clean[std_sp])
std_yes=pd.DataFrame(std_num,columns=std_sp)
customer=std_yes

对要不要加入总消费的思考:虽然总消费与部分消费有线性性,但有其独特之处,体现了总量特征,故不删除

2.PCA

一般来说提取2-3个,我选择分成三类,3类误差更小,我们希望尽量保留特征,在不引起维度灾难的情况下。

from sklearn.decomposition import PCA
dim_=3
pca_model=PCA(n_components=dim_)
pca_fit= pca_model.fit_transform(customer)
pca_results= pd.DataFrame(pca_fit,columns=['atr1','atr2','atr3'])

技巧:单独设置维度变量,方便后续修改测试

四、分类(KMeans vs AGNES)

1. 函数准备

#KMeans肘部法
from sklearn.cluster import KMeans
#AGENS
from sklearn.cluster import AgglomerativeClustering
#轮廓法
from sklearn.metrics import silhouette_score
#DB指数
from sklearn.metrics import davies_bouldin_score

2.代码实现

kmeans

wcss_list=[]
cishu=range(2,11)
lunkuo=[]
dbi=[]
for i in cishu:
    kms_model=KMeans(n_clusters=i,random_state=222)#默认贪心算法
    kms_fits=kms_model.fit(pca_results)
    wcss_list.append(kms_model.inertia_) #肘部
    score=silhouette_score(pca_results,kms_fits.labels_) #轮廓
    lunkuo.append(score)
    db_score=davies_bouldin_score(pca_results,kms_fits.labels_)
    dbi.append(db_score)
fig,(ax1,ax2,ax3)=plt.subplots(3,1,figsize=(10,12))
ax1.plot(cishu,wcss_list)
ax1.set(title='elbow')
ax2.plot(cishu,lunkuo)
ax2.set(title='lunkuo')
ax3.plot(cishu,dbi)
ax3.set(title='DBI')
plt.savefig('kmeans.png')

AGNES

ac_lun=[]
ac_dbi=[]
ac_cishu=range(2,10)
for i in ac_cishu:
    ac_model=AgglomerativeClustering(n_clusters=i)
    ac_fit=ac_model.fit(pca_results)
    ac_score=silhouette_score(pca_results,ac_fit.labels_)
    ac_lun.append(ac_score)
    ac_dbs=davies_bouldin_score(pca_results,ac_fit.labels_)
    ac_dbi.append(ac_dbs)
    
fig,(ax2,ax3)=plt.subplots(2,1,figsize=(10,8))
ax2.plot(ac_cishu,ac_lun)
ax2.set(title='lunkuo')
ax3.plot(ac_cishu,ac_dbi)
ax3.set(title='DBI')
plt.savefig('ac.png')

3.性能评估(选择)

在这里插入图片描述
在这里插入图片描述
两种方式大差不差,Kmeans稍忧。一个比较奇怪的点是K显示3方差和在缩小,性能却在下降。
考虑到实际情况, 我们更希望客户群能够细分,故而选择分为三类, 兼顾性能与实际,最终选择K均值法

4. 分类

cluster=3
km=KMeans(n_clusters=cluster)
pred=km.fit_predict(pca_results)
df_clean['cluster']=pred

五、画像与分析

这一部分的图表需要面向大众展示,故而使用Pyecharts,其优势在于:默认字体支持中文,数据项默认显示

from pyecharts.render import make_snapshot
from snapshot_pyppeteer import snapshot
from pyecharts import options as opts
from pyecharts.globals import ThemeType
from pyecharts.commons.utils import JsCode

1.客户结构

from pyecharts.charts import Bar
#客户分布
counts_cluster=df_clean.cluster.value_counts().sort_index()
#改变柱条颜色
color_function = """
        function (params) {
            if (params.dataIndex==0 ) {
                return 'gold';
            } else if (params.dataIndex == 1) {
                return 'dodgerblue';
            }
            return 'plum';
        }
        """
#https://matplotlib.org/stable/gallery/color/named_colors.html
bar_cluster = ( Bar(init_opts=opts.InitOpts(animation_opts=opts.AnimationOpts(animation=False),
                                        bg_color='rgb(255,255,255)')) #关闭动画
            .add_xaxis(counts_cluster.index.tolist())
            .add_yaxis('',counts_cluster.values.tolist(),bar_width='40%',
                       itemstyle_opts=opts.ItemStyleOpts(color=JsCode(color_function)))
            .set_global_opts(title_opts=opts.TitleOpts(title='客户分布',pos_left='center',
                                                       title_textstyle_opts=opts.TextStyleOpts(font_size=25)),#标题大小
                             xaxis_opts=opts.AxisOpts(splitline_opts=opts.SplitLineOpts(is_show=False)))# 关闭x轴网格线
            .set_series_opts(label_opts=opts.LabelOpts(position='top'),font_size=14))

# bar_render=bar_cluster.render('customer.html')
# make_snapshot(snapshot,bar_render,'customer.png',pixel_ratio=3,notebook=True)

在这里插入图片描述

2. 客户画像

通过pairplot大致排除特征区别不大的量

plt.figure(figsize=(15,15),dpi=150)
relation=['Income', 'Recency','age','days',
        'edu', 'together', 'children', 'amount_sum','cluster']
sns.pairplot(df_clean[relation],hue='cluster',corner=True)
plt.savefig('relation.png')

对比总体平均值和组间平均值

hua=['Income','age','children','amount_sum','days','edu']#
huaxiang= df_clean.groupby('cluster')[hua].mean().round(2)
raw=df_clean[hua].describe().round(2)

得出画像:

group0:
1108
低收入,低消费
50岁以下
基本上都有孩子,有30%为二孩家庭
受教育水平较低
group1:
571
较高收入,较高消费
最‘老’客户
基本上都有孩子,多为一孩
高教育水平
group2:
533
高收入,高消费
新客户
50岁左右
没有孩子
高教育水平

3.消费偏好

shangpin=['MntWines', 'MntFruits', 'MntMeatProducts',
          'MntFishProducts', 'MntSweetProducts', 'MntGoldProds']
jiegou_dict=jiegou.to_dict(orient ='records')
jiegou_list = [[(k, v) for k, v in d.items()] for d in jiegou_dict]
from pyecharts.charts import Pie
pie_jiegou = (Pie(init_opts=opts.InitOpts(animation_opts=opts.AnimationOpts(animation=False),
                                          bg_color='rgb(255,255,255)')) #关闭动画
              .add('group0',jiegou_list[0],center=['25%','25%'],radius=[0,'35%'])
              .add('group1',jiegou_list[1],center=['75%','25%'],radius=[0,'35%'])
              .add('group2',jiegou_list[2],center=['50%','75%'],radius=[0,"35%"])
              .set_global_opts(title_opts=opts.TitleOpts(title='细分客户消费偏好',pos_left='center',
                                                         title_textstyle_opts=opts.TextStyleOpts(font_size=25)),
                              legend_opts=opts.LegendOpts(pos_left='80%',pos_top="50%",orient='vertical'))
              .set_series_opts(label_opts={'formatter':"{d}%",'fontsize':18})                
             )

在这里插入图片描述

4.渠道及其他

qudao = ['NumWebPurchases', 'NumCatalogPurchases',
       'NumStorePurchases']
fangshi=df_clean.groupby('cluster')[qudao].sum();fangshi
fangshi_dict=fangshi.to_dict(orient ='records')
fangshi_list = [[(k, v) for k, v in d.items()] for d in fangshi_dict]
pie_fangshi = (Pie(init_opts=opts.InitOpts(animation_opts=opts.AnimationOpts(animation=False),
                                          bg_color='rgb(255,255,255)')) #关闭动画
              .add('group0',fangshi_list[0],center=['25%','25%'],radius=[0,'35%'])
              .add('group1',fangshi_list[1],center=['75%','25%'],radius=[0,'35%'])
              .add('group2',fangshi_list[2],center=['50%','75%'],radius=[0,"35%"])
              .set_global_opts(title_opts=opts.TitleOpts(title='细分客户渠道偏好',pos_left='center',
                                                         title_textstyle_opts=opts.TextStyleOpts(font_size=25)),
                              legend_opts=opts.LegendOpts(pos_left='80%',pos_top="50%",orient='vertical'))
              .set_series_opts(label_opts={'formatter':"{d}%",'fontsize':18})                
             )

在这里插入图片描述

5.其他

同理,可以用groupby+的方式讨论其他方面,由于代码结构大致相同,不一一赘述

六、结果报告

见下一集

  • 33
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值