1. 《基于客户细分的在线零售数据分析及营销策略》

2. 客户细分数据分析文档说明

3. 项目概述

通过对在线零售数据集的分析,实现客户细分,帮助企业更好地了解客户群体特征,制定精准的营销策略。分析过程包括数据预处理、探索性数据分析、特征工程和聚类分析等步骤,最终将客户分为不同的群体。

4. 环境准备与数据导入

5. 2.1导入必要的库

import numpy as np # 数值计算库  
import pandas as pd # 数据处理库
import matplotlib.pyplot as plt # 绘图库
import seaborn as sns # 统计可视化库
from sklearn.model\_selection import train\_test\_split, GridSearchCV # 模型选择相关
from sklearn.preprocessing import LabelEncoder, StandardScaler # 数据预处理
from sklearn.cluster import KMeans, DBSCAN # 聚类算法
from sklearn.decomposition import PCA # 主成分分析
import warnings
warnings.filter warnings("ignore") # 忽略警告信息

6. 读取并查看数据集

df =  
pd.read\_\excel("/kaggle/Input/customer-segmentation-dataset/OnlineRetail.xlsx")
df.head()
InvoiceNoStockCodeDescriptionQuantityInvoiceDateUnitPriceCustomerIDCountry
053636585123AWHITE HANGING HEART T-LIGHT HOLDER62010-12-01 08:26:002.5517850.0United Kingdom
153636571053WHITE METAL LANTERN62010-12-01 08:26:003.3917850.0United Kingdom
253636584406BCREAM CUPID HEARTS COAT HANGER82010-12-01 08:26:002.7517850.0United Kingdom
353636584029GKNITTED UNION FLAG HOT WATER BOTTLE62010-12-01 08:26:003.3917850.0United Kingdom
453636584029ERED WOOLLY HOTTIE WHITE HEART.62010-12-01 08:26:003.3917850.0United Kingdom

数据集包含在线零售交易记录,主要字段包括:

InvoiceNo:发票编号
StockCode:商品代码
Description:商品描述
Quantity:购买数量

  • InvoiceDate:发票日期
    UnitPrice:单价
  • CustomerID:客户编号
    Country:国家

7. 数据检查与探索性分析(EDA)

8. 数据基本信息检查

9. 查看数据维度(行列数)

print(“Number of Columns in the Data: “, df.shape[1])
print(“Number of Rows in the Data: “, df.shape[0])

Number of Columns in the Data: 8  
Number of Rows in the Data: 541909

可以看到该数据集有 8 列,541909 行

10. 检查各列数据类型

df.info()

<class 'pandas.core.frame.DataFrame'>  
RangeIndex: 541909 entries, 0 to 541908
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
0 InvoiceNo 541909 non-null object
1 StockCode 541909 non-null object
2 Description 540455 non-null object
3 Quantity 541909 non-null int64
4 InvoiceDate 541909 non-null datetime64[ns]
5 UnitPrice 541909 non-null float64
6 CustomerID 406829 non-null float64
7 Country 541909 non-null object
dtypes: datetime64[ns](1), float64(2), int64(1), object(4)
memory usage: 33.1+ MB

11. 统计非空值情况、缺失值数量和重复记录数量

检查数据中的缺失值数量

df.isnull().sum()
检查数据中的重复值数量
df.duplicated().sum()
检查数据中的缺失值数
df.isnull().sum()
InvoiceNo 0
StockCode 0
Description 1454
Quantity 0
InvoiceDate 0
UnitPrice 0
CustomerID 135080
Country 0
dtype: int64
检查数据中的重复值数量
df.duplicated().sum()

12. 3.2探索性数据分析(EDA)

13. 描述性统计

des $=$ df.describe().transpose()

palette = sns.color_\palette(“icefire”, as_\cmap=True)

des.stylebackground_\gradient(cmap=palette)

生成数值型变量的描述性统计表格,包括均值、标准差、四分位数等。

countmeanmin25%50%75%maxstd
Quantity541909.0000009.552250-80995.0000001.0000003.00000010.00000080995.000000218.081158
InvoiceDate541909 2011-07-04 13:34:57.1563860482010-12-01 08:26:002011-03-28 11:34:002011-07-19 17:17:002011-10-19 11:27:002011-12-09 12:50:00nan
UnitPrice541909.0000004.611114-11062.0500001.2500002.0800004.13000038970.00000096.759853
CustomerID406829.00000015287.69057012346.00000013953.00000015152.00000016791.00000018287.0000001713.600303

