顾客喜好分析项目(用户画像)

注意:本文引用自专业人工智能社区Venus AI

更多AI知识请参考原站 ([www.aideeplearning.cn])

项目背景

客户个性分析是对公司理想客户的详细分析,有助于企业更好地了解其客户,以满足不同类型客户的特定需求、行为和关注点。通过客户个性分析,企业可以更精确地调整其产品、服务和市场策略,提高客户满意度和业务绩效。

项目目标

本项目的主要目标是进行客户个性分析,通过聚类客户,识别不同类型的客户群体。我们的目标是回答以下问题:

  1. 人们对产品的评价:是什么决定了客户对产品的态度。
  2. 人们做了什么:揭示了人们在做什么,而不是他们对产品的评价。

项目应用

客户个性分析在市场营销和业务决策中具有广泛的应用。一些潜在的应用包括:

  • 定制市场策略:根据不同客户群体的需求和偏好,调整产品推广和定价策略。
  • 提高客户满意度:根据客户个性,提供更个性化的客户支持和服务。
  • 产品优化:根据客户反馈和行为,改进产品设计和功能。

数据集

我们使用的数据集包含了客户的各种属性,包括出生年份、教育程度、婚姻状况、家庭年收入、家庭成员数量、投诉历史、购买历史等。这些属性将帮助我们了解客户的特点和行为。

属性

  • ID:客户的唯一标识符
  • Year_Birth:客户的出生年份
  • Education:客户的教育程度
  • Marital_Status:客户的婚姻状况
  • Income:客户的家庭年收入
  • Kidhome:客户家庭中的儿童数量
  • Teenhome:客户家庭中青少年的数量
  • Dt_Customer:客户在公司注册的日期
  • Recency:自客户上次购买以来的天数
  • Complain:如果客户在过去 2 年内投诉过,则为 1,否则为 0 产品

  • MntWines:过去 2 年在葡萄酒上的花费金额
  • MntFruits:过去 2 年在水果上花费的金额
  • MntMeatProducts:过去 2 年在肉类上的花费金额
  • MntFishProducts:过去 2 年在鱼类上花费的金额
  • MntSweetProducts:过去 2 年在糖果上花费的金额
  • MntGoldProds:过去 2 年促销中花费在黄金上的金额

  • NumDealsPurchases:折扣购买数量
  • AcceptedCmp1:如果客户在第一个活动中接受了优惠,则为 1,否则为 0
  • AcceptedCmp2:如果客户在第二次活动中接受了优惠,则为 1,否则为 0
  • AcceptedCmp3:如果客户在第三次活动中接受了报价,则为 1,否则为 0
  • AcceptedCmp4:如果客户在第四次活动​​中接受了报价,则为 1,否则为 0
  • AcceptedCmp5:如果客户在第五次活动中接受了报价,则为 1,否则为 0
  • Response:如果客户在上次活动中接受了优惠,则为 1,否则为 0 放置

  • NumWebPurchases:通过公司网站进行的购买数量
  • NumCatalogPurchases:使用目录进行的购买数量
  • NumStorePurchases:直接在商店购买的数量
  • NumWebVisitsMonth:上个月公司网站的访问次数

模型方法

项目的主要方法包括以下步骤:

  1. 数据收集和分析:导入数据集,分析数据的行数、列数和缺失值,并对二进制属性列进行可视化。
  2. 数据预处理:填充缺失值、合并相似属性列、删除无用的列。
  3. 探索性数据分析:使用Matplotlib、Seaborn和Plotly进行数据可视化,帮助我们更好地了解数据分布和关联。
  4. 客户聚类:使用K-Means聚类算法将客户分为不同的群体,找到合适的聚类数量。
  5. 可视化和报告:将不同客户群体与其他属性进行可视化,并撰写报告以总结客户个性类型。

结果可视化

图片[1]-顾客喜好分析项目(用户画像)-VenusAI

图片[2]-顾客喜好分析项目(用户画像)-VenusAI

代码实现

