项目5.2【订单数据分析和聚类】
一、 数据读取
In [1]:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from pyecharts.charts import *
from pyecharts import options as opts
import warnings
warnings.filterwarnings('ignore') #忽略警告信息
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False
In [3]:
df = pd.read_excel('order2021kmeans.xlsx')
df.head()

-
库导入部分:
import pandas as pd:导入Pandas库并简写为pd,用于数据处理和分析45import numpy as np:导入NumPy库并简写为np,提供多维数组支持和数学运算功能68import matplotlib.pyplot as plt:导入Matplotlib的pyplot模块并简写为plt,用于数据可视化15import matplotlib as mpl:导入Matplotlib主库用于全局配置1from pyecharts.charts import *:导入pyecharts的所有图表类型,用于交互式可视化3from pyecharts import options as opts:导入pyecharts的配置选项模块3
-
警告设置:
warnings.filterwarnings('ignore'):忽略所有警告信息,避免干扰输出结果7
-
中文显示配置:
mpl.rcParams['font.sans-serif']=['SimHei']:设置Matplotlib默认使用黑体显示中文13mpl.rcParams['axes.unicode_minus']=False:解决负号显示为方框的问题3
-
数据读取:
df = pd.read_excel('order2021kmeans.xlsx'):使用Pandas读取Excel文件到DataFrame对象45df.head():显示数据框的前5行内容,用于快速查看数据结构5
这段代码是典型的数据分析准备工作流程,结合了数据处理(Pandas/NumPy)和可视化
二、数据预处理
In [4]:
#查看一下数据的整体信息以及缺失值和重复值 df.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 104557 entries, 0 to 104556 Data columns (total 11 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 订单顺序编号 104557 non-null int64 1 订单号 104557 non-null object 2 用户名 104557 non-null object 3 商品编号 104557 non-null object 4 订单金额 104557 non-null float64 5 付款金额 104557 non-null float64 6 渠道编号 104549 non-null object 7 平台类型 104557 non-null object 8 下单时间 104557 non-null datetime64[ns] 9 付款时间 104557 non-null datetime64[ns] 10 是否退款 104557 non-null object dtypes: datetime64[ns](2), float64(2), int64(1), object(6) memory usage: 8.8+ MB
In [5]:
# 删除重复值 df.duplicated().sum()
Out[5]:
0
In [7]:
df.columns = df.columns.str.strip()
In [8]:
#查看数据分布 df.describe().T


-
-
-
数据概览检查:
df.info():显示DataFrame的摘要信息,包括:- 行数范围(RangeIndex: 104557 entries)
- 11列数据名称及数据类型3
- 每列非空值数量(如"渠道编号"列有8个缺失值)6
- 内存使用情况(8.8+ MB)5
-
重复值处理:
df.duplicated().sum():统计完全重复的行数,结果为0表示无重复行2df.columns = df.columns.str.strip():去除列名首尾的空格字符,避免后续引用列名时出错7
-
数值型数据分布分析:
df.describe().T:生成数值型列的统计摘要(包括计数、均值、标准差等),.T表示转置显示使结果更易读5- 默认统计:订单顺序编号、订单金额、付款金额三列数值型数据5
- 输出内容包含:25%/50%/75%分位数、最大值最小值等
-
-
#去除退款用户数据 data = df[df['是否退款']=='否'] data.head()

三、数据可视化分析
查看按渠道划分的收益
In [20]:
#统计不同渠道付款总额,并降序,转换成DF数据
channel_revenue=data.groupby('渠道编号')['付款金额'].sum().sort_values(ascending=False).reset_index()
#channel_revenue
#绘制柱状图
plt.figure(figsize=(12,4))
plt.title("不同渠道的总收益",fontsize=16)
plt.bar(channel_revenue['渠道编号'],channel_revenue['付款金额'])
plt.xlabel("渠道编号")
plt.ylabel("渠道收益")
plt.xticks(rotation=45)
plt.tight_layout()#自动调整子图布局
plt.show()