14. 国家分布分析

绘制前10个最频繁国家的柱状图

country_count = df.Country.value_counts().head(10)

plt.figure(figsize=(10,6))

sns.barplot(x=country_count.values, y=country_count.index,

palette=’viridis’)

plt.title(“Top 10 Most Frequent Countries”, fontsize=16)

plt.xlabel(“Number of Repetitions”)

plt;ylabel(“Country”)

plt.show()

绘制条形图展示购买次数最多的前10个国家,帮助了解主要市场分布。可以看到购买次数最多的就是United Kingdom,其购买数量远远超过其他国家


Top 10 Most Frequent Countries

15. 时间趋势分析

绘制前10个最频繁时间的柱状图

df[‘InvoiceDate’] = pd.todatetime(df[‘InvoiceDate’], format=’%Y-%m-%d.%f’)

time_trfriend $=$ df[‘InvoiceDate’].value_counts().head(10)

plt.figure(figsize=(10,6))

sns.barplot(x = time_trfriend.values, y = time_trfriend.index,

palette=’viridis’)

plt.title(‘Top 10 Most Frequent Time’, fontsize=16)

plt.xlabel(‘Number of Orders’)

pltylabel(‘Time’)

plt.show()

将发票日期转换为 datetime 格式,分析订单最频繁的前 10 个时间点,了解购买时间趋势。如图所示,购买时间最多的时 2011-10-31 14:41:00,其他的时间购买频数远低于这

个时间。


Top 10 Most Frequent Time

16. 商品描述分析

绘制前10个最频繁商品的柱状图

description_count = df[“Description”].value_counts().head(10)

plt.figure(figsize=(10,6))

sns.barplot(x=description_count.values, y=description_count.index, palette=’viridis’)

plt.title(“Top 10 Frequent Items”, fontsize=16)

plt.xlabel(“Number of Orders”)

plt;ylabel(“Items”)

plt.show()

分析购买次数最多的前10种商品,帮助识别畅销商品。

  • WHITE HANGING HEART T - LIGHT HOLDER:白色悬挂心形小烛台
  • REGENCY CAKESTAND 3 TIER:摄政风格三层蛋糕架
    JUMBO BAG RED RETROSPOT:大号红色复古波点袋
    PARTY BUNTING:派对彩旗
    LUNCH BAG RED RETROSPOT:红色复古波点午餐袋
    ASSORTED COLOUR BIRD ORNAMENT:各色鸟类装饰品
  • SET OF 3 CAKE TINS PANTRY DESIGN:三件套食品间风格蛋糕模具
  • PACK OF 72 RETROSPOT CAKE CASES : 72 个装复古波点蛋糕纸杯
    LUNCH BAG BLACK SKULL:黑色骷髅图案午餐袋
  • NATURAL SLATE HEART CHALKBOARD:天然板岩心形小黑板


Top 10 Frequent Items

17. 数值型变量分布与异常值检查

绘制数值型特征的直方图

plt.figure(figsize=(10,4))   
for i,feature in
enumerate(df.select_dtypes(include $=$ "number").columns): plt.subplot(1,3,i+1) sns.histplot(data $\equiv$ df,x $\equiv$ df[feature],bins=30,kde=True, color $=$ 'blue') plt.title(f{'feature}') plt.xlabel('') pltylabel feature)
plt.tight.layout()

绘制数值型特征的箱线图