import pandas as pd
import pandas as pd
import seaborn as sns
import plotly.express as px
import plotly.graph_objects as plt
import matplotlib.pyplot as plt
%matplotlib inline
import warnings
warnings.filterwarnings('ignore')
# 加载数据
main_df = pd.read_csv('marketing_campaign.csv', sep='\t')
df = main_df.copy()
df.head(10)
IDYear_BirthEducationMarital_StatusIncomeKidhomeTeenhomeDt_CustomerRecencyMntWines...NumWebVisitsMonthAcceptedCmp3AcceptedCmp4AcceptedCmp5AcceptedCmp1AcceptedCmp2ComplainZ_CostContactZ_RevenueResponse
055241957GraduationSingle58138.00004-09-201258635...70000003111
121741954GraduationSingle46344.01108-03-20143811...50000003110
241411965GraduationTogether71613.00021-08-201326426...40000003110
361821984GraduationTogether26646.01010-02-20142611...60000003110
453241981PhDMarried58293.01019-01-201494173...50000003110
574461967MasterTogether62513.00109-09-201316520...60000003110
69651971GraduationDivorced55635.00113-11-201234235...60000003110
761771985PhDMarried33454.01008-05-20133276...80000003110
848551974PhDTogether30351.01006-06-20131914...90000003111
958991950PhDTogether5648.01113-03-20146828...201000003110

10 rows × 29 columns

数据分析

#shape of the dataset
df.shape
(2240, 29)
# basic information of dataset
df.info()
<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  
 7   Dt_Customer          2240 non-null   object 
 8   Recency              2240 non-null   int64  
 9   MntWines             2240 non-null   int64  
 10  MntFruits            2240 non-null   int64  
 11  MntMeatProducts      2240 non-null   int64  
 12  MntFishProducts      2240 non-null   int64  
 13  MntSweetProducts     2240 non-null   int64  
 14  MntGoldProds         2240 non-null   int64  
 15  NumDealsPurchases    2240 non-null   int64  
 16  NumWebPurchases      2240 non-null   int64  
 17  NumCatalogPurchases  2240 non-null   int64  
 18  NumStorePurchases    2240 non-null   int64  
 19  NumWebVisitsMonth    2240 non-null   int64  
 20  AcceptedCmp3         2240 non-null   int64  
 21  AcceptedCmp4         2240 non-null   int64  
 22  AcceptedCmp5         2240 non-null   int64  
 23  AcceptedCmp1         2240 non-null   int64  
 24  AcceptedCmp2         2240 non-null   int64  
 25  Complain             2240 non-null   int64  
 26  Z_CostContact        2240 non-null   int64  
 27  Z_Revenue            2240 non-null   int64  
 28  Response             2240 non-null   int64  
dtypes: float64(1), int64(25), object(3)
memory usage: 507.6+ KB
  • Here we have only 3 object type datatype and rest are numerical
# 检查特征唯一值数量
df.nunique()
ID                     2240
Year_Birth               59
Education                 5
Marital_Status            8
Income                 1974
Kidhome                   3
Teenhome                  3
Dt_Customer             663
Recency                 100
MntWines                776
MntFruits               158
MntMeatProducts         558
MntFishProducts         182
MntSweetProducts        177
MntGoldProds            213
NumDealsPurchases        15
NumWebPurchases          15
NumCatalogPurchases      14
NumStorePurchases        14
NumWebVisitsMonth        16
AcceptedCmp3              2
AcceptedCmp4              2
AcceptedCmp5              2
AcceptedCmp1              2
AcceptedCmp2              2
Complain                  2
Z_CostContact             1
Z_Revenue                 1
Response                  2
dtype: int64
  • 在上面的单元格中,“Z_CostContact”和“Z_Revenue”在所有行中都有相同的值,这就是为什么它们不会在模型构建中做出任何贡献。 这样我们就可以放弃它们
# 检查空值
df.isna().any()
ID                     False
Year_Birth             False
Education              False
Marital_Status         False
Income                  True
Kidhome                False
Teenhome               False
Dt_Customer            False
Recency                False
MntWines               False
MntFruits              False
MntMeatProducts        False
MntFishProducts        False
MntSweetProducts       False
MntGoldProds           False
NumDealsPurchases      False
NumWebPurchases        False
NumCatalogPurchases    False
NumStorePurchases      False
NumWebVisitsMonth      False
AcceptedCmp3           False
AcceptedCmp4           False
AcceptedCmp5           False
AcceptedCmp1           False
AcceptedCmp2           False
Complain               False
Z_CostContact          False
Z_Revenue              False
Response               False
dtype: bool
# Checking number of null values
df.isnull().sum()
ID                      0
Year_Birth              0
Education               0
Marital_Status          0
Income                 24
Kidhome                 0
Teenhome                0
Dt_Customer             0
Recency                 0
MntWines                0
MntFruits               0
MntMeatProducts         0
MntFishProducts         0
MntSweetProducts        0
MntGoldProds            0
NumDealsPurchases       0
NumWebPurchases         0
NumCatalogPurchases     0
NumStorePurchases       0
NumWebVisitsMonth       0
AcceptedCmp3            0
AcceptedCmp4            0
AcceptedCmp5            0
AcceptedCmp1            0
AcceptedCmp2            0
Complain                0
Z_CostContact           0
Z_Revenue               0
Response                0
dtype: int64
  • 收入列中有一些缺失值,因此我们需要用maean或中位数来填充它。