-
数据聚合处理:
groupby('渠道编号')['付款金额'].sum():按渠道分组计算各渠道付款总金额sort_values(ascending=False):按金额降序排列reset_index():将分组结果转换为标准DataFrame格式
-
可视化实现:
plt.figure(figsize=(12,4)):创建12英寸宽、4英寸高的画布plt.title():设置图表标题及字体大小plt.bar():绘制柱状图,x轴为渠道编号,y轴为对应金额plt.xticks(rotation=45):将x轴标签旋转45度防止重叠plt.tight_layout():自动调整图表元素间距plt.show():显示最终图表
该可视化能直观展示各渠道的收益贡献排名,旋转的x轴标签解决了长渠道编号显示重叠的问题,适合用于渠道效益对比分析。
查看按月份划分的收益
In [25]:
#数据准备
data['付款月份'] = data['付款时间'].dt.month #提取月份
#data['付款月份'] = [i.month for i in data['付款时间']]
data['付款月份名称'] = data['付款时间'].dt.month_name() #提取月份名称
#按月份统计总收益
month_revenue = data.groupby(['付款月份', '付款月份名称'])['付款金额'].sum().reset_index()
#绘制柱状图
plt.figure(figsize=(12, 4))
plt.title("Total Revenue by month", fontsize=16)
plt.bar(month_revenue["付款月份名称"],month_revenue["付款金额"])
plt.ylabel("Total Revenue", fontsize=12)
plt.xticks(rotation=45, fontsize=10)
plt.yticks(fontsize=10)
plt.tight_layout()
plt.show()

-
数据准备阶段:
data['付款月份'] = data['付款时间'].dt.month:从付款时间列提取月份数字(1-12)34data['付款月份名称'] = data['付款时间'].dt.month_name():获取英文月份名称(如January)3month_revenue = data.groupby(...).sum().reset_index():按月份分组计算付款总金额57
-
可视化阶段:
plt.figure(figsize=(12,4)):设置画布尺寸为12x4英寸1plt.bar():绘制柱状图,x轴为月份名称,y轴为对应金额1plt.xticks(rotation=45):将x轴标签旋转45度防止重叠1plt.tight_layout():自动调整图表元素间距1plt.show():显示最终图表1
该分析通过提取时间特征和分组聚合,直观展示了各月份的收益分布情况,适用于业务周期性分析57。注释掉的列表推导式方案是另一种实现方式
查看按每天小时划分的收益
In [27]:
#数据准备
data['付款小时'] =data['付款时间'].dt.hour #获取时间
data['付款天数'] = data['付款时间'].dt.day #获取天数
data['付款天数名称'] = data['付款时间'].dt.day_name()#获取时间名字
hourly_sales =data.groupby(['付款天数名称','付款小时'])['付款金额'].sum().reset_index()
hourly_sales = hourly_sales.rename(columns={'付款金额': 'TotalValue'})
hourly_sales

# 创建一个空列表来存储所有切分后的DataFrame
split_dfs = []
# 计算需要切分的组数
num_groups = len(hourly_sales) // 24
for i in range(num_groups):
# 每次迭代选取24行
start_index = i * 24
end_index = start_index + 24
split_df = hourly_sales.iloc[start_index:end_index]
split_dfs.append(split_df)
#查看第一个切分后的DataFrame
print(split_dfs[0])
付款天数名称 付款小时 TotalValue 0 Friday 0 3.208984e+05 1 Friday 1 1.104411e+05 2 Friday 2 9.343783e+04 3 Friday 3 1.139179e+04 4 Friday 4 5.626410e+03 5 Friday 5 5.533420e+03 6 Friday 6 1.560106e+04 7 Friday 7 6.796755e+04 8 Friday 8 1.060996e+05 9 Friday 9 2.194146e+05 10 Friday 10 3.375344e+05 11 Friday 11 6.278208e+05 12 Friday 12 1.014062e+06 13 Friday 13 1.410802e+06 14 Friday 14 1.232234e+06 15 Friday 15 7.496987e+05 16 Friday 16 7.528851e+05 17 Friday 17 8.270167e+05 18 Friday 18 1.153808e+06 19 Friday 19 2.028809e+06 20 Friday 20 2.508399e+06 21 Friday 21 1.794541e+06 22 Friday 22 1.083644e+06 23 Friday 23 5.885448e+05
In [54]:
# 创建折线图并绘制
name=hourly_sales['付款天数名称'].unique()
line=(
Line()
.add_xaxis(split_dfs[0]['付款小时'].astype(str).tolist()) # X轴为每个点的索引
.set_global_opts(
title_opts={"text":"每日每小时收益总额"},
legend_opts=opts.LegendOpts( #图例配置
is_show=True, # 是否显示图例
orient='vertical',#垂直显示
pos_top='5%', # 图例位置,例如顶部5%
pos_right='5%' # 图例位置,例如右侧5%
)
)
)
for i in range(num_groups):
line.add_yaxis(name[i],split_dfs[i]['TotalValue'].tolist(),label_opts=opts.LabelOpts(is_show=False))
line.render_notebook()

