拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Python电商数据分析实战:从用户行为清洗到RFM分层与可视化

在实际电商业务中用户行为数据是驱动决策的核心燃料。无论是产品经理优化页面路径还是运营人员策划精准营销活动亦或是数据分析师评估渠道效果都需要一套从原始日志到可执行洞察的完整分析流程。仅仅会写 SQL 查询或使用几个图表库往往难以应对真实项目中数据混乱、口径不一、分析维度复杂的挑战。一个能完整跑通的、包含数据清洗、核心指标计算、用户分层模型如 RFM和可视化呈现的项目是检验数据分析能力、丰富简历和应对面试的绝佳实践。本文将以一个模拟的电商用户行为数据集为例带你从零开始完成一个结构清晰、可复现的 Python 数据分析与可视化项目。我们将重点解决几个工程化问题如何处理原始数据中的脏数据如何构建关键分析指标如用户漏斗转化率如何应用 RFM 模型对用户进行价值分层以及如何选择合适的可视化方式清晰呈现分析结果。整个过程将使用 Pandas 进行数据处理Matplotlib/Seaborn 进行静态图表绘制并简要介绍 Pyecharts 用于制作交互式可视化大屏的思路。文章末尾会提供完整的、可运行的源码结构。1. 项目目标、数据理解与环境准备在开始写代码之前必须明确分析目标和理解数据并搭建一个隔离、可复现的 Python 环境。这是避免后续依赖冲突和结果不一致的关键。1.1 明确分析目标与数据字段本项目旨在通过一份电商用户行为日志回答几个核心业务问题用户转化漏斗从浏览商品到最终支付每一步的转化率如何流失最严重的环节在哪里用户价值分层使用 RFM 模型根据用户最近一次消费时间Recency、消费频率Frequency、消费金额Monetary对用户进行分类如重要价值用户、一般保持用户等。用户行为可视化将上述分析结果通过图表清晰呈现形成可用于报告或仪表盘的数据支撑。我们模拟一份包含以下核心字段的user_behavior.csv数据集user_id: 用户唯一标识item_id: 商品唯一标识category_id: 商品类目behavior_type: 用户行为类型 (pv-浏览,cart-加购,buy-购买)timestamp: 行为时间戳毫秒级或标准格式注意实际项目中数据可能来自数据库导出、日志文件或数据平台。字段名称和格式需根据实际情况调整。本文使用模拟数据以保证流程可复现。1.2 搭建 Python 分析环境推荐使用conda或venv创建独立的虚拟环境避免污染系统 Python 环境。步骤 1创建并激活虚拟环境# 使用 conda (推荐) conda create -n ecommerce_analysis python3.9 conda activate ecommerce_analysis # 或使用 venv python -m venv venv_ecommerce # Windows 激活 venv_ecommerce\Scripts\activate # Linux/Mac 激活 source venv_ecommerce/bin/activate步骤 2安装核心依赖库在激活的环境下使用pip安装以下库。建议将依赖写入requirements.txt文件。# 创建 requirements.txt 文件内容如下 # pandas1.5.3 # numpy1.24.3 # matplotlib3.7.1 # seaborn0.12.2 # jupyter1.0.0 # 可选用于交互式分析 # pyecharts2.0.3 # 可选用于交互式图表 # 安装所有依赖 pip install -r requirements.txt # 或直接安装 pip install pandas numpy matplotlib seaborn环境验证 启动 Python 解释器尝试导入库确保无报错。import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns print(所有库导入成功)2. 数据加载与探索性清洗拿到数据后的第一步不是直接分析而是进行探索性数据分析EDA和数据清洗。脏数据会导致后续所有分析结论失真。2.1 加载数据并查看概览import pandas as pd import numpy as np # 加载数据假设文件在当前目录 df pd.read_csv(user_behavior.csv) # 查看数据前5行了解数据结构 print(数据前5行) print(df.head()) # 查看数据基本信息行数、列数、每列数据类型、非空值数量 print(\n数据基本信息) print(df.info()) # 查看数值型列的统计描述 print(\n数值型列统计描述) print(df.describe()) # 查看行为类型分布 print(\n行为类型分布) print(df[behavior_type].value_counts())运行上述代码后你可能会发现以下典型问题存在缺失值NaN。timestamp列为整数或字符串不是日期时间类型。可能存在重复记录。behavior_type的值可能超出预期的(pv, cart, buy)。2.2 执行数据清洗操作根据 EDA 发现的问题执行清洗流程。清洗顺序很重要。1. 处理缺失值# 检查每列缺失值数量 print(缺失值统计) print(df.isnull().sum()) # 根据业务逻辑处理缺失值 # 例1user_id缺失无意义直接删除该行 df df.dropna(subset[user_id]) # 例2category_id缺失若无法填补可标记为‘未知’或使用众数填补需谨慎 # df[category_id].fillna(df[category_id].mode()[0], inplaceTrue) # 例3少量行为类型缺失可考虑删除或根据上下文推断这里直接删除 df df.dropna(subset[behavior_type])2. 转换时间戳格式# 假设timestamp是毫秒级整数 # 先转换为datetime对象再创建易于分析的日期、小时列 df[behavior_time] pd.to_datetime(df[timestamp], unitms) # 如果是秒则 units # 如果timestamp是字符串如 2023-10-01 12:00:00 # df[behavior_time] pd.to_datetime(df[timestamp]) # 提取日期、小时等维度 df[behavior_date] df[behavior_time].dt.date df[behavior_hour] df[behavior_time].dt.hour df[behavior_weekday] df[behavior_time].dt.weekday # 周一0, 周日6 # 可以删除原始的timestamp列 # df.drop(timestamp, axis1, inplaceTrue)3. 处理重复数据# 检查完全重复的行 duplicate_rows df[df.duplicated()] print(f完全重复的行数{len(duplicate_rows)}) # 通常用户行为日志允许同一用户对同一商品有多次相同行为如多次浏览 # 但如果是完全相同的记录所有字段相同可能是日志重复上报可以删除 df df.drop_duplicates() # 也可以根据业务判断例如保留每个用户-商品-行为类型-小时的最新记录 # df df.sort_values(behavior_time).drop_duplicates(subset[user_id, item_id, behavior_type, behavior_hour], keeplast)4. 处理异常值与数据一致性# 检查behavior_type是否只有预期值 expected_behavior [pv, cart, buy] unexpected_behavior df[~df[behavior_type].isin(expected_behavior)] print(f非预期行为类型记录数{len(unexpected_behavior)}) # 如果有可以查看具体值决定是映射、删除还是保留为‘其他’ # df.loc[~df[behavior_type].isin(expected_behavior), behavior_type] other # 检查是否有未来时间戳数据采集时间之后 current_time pd.Timestamp.now() future_records df[df[behavior_time] current_time] print(f未来时间记录数{len(future_records)}) # 通常删除或修正为采集时间 # df df[df[behavior_time] current_time]清洗完成后建议将清洗后的数据保存为新文件以便后续分析直接使用避免重复清洗。df_clean df.copy() df_clean.to_csv(user_behavior_clean.csv, indexFalse)3. 核心分析一用户行为漏斗分析漏斗分析用于追踪用户在完成一个多步骤流程中的转化与流失情况。在电商场景中典型漏斗是浏览(PV) - 加购(Cart) - 购买(Buy)。3.1 计算漏斗各环节用户数关键点一个用户可能在一天内完成多个步骤也可能跨天完成。我们需要定义一个分析周期如一天并计算在该周期内完成从第一步到下一步的用户数。# 使用清洗后的数据 df_clean # 首先确定分析日期范围例如分析最近7天 analysis_end_date df_clean[behavior_date].max() analysis_start_date analysis_end_date - pd.Timedelta(days6) df_period df_clean[(df_clean[behavior_date] analysis_start_date) (df_clean[behavior_date] analysis_end_date)] # 计算独立用户数在分析周期内至少有过浏览行为的用户 pv_users df_period[df_period[behavior_type] pv][user_id].nunique() # 计算在浏览过的用户中有多少人进行了加购 # 思路先找到有加购行为的用户然后判断这些用户是否在分析周期内有过浏览不一定在加购之前但在周期内即可 cart_users df_period[df_period[behavior_type] cart][user_id].nunique() cart_users_with_pv df_period[df_period[user_id].isin(df_period[df_period[behavior_type] pv][user_id].unique()) (df_period[behavior_type] cart)][user_id].nunique() # 计算在加购的用户中有多少人最终购买 buy_users df_period[df_period[behavior_type] buy][user_id].nunique() buy_users_with_cart df_period[df_period[user_id].isin(df_period[df_period[behavior_type] cart][user_id].unique()) (df_period[behavior_type] buy)][user_id].nunique() # 更严谨的漏斗计算按用户-会话粒度但这里简化按用户周期内行为计算 funnel_data { 环节: [浏览(PV), 加购(Cart), 购买(Buy)], 用户数: [pv_users, cart_users_with_pv, buy_users_with_cart] } funnel_df pd.DataFrame(funnel_data) funnel_df[转化率] funnel_df[用户数] / funnel_df[用户数].iloc[0] # 相对于第一步的转化率 funnel_df[环节转化率] funnel_df[用户数].pct_change().fillna(0) # 相邻环节的转化率 funnel_df[环节转化率] funnel_df[环节转化率].apply(lambda x: f{x:.2%}) print(用户行为漏斗数据) print(funnel_df)3.2 漏斗结果可视化使用 Matplotlib 绘制漏斗图。import matplotlib.pyplot as plt import matplotlib.patches as mpatches fig, ax plt.subplots(figsize(10, 6)) # 数据 stages funnel_df[环节] values funnel_df[用户数] conversion funnel_df[转化率].apply(lambda x: f{x:.2%}) # 假设转化率已是小数 # 绘制条形图横向 y_pos range(len(stages)) ax.barh(y_pos, values, color[#FF6B6B, #4ECDC4, #45B7D1]) ax.set_yticks(y_pos) ax.set_yticklabels(stages) ax.invert_yaxis() # 让浏览在最上面 ax.set_xlabel(用户数) ax.set_title(f用户行为转化漏斗周期{analysis_start_date} 至 {analysis_end_date}) # 在条形末端添加数据标签 for i, v in enumerate(values): ax.text(v max(values)*0.01, i, f{v}\n({conversion.iloc[i]}), vacenter, fontsize10) # 添加转化率箭头示意 for i in range(len(stages)-1): ax.annotate(, xy(values[i1], i1), xytext(values[i], i), arrowpropsdict(arrowstyle-, colorgray, lw1.5)) plt.tight_layout() plt.savefig(funnel_analysis.png, dpi300, bbox_inchestight) plt.show()4. 核心分析二RFM 用户分层模型RFM 模型通过三个维度对用户进行价值细分R (Recency): 用户最近一次消费距离分析日期的天数。越小代表用户越活跃。F (Frequency): 用户在分析周期内的消费次数。越大代表用户消费越频繁。M (Monetary): 用户在分析周期内的消费总金额。越大代表用户价值越高。由于我们的模拟数据没有金额字段我们将用“购买次数”近似代替 Monetary即 M F。在实际项目中需要使用真实的交易金额数据。4.1 计算每个用户的 R、F、M 值# 准备交易数据假设只有购买行为‘buy’是交易 df_purchase df_clean[df_clean[behavior_type] buy].copy() # 定义分析截止日期通常是数据最新日期 analysis_date df_clean[behavior_time].max().date() # 按用户聚合计算 RFM rfm df_purchase.groupby(user_id).agg({ behavior_time: lambda x: (analysis_date - x.max().date()).days, # Recency: 最近一次购买距今天数 user_id: count, # Frequency: 购买次数 }).rename(columns{behavior_time: Recency, user_id: Frequency}) # 由于没有金额我们用Frequency作为Monetary的代理或假设每笔订单金额为1 rfm[Monetary] rfm[Frequency] # 或 df_purchase.groupby(user_id)[amount].sum() print(RFM 数据前5行) print(rfm.head()) print(f\n总购买用户数{len(rfm)})4.2 对 R、F、M 进行评分与分层常用的方法是分位数评分如四分位或自定义阈值。# 使用分位数将每个维度分为4组1-4分分数越高越好 # 注意Recency越小越好所以需要反向打分 rfm[R_Score] pd.qcut(rfm[Recency], q4, labels[4, 3, 2, 1]) # 天数越小分数越高 rfm[F_Score] pd.qcut(rfm[Frequency], q4, labels[1, 2, 3, 4]) rfm[M_Score] pd.qcut(rfm[Monetary], q4, 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) # 计算RFM总分或拼接字符串 rfm[RFM_Score] rfm[R_Score].astype(str) rfm[F_Score].astype(str) rfm[M_Score].astype(str) rfm[RFM_Sum] rfm[[R_Score, F_Score, M_Score]].sum(axis1) print(RFM 评分后数据前5行) print(rfm[[Recency, Frequency, Monetary, R_Score, F_Score, M_Score, RFM_Score]].head())4.3 根据 RFM 分数进行用户分层我们可以根据业务经验定义分层规则。以下是一个常见的8层分类规则# 定义分层函数 def rfm_segment(row): if row[R_Score] 4 and row[F_Score] 4 and row[M_Score] 4: return 重要价值用户 elif row[R_Score] 4 and row[F_Score] 4 and row[M_Score] 4: return 重要发展用户 elif row[R_Score] 4 and row[F_Score] 4 and row[M_Score] 4: return 重要保持用户 elif row[R_Score] 4 and row[F_Score] 4 and row[M_Score] 4: return 重要挽留用户 elif row[R_Score] 4 and row[F_Score] 4 and row[M_Score] 4: return 一般价值用户 elif row[R_Score] 4 and row[F_Score] 4 and row[M_Score] 4: return 一般发展用户 elif row[R_Score] 4 and row[F_Score] 4 and row[M_Score] 4: return 一般保持用户 else: return 一般挽留用户 rfm[RFM_Segment] rfm.apply(rfm_segment, axis1) # 查看各分层用户数量 segment_counts rfm[RFM_Segment].value_counts().sort_values(ascendingFalse) print(\nRFM 用户分层统计) print(segment_counts)4.4 RFM 分层结果可视化import seaborn as sns plt.figure(figsize(12, 6)) # 绘制分层用户数条形图 sns.barplot(xsegment_counts.index, ysegment_counts.values, paletteviridis) plt.title(RFM 用户分层分布) plt.xlabel(用户分层) plt.ylabel(用户数量) plt.xticks(rotation45, haright) # 旋转x轴标签 for i, v in enumerate(segment_counts.values): plt.text(i, v max(segment_counts.values)*0.01, str(v), hacenter, fontsize10) plt.tight_layout() plt.savefig(rfm_segment_distribution.png, dpi300, bbox_inchestight) plt.show() # 绘制 R、F、M 与分层的箱线图观察各分层在三个维度上的特征 fig, axes plt.subplots(1, 3, figsize(15, 5)) metrics [Recency, Frequency, Monetary] titles [最近消费天数 (R), 消费频率 (F), 消费金额 (M)] for idx, (ax, metric, title) in enumerate(zip(axes, metrics, titles)): # 按分层排序后的数据绘图更清晰 order segment_counts.index sns.boxplot(datarfm, xRFM_Segment, ymetric, axax, orderorder, paletteSet2) ax.set_title(title) ax.set_xlabel() ax.set_xticklabels(ax.get_xticklabels(), rotation45, haright) if idx 0: ax.set_ylabel() plt.suptitle(各 RFM 分层在 R/F/M 维度上的分布, fontsize14) plt.tight_layout() plt.savefig(rfm_metrics_by_segment.png, dpi300, bbox_inchestight) plt.show()5. 进阶可视化与项目结构整合基础图表能满足分析需求但为了制作更美观的仪表盘或报告可以使用 Pyecharts 等交互式图表库。5.1 使用 Pyecharts 制作交互式漏斗图from pyecharts import options as opts from pyecharts.charts import Funnel # 准备数据 funnel_stages funnel_df[环节].tolist() funnel_values funnel_df[用户数].tolist() # 创建漏斗图 funnel_chart ( Funnel() .add( series_name用户转化, data_pair[list(z) for z in zip(funnel_stages, funnel_values)], gap2, tooltip_optsopts.TooltipOpts(triggeritem, formatter{a} br/{b} : {c} ({d}%)), label_optsopts.LabelOpts(positioninside), ) .set_global_opts( title_optsopts.TitleOpts(title用户行为转化漏斗 (Pyecharts)), tooltip_optsopts.TooltipOpts(triggeritem), ) ) # 渲染为HTML文件可在浏览器中打开交互 funnel_chart.render(funnel_pyecharts.html) print(交互式漏斗图已生成funnel_pyecharts.html)5.2 项目源码结构与运行说明一个完整的、可复现的项目应该具备清晰的结构。建议按如下方式组织你的项目文件夹ecommerce_analysis_project/ │ ├── data/ # 数据目录 │ ├── raw/ # 原始数据不上传Git │ │ └── user_behavior.csv │ └── processed/ # 清洗后数据 │ └── user_behavior_clean.csv │ ├── notebooks/ # Jupyter Notebook 用于探索性分析 │ └── 01_eda_and_cleaning.ipynb │ ├── src/ # 源代码 │ ├── __init__.py │ ├── data_loader.py # 数据加载与清洗函数 │ ├── funnel_analysis.py # 漏斗分析模块 │ ├── rfm_analysis.py # RFM分析模块 │ └── visualization.py # 可视化函数 │ ├── main.py # 主程序串联整个分析流程 ├── requirements.txt # 项目依赖 ├── README.md # 项目说明文档 └── outputs/ # 生成的图表和报告 ├── funnel_analysis.png ├── rfm_segment_distribution.png ├── rfm_metrics_by_segment.png └── funnel_pyecharts.htmlmain.py示例import sys import os sys.path.append(os.path.dirname(os.path.abspath(__file__))) from src.data_loader import load_and_clean_data from src.funnel_analysis import calculate_funnel, plot_funnel_matplotlib from src.rfm_analysis import calculate_rfm, segment_users, plot_rfm_results def main(): print(开始电商用户行为分析项目...) # 1. 加载与清洗数据 print(步骤1: 加载与清洗数据...) df_clean load_and_clean_data(data/raw/user_behavior.csv) # 2. 漏斗分析 print(步骤2: 进行用户行为漏斗分析...) funnel_df calculate_funnel(df_clean, days7) plot_funnel_matplotlib(funnel_df, outputs/funnel.png) # 3. RFM分析 print(步骤3: 进行RFM用户分层分析...) rfm_df calculate_rfm(df_clean) rfm_segmented segment_users(rfm_df) plot_rfm_results(rfm_segmented, outputs/) print(分析完成结果已保存至 outputs/ 目录。) if __name__ __main__: main()6. 常见问题排查与最佳实践在实际运行项目时你可能会遇到以下典型问题。6.1 数据加载与清洗阶段问题1pd.read_csv报错UnicodeDecodeError现象读取 CSV 文件时提示编码错误。原因文件可能使用非 UTF-8 编码如 GBK。解决指定编码参数如pd.read_csv(file.csv, encodinggbk)或encodinglatin1。可用chardet库检测文件编码。问题2时间戳转换失败现象pd.to_datetime抛出错误提示无法解析字符串。原因时间戳格式与预期不符。解决先打印几行原始timestamp列查看格式。使用pd.to_datetime(df[timestamp], format%Y-%m-%d %H:%M:%S)指定明确格式或使用errorscoerce参数将无法解析的设为 NaT 再处理。问题3分组或聚合后结果为空或异常现象计算漏斗或 RFM 时用户数为 0 或明显不合理。原因数据清洗时可能误删了关键记录分组键如user_id有空格或类型不一致分析周期选择不当该周期内无数据。解决检查清洗后的数据行数print(len(df_clean))。检查关键字段的唯一值数量print(df_clean[user_id].nunique())。确认behavior_type的值是否与代码中的筛选条件如buy完全匹配注意大小写。打印分析周期的起止日期确认数据集中存在该日期范围内的记录。6.2 分析与可视化阶段问题4漏斗图转化率极低或为0现象从浏览到加购的用户数骤降转化率接近0。原因漏斗计算逻辑有误。代码中cart_users_with_pv的计算可能过于严格要求用户必须在同一周期内先浏览再加购。现实中用户可能浏览后隔天才加购。解决根据业务定义调整漏斗口径。例如可以放宽为“在统计周期内加购的用户只要其历史上或较长周期内有过浏览行为即可”。这需要根据user_id关联更长时间范围的数据。问题5RFM 分层中所有用户都被分到同一类如“一般挽留用户”现象分层结果高度集中没有区分度。原因评分规则如四分位可能不适用于当前数据分布。如果所有用户的 R、F、M 值都非常接近分位数切割会失效。解决查看 R、F、M 的分布直方图rfm[[Recency,Frequency,Monetary]].hist()。考虑使用自定义阈值替代分位数例如R: 30天4分30-60天3分60-90天2分90天1分。尝试使用 K-Means 等聚类方法进行自动分层。问题6Matplotlib 图表中文显示为方框现象图表标题、标签中的中文无法显示。解决添加中文字体支持。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, DejaVu Sans] # 用来正常显示中文标签 plt.rcParams[axes.unicode_minus] False # 用来正常显示负号6.3 项目工程化最佳实践配置与路径分离不要将数据文件路径、数据库连接字符串等硬编码在代码中。使用配置文件如config.yaml或环境变量管理。日志记录在main.py和各模块中使用logging模块记录运行状态、警告和错误便于排查。异常处理对文件读取、数据转换、外部 API 调用等可能失败的操作使用try...except并给出友好的错误提示。代码复用与模块化如示例所示将数据加载、清洗、分析、可视化功能封装成函数和类放在src/目录下使main.py简洁清晰。版本控制使用 Git 管理代码将data/raw/和outputs/目录加入.gitignore避免将原始数据和生成的大文件提交到仓库。生产环境考虑如果分析需要定期自动运行可以考虑使用 Apache Airflow 等调度工具如果数据量巨大GB/TB 级Pandas 可能内存不足需考虑使用 Dask 或 PySpark。7. 扩展方向与面试要点完成基础分析后你可以从以下几个方向深化项目这也会在面试中成为亮点。引入更多维度商品维度分析热销品类、商品关联规则购物篮分析。时间维度分析用户活跃时段、购买周期、季节性趋势。渠道维度如果数据包含来源如 app, web, 小程序分析各渠道转化差异。模型优化RFM 权重调整业务上可能认为近期消费R比消费金额M更重要可以给 R、F、M 分配不同权重计算综合得分。使用聚类算法用 K-Means 对 R、F、M 标准化后的数据进行聚类与规则分层结果对比。预测模型基于历史行为使用机器学习如逻辑回归、XGBoost预测用户下次购买时间或流失概率。构建自动化仪表盘使用Plotly Dash或Streamlit快速构建一个包含漏斗图、RFM 分层分布、用户明细查询等功能的交互式 Web 仪表盘。将分析流程封装成 API供其他系统调用。面试陈述要点清晰的项目目标不是“我做了数据分析”而是“我通过构建用户漏斗和 RFM 模型定位了转化流失环节并识别出高价值用户为精准营销提供了数据支持”。处理数据问题的能力主动提及在清洗数据时遇到的缺失值、异常值问题及你的解决策略。对业务的理解解释为什么选择这些指标如漏斗步骤、RFM它们如何映射到业务动作如针对“重要挽留用户”应发送优惠券。工程化思维展示你的项目结构、模块化代码、使用版本控制和虚拟环境。结果的视觉化与解读不仅能画出图表还能从图表中得出有业务意义的结论并提出后续行动建议。这个项目从数据清洗到可视化呈现的全流程覆盖了数据分析岗位所需的核心技能点。关键在于理解每一步背后的业务逻辑和工程考量而不仅仅是代码的堆砌。在实际应用中你需要根据具体的数据情况和业务需求灵活调整分析方法和参数。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门