# Checking number of null values
df.isnull().sum()
<Axes: >

# 删除该列,因为它们不会对模型构建做出贡献
df = df.drop(columns=["Z_CostContact", "Z_Revenue"], axis=1)
df.head(10)
IDYear_BirthEducationMarital_StatusIncomeKidhomeTeenhomeDt_CustomerRecencyMntWines...NumCatalogPurchasesNumStorePurchasesNumWebVisitsMonthAcceptedCmp3AcceptedCmp4AcceptedCmp5AcceptedCmp1AcceptedCmp2ComplainResponse
055241957GraduationSingle58138.00004-09-201258635...10470000001
121741954GraduationSingle46344.01108-03-20143811...1250000000
241411965GraduationTogether71613.00021-08-201326426...21040000000
361821984GraduationTogether26646.01010-02-20142611...0460000000
453241981PhDMarried58293.01019-01-201494173...3650000000
574461967MasterTogether62513.00109-09-201316520...41060000000
69651971GraduationDivorced55635.00113-11-201234235...3760000000
761771985PhDMarried33454.01008-05-20133276...0480000000
848551974PhDTogether30351.01006-06-20131914...0290000001
958991950PhDTogether5648.01113-03-20146828...00201000000

10 rows × 27 columns

  • 让我们计算出过去两年客户投诉的数量以及正面或负面的回复数量。
# Complain: 1 if customer complained in the last 2 years, 0 otherwise
label_complain = ["No Complain","Complain"]

count_complain = pd.value_counts(df['Complain'], sort=True)
count_complain.plot(kind='bar', rot=0,color=['Green','Red'])
plt.title("Complain class Distribution")
plt.xticks(range(2),label_complain)
plt.xlabel("Complain")
plt.ylabel("Count of Complain")
Text(0, 0.5, 'Count of Complain')

df['Complain'].value_counts()
# 1 if customer complained in the last 2 years, 0 otherwise
0    2219
1      21
Name: Complain, dtype: int64
  • 从上图可以看出,顾客的投诉并不多。
# 让我们检查一下响应

# 响应:如果客户在最近 2 次活动中接受了优惠,则为 1,否则为 0
label_response = ["Denied","Accepted"]

count_response = pd.value_counts(df['Response'], sort=True)
count_response.plot(kind='bar', rot=0,color=['Green','Red'])
plt.title("Reponse class Distribution")
plt.xticks(range(2),label_response)
plt.xlabel("Respons")
plt.ylabel("Count of Response")
Text(0, 0.5, 'Count of Response')

df['Response'].value_counts()
0    1906
1     334
Name: Response, dtype: int64
  • 此图显示最后的优惠已被大多数客户拒绝

查看所有情况

  • AcceptedCmp1:如果客户在第一个活动中接受了优惠,则为 1,否则为 0
  • AcceptedCmp2:如果客户在第二次活动中接受了优惠,则为 1,否则为 0
  • AcceptedCmp3:如果客户在第三次活动中接受了优惠,则为 1,否则为 0
  • AcceptedCmp4:如果客户在第四次活动中接受了优惠,则为 1,否则为 0
  • AcceptedCmp5:如果客户在第五次活动中接受了优惠,则为 1,否则为 0
  • 响应:如果客户在上次活动中接受了优惠,则为 1,否则为 0
#Campagin 1

labels_c1 = ["Denied", "Accepted"]

count_c1 = pd.value_counts(df['AcceptedCmp1'], sort=True)
count_c1.plot(kind='bar', rot=0,color=['Green','Red'])
plt.title("AcceptedCmp1 class Distribution")
plt.xticks(range(2),labels_c1)
plt.xlabel("Campaign 1")
plt.ylabel("Count of Campagin1")
Text(0, 0.5, 'Count of Campagin1')