-
数据切分处理:
split_dfs = []:初始化空列表存储切分后的DataFrame4num_groups = len(hourly_sales) // 24:计算完整24小时周期的组数4for循环:通过iloc按24行一组切分数据,每组包含完整一天的24小时记录45print(split_dfs[0]):展示第一组数据,包含周五(Friday)24小时的销售额5
-
可视化实现:
Line():创建pyecharts折线图对象5.add_xaxis():设置X轴为0-23小时5.set_global_opts():配置标题和图例样式5for循环:为每组数据添加Y轴系列,对应不同日期的销售曲线5render_notebook():在Jupyter Notebook中渲染图表5
该方案适用于分析不同日期相同时段的销售趋势对比,通过标准化的24小时切分确保数据可比性45。输出结果显示了周五全天的销售额分布,峰值出现在20点(2.5M)而谷值在4点(5.6K)
消费群体画像LRFM_基于K-means聚类分析
开始构建LRFM模型,其中
L:客户生命周期,表示客户最后一次购买与第一次购买的时间之差,该指标可以揭示客户与品牌或超市之间的长期关系以及客户的忠诚度
R:最近一次消费 (Recency):天。表示用户最近是否活跃,时间越新鲜越好,若R时间过去太久可能用户已流失
F:消费频率 (Frequency):频率越高 说明用户忠诚度越高
M:消费金额 (Monetary):金额越大说明用户为重要用户
一般的价值模型只有RFM,关于引入L的进一步含义:'客户生命周期'越长,说明客户与商家之间的关系越持久,一般意味着客户对产品或者商家较高的满意度和信任,且存在较高的复购概率。
通过对不同生命周期客户群体的划分,更长周期的客户提供增值服务,较短周期的客户加强营销推广,能够进一步的优化营销策略,进而实现更大的商业价值。
In [56]:
#构建L指标:
L = (data.groupby('用户名')['付款时间'].max() - data.groupby('用户名')['付款时间'].min()).dt.days.reset_index()
#构建R指标:
data['付款时间']=pd.to_datetime(data['付款时间'])
#计算数据集中最大日期与每条记录的事务日期之间的时间差,做时间间隔diff
max_date = max(data['付款时间'])
data['diff']= max_date - data['付款时间']
data['diff']=data['diff'].dt.days
R = data.groupby('用户名')['diff'].min().reset_index()
#注意,字符串需要'',不论英汉。对用户名分组,计算至今天数的最小值,进行索引重置,将series变为dataframe,并且保留原列
F = data.groupby('用户名')['订单号'].count().reset_index()
M = data.groupby('用户名')['付款金额'].sum().reset_index()
#进行合并:LRFMdata = L.merge(R,on='用户名') 因为on的字节都一样,所以直接省略掉
LRFMdata = L.merge(R).merge(F).merge(M)
LRFMdata