plt.figure(figsize=(10,4))   
for i,feature in
enumerate(df.select_dtypes(include $=$ "number").columns): plt.subplot(13,i+1) sns.boxplot(data $\equiv$ df,x $\equiv$ df[feature],color $=$ 'red') plt.title(f{'feature}]') plt.xlabel('') pltylabel(f{'{feature}'}
plt.tight.layout()

绘制直方图和箱线图,分析数值型变量 (Quantity, UnitPrice 等) 的分布特征和异常值情况。

18. 数据预处理

19. 数据去重

删除重复值

df.drop_duplicates(inplace=True)

移除重复记录,确保数据唯一性,有助于提高聚类模型的泛化能力。

20. 4.2异常值处理

使用四分位距(IQR)方法识别并移除异常值:

  1. 计算第 1 四分位数 (q1) 和第 3 四分位数 (q3)
  2. 计算 $\mathrm{IQR} = \mathrm{q3 - q1}$
  3. 定义异常值边界:upper_limit = q3 + 1.5IQR, lower_limit = q1 - 1.5IQR
  4. 保留在边界范围内的数据

21. 4.2.1“数量”特征异常值处理

处理“数量”特征的异常值

q1 = df["Quantity"].quantile(0.30)  
q3 = df["Quantity"].quantile(0.70)
iqr = q3 - q1
upper_limit = q3 + (1.5 * iqr)  
lower_limit = q1 - (1.5 * iqr)
df = df.loc[(df["Quantity"] < upper_limit) & (df["Quantity"] > lower_limit)]

22. 数值型变量异常值处理

处理“单价”特征的异常值

q1 = df["UnitPrice"].quantile(0.25)  
q3 = df["UnitPrice"].quantile(0.65)
iqr = q3 - q1
upper_limit = q3 + (1.5 * iqr)  
lower_limit = q1 - (1.5 * iqr)
df = df.loc[(df["UnitPrice"] < upper_limit) & (df["UnitPrice"] > lower_limit)]

23. 数据处理后的箱线图

绘制处理后数值型特征的箱线图

plt.figure(figsize=(10,4))   
for i,feature in
enumerate(df.select_dtypes(include $=$ "number").columns): plt.subplot(13,i+1) sns.boxplot(data $\equiv$ df,x $\equiv$ df[feature],color $=$ 'red') plt.title(f{'feature}]') plt.xlabel('') pltylabel(f{'{feature}'}
plt.tight.layout()on

从这组箱线图(Box Plot)来看,可初步对三个变量

(Quantity、UnitPrice、CustomerID) 的数据集分布做如下分析:

24. Quantity (数量)

集中趋势:中位数(箱内横线位置)大概在3-7区间(因图中刻度,大致估算),说明半数数据的数量处于这个中间位置,数据有一定集中性。

离散程度:箱体上下限代表四分位数范围(IQR,即第25百分位数到第75百分位数),能看到数据在箱体范围内相对集中;须线延伸到-5到15左右(非异常值的最值),不过左侧须线到-5,但数量理论上不能为负,可能存在异常值或者数据录入问题,也可能是业务中特殊场景(比如退货等导致数量为负,需结合实际业务判断),整体离散程度可通过箱体长度和须线判断,数据有一定波动。

异常值:从须线看,若超出 $1.5^{*}\mathrm{IQR}$ 范围可视为异常值,这里左右须线延伸情况,结合数量不能为负,左侧可能存在异常负值,需进一步核查数据。

25. UnitPrice (单价)

集中趋势:中位数在2-3左右,表明半数数据的单价处于此区间,数据集中趋势较明显,大部分单价围绕这个中间值分布。

离散程度:箱体较短,说明四分位数范围内数据比较集中;须线延伸到0到6左右,整体数据离散程度相对小,单价波动不算特别大,大部分单价在较窄区间内。异常值:图中未明显看出超出 $1.5^{*}\mathrm{IQR}$ 范围的极端异常值,数据分布相对规整

26. CustomerID(客户编号)

集中趋势:中位数在14000-16000区间,客户编号是标识类数据,这里看其数值分布的中间位置,反映数据集中点。

离散程度:箱体展示了客户ID数值的四分位数范围,须线延伸到14000以下和18000左右,因客户ID是编号,理论上是有序或随机分配的数值,从箱线图看有一定离散性,不过主要关注其分布是否符合业务逻辑(比如是否连续、有无不合理间隔等),若客户ID是按顺序分配,这种离散可能是正常的新老客户编号跨度。

异常值:未明显看到极端异常的客户ID数值(如极大或极小的突兀值),数据分布相对合理

27. 特征编码

准备用于分析的特征变量,对分类变量 “Country” 使用标签编码 (Label Encoding) 转换为数值型。

选择用于聚类的特征

X = df[["Quantity", "UnitPrice", "Country"]]

对“国家”特征进行编码

encoder = LabelEncoder()
X["Country"] = encoder.fit_transform(X["Country'])

28. 4.4缺失值处理

移除包含缺失值的记录,确保分析数据的完整性。

删除缺失值

X.dropna(inplace=True)

检查缺失值的数量

X.isnull().sum()

29. 特征工程

创建新特征”total_price”,表示每个订单的总金额

X["total_price"] = X["Quantity"] * X["UnitPrice"]

30. 相关性分析

计算特征之间的相关系数矩阵,并通过热力图可视化:

计算特征之间的相关性矩阵

corr = X.corr()

绘制相关性矩阵的热力图

plt.figure(figsize $\coloneqq$ (10,6))

sns_heatmap(corr, annot=True, fmt=”.2f”, cmap=”viridis”)

plt.title(“Correlation Matrix”)

plttightlayout()

plt.show()


颜色深浅表示相关程度数值显示具体相关系数

31. 数据标准化

使用标准化(Standardization)处理特征数据:将数据转换为均值为0,标准差为1的分布,确保不同量纲的特征在聚类分析中具有相同的权重

scalar = StandardScaler()
X =Scaler.fit\transform(X)

32. 聚类分析

33. 肘部法则确定最佳聚类数量

使用肘部法则 (Elbow Method) 确定最佳聚类数量:计算 2 到 10 个聚类时的 WCSS (Within-Cluster Sum of Squares),WCSS 随聚类数量增加而减小,选择曲线拐点处的聚类数量作为最佳值

34. 使用K-Means聚类算法计算不同聚类数量下的WCSS值

wcss  $= []$    
for i in range(2, 11): kmeans $=$ KMeans(i, random_state $\equiv$ 42) kmeans.fit(X)
wcss.append(kmeans.inertia_)

绘制肘部曲线

plt.figure(figsize=(10,6))  
plt.plot(range(2,11),wcss,marker='o')
plt.title('Elbow Method')  
plt.xlabel('Number of Clusters')
pltylabel('WCSS')
plt.show()

从图中可以看出,随着聚类数的增大,WCSS逐渐减小。在前几个聚类数(2到4)中,WCSS的减少幅度比较大,而从4到10增加聚类数时,WCSS的下降幅度开始减缓。根据图的形状,3类是一个合理的选择,因为在这个点上,增加聚类数并没有带来显著的WCSS降低。选择3类可以更好地平衡模型的复杂度与解释力。

35. K-means 聚类

使用K-Means聚类算法对数据进行聚类  
kmeans = KMeans(n_clusters=5, random_state=42)
df['Cluster'] = kmeans.fit.predict(X)

使用K-means算法将客户分为3个群体:

  • nclusters=3:根据肘部法则确定的最佳聚类数量
  • random state=42:确保结果可重现

36. 聚类结果可视化

绘制聚类结果的散点图矩阵  
sns.hist(df, hue='Cluster', vars=['Quantity', 'UnitPrice'], palette='viridis')
plt.show()

使用散点图矩阵可视化聚类结果:

左上:Quantity 按 Cluster 分布的直方图

横轴是 Quantity(数量),纵轴是数量频次。
不同颜色(对应聚类0、1、2)的分布形态差异大:

  • 聚类2(黄色):数量集中在0-5区间,峰值高,是最“集中”的类别。
  • 聚类1(青绿色):数量在5-15有分布,且有多个小峰值,离散性更强。
  • 聚类 0(紫色):数量分布较零散,覆盖负值到正数区间,可能包含特殊场景(如退货导致负数量)。

右上:Cluster 分组的散点矩阵(侧重类别分布)

横轴无明确数值,主要看聚类标签(0、1、2)的“块分布”。
• 聚类 1 (青绿色) 集中在上半区, 聚类 2 (黄色) 在下半区, 聚类 0 (紫色) 穿插其中。

  • 直观呈现:聚类 1 和 2 有明显 “区域划分”,聚类 0 相对分散(可能是边界样本或异常点)。

左下:Quantity vs UnitPrice 散点图(带聚类颜色)

横轴 Quantity(数量),纵轴 UnitPrice(单价)。
• 聚类 2(黄色):集中在低单价(0-2)、低数量(0-5)区域,是“小单低价”群体。

  • 聚类 1(青绿色):覆盖中高单价(2-6)、中高数量(5-15)区间,是“大单中高价”群体。
  • 聚类 0(紫色):分布零散,部分重叠聚类 1/2,可能是 混合特征样本(如退货、特殊订单)。

右下:UnitPrice 按 Cluster 分布的直方图

横轴 UnitPrice(单价),纵轴是单价频次。

  • 聚类2(黄色):单价集中在0-2区间,与左上直方图呼应(“小单低价”)。
  • 聚类1(青绿色):单价在2-6有多个峰值,体现“中高价”的多样性。
  • 聚类 0 (紫色) : 单价分布零散, 与其他聚类有重叠, 进一步验证 “边界 / 异常样本” 特征。

聚类2(黄色):小单低价(低数量、低单价,集中性强)。

聚类1(青绿色):大单中高价(中高数量、中高单价,离散但有区间)。

聚类0(紫色):混合/异常样本(特征零散,可能包含退货、特殊订单等)。