df['AcceptedCmp1'].value_counts()
0    2096
1     144
Name: AcceptedCmp1, dtype: int64
#Campagin 2

labels_c2 = ["Denied", "Accepted"]

count_c2 = pd.value_counts(df['AcceptedCmp2'], sort=True)
count_c2.plot(kind='bar', rot=0,color=['Green','Red'])
plt.title("AcceptedCmp2 class Distribution")
plt.xticks(range(2),labels_c2)
plt.xlabel("Campaign 2")
plt.ylabel("Count of Campagin2")
Text(0, 0.5, 'Count of Campagin2')

df["AcceptedCmp2"].value_counts()
0    2210
1      30
Name: AcceptedCmp2, dtype: int64
#Campagin 3

labels_c3 = ["Denied", "Accepted"]

count_c3 = pd.value_counts(df['AcceptedCmp3'], sort=True)
count_c3.plot(kind='bar', rot=0,color=['Green','Red'])
plt.title("AcceptedCmp3 class Distribution")
plt.xticks(range(2),labels_c3)
plt.xlabel("Campaign 3")
plt.ylabel("Count of Campagin3")
Text(0, 0.5, 'Count of Campagin3')

df["AcceptedCmp3"].value_counts()
0    2077
1     163
Name: AcceptedCmp3, dtype: int64
#Campagin 4

labels_c4 = ["Denied", "Accepted"]

count_c4 = pd.value_counts(df['AcceptedCmp4'], sort=True)
count_c4.plot(kind='bar', rot=0,color=['Green','Red'])
plt.title("AcceptedCmp4 class Distribution")
plt.xticks(range(2),labels_c4)
plt.xlabel("Campaign 4")
plt.ylabel("Count of Campagin4")
Text(0, 0.5, 'Count of Campagin4')

df["AcceptedCmp4"].value_counts()
0    2073
1     167
Name: AcceptedCmp4, dtype: int64

活动接受度比较。

  • 从上面的数据我们可以清楚地看到,所有活动中的大部分优惠都被顾客拒绝了。
  • 但活动 4 的接受度更高。
  • 活动 4 > 活动 3 > 活动 1 > 活动 2
#Finding the correlation between the feature column

plt.figure(figsize=(20,18))
sns.heatmap(df.corr(), annot=True)
plt.show()

  • 没有两列彼此有太多相关性,因此我们不能删除任何列

数据预处理

# Filling the missing value in the income by mean
df['Income'] = df['Income'].fillna(df['Income'].mean())
df.isnull().sum()
ID                     0
Year_Birth             0
Education              0
Marital_Status         0
Income                 0
Kidhome                0
Teenhome               0
Dt_Customer            0
Recency                0
MntWines               0
MntFruits              0
MntMeatProducts        0
MntFishProducts        0
MntSweetProducts       0
MntGoldProds           0
NumDealsPurchases      0
NumWebPurchases        0
NumCatalogPurchases    0
NumStorePurchases      0
NumWebVisitsMonth      0
AcceptedCmp3           0
AcceptedCmp4           0
AcceptedCmp5           0
AcceptedCmp1           0
AcceptedCmp2           0
Complain               0
Response               0
dtype: int64
  • 数据集中没有空值
df.head()
IDYear_BirthEducationMarital_StatusIncomeKidhomeTeenhomeDt_CustomerRecencyMntWines...NumCatalogPurchasesNumStorePurchasesNumWebVisitsMonthAcceptedCmp3AcceptedCmp4AcceptedCmp5AcceptedCmp1AcceptedCmp2ComplainResponse
055241957GraduationSingle58138.00004-09-201258635...10470000001
121741954GraduationSingle46344.01108-03-20143811...1250000000
241411965GraduationTogether71613.00021-08-201326426...21040000000
361821984GraduationTogether26646.01010-02-20142611...0460000000
453241981PhDMarried58293.01019-01-201494173...3650000000

5 rows × 27 columns

# 检查“Marital_Status”中存在的唯一类别的数量

df['Marital_Status'].value_counts()
Married     864
Together    580
Single      480
Divorced    232
Widow        77
Alone         3
Absurd        2
YOLO          2
Name: Marital_Status, dtype: int64
df['Marital_Status'] = df['Marital_Status'].replace(['Married', 'Together'],'relationship')
df['Marital_Status'] = df['Marital_Status'].replace(['Divorced', 'Widow', 'Alone', 'YOLO', 'Absurd','single'],'Single')
  • 在上面的单元格中,我们将“已婚”、“在一起”分组为“关系”
  • 而“离婚”、“寡妇”、“孤独”、“YOLO”、“荒谬”则为“单身”