-
指标构建逻辑:
- L指标:
(max付款时间-min付款时间).dt.days计算每个客户从首次到最后次消费的天数差3 - R指标:通过
max_date - 付款时间计算客户最近消费距今天数,取最小值作为活跃度指标3 - F指标:
订单号.count()统计客户消费频次3 - M指标:
付款金额.sum()汇总客户消费总额3
- L指标:
-
技术实现要点:
- 所有指标均按用户名分组计算,保持维度一致3
- 使用
.dt.days将时间差转换为整数天数3 - 通过
reset_index()保持DataFrame结构,便于后续合并3 - 链式
merge()自动按相同列名(用户名)合并四个指标3
-
业务价值:
- 相比传统RFM,增加的L指标能识别长期忠诚客户3
- 最终生成的LRFMdata包含用户名、生命周期、最近消费、频次、金额五个字段3
- 为后续K-means聚类提供标准化输入,支持客户分群运营策略制定3
该实现通过时间差计算和分组聚合,完整构建了客户价值评估的四维指标体系,适用于精细化用户运营场景。
#对columns进行重命名:
LRFMdata.rename(columns={
'付款时间':'L',
'diff':'R',
'订单号':'F',
'付款金额':'M'
},inplace=True)
LRFMdata
Out[57]:
| 用户名 | L | R | F | M | |
|---|---|---|---|---|---|
| 0 | user-100000 | 0 | 79 | 1 | 1770.81 |
| 1 | user-100003 | 0 | 221 | 1 | 511.59 |
| 2 | user-100006 | 0 | 47 | 1 | 443.55 |
| 3 | user-100007 | 0 | 351 | 1 | 2162.14 |
| 4 | user-100008 | 0 | 45 | 1 | 4879.94 |
| ... | ... | ... | ... | ... | ... |
| 71259 | user-299980 | 277 | 74 | 2 | 719.77 |
| 71260 | user-299983 | 0 | 4 | 1 | 706.80 |
| 71261 | user-299989 | 207 | 50 | 2 | 1637.47 |
| 71262 | user-299992 | 0 | 364 | 1 | 440.17 |
| 71263 | user-299995 | 0 | 276 | 1 | 350.87 |
71264 rows × 5 columns
聚类分析
In [58]:
#1.数据标准化处理:防止特征值数据差异过大,结果偏好 #开始准备进行模型聚类;再备份一下数据,防止出现意外: model_data = LRFMdata.copy() # 数据标准化 from sklearn.preprocessing import MinMaxScaler sacle_matrix = model_data.iloc[:, 1:5] # 获得要转换的矩阵 model_scaler = MinMaxScaler() #构建最大最小标准化模型 data_scaled = model_scaler.fit_transform(sacle_matrix) #模拟计算 print(data_scaled.round(3))
[[0. 0.217 0. 0.021] [0. 0.607 0. 0.006] [0. 0.129 0. 0.005] ... [0.57 0.137 0.167 0.02 ] [0. 1. 0. 0.005] [0. 0.758 0. 0.004]]
In [59]:
#2.构建K-means模型
from sklearn.cluster import KMeans
#定义SSE列表,用来存放不同K值下的SSE:误差平方和
SSE = []
#定义候选K值
for i in range(1,10):
kmeans = KMeans(n_clusters = i,random_state = 10)
kmeans.fit(data_scaled)
SSE.append(kmeans.inertia_) # 样本到质心的距离平方和
#使用手肘法看K值
plt.plot(range(1,10),SSE,marker = 'o') #表示在每个K值的位置上画一个圆形标记。
plt.show()
这段代码实现了基于K-means算法的客户价值聚类分析,主要分为数据标准化和模型构建两个阶段:
-
数据标准化处理:
- 使用
MinMaxScaler对LRFM指标进行最大最小标准化,将特征值缩放到[0,1]区间78 sacle_matrix = model_data.iloc[:, 1:5]提取L/R/F/M四列指标数据3- 标准化后输出显示各特征值均被转换为0-1范围内的数值7
- 使用
-
K-means模型构建:
- 通过循环测试K=1到9的聚类方案,记录每个K值对应的SSE(误差平方和)2
kmeans.inertia_获取簇内样本到质心的距离平方和,用于评估聚类效果2- 使用手肘法绘制SSE随K值变化曲线,通过拐点确定最佳聚类数25
该实现遵循了聚类分析的典型流程:数据标准化消除量纲影响→通过SSE评估确定最佳K值→最终建立分群模型34。标准化处理能避免高量纲特征主导聚类结果78,而手肘法则通过可视化辅助决策最优分群数量
#用轮廓系数调优找到最优K值并进行kmeans模型聚类:(直接进行轮廓系数的优选运行比较慢,
#可以首先考虑手肘法看K值,然后进一步再选择拟合程度好的拐点进行轮廓系数分析)
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
score_list = list() # 用来存储每个K下模型的平局轮廓系数
silhouette_int = -1 # 初始化的平均轮廓系数阀值
for n_clusters in range(3, 5):
model_kmeans = KMeans(n_clusters=n_clusters) # 建立聚类模型对象
labels_tmp = model_kmeans.fit_predict(data_scaled) # 训练聚类模型
silhouette_tmp = silhouette_score(data_scaled, labels_tmp) # 得到每个K下的平均轮廓系数
if silhouette_tmp > silhouette_int: # 如果平均轮廓系数更高
best_k = n_clusters # 保存
silhouette_int = silhouette_tmp
best_kmeans = model_kmeans
cluster_labels_k = labels_tmp
score_list.append([n_clusters, silhouette_tmp]) # 将每次K及其得分追加到列表
print('{:*^60}'.format('K值对应的轮廓系数:'))
print(np.array(score_list))
print('优K值:{0} \n轮廓系数是:{1}'.format(best_k, silhouette_int))
#显然轮廓系数越大越好,组内聚合和组间离别得效果
*************************K值对应的轮廓系数:************************* [[3. 0.529391 ] [4. 0.48905871]] 优K值:3 轮廓系数是:0.5293910039364125
In [61]:
# 获得训练集下的标签信息,并与原数据dataframe进行concat合并(为后续最后的客户细分做索引保留) cluster_labels = pd.DataFrame(cluster_labels_k, columns=['Cluster_Id']) merge_data = pd.concat((model_data, cluster_labels), axis=1) merge_data.head()
Out[61]:
| 用户名 | L | R | F | M | Cluster_Id | |
|---|---|---|---|---|---|---|
| 0 | user-100000 | 0 | 79 | 1 | 1770.81 | 0 |
| 1 | user-100003 | 0 | 221 | 1 | 511.59 | 1 |
| 2 | user-100006 | 0 | 47 | 1 | 443.55 | 0 |
| 3 | user-100007 | 0 | 351 | 1 | 2162.14 | 1 |
| 4 | user-100008 | 0 | 45 | 1 | 4879.94 | 0 |
In [62]:
#进一步对聚类后的数据进行统计分析:选取只与画像有关的列
merged_rfm = merge_data[['M','F','R','L','Cluster_Id']]
# 统计LRFM各指标的均值和各标签的总人数
mean_lrfm = merged_rfm.groupby('Cluster_Id').mean().reset_index() # Compute the mean for each Cluster_Id
mean_lrfm['count'] = merged_rfm['Cluster_Id'].value_counts().values # Add the count of each Cluster_Id
mean_lrfm

