项目描述
你拥有一个超市(Supermarket Mall)。通过会员卡,你用有一些关于你的客户的基本数据,如客户ID,年龄,性别,年收入和消费分数。消费分数是根据客户行为和购买数据等定义的参数分配给客户的。
问题陈述:你拥有这个商场。想要了解怎么样的消费者可以很容易地聚集在一起(目标顾客),以便可以给营销团队以灵感并相应地计划策略。
数据集说明:
这是一个商场,通过会员卡,收集的顾客数据。
• CustomerID:顾客编号
• Gender:性别
• Age:年龄
• Annual Income (k$):年收入 单位:千(美元)
• Spending Score (1-100):商场根据顾客消费行为评的综合分数(分值1至00)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from pandas import plotting
import seaborn as sns
import plotly.graph_objs as go
import plotly.offline as py
1、读取数据
数据量很少
data = pd.read_csv('./Mall_Customers.csv')
data1 = data.copy()
data
2、计算顾客年龄平均数、中位数、众数
data['Age'].mean() # 平均年龄
# 38.85
data['Age'].median() # 中位数
# 36.0
data['Age'].mode() # 众数
# 32
3、计算顾客男女比例(饼图)
genders = data.groupby('Gender').count()
genders
female_num = genders.loc['Female','CustomerID']
male_num = genders.loc['Male','CustomerID']# 设置字体为SimHei显示中文
plt.rcParams['font.sans-serif'] = 'SimHei'
# 设置正常显示字符
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(9,5),facecolor='white',dpi=200)# 画图
labels = ['Male','Female']
sizes = [male_num,female_num]
explode = (0,0)
plt.pie(sizes,explode=explode,labels=labels,shadow=False,startangle=90,autopct='%1.1f%%')
plt.title("顾客男女比例")
plt.show()
女性以56%的份额居于领先地位,而男性则占整体的44%。特别是当男性人口相对高于女性时,这是一个比较大的差距。
4、计算男性顾客和女性顾客的平均评分(柱状图)
d = data.groupby('Gender').agg({'Spending Score (1-100)':'mean'})
d
female_mts = d.loc['Female'].values
female_mts = float(female_mts)male_mts = d.loc['Male'].values
male_mts = float(male_mts)# 柱状图x = ['Male','Female']
y = [male_mts,female_mts]
plt.bar(x = x , height = y, color='steelblue')
plt.title("男女平均评分对比")
plt.xlabel("性别")
plt.ylabel("分数")
plt.show()
5、将客户年龄分为少年【0-20】,青年【20-40】,中年【40-60】,老年【60-80】,4个年龄段,计算四个年龄段的顾客平均收入(柱状图)
# 针对年龄进行人群分类的函数
def classification(x):if x >= 0 and x < 20:return "少年"elif x >= 20 and x < 40:return "青年"elif x >= 40 and x < 60:return "中年"elif x >= 60 and x < 80:return "老年"else:return "年龄超限"
data['年龄分类'] = data['Age'].apply(classification)# 根据年龄分类计算平均收入
mtai = data.groupby("年龄分类").mean()
kid_mtai = mtai['Annual Income (k$)']['少年'].round(decimals=2) # 55.08
young_mtai = mtai['Annual Income (k$)']['青年'].round(decimals=2) # 62.6
middle_mtai = mtai['Annual Income (k$)']['中年'].round(decimals=2) # 61.89
old_mtai = mtai['Annual Income (k$)']['老年'].round(decimals=2) # 49.0
kid_mtai
# 柱状图
x = ['少年','青年','中年','老年']
y = [kid_mtai,young_mtai,middle_mtai,old_mtai]
plt.bar(x=x, height=y, color='steelblue', alpha=1)
for x1, y1 in zip(x, y):plt.text(x1, y1-y1/2, str(y1), ha='center', va='top', fontsize=20)
plt.title("各年龄段平均收入对比")
plt.xlabel("年龄段")
plt.ylabel("平均收入")
plt.show()
6、平行坐标图
平行坐标图(Parallel coordinates plot)用于多元数据的可视化,将高维数据的各个属性(变量)用一系列相互平行的坐标轴表示, 纵向是属性值,横向是属性类别。
若在某个属性上相同颜色折线较为集中,不同颜色有一定的间距,则说明该属性对于预标签类别判定有较大的帮助。
若某个属性上线条混乱,颜色混杂,则可能该属性对于标签类别判定没有价值。
# 修改列名
data1.rename(columns={'Annual Income (k$)': 'Annual Income', 'Spending Score (1-100)': 'Spending Score'}, inplace=True)plotting.parallel_coordinates(data1.drop('CustomerID', axis=1), 'Gender')
plt.title('平行坐标图', fontsize=12)
plt.grid(linestyle='-.')
plt.show()
7、年龄/年收入/消费分数的分布
这里用了直方图和核密度图。(注:核密度图看的是(x<X)的面积,而不是高度)
# 绘图
plt.figure(1, figsize=(13, 6))
n = 0
for x in ['Age', 'Annual Income', 'Spending Score']:n += 1plt.subplot(1, 3, n)plt.subplots_adjust(hspace=0.5, wspace=0.5)sns.distplot(data1[x], bins=16, kde=True) # kde 密度曲线plt.title('{}分布情况'.format(x))plt.tight_layout()
plt.show()
年龄方面:[30,36]范围的客户是最多的另外,在[20,21]也不少,但是60岁以上的老年人是最不常来消费的。
年收入方面:大部分的客户集中在[53,83]范围里,在15以下和105以上的很少。
消费分数方面:消费分数在[40,55]的占了大多数,在[70,80]范围的次之。
8、年龄/年收入/消费分数的柱状图
plt.figure(1, figsize=(13, 6))
k = 0
for x in ['Age', 'Annual Income', 'Spending Score']:k += 1plt.subplot(3, 1, k)plt.subplots_adjust(hspace=0.5, wspace=0.5)sns.countplot(data1[x], palette='rainbow', alpha=0.8)plt.title('{}分布情况'.format(x))plt.tight_layout()
plt.show()
年龄方面:[27,40]范围的客户居多。其中,32岁的客户是商城的常客,55,、56、64、69岁的用户却很少。总的来说,年龄较大的人群较少,年龄较少的人群较多。
年收入方面:年收入在54和78的频数是最多的。其他在各个收入的客户频数看起来相差不太大。
消费分数方面:消费分数在42的客户数是最多的,56次之。有的客户的分数甚至达到了99,而分数为1的客户也存在,没有分数为0的客户。
9、两两特征之间的关系
pairplot主要展现的是属性(变量)两两之间的关系(线性或非线性,有无较为明显的相关关系)。
对男、女性的数据点进行了区分,但是感觉数据在性别上的差异不大
# df_a_a_s = df.drop(['CustomerID'], axis=1)
sns.pairplot(data1, vars=['Age', 'Annual Income', 'Spending Score'], hue='Gender', aspect=1.5, kind='reg')
plt.show()
10、两两特征之间的分布¶
增强箱图,可以通过绘制更多的分位数来提供数据分布的信息,适用于大数据。
# 根据分类变量分组绘制一个纵向的增强箱型图
plt.rcParams['axes.unicode_minus'] = False # 解决无法显示符号的问题
sns.set(font='SimHei', font_scale=0.8) # 解决Seaborn中文显示问题
sns.boxenplot(data1['Gender'], data1['Spending Score'], palette='Blues')
# x:设置分组统计字段,y:数据分布统计字段
sns.swarmplot(x=data1['Gender'], y=data1['Spending Score'], data=data1, palette='dark', alpha=0.5, size=6)
plt.title('男女性的消费能力比较', fontsize=12)
plt.show()
男性的消费得分集中在[25,70],而女性的消费得分集中在[35,75],一定程度上说明了女性在购物方面表现得比男性好。
plt.rcParams['axes.unicode_minus'] = False # 解决无法显示符号的问题
sns.set(font='SimHei', font_scale=0.8) # 解决Seaborn中文显示问题
m = 0
for feature in ['Age', 'Annual Income', 'Spending Score']:m += 1plt.subplot(1, 3, m)plt.subplots_adjust(hspace=0.3, wspace=0.3)sns.violinplot(x=feature, y='Gender', data=data1, palette='Blues')sns.swarmplot(x=feature, y='Gender', data=data1, palette='dark', alpha=0.5, size=4)plt.ylabel('性别' if m == 1 else '')
plt.show()
年龄方面:男性分布较为均匀,20多岁的比较多;女性的年龄大部分集中在20+~30+这个范围,整体上较为年轻?
收入方面:男性略胜一筹
11、K-means聚类分析
相关参考资料: https://www.jianshu.com/p/335b376174d4
基于年龄和消费分数的聚类
from sklearn.cluster import KMeansdf_a_sc = df[['Age', 'Spending Score']].values
# 存放每次聚类结果的误差平方和
inertia1 = []
# 使用手肘法确定最合适的k kk值for n in range(1, 11):# 构造聚类器km1 = (KMeans(n_clusters=n, # 要分成的簇数,int类型,默认值为8init='k-means++', # 初始化质心,k-means++是一种生成初始质心的算法n_init=10, # 设置选择质心种子次数,默认为10次。返回质心最好的一次结果(好是指计算时长短)max_iter=300, # 每次迭代的最大次数tol=0.0001, # 容忍的最小误差,当误差小于tol就会退出迭代random_state=111, # 随机生成器的种子 ,和初始化中心有关algorithm='elkan')) # 'full'是传统的K-Means算法,'elkan'是采用elkan K-Means算法# 用训练数据拟合聚类器模型km1.fit(df_a_sc)# 获取聚类标签inertia1.append(km1.inertia_)
# 绘图确定k kk值,这里将k kk确定为4。
plt.figure(1, figsize=(15, 6))
plt.plot(np.arange(1, 11), inertia1, 'o')
plt.plot(np.arange(1, 11), inertia1, '-', alpha=0.7)
plt.title('手肘法图', fontsize=12)
plt.xlabel('聚类数'), plt.ylabel('SSE')
plt.grid(linestyle='-.')
plt.show()
# 确定k kk=4后。重新构建k kk=4的K-means模型,并且绘制聚类图km1_result = (KMeans(n_clusters=4, init='k-means++', n_init=10, max_iter=300,tol=0.0001, random_state=111, algorithm='elkan'))
# 先fit()再predict(),一次性得到聚类预测之后的标签
y1_means = km1_result.fit_predict(df_a_sc)
# 绘制结果图
plt.scatter(df_a_sc[y1_means == 0][:, 0], df_a_sc[y1_means == 0][:, 1], s=70, c='blue', label='1', alpha=0.6)
plt.scatter(df_a_sc[y1_means == 1][:, 0], df_a_sc[y1_means == 1][:, 1], s=70, c='orange', label='2', alpha=0.6)
plt.scatter(df_a_sc[y1_means == 2][:, 0], df_a_sc[y1_means == 2][:, 1], s=70, c='pink', label='3', alpha=0.6)
plt.scatter(df_a_sc[y1_means == 3][:, 0], df_a_sc[y1_means == 3][:, 1], s=70, c='purple', label='4', alpha=0.6)
plt.scatter(km1_result.cluster_centers_[:, 0], km1_result.cluster_centers_[:, 1], s=260, c='gold', label='质心')
plt.title('聚类图(K=4)', fontsize=12)
plt.xlabel('年收入(k$)')
plt.ylabel('消费分数(1-100)')
plt.legend()
plt.grid(linestyle='-.')
plt.show()
基于年收入和消费分数的聚类
df_ai_sc = df[['Annual Income', 'Spending Score']].values
# 存放每次聚类结果的误差平方和
inertia2 = []
# 使用手肘法确定合适的k kk值for n in range(1, 11):# 构造聚类器km2 = (KMeans(n_clusters=n, init='k-means++', n_init=10, max_iter=300, tol=0.0001, random_state=111, algorithm='elkan'))# 用训练数据拟合聚类器模型km2.fit(df_ai_sc)# 获取聚类标签inertia2.append(km2.inertia_)
# 绘制手肘图确定K值
plt.figure(1, figsize=(15, 6))
plt.plot(np.arange(1, 11), inertia1, 'o')
plt.plot(np.arange(1, 11), inertia1, '-', alpha=0.7)
plt.title('手肘法图', fontsize=12)
plt.xlabel('聚类数'), plt.ylabel('SSE')
plt.grid(linestyle='-.')
plt.show()
# 确定k kk=5后。重新构建k kk=5的K-means模型,并且绘制聚类图km2_result = (KMeans(n_clusters=5, init='k-means++', n_init=10, max_iter=300,tol=0.0001, random_state=111, algorithm='elkan'))
# 先fit()再predict(),一次性得到聚类预测之后的标签
y2_means = km2_result.fit_predict(df_ai_sc)
# 绘制结果图
plt.scatter(df_ai_sc[y2_means == 0][:, 0], df_ai_sc[y2_means == 0][:, 1], s=70, c='blue', label='1', alpha=0.6)
plt.scatter(df_ai_sc[y2_means == 1][:, 0], df_ai_sc[y2_means == 1][:, 1], s=70, c='orange', label='2', alpha=0.6)
plt.scatter(df_ai_sc[y2_means == 2][:, 0], df_ai_sc[y2_means == 2][:, 1], s=70, c='pink', label='3', alpha=0.6)
plt.scatter(df_ai_sc[y2_means == 3][:, 0], df_ai_sc[y2_means == 3][:, 1], s=70, c='purple', label='4', alpha=0.6)
plt.scatter(df_ai_sc[y2_means == 4][:, 0], df_ai_sc[y2_means == 4][:, 1], s=70, c='green', label='5', alpha=0.6)
plt.scatter(km2_result.cluster_centers_[:, 0], km2_result.cluster_centers_[:, 1], s=260, c='gold', label='质心')
plt.title('聚类图(K=5)', fontsize=12)
plt.xlabel('年收入(k$)')
plt.ylabel('消费分数(1-100)')
plt.legend()
plt.grid(linestyle='-.')
plt.show()