df['Marital_Status'].value_counts()
relationship    1444
Single           796
Name: Marital_Status, dtype: int64
# Relationship vs Single

labels_status = ["Relationship", "Single"]

count_status = pd.value_counts(df['Marital_Status'], sort=True)
count_c4.plot(kind='bar', rot=0,color=['Orange','Blue'])
plt.title("Marital Status")
plt.xticks(range(2),labels_status)
plt.xlabel("Marital Status")
plt.ylabel("Count of Marital Status")
Text(0, 0.5, 'Count of Marital Status')

将不同的数据帧组合成一列以减少维数

df['Kids'] = df['Kidhome'] + df['Teenhome']
df['Expenses'] = df['MntWines'] + df['MntFruits'] + df['MntMeatProducts'] + df['MntFishProducts'] + df['MntSweetProducts'] + df['MntGoldProds']
df['TotalAcceptedCmp'] = df['AcceptedCmp1'] + df['AcceptedCmp2'] + df['AcceptedCmp3'] + df['AcceptedCmp4'] + df['AcceptedCmp5'] + df['Response']
df['NumTotalPurchases'] = df['NumWebPurchases'] + df['NumCatalogPurchases'] + df['NumStorePurchases'] + df['NumDealsPurchases']
#保存表格数据
df.to_csv('data_visuals.csv')
# 删除一些列以减少模型的维度和复杂性
col_del = ["AcceptedCmp1" , "AcceptedCmp2", "AcceptedCmp3" , "AcceptedCmp4","AcceptedCmp5", "Response","NumWebVisitsMonth", "NumWebPurchases","NumCatalogPurchases","NumStorePurchases","NumDealsPurchases" , "Kidhome", "Teenhome","MntWines", "MntFruits", "MntMeatProducts", "MntFishProducts", "MntSweetProducts", "MntGoldProds"]
df=df.drop(columns=col_del,axis=1)
df.head()
IDYear_BirthEducationMarital_StatusIncomeDt_CustomerRecencyComplainKidsExpensesTotalAcceptedCmpNumTotalPurchases
055241957GraduationSingle58138.004-09-201258001617125
121741954GraduationSingle46344.008-03-201438022706
241411965Graduationrelationship71613.021-08-20132600776021
361821984Graduationrelationship26646.010-02-201426015308
453241981PhDrelationship58293.019-01-20149401422019
# Adding 'Age' column

df['Age'] = 2015 - df['Year_Birth']
df['Education'].value_counts()
Graduation    1127
PhD            486
Master         370
2n Cycle       203
Basic           54
Name: Education, dtype: int64
# 仅将类别更改为 UG 和 PG

df['Education'] = df['Education'].replace(['PhD','2n Cycle','Graduation', 'Master'],'PG')  
df['Education'] = df['Education'].replace(['Basic'], 'UG')
# 客户与公司互动的天数

# 将 bt_customer 更改为时间戳格式
df['Dt_Customer'] = pd.to_datetime(df.Dt_Customer)
df['first_day'] = '01-01-2015'
df['first_day'] = pd.to_datetime(df.first_day)
df['day_engaged'] = (df['first_day'] - df['Dt_Customer']).dt.days
df=df.drop(columns=["ID", "Dt_Customer", "first_day", "Year_Birth", "Dt_Customer", "Recency", "Complain"],axis=1)
df.shape
(2240, 9)
df.head()
EducationMarital_StatusIncomeKidsExpensesTotalAcceptedCmpNumTotalPurchasesAgeday_engaged
0PGSingle58138.00161712558997
1PGSingle46344.02270661151
2PGrelationship71613.0077602150498
3PGrelationship26646.0153083191
4PGrelationship58293.0142201934347

数据可视化