-
最优K值选择
- 通过循环测试K=3到4的聚类方案,计算每个K值对应的轮廓系数1
silhouette_score评估聚类质量,系数越接近1表示组内聚合和组间分离效果越好12- 最终选择K=3(轮廓系数0.529)作为最优分群数1
-
数据标签合并
- 将聚类标签
cluster_labels_k转换为DataFrame并与原始LRFM数据合并3 - 输出结果展示客户ID、L/R/F/M指标及所属分群ID(如用户100000被分到群组0)3
- 将聚类标签
-
分群特征分析
- 按
Cluster_Id分组计算各群组的LRFM均值3 - 追加
value_counts()统计各分群人数,形成完整的客户画像统计表3
- 按
该实现结合了轮廓系数法与手肘法的优势,先通过轮廓系数定量确定最佳K值,再通过分组统计揭示不同客户群的行为特征12。最终输出包含各分群的消费金额、频次、活跃度和生命周期等核心指标的均值分布
绘制客户特征雷达图
In [63]:
r = pd.DataFrame(best_kmeans.cluster_centers_) # 聚类中心
labels = np.array(['M', 'F', 'R', 'L'])
labels = np.concatenate((labels, [labels[0]])) # 闭合标签
# 聚类数量
N = len(r) + 1 # 聚类数量,加1以闭合雷达图
angles = np.linspace(0, 2 * np.pi, N, endpoint=False) # 均匀分布的角度
data = pd.concat([r, r.iloc[:, 0]], axis=1) # 聚类中心数据并闭合图形
angles = np.concatenate((angles, [angles[0]])) # 闭合角度
# 创建雷达图
fig = plt.figure(figsize=(8, 8)) # 增加图形尺寸
ax = fig.add_subplot(111, polar=True)
colors = ['#FF69B4', '#87CEEB', '#800080', '#32CD32', '#FFD700']
line_styles = ['-', '--', '-.', ':', '-']
# 绘制每个聚类的雷达图
for i in range(len(r)):
ax.plot(angles, data.loc[i, :], linewidth=2, linestyle=line_styles[i % len(line_styles)], label=f"客户群 {i}", color=colors[i % len(colors)]) # 添加样式和颜色
ax.fill(angles, data.loc[i, :], color=colors[i % len(colors)], alpha=0.25) # 为每个聚类区域添加填充色
ax.set_thetagrids(angles * 180 / np.pi, labels, fontsize=12) # 增加字体大小
ax.grid(True, color='gray', linestyle='-', linewidth=0.5, alpha=0.5) # 调整网格线的透明度
plt.title(u'客户特征雷达图', fontsize=16, pad=20)
plt.legend(loc='lower right', fontsize=12)
plt.show()

-
数据准备阶段
- 提取K-means聚类中心点数据(标准化后的L/R/F/M均值)1
- 通过
np.concatenate闭合标签和角度数据,确保雷达图形成闭合多边形1 np.linspace生成等分极坐标角度(0-2π区间)1
-
可视化核心逻辑
- 创建极坐标系
polar=True作为画布基础2 - 使用循环为每个客户群绘制:
▪ax.plot()绘制带样式的折线(不同颜色/线型)2
▪ax.fill()填充半透明色块增强视觉区分2 - 通过
set_thetagrids将角度转换为度数并标注指标名称2
- 创建极坐标系
-
样式优化设计
- 采用粉色、天蓝等对比色系提升可读性2
- 调整网格线透明度(alpha=0.5)避免干扰主数据2
- 图例定位右下角,标题增加padding保持美观2
最终输出雷达图能直观展示不同客户群在消费金额(M)、频次(F)、活跃度(R)和生命周期(L)四个维度的特征差异,便于快速识别高价值客户群的行为模式
更多推荐
所有评论(0)