这次我们来看一个完整的Python电商用户行为数据分析与可视化实战项目。这个项目不是简单的概念讲解,而是从数据清洗、漏斗分析、RFM用户分层到可视化大屏的全流程代码实现。如果你正在准备数据分析相关的毕业设计、简历项目或者面试案例,这篇文章提供的源码和思路可以直接复用。
项目核心是使用Python处理真实的电商用户行为数据,通过Pandas进行数据清洗和转换,利用Matplotlib和Seaborn完成可视化,最终构建一个能够反映用户价值分层的RFM模型。整个过程不需要复杂的机器学习算法,重点在于业务逻辑的理解和数据分析流程的规范化。我们将重点关注如何用代码实现每一个分析步骤,以及如何将分析结果转化为直观的图表。
本文会带你完成从环境搭建、数据加载、数据清洗、分析建模到可视化输出的全过程。你将获得一套可运行的完整源码,并理解每个环节的技术选型和实现细节。无论是数据分析初学者希望有一个实战项目练手,还是开发者需要为业务系统集成用户分析模块,都能从中找到可参考的内容。
1. 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目类型 | 电商用户行为数据分析与可视化实战项目 |
| 技术栈 | Python, Pandas, NumPy, Matplotlib, Seaborn, Jupyter Notebook |
| 核心分析模型 | RFM模型(最近一次消费、消费频率、消费金额) |
| 主要分析内容 | 用户行为数据清洗、购买漏斗分析、用户价值分层、多维度可视化 |
| 数据要求 | 结构化的用户交易数据(如订单表、用户行为日志) |
| 输出成果 | 清洗后的数据集、RFM评分表、多种统计分析图表、可视化报告 |
| 适合场景 | 数据分析学习、毕业设计、简历项目、业务数据分析原型开发 |
| 硬件门槛 | 普通电脑即可,内存建议8G以上,用于处理较大数据集 |
| 启动方式 | 通过Jupyter Notebook或Python脚本按步骤运行 |
2. 适用场景与使用边界
这个项目主要适合以下几类人群:
- 在校学生:需要完成数据分析相关的课程设计、毕业设计,项目提供完整的流程和源码,便于理解和答辩。
- 求职者:希望丰富简历中的数据项目经验,RFM模型是电商数据分析中非常经典和实用的案例。
- 初级数据分析师:想要通过一个完整项目掌握从原始数据到分析报告的标准工作流。
- 业务人员或产品经理:希望通过Python自动化地生成用户分层报告,辅助运营决策。
项目能解决的核心问题:
- 如何将杂乱、原始的电商日志或订单数据清洗成可供分析的结构化数据。
- 如何计算关键的用户行为指标,如转化率、复购率等。
- 如何应用RFM模型对用户进行价值分层,识别出重要保持用户、重要发展用户、重要挽留用户和一般价值用户。
- 如何将分析结果通过多种图表(柱状图、饼图、热力图、雷达图等)进行可视化展示。
使用边界与注意事项:
- 数据敏感性:本项目处理的是用户交易和行为数据,在实际工作中必须严格遵守数据隐私和安全法规,确保数据脱敏,仅用于合规的分析目的。
- 模型局限性:RFM模型是一个较为宏观的静态分层模型,对于预测单个用户未来的具体行为存在局限。它更适合用于群体分析和制定宏观运营策略。
- 业务适配性:RFM中R、F、M的阈值划分需要根据具体业务的实际情况(如行业特点、消费周期)进行调整,本文提供的划分方法仅为示例。
- 结果解读:数据分析结果需要结合业务知识进行解读,避免陷入“唯数据论”。
3. 环境准备与前置条件
在开始编码之前,需要准备好以下环境和数据:
3.1 软件与工具
- Python 3.7+:这是运行数据分析库的基础。建议使用Anaconda发行版,它集成了许多科学计算包。
- Jupyter Notebook / Jupyter Lab:非常适合进行交互式数据分析和可视化,方便一步步执行和查看结果。也可以通过PyCharm、VSCode等IDE运行
.py脚本。 - 必要的Python库:我们将通过
pip或conda安装以下核心库。# 使用pip安装 pip install pandas numpy matplotlib seaborn scikit-learn # 如果使用Jupyter Notebook pip install jupyter
3.2 数据集准备项目需要一个包含用户交易记录的数据集。通常包含以下字段(字段名可根据实际数据调整):
user_id: 用户唯一标识order_id: 订单唯一标识order_date: 订单日期(需为日期时间格式)product_id: 商品IDcategory: 商品类别amount: 订单金额(或商品金额)behavior_type: 用户行为类型(如:pv-浏览, buy-购买, cart-加购, fav-收藏)
你可以使用公开的电商数据集(如阿里天池的“User Behavior Data from Taobao”),或使用自己生成的模拟数据。为了便于演示,下文会包含一个创建模拟数据的代码块。
3.3 目录结构建议创建一个清晰的项目目录,便于管理代码、数据和输出结果。
ecommerce_analysis_project/ │ ├── data/ # 存放原始数据和清洗后的数据 │ ├── raw/ # 原始数据(如.csv文件) │ └── processed/ # 清洗处理后的数据 │ ├── notebooks/ # Jupyter Notebook文件 │ └── rfm_analysis.ipynb │ ├── src/ # Python脚本(如果将Notebook转化为.py) │ ├── data_cleaning.py │ ├── rfm_calculation.py │ └── visualization.py │ ├── output/ # 生成的图表和报告 │ ├── figures/ # 图片文件(.png, .jpg) │ └── reports/ # 汇总表格(.csv, .xlsx) │ └── README.md # 项目说明4. 数据加载与初步探索
任何数据分析的第一步都是了解你的数据。我们使用Pandas来加载和查看数据。
4.1 加载数据假设我们有一个名为user_behavior.csv的原始数据文件。
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 设置中文显示和图表样式 plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans'] plt.rcParams['axes.unicode_minus'] = False sns.set_style("whitegrid") # 加载数据 df = pd.read_csv('./data/raw/user_behavior.csv') print("数据形状:", df.shape) print("\n数据前5行:") print(df.head()) print("\n数据基本信息:") print(df.info()) print("\n数据描述性统计:") print(df.describe())4.2 识别数据问题通过初步查看,我们需要检查以下常见问题:
- 缺失值:
df.isnull().sum() - 重复值:
df.duplicated().sum() - 异常值:查看金额、日期等字段的分布。
- 数据类型:确保
order_date是datetime类型。 - 关键字段唯一性:检查
user_id,order_id是否有重复。
5. 数据清洗与预处理
原始数据往往不能直接用于分析,清洗是关键一步。
5.1 处理缺失值与重复值
# 删除完全重复的行 df_clean = df.drop_duplicates() # 处理缺失值:根据业务逻辑,这里示例删除‘user_id’或‘amount’为空的记录 df_clean = df_clean.dropna(subset=['user_id', 'amount']) # 对于行为类型等字段,可以用‘unknown’填充 df_clean['behavior_type'] = df_clean['behavior_type'].fillna('unknown') print(f"清洗后数据形状: {df_clean.shape}")5.2 转换数据类型与提取特征
# 转换日期字段 df_clean['order_date'] = pd.to_datetime(df_clean['order_date']) # 提取日期特征,便于后续按时间维度分析 df_clean['order_year'] = df_clean['order_date'].dt.year df_clean['order_month'] = df_clean['order_date'].dt.month df_clean['order_day'] = df_clean['order_date'].dt.day df_clean['order_dayofweek'] = df_clean['order_date'].dt.dayofweek # 周一为0 df_clean['order_hour'] = df_clean['order_date'].dt.hour print(df_clean[['order_date', 'order_year', 'order_month', 'order_dayofweek']].head())5.3 处理异常值对于金额amount,我们可能需要处理负值或极大值。
# 假设金额应为正数,且我们定义一个合理的上限(如99%分位数) amount_q99 = df_clean['amount'].quantile(0.99) print(f"金额的99%分位数是: {amount_q99}") # 筛选出金额在合理范围内的记录,或者将异常值截断 df_clean = df_clean[(df_clean['amount'] > 0) & (df_clean['amount'] <= amount_q99)] # 另一种方式:将大于上限的值赋值为上限值 # df_clean.loc[df_clean['amount'] > amount_q99, 'amount'] = amount_q99清洗完成后,将数据保存,供后续分析使用。
df_clean.to_csv('./data/processed/cleaned_user_behavior.csv', index=False)6. 用户行为漏斗分析
漏斗分析用于追踪用户从浏览到最终购买的转化过程。我们假设数据中有behavior_type字段,记录了“pv”(浏览)、“cart”(加购)、“fav”(收藏)、“buy”(购买)等行为。
6.1 计算各环节用户数
# 统计独立用户在不同行为阶段的数量 # 注意:一个用户可能有多条同类型行为记录,我们按user_id去重 funnel_data = {} behavior_stages = ['pv', 'cart', 'fav', 'buy'] # 根据你的数据定义环节 for stage in behavior_stages: # 获取在该阶段有过行为的独立用户数 stage_users = df_clean[df_clean['behavior_type'] == stage]['user_id'].nunique() funnel_data[stage] = stage_users print(f"{stage}环节用户数: {stage_users}") # 转换为DataFrame便于绘图 funnel_df = pd.DataFrame(list(funnel_data.items()), columns=['stage', 'user_count']) print(funnel_df)6.2 计算转化率与可视化
# 计算各环节之间的转化率 funnel_df['conversion_rate'] = 0.0 for i in range(1, len(funnel_df)): funnel_df.loc[i, 'conversion_rate'] = funnel_df.loc[i, 'user_count'] / funnel_df.loc[i-1, 'user_count'] print(funnel_df) # 绘制漏斗图 plt.figure(figsize=(10, 6)) # 使用条形图模拟漏斗 bars = plt.barh(funnel_df['stage'], funnel_df['user_count'], color=sns.color_palette("Blues_r", len(funnel_df))) plt.xlabel('独立用户数') plt.title('用户购买行为漏斗分析') # 在条形上添加数量和转化率标签 for i, (bar, count, rate) in enumerate(zip(bars, funnel_df['user_count'], funnel_df['conversion_rate'])): width = bar.get_width() plt.text(width + max(funnel_df['user_count'])*0.01, bar.get_y() + bar.get_height()/2, f'{int(count)} ({rate:.1%})' if i>0 else f'{int(count)}', va='center') plt.gca().invert_yaxis() # 让第一个环节在最上面 plt.tight_layout() plt.savefig('./output/figures/user_behavior_funnel.png', dpi=300) plt.show()通过这个漏斗图,可以清晰看到从浏览到购买的转化路径和损耗点,帮助定位问题环节。
7. RFM模型构建与用户分层
RFM模型是衡量客户价值的经典工具。
- R(Recency):用户最近一次消费距离分析时点的时间。时间越近,价值越高。
- F(Frequency):用户在一定时间内的消费频率。频率越高,价值越高。
- M(Monetary):用户在一定时间内的消费总金额。金额越高,价值越高。
7.1 计算RFM指标我们需要设定一个分析截止日期(snapshot_date),通常是数据中最新日期之后的一天。
# 设定分析快照日期 snapshot_date = df_clean['order_date'].max() + pd.Timedelta(days=1) print(f"分析快照日期: {snapshot_date}") # 按用户聚合计算R、F、M rfm = df_clean.groupby('user_id').agg({ 'order_date': lambda x: (snapshot_date - x.max()).days, # Recency: 最近一次消费距今天数 'order_id': 'nunique', # Frequency: 订单数(消费次数) 'amount': 'sum' # Monetary: 消费总金额 }).reset_index() # 重命名列 rfm.columns = ['user_id', 'recency', 'frequency', 'monetary'] print(rfm.head()) print(rfm.describe())7.2 对RFM指标进行评分常用的方法是分位数评分(如四分位)或自定义阈值。这里使用四分位数将每个指标分为4组(1-4分),分数越高代表表现越好。注意:对于R(最近消费天数),天数越少越好,所以需要反向打分。
# 使用qcut进行分位数分组并打分 (4分制) # Recency: 天数越少,分数越高(反向) rfm['r_score'] = pd.qcut(rfm['recency'], q=4, labels=[4, 3, 2, 1]) # Frequency和Monetary: 值越大,分数越高 rfm['f_score'] = pd.qcut(rfm['frequency'], q=4, labels=[1, 2, 3, 4]) rfm['m_score'] = pd.qcut(rfm['monetary'], q=4, labels=[1, 2, 3, 4]) # 将分数转换为整数类型 rfm['r_score'] = rfm['r_score'].astype(int) rfm['f_score'] = rfm['f_score'].astype(int) rfm['m_score'] = rfm['m_score'].astype(int) print(rfm[['user_id', 'recency', 'r_score', 'frequency', 'f_score', 'monetary', 'm_score']].head())7.3 计算RFM总分与用户分层可以将三个分数拼接,或者计算加权总分,然后进行分层。
# 方法1:拼接RFM分数(如434) rfm['rfm_group'] = rfm['r_score'].astype(str) + rfm['f_score'].astype(str) + rfm['m_score'].astype(str) # 方法2:计算加权总分(假设权重相同) rfm['rfm_score'] = rfm['r_score'] + rfm['f_score'] + rfm['m_score'] print(rfm[['user_id', 'rfm_group', 'rfm_score']].head())7.4 基于规则的用户价值分层根据业务经验,我们可以定义一些规则对用户进行分类。以下是一个常见的八分法示例:
# 定义分层函数 def rfm_segment(row): if row['r_score'] >= 3 and row['f_score'] >= 3 and row['m_score'] >= 3: return '重要价值用户' elif row['r_score'] >= 3 and row['f_score'] >= 3 and row['m_score'] < 3: return '重要发展用户' elif row['r_score'] >= 3 and row['f_score'] < 3 and row['m_score'] >= 3: return '重要保持用户' elif row['r_score'] >= 3 and row['f_score'] < 3 and row['m_score'] < 3: return '重要挽留用户' elif row['r_score'] < 3 and row['f_score'] >= 3 and row['m_score'] >= 3: return '一般价值用户' elif row['r_score'] < 3 and row['f_score'] >= 3 and row['m_score'] < 3: return '一般发展用户' elif row['r_score'] < 3 and row['f_score'] < 3 and row['m_score'] >= 3: return '一般保持用户' else: return '一般挽留用户' rfm['user_segment'] = rfm.apply(rfm_segment, axis=1) # 查看分层结果分布 segment_distribution = rfm['user_segment'].value_counts().sort_values(ascending=False) print(segment_distribution)8. 数据分析可视化
将清洗后的数据和分析结果通过图表展示,是数据分析报告的关键。
8.1 用户价值分层分布图
# 用户分层分布(饼图/柱状图) plt.figure(figsize=(12, 6)) # 柱状图 ax1 = plt.subplot(1, 2, 1) segment_distribution.plot(kind='bar', ax=ax1, color='steelblue') ax1.set_title('用户价值分层分布(柱状图)') ax1.set_xlabel('用户分层') ax1.set_ylabel('用户数') plt.xticks(rotation=45, ha='right') # 饼图 ax2 = plt.subplot(1, 2, 2) colors = sns.color_palette('pastel')[0:len(segment_distribution)] wedges, texts, autotexts = ax2.pie(segment_distribution.values, labels=segment_distribution.index, autopct='%1.1f%%', colors=colors, startangle=90) ax2.set_title('用户价值分层分布(饼图)') # 美化饼图文本 for autotext in autotexts: autotext.set_color('white') autotext.set_fontweight('bold') plt.tight_layout() plt.savefig('./output/figures/user_segment_distribution.png', dpi=300) plt.show()8.2 RFM指标分布直方图
# 绘制R、F、M三个指标的分布 fig, axes = plt.subplots(1, 3, figsize=(15, 4)) metrics = ['recency', 'frequency', 'monetary'] titles = ['最近消费天数分布', '消费频率分布', '消费金额分布'] for idx, (metric, title) in enumerate(zip(metrics, titles)): ax = axes[idx] # 使用Seaborn的histplot,可以显示密度曲线 sns.histplot(rfm[metric], kde=True, ax=ax, bins=30, color='skyblue') ax.set_title(title) ax.set_xlabel(metric) ax.set_ylabel('用户数') plt.tight_layout() plt.savefig('./output/figures/rfm_distribution.png', dpi=300) plt.show()8.3 用户分层与RFM均值热力图查看不同分层用户的R、F、M平均值。
# 计算各分层的RFM平均值 segment_rfm_mean = rfm.groupby('user_segment')[['recency', 'frequency', 'monetary']].mean().round(2) print(segment_rfm_mean) # 绘制热力图 plt.figure(figsize=(10, 8)) sns.heatmap(segment_rfm_mean, annot=True, fmt='g', cmap='YlOrRd', linewidths=.5) plt.title('不同用户分层的RFM指标平均值热力图') plt.tight_layout() plt.savefig('./output/figures/segment_rfm_heatmap.png', dpi=300) plt.show()8.4 消费趋势时间序列图分析每月或每周的消费总额和订单数趋势。
# 按月份聚合消费数据 df_clean['order_year_month'] = df_clean['order_date'].dt.to_period('M') monthly_sales = df_clean.groupby('order_year_month').agg({ 'amount': 'sum', 'order_id': 'nunique', 'user_id': 'nunique' }).reset_index() monthly_sales['order_year_month'] = monthly_sales['order_year_month'].astype(str) # 转换为字符串便于绘图 fig, axes = plt.subplots(2, 1, figsize=(14, 10)) # 月度消费金额趋势 axes[0].plot(monthly_sales['order_year_month'], monthly_sales['amount'], marker='o', color='green', linewidth=2) axes[0].set_title('月度消费总金额趋势') axes[0].set_xlabel('年月') axes[0].set_ylabel('消费金额') axes[0].tick_params(axis='x', rotation=45) axes[0].grid(True, linestyle='--', alpha=0.7) # 月度订单数与消费用户数趋势(双Y轴) ax2 = axes[1] line1 = ax2.plot(monthly_sales['order_year_month'], monthly_sales['order_id'], marker='s', color='blue', label='订单数') ax2.set_xlabel('年月') ax2.set_ylabel('订单数', color='blue') ax2.tick_params(axis='y', labelcolor='blue') ax2.tick_params(axis='x', rotation=45) ax3 = ax2.twinx() line2 = ax3.plot(monthly_sales['order_year_month'], monthly_sales['user_id'], marker='^', color='red', label='消费用户数') ax3.set_ylabel('消费用户数', color='red') ax3.tick_params(axis='y', labelcolor='red') # 合并图例 lines = line1 + line2 labels = [l.get_label() for l in lines] ax2.legend(lines, labels, loc='upper left') axes[1].set_title('月度订单数与消费用户数趋势') axes[1].grid(True, linestyle='--', alpha=0.7) plt.tight_layout() plt.savefig('./output/figures/monthly_sales_trend.png', dpi=300) plt.show()9. 结果输出与报告生成
将关键的分析结果保存为文件,便于生成报告或集成到其他系统。
9.1 保存RFM评分与分层结果
# 保存详细的RFM评分表 rfm.to_csv('./output/reports/rfm_score_card.csv', index=False) print("RFM评分表已保存。") # 保存用户分层统计摘要 segment_summary = rfm['user_segment'].value_counts().reset_index() segment_summary.columns = ['user_segment', 'user_count'] segment_summary['percentage'] = (segment_summary['user_count'] / segment_summary['user_count'].sum() * 100).round(2) segment_summary.to_csv('./output/reports/user_segment_summary.csv', index=False) print("用户分层统计摘要已保存。")9.2 生成简单的文本分析报告
# 生成一个简单的文本报告 report_lines = [] report_lines.append("="*50) report_lines.append("电商用户行为数据分析报告") report_lines.append("="*50) report_lines.append(f"分析日期: {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}") report_lines.append(f"分析用户总数: {rfm.shape[0]}") report_lines.append(f"数据时间范围: {df_clean['order_date'].min().date()} 至 {df_clean['order_date'].max().date()}") report_lines.append("-"*50) report_lines.append("用户价值分层结果:") for segment, count in segment_distribution.items(): report_lines.append(f" {segment}: {count} 人 ({count/rfm.shape[0]*100:.1f}%)") report_lines.append("-"*50) top_segment = segment_distribution.index[0] report_lines.append(f"核心用户群体: 【{top_segment}】") report_lines.append("="*50) report_text = '\n'.join(report_lines) print(report_text) # 将报告保存为文本文件 with open('./output/reports/analysis_summary.txt', 'w', encoding='utf-8') as f: f.write(report_text)10. 项目总结与扩展方向
通过以上步骤,我们完成了一个从原始数据到可视化报告的完整电商用户行为分析项目。核心流程包括数据清洗、漏斗分析、RFM建模和可视化展示。这套代码和流程可以直接作为毕业设计或面试项目的骨架。
最值得尝试的几点:
- 数据清洗的完整性:真实数据往往比示例复杂,尝试处理更多样的数据问题(如地址信息解析、商品类目映射)能极大提升实战能力。
- RFM阈值的动态调整:不要固守四分位法。尝试根据业务实际(如“高价值用户”的金额标准)或聚类算法(如K-Means)来自动划分R、F、M的阈值,使分层更贴合业务。
- 可视化大屏集成:可以将多个关键图表(如漏斗图、分层分布图、趋势图、热力图)布局在一个仪表盘中,使用
Plotly Dash或Streamlit库构建交互式可视化大屏,这会让项目展示效果更上一层楼。
最容易踩的坑:
- 日期格式错误:
pd.to_datetime转换时注意原始数据的格式,否则会导致大量NaT(非时间)值。 - 数据重复计算:在计算F(频率)时,务必明确是按订单数(
order_id)还是按交易次数计算,避免因退款订单等造成重复。 - 内存不足:如果原始数据量极大(千万级以上),直接使用Pandas可能内存溢出。可以尝试分块读取(
chunksize)、使用Dask库或连接数据库直接进行聚合查询。 - 图表美化:Matplotlib的默认样式比较基础,多花时间学习Seaborn或调整Matplotlib参数(颜色、字体、布局),能让你的报告更专业。
下一步扩展方向:
- 结合更多模型:在RFM基础上,可以尝试聚类分析(如K-Means)进行无监督用户分群,与RFM结果相互验证。
- 预测分析:利用历史RFM数据,构建分类模型(如逻辑回归、XGBoost)预测用户下次是否会购买或流失。
- 个性化推荐:基于用户行为数据(浏览、购买),实现一个简单的协同过滤商品推荐原型。
- 自动化与部署:将整个分析流程脚本化,使用
cron(Linux)或Task Scheduler(Windows)定期自动运行,并通过邮件发送分析报告。
这个项目提供了数据分析的完整闭环。建议你用自己的数据或寻找更复杂的公开数据集运行一遍,在解决实际错误的过程中,你的数据分析能力会得到真正的巩固。