fig = px.bar(df, x='Marital_Status', y='Expenses', color="Marital_Status")
fig.show()
# Less number of single customer
fig = px.histogram (df, x = "Expenses",  facet_row = "Marital_Status",  template = 'plotly_dark')
fig.show ()
fig = px.histogram (df, x = "Expenses",  facet_row = "Education",  template = 'plotly_dark')
fig.show ()
fig = px.histogram (df, x = "NumTotalPurchases",  facet_row = "Education",  template = 'plotly_dark')
fig.show ()
fig = px.histogram (df, x = "Age",  facet_row = "Marital_Status",  template = 'plotly_dark')
fig.show ()
fig = px.histogram (df, x = "Income",  facet_row = "Marital_Status",  template = 'plotly_dark')
fig.show ()
fig =  px.pie (df, names = "Marital_Status", hole = 0.4, template = "gridon")
fig.show ()
  • 35% 的客户是单身,而 64% 的客户是恋爱中的客户。
fig =  px.pie (df, names = "Education", hole = 0.4, template = "plotly_dark")
fig.show ()
  • 超过97%的客户来自PG背景。 和大约。 2%来自UG。
sns.barplot(x=df['Expenses'], y=df['Education'])
plt.title('Total expense based on the education level')
Text(0.5, 1.0, 'Total expense based on the education level')

sns.barplot(x=df['Income'], y=df['Education'])
plt.title('Total Income based on the Education Level')
Text(0.5, 1.0, 'Total Income based on the Education Level')

df.describe()
IncomeKidsExpensesTotalAcceptedCmpNumTotalPurchasesAgeday_engaged
count2240.0000002240.0000002240.0000002240.0000002240.0000002240.0000002240.000000
mean52247.2513540.950446605.7982140.44687514.86205446.194196538.043304
std25037.7971680.751803602.2492880.8905437.67717311.984069232.229893
min1730.0000000.0000005.0000000.0000000.00000019.00000026.000000
25%35538.7500000.00000068.7500000.0000008.00000038.000000366.750000
50%51741.5000001.000000396.0000000.00000015.00000045.000000539.000000
75%68289.7500001.0000001045.5000001.00000021.00000056.000000711.250000
max666666.0000003.0000002525.0000005.00000044.000000122.0000001089.000000
sns.heatmap(df.corr(),annot=True)
<Axes: >

obj = []
for i in df.columns:
    if(df[i].dtypes=="object"):
        obj.append(i)

print(obj)
['Education', 'Marital_Status']
# Label Encoding
from sklearn.preprocessing import LabelEncoder
df['Marital_Status'].value_counts()
relationship    1444
Single           796
Name: Marital_Status, dtype: int64
lbl_encode = LabelEncoder()
for i in obj:
    df[i] = df[[i]].apply(lbl_encode.fit_transform)
df1 = df.copy()
df1.head()
EducationMarital_StatusIncomeKidsExpensesTotalAcceptedCmpNumTotalPurchasesAgeday_engaged
00058138.00161712558997
10046344.02270661151
20171613.0077602150498
30126646.0153083191
40158293.0142201934347

数据标准化

from sklearn.preprocessing import StandardScaler
scaled_features = StandardScaler().fit_transform(df1.values)
scaled_features_df = pd.DataFrame(scaled_features, index=df1.index, columns=df1.columns)
scaled_features_df.head()
EducationMarital_StatusIncomeKidsExpensesTotalAcceptedCmpNumTotalPurchasesAgeday_engaged
0-0.157171-1.3468740.235327-1.2645051.6794170.6212481.3208260.9853451.976745
1-0.157171-1.346874-0.2358261.396361-0.961275-0.501912-1.1545961.235733-1.667011
2-0.1571710.7424600.773633-1.2645050.282673-0.5019120.7996850.317643-0.172468
3-0.1571710.742460-1.0227320.065928-0.918094-0.501912-0.894025-1.268149-1.925433
4-0.1571710.7424600.2415190.065928-0.305254-0.5019120.539114-1.017761-0.822831

肘部法则确定聚类数量

from sklearn.cluster import KMeans
wcss = []
for i in range(1,11):
    kmeans = KMeans(n_clusters=i, init='k-means++', random_state=42)
    kmeans.fit(scaled_features_df)
    wcss.append(kmeans.inertia_)
    # inetia_: Sum of squared distances of samples to their closest cluster center, weighted by the sample weights if provided.
plt.figure(figsize=(16,8))
plt.plot(range(1,11), wcss, 'bx-')
plt.title('The Elbow Maethod')
plt.xlabel('Number of Clusters')
plt.ylabel('WCSS')
plt.show()

拐点不清晰,肘法中并不太清楚应该选择哪个K值

剪影系数

from sklearn.metrics import silhouette_score
silhouette_scores = []
for i in range(2,10):
    m1 = KMeans(n_clusters=i, random_state=42)
    c = m1.fit_predict(scaled_features_df)
    silhouette_scores.append(silhouette_score(scaled_features_df, m1.fit_predict(scaled_features_df)))
    
plt.bar(range(2,10), silhouette_scores)
plt.xlabel('Number of clusters', fontsize=20)
plt.ylabel('S(i)', fontsize=20)
plt.show()

# 现在我们用Silhouette Score来衡量K的值
silhouette_scores
[0.24145101432627075,
 0.2630066765900862,
 0.22547869857815794,
 0.2112495373878677,
 0.2149228429852001,
 0.1997135405176978,
 0.19301680336746188,
 0.19495794809915995]
# 获取轮廓分数的最大值并在索引中添加2 因为索引从2开始

sc = max(silhouette_scores)
num_of_clusters = silhouette_scores.index(sc)+2
print("Number of Cluster Required is: ", num_of_clusters)
Number of Cluster Required is:  3

建立模型

# 使用 K 均值算法训练预测。

kmeans = KMeans(n_clusters = num_of_clusters, random_state=42).fit(scaled_features_df)
pred = kmeans.predict(scaled_features_df)
pred
array([1, 0, 1, ..., 1, 1, 0])
# 添加这些聚类值到 main dataframe (without standardization)中
df['cluster'] = pred + 1
df.head()
EducationMarital_StatusIncomeKidsExpensesTotalAcceptedCmpNumTotalPurchasesAgeday_engagedcluster
00058138.001617125589972
10046344.022706611511
20171613.00776021504982
30126646.01530831911
40158293.01422019343471
# 保存数据
df.to_csv('data_visuals2.csv')
pl = sns.countplot(x=df["cluster"])
pl.set_title("Distribution Of The Clusters")
plt.show()

正如我们在这里看到的,与其他聚类相比,聚类 1 中的权重更大。

sns.set(rc={'axes.facecolor':'black', 'figure.facecolor':'black', 'axes.grid' : False}) 
for i in df:
    diag = sns.FacetGrid(df, col="cluster", hue="cluster", palette="Set1")
    diag.map(plt.hist, i, bins=6, ec="k") 
    diag.set_xticklabels(rotation=25, color='white')
    diag.set_yticklabels(color='white')
    diag.set_xlabels(size=16, color='white')
    diag.set_titles(size=16, color='#f01132', fontweight="bold")

报告

根据以上信息,我们可以将客户分为三部分:-

  1. 高度活跃客户:这些客户属于集群一。
  2. 中等活跃客户:- 这些客户属于集群二。
  3. 最不活跃的客户 :- 这些客户属于第三集群。

高度活跃客户的特征

  • 在教育方面

    • 高活跃客户来自PG背景
  • 就婚姻状况而言

    • 恋爱关系中的人数约为。 单身人士的两倍
  • 就收入而言

    • 高度活跃客户的收入略低于中等活跃客户。
  • 就孩子而言

    • 与其他顾客相比,高度活跃的顾客拥有更多的孩子(平均 1 个孩子)。
  • 就费用而言

    • 高度活跃客户的费用低于中等客户。
    • 这些客户的平均花费。 约。 100-200单位钱。
  • 就年龄而言

    • 这些顾客的年龄在25岁至75岁之间。
    • 顾客年龄上限为 40 至 50 岁。
  • 就参与天数而言

    • 高度活跃的客户由于与公司接触的时间较长而更加忠诚。

中等活跃客户的特征

  • 在教育方面

    • 中等活跃客户也来自PG背景
  • 就婚姻状况而言

    • 与单身人士相比,恋爱中的人数略多。
  • 就收入而言

    • 中等活跃客户的收入高于其他客户。
  • 就孩子而言

    • 与高度活跃的客户相比,中等活跃的客户的子女数量较少(最多客户没有子女)。
  • 就费用而言

    • 与活跃客户相比,中等活跃客户的费用更高。
    • 这些客户的平均花费。 约。 500-2000单位钱。
  • 就年龄而言

    • 这些顾客的年龄在25岁至75岁之间。
    • 顾客年龄上限为 35 至 60 岁。
  • 就参与天数而言

    • 与高度活跃的客户相比,中等活跃的客户与公司的互动程度略低

 代码与数据集下载

详情请见顾客喜好分析项目(用户画像)-VenusAI (aideeplearning.cn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值