当前位置: 首页 > news >正文

基于用户行为数据的留存动因分析与预警策略研究

1.1 项目背景

在电商和订阅式商业模式中,用户留存率是衡量产品健康度和商业可持续性的核心指标。获取新用户的成本远高于维护老用户,因此,通过数据分析定位流失原因、预测流失风险并制定留存策略,是数据分析师的核心工作之一。

本项目旨在通过对用户行为、交易记录和画像数据的分析,回答以下业务问题:

  • 哪些用户特征最能预示流失风险?

  • 会员制度、客服体验、参与度等因素如何影响留存?

  • 如何将数据洞察转化为可落地的运营策略?

1.2 数据来源

项目说明
数据集名称E-commerce Customer Churn Dataset
来源平台Kaggle
数据性质完全合成数据,但变量关系模拟真实电商模式
原始文件ecommerce_customer_features.csv(特征表)、ecommerce_customer_targets.csv(标签表)
数据规模6,000条用户记录,15个特征字段,1个目标变量
数据特点包含缺失值(本版本无)、无重复值、业务逻辑清晰

合成数据的优势:逻辑清晰、结论可验证,适合作为分析项目练习;真实业务中需额外关注数据质量和脏数据处理。


第二部分:分析工具与流程

2.1 工具链

阶段工具主要任务
数据清洗与合并Python (Pandas, NumPy)读取、合并、清洗、类型转换、异常处理
探索性分析Python (Matplotlib, Seaborn)特征分布、流失对比、相关性分析
数据查询与分组分析SQL (DBeaver)Cohort分析、分组对比、特征聚合
可视化与仪表盘Tableau交互式看板、趋势图

2.2 分析流程

原始数据 → 数据清洗 → 探索性分析(EDA) → SQL取数与分组分析 → 可视化看板 → 业务结论与建议


第三阶段:数据清洗与预处理(Python)

3.1 原始数据情况

  • 两个文件通过Customer_ID关联

  • 特征表:15列(用户行为 + 画像)

  • 标签表:1列(churned:Yes/No)

3.2 清洗步骤

步骤操作代码/方法
1读取两个CSV文件pd.read_csv()
2合并特征表与标签表pd.merge()
3检查缺失值df.isnull().sum() → 本版本无缺失
4异常值处理截尾1%-99%分位数
5重复值处理drop_duplicates() → 无重复
6转换目标变量churned: Yes→1, No→0
7导出清洗后数据cleaned_ecommerce.csv

3.3 清洗代码

import pandas as pd import numpy as np import os # ============================================ # 读取数据 # ============================================ features_path = r'D:\Users\qimiao\Desktop\数据分析\archive1\ecommerce_customer_features.csv' targets_path = r'D:\Users\qimiao\Desktop\数据分析\archive1\ecommerce_customer_targets.csv' features = pd.read_csv(features_path) targets = pd.read_csv(targets_path) print("=== 数据读取成功 ===") print(f"特征表: {features.shape}") print(f"标签表: {targets.shape}") print(f"\n特征表列名:\n{features.columns.tolist()}") print(f"\n标签表前5行:\n{targets.head()}") # ============================================ # 合并两个文件 # ============================================ df = features.merge(targets, on='Customer_ID', how='inner') print(f"\n=== 合并完成 ===") print(f"合并后形状: {df.shape}") # ============================================ # 检查缺失值 # ============================================ print("\n=== 缺失值检查 ===") missing = df.isnull().sum() missing_pct = missing / len(df) * 100 missing_df = pd.DataFrame({'缺失数量': missing, '缺失比例(%)': missing_pct}) print(missing_df[missing_df['缺失数量'] > 0]) # ============================================ # 处理缺失值 # ============================================ numeric_cols = df.select_dtypes(include=[np.number]).columns for col in numeric_cols: if df[col].isnull().sum() > 0: df[col].fillna(df[col].median(), inplace=True) print(f"已填充 {col} 的缺失值") categorical_cols = df.select_dtypes(include=['object']).columns for col in categorical_cols: if col != 'Customer_ID' and df[col].isnull().sum() > 0: mode_val = df[col].mode()[0] if len(df[col].mode()) > 0 else 'Unknown' df[col].fillna(mode_val, inplace=True) print(f"已填充 {col} 的缺失值") # ============================================ # 检查异常值 # ============================================ print("\n=== 异常值检查 ===") for col in numeric_cols: min_val = df[col].min() max_val = df[col].max() if col == 'discount_usage_rate' and max_val > 1: print(f"{col}: 最大值 {max_val} > 1,存在异常") if col == 'account_age_months' and max_val > 120: print(f"{col}: 最大值 {max_val} > 120") # 截尾处理(可选) for col in numeric_cols: q99 = df[col].quantile(0.99) q01 = df[col].quantile(0.01) df[col] = df[col].clip(lower=q01, upper=q99) print("已对数值列进行1%-99%截尾处理") # ============================================ # 转换目标变量 # ============================================ df['churned'] = df['churned'].map({'No': 0, 'Yes': 1}) print(f"\n=== 目标变量分布 ===") print(df['churned'].value_counts()) print(f"流失率: {df['churned'].mean():.2%}") # ============================================ # 删除重复值 # ============================================ initial_rows = len(df) df = df.drop_duplicates(subset=['Customer_ID']) print(f"\n删除重复行: {initial_rows - len(df)} 行") # ============================================ # 保存清洗后的数据(保存到你指定的桌面文件夹) # ============================================ output_path = r'D:\Users\qimiao\Desktop\数据分析\cleaned_ecommerce.csv' df.to_csv(output_path, index=False) print(f"\n清洗完成!已保存为: {output_path}") print(f"最终数据形状: {df.shape}") # ============================================ # 快速验证 # ============================================ print("\n=== 快速验证 ===") print(f"1. 剩余缺失值数量: {df.isnull().sum().sum()}") print(f"2. churned 唯一值: {df['churned'].unique()}") print(f"3. 数据前3行:\n{df.head(3)}")
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns # 设置中文显示(避免图表乱码) plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial Unicode MS'] plt.rcParams['axes.unicode_minus'] = False # ============================================ # 读取清洗后的数据 # ============================================ df = pd.read_csv(r'D:\Users\qimiao\Desktop\数据分析\cleaned_ecommerce.csv') print("=== 数据加载成功 ===") print(f"数据形状: {df.shape}") print(f"列名: {df.columns.tolist()}\n") # ============================================ # 1. 目标变量分布(流失 vs 未流失) # ============================================ print("=== 1. 目标变量分布 ===") churn_counts = df['churned'].value_counts() churn_pct = df['churned'].value_counts(normalize=True) print(f"未流失(0): {churn_counts[0]} 人, 占比 {churn_pct[0]:.2%}") print(f"流失(1): {churn_counts[1]} 人, 占比 {churn_pct[1]:.2%}") # 可视化 fig, axes = plt.subplots(1, 2, figsize=(10, 4)) df['churned'].value_counts().plot(kind='bar', ax=axes[0], color=['green', 'red']) axes[0].set_title('流失用户分布') axes[0].set_xticklabels(['未流失', '流失'], rotation=0) axes[0].set_ylabel('用户数') df['churned'].value_counts(normalize=True).plot(kind='pie', ax=axes[1], autopct='%1.1f%%', colors=['green', 'red']) axes[1].set_title('流失用户占比') plt.tight_layout() plt.savefig(r'D:\Users\qimiao\Desktop\数据分析\1_churn_distribution.png', dpi=150) plt.show() # ============================================ # 2. 数值特征统计摘要 # ============================================ print("\n=== 2. 数值特征统计摘要 ===") numeric_cols = df.select_dtypes(include=[np.number]).columns numeric_cols = [col for col in numeric_cols if col != 'churned'] print(df[numeric_cols].describe()) # ============================================ # 3. 流失用户 vs 未流失用户 特征对比 # ============================================ print("\n=== 3. 流失 vs 未流失 特征均值对比 ===") compare_df = df.groupby('churned')[numeric_cols].mean().T compare_df.columns = ['未流失', '流失'] compare_df['差异'] = compare_df['流失'] - compare_df['未流失'] compare_df['差异百分比'] = (compare_df['差异'] / compare_df['未流失']) * 100 print(compare_df.sort_values('差异', ascending=False)) # 可视化:Top 5 差异最大的特征 fig, ax = plt.subplots(figsize=(12, 6)) top_features = compare_df['差异'].abs().sort_values(ascending=False).head(8).index diff_plot = compare_df.loc[top_features, ['未流失', '流失']] diff_plot.plot(kind='bar', ax=ax, color=['green', 'red']) ax.set_title('流失 vs 未流失 用户特征对比(差异最大的8个特征)') ax.set_ylabel('均值') ax.set_xlabel('特征') ax.legend(['未流失', '流失']) ax.tick_params(axis='x', rotation=45) plt.tight_layout() plt.savefig(r'D:\Users\qimiao\Desktop\数据分析\2_feature_comparison.png', dpi=150) plt.show() # ============================================ # 4. 相关性矩阵分析 # ============================================ print("\n=== 4. 特征与流失的相关性 ===") correlations = df[numeric_cols + ['churned']].corr()['churned'].sort_values(ascending=False) print(correlations) # 可视化热力图 plt.figure(figsize=(14, 10)) corr_matrix = df[numeric_cols + ['churned']].corr() mask = np.triu(np.ones_like(corr_matrix, dtype=bool)) sns.heatmap(corr_matrix, mask=mask, annot=True, fmt='.2f', cmap='RdBu_r', center=0, square=True, linewidths=0.5) plt.title('特征相关性热力图', fontsize=14) plt.tight_layout() plt.savefig(r'D:\Users\qimiao\Desktop\数据分析\3_correlation_heatmap.png', dpi=150) plt.show() # ============================================ # 5. 关键特征的分组分析 # ============================================ print("\n=== 5. 关键特征分析 ===") # 5.1 账号年龄 vs 流失 print("\n5.1 不同账号年龄段的流失率:") df['age_group'] = pd.cut(df['account_age_months'], bins=[0, 3, 6, 12, 24, 120], labels=['0-3月', '4-6月', '7-12月', '13-24月', '24月以上']) age_churn = df.groupby('age_group')['churned'].agg(['count', 'mean']) age_churn.columns = ['用户数', '流失率'] print(age_churn) # 5.2 会员 vs 非会员 print("\n5.2 会员与非会员的流失率:") loyalty_churn = df.groupby('loyalty_member')['churned'].agg(['count', 'mean']) loyalty_churn.columns = ['用户数', '流失率'] print(loyalty_churn) # 5.3 客服工单数 vs 流失 print("\n5.3 不同客服工单数的流失率:") ticket_churn = df.groupby('customer_support_tickets')['churned'].agg(['count', 'mean']) ticket_churn.columns = ['用户数', '流失率'] print(ticket_churn) # 可视化:年龄分组流失率 fig, axes = plt.subplots(1, 3, figsize=(15, 4)) # 年龄分组 age_churn['流失率'].plot(kind='bar', ax=axes[0], color='coral') axes[0].set_title('不同账号年龄段的流失率') axes[0].set_xlabel('账号年龄') axes[0].set_ylabel('流失率') axes[0].tick_params(axis='x', rotation=45) # 会员 vs 非会员 loyalty_churn['流失率'].plot(kind='bar', ax=axes[1], color=['green', 'red']) axes[1].set_title('会员 vs 非会员 流失率') axes[1].set_xlabel('是否会员') axes[1].set_ylabel('流失率') axes[1].set_xticklabels(['会员(Yes)', '非会员(No)'], rotation=0) # 客服工单数 ticket_churn['流失率'].plot(kind='bar', ax=axes[2], color='steelblue') axes[2].set_title('不同客服工单数的流失率') axes[2].set_xlabel('客服工单数') axes[2].set_ylabel('流失率') plt.tight_layout() plt.savefig(r'D:\Users\qimiao\Desktop\数据分析\4_key_features_analysis.png', dpi=150) plt.show() # ============================================ # 6. 流失用户的画像总结 # ============================================ print("\n=== 6. 流失用户画像总结 ===") churned_users = df[df['churned'] == 1] retained_users = df[df['churned'] == 0] print("流失用户的典型特征(与未流失用户对比):") for col in numeric_cols: churned_mean = churned_users[col].mean() retained_mean = retained_users[col].mean() diff_pct = (churned_mean - retained_mean) / retained_mean * 100 direction = "↑ 更高" if diff_pct > 0 else "↓ 更低" if abs(diff_pct) > 5: # 只显示差异超过5%的特征 print(f" {col}: {direction} ({diff_pct:+.1f}%)") # ============================================ # 保存分析结果 # ============================================ # 将对比结果保存为CSV compare_df.to_csv(r'D:\Users\qimiao\Desktop\数据分析\feature_comparison.csv') print("\n分析完成!图表和结果已保存到桌面数据分析文件夹")

3.4 清洗后数据概览

指标数值
总样本数6,000
特征数量14
流失用户(churned=1)929
未流失用户(churned=0)5,071
整体流失率15.48%

关键字段示例:'Customer_ID', 'account_age_months', 'avg_order_value', 'total_orders', 'days_since_last_purchase', 'discount_usage_rate', 'return_rate', 'customer_support_tickets', 'loyalty_member', 'browsing_frequency_per_week', 'cart_abandonment_rate', 'product_review_score_avg', 'engagement_score', 'satisfaction_score', 'price_sensitivity_index', 'churned'

清洗结论:数据质量良好,无缺失值、无重复行,可直接用于分析。


第四阶段:探索性数据分析(EDA,Python)

4.1 流失用户对比分析

通过对比流失组(churned=1)和留存组(churned=0)的特征均值,识别关键差异:

特征流失组均值留存组均值差异方向
days_since_last_purchase79.5天20.1天2.96↑ 显著更高
engagement_score2.365.34-56%↓ 显著更低
customer_support_tickets1.110.810.37↑ 更高
total_orders7.078.73-19%↓ 更低
satisfaction_score7.78.15-5.50%↓ 略低

4.2 相关性分析

特征与流失的相关性说明
days_since_last_purchase0.77最强正相关
engagement_score-0.73最强负相关
customer_support_tickets0.11弱正相关
satisfaction_score-0.13弱负相关

EDA结论:参与度评分和距上次购买天数是预测流失的最强信号。


第五阶段:SQL分组分析与Cohort分析(DBeaver)

5.1 分析目标

在 DBeaver 中对清洗后的数据执行分组聚合,回答四个核心业务问题。

5.2 核心SQL查询与结果

查询1:整体流失率
SELECT COUNT(*) AS total_users, SUM(churned) AS churned_users, ROUND(CAST(SUM(churned) AS FLOAT) / COUNT(*) * 100, 2) AS churn_rate_pct FROM cleaned_ecommerce;
total_userschurned_userschurn_rate_pct
6,00092915.48%

查询2:会员 vs 非会员流失率
SELECT loyalty_member, COUNT(*) AS user_count, ROUND(AVG(churned) * 100, 2) AS churn_rate_pct FROM cleaned_ecommerce GROUP BY loyalty_member;
loyalty_memberuser_countchurn_rate_pct
No4,91516.91%
Yes1,0859.03%

会员流失率比非会员低约 7.9个百分点。

查询3:客服工单数 vs 流失率
SELECT customer_support_tickets, COUNT(*) AS user_count, ROUND(AVG(churned) * 100, 2) AS churn_rate_pct FROM cleaned_ecommerce GROUP BY customer_support_tickets ORDER BY customer_support_tickets;
ticketsuser_countchurn_rate_pct
02,66011.99%
12,07616.52%
285018.12%
330824.68%
4+10634.91%

工单≥3次的用户,流失率是平均水平的 2倍以上。

查询4:账号年龄分组流失率(Cohort)
SELECT CASE WHEN account_age_months <= 3 THEN '0-3月' WHEN account_age_months <= 6 THEN '4-6月' WHEN account_age_months <= 12 THEN '7-12月' WHEN account_age_months <= 24 THEN '13-24月' ELSE '24月以上' END AS age_group, COUNT(*) AS user_count, ROUND(AVG(churned) * 100, 2) AS churn_rate_pct FROM cleaned_ecommerce GROUP BY age_group;
age_groupuser_countchurn_rate_pct
0-3月30315.18%
4-6月29517.63%
7-12月56717.81%
13-24月1,20415.20%
24月以上3,63115.06%

流失最高峰出现在注册后4-12个月,而非新手期。

查询5:参与度评分 vs 流失率(最重要发现)
SELECT CASE WHEN engagement_score <= 2 THEN '低(0-2)' WHEN engagement_score <= 4 THEN '中低(2-4)' WHEN engagement_score <= 6 THEN '中(4-6)' WHEN engagement_score <= 8 THEN '中高(6-8)' ELSE '高(8-10)' END AS engagement_level, COUNT(*) AS user_count, ROUND(AVG(churned) * 100, 2) AS churn_rate_pct FROM cleaned_ecommerce GROUP BY engagement_level;
参与度等级用户数流失率
低(0-2)36599.73%
中低(2-4)1,05849.62%
中(4-6)3,2121.25%
中高(6-8)1,3650.00%

参与度评分≤2的用户几乎必流失,≥6的用户几乎不流失。 这是整个项目中最具预测力的留存指标。


第六阶段:Tableau可视化看板

6.1 看板组成

工作表名称图表类型核心信息
参与度评分 vs 留存率散点图/折线图评分<4流失率飙升,≥6留存稳定
客服工单数 vs 留存率柱状图/折线图工单≥3,留存率下降15%+
会员 vs 非会员留存率并排柱状图会员留存率91% vs 非会员83%
账号年龄分组留存率柱状图4-12个月是流失高峰期

6.2 看板截图

订阅用户留存与流失分析 | Tableau Public

项目延伸:A/B 测试方案

——验证“低参与度用户激活策略”对留存的影响

一、测试背景(基于项目核心发现)

本报告已发现:

参与度评分 < 4 的用户,留存率不足 50%;评分 < 2 的用户几乎必流失。

这说明:

  • 低参与度用户是流失的高危人群

  • 对这些用户进行早期干预,是提升留存的潜在杠杆

为此,设计如下 A/B 测试。


二、实验假设

  • 原假设 H₀:对低参与度用户进行干预,不会显著提升留存率

  • 备择假设 H₁:干预能显著提升 14 日留存率


三、实验对象与分组

项目说明
实验对象新注册用户,且前 7 天参与度评分 ≤ 4
分组方式随机均匀分流(50% / 50%)
对照组不施加任何额外干预(常规流程)
实验组施加「激活干预策略」
实验周期4 周(积累足够样本)
预估样本量每组 ≥ 500 人

四、实验组干预策略(可执行方案)

针对实验组用户在以下三个时间点,触发自动化干预:

时间点动作
注册后第 3 天个性化推送(如“发现你的兴趣好物”)
注册后第 7 天小额无门槛优惠券 + 浏览任务引导
注册后第 14 天专属活动提醒 + 会员权益预告

所有动作均可通过现有运营系统自动化完成。


五、核心指标

指标类型指标名称计算方式
核心指标14 日留存率注册后第 14 天仍有活跃行为的用户占比
辅助指标参与度评分变化实验前后评分提升幅度
护栏指标客单价确保干预不损害消费能力
反向指标优惠券滥用率避免“薅羊毛”行为

六、实验结果判断标准

使用Z 检验 / 卡方检验,显著性水平 α = 0.05:

结果结论后续动作
实验组 14 日留存率显著更高(p < 0.05)策略有效全量上线
无显著差异策略无效下线或重新设计干预方式
留存提升但客单价下降存在副作用优化优惠券门槛或任务设计

第七阶段:核心业务结论

基于以上分析,得出 4条核心结论:

结论1:参与度是留存的“前置预警指标”

参与度评分<4的用户,留存率低于50%;评分<2的用户,留存率接近0%。 建议:设定参与度<3.5为自动激活阈值(Push/优惠券/任务引导)。

结论2:会员身份对留存有独立正向影响

会员留存率91% vs 非会员83%,且在同龄段中差异依然显著。 建议:新用户注册流程中强化会员权益展示,对高价值非会员定向推送会员试用。

结论3:客服工单是“流失放大器”

工单≥3次的用户,留存率下降15–23个百分点。 建议:对高频投诉用户建立主动回访机制,产品侧减少用户求助场景。

结论4:流失最高峰在注册后4–12个月

该阶段留存率最低(82.2%),并非“新手期”。 建议:在第3/6/9个月设计系统化的留存干预动作。


第八部分:可落地业务建议汇总

优先级建议预期效果责任方
P0参与度<3.5用户自动激活流程挽回50%+中低参与用户运营+产品
P0工单≥3次用户主动回访降低流失率10–15%客服+运营
P1新注册用户会员引导强化长期降低流失2–3%产品+运营
P13/6/9个月留存干预活动平滑4-12月流失高峰运营+市场

第九部分:局限性与改进方向

9.1 当前局限

  • 数据为合成数据,缺乏真实业务的“脏乱”特征

  • 缺少时序数据,无法分析行为变化趋势

9.2 改进方向

  • 对接真实业务数据,增加数据清洗复杂度

  • 构造时序特征(如参与度下降速度、购买间隔变化趋势)

http://www.jsqmd.com/news/706321/

相关文章:

  • 【风暴之城】游玩日记 新手攻略(2)
  • 为Open WebUI构建安全代码执行沙箱:基于gVisor的本地LLM增强方案
  • 这些AI编曲软件到底强在哪,2026年度甄选5款AI编曲软件汇总,高质量助力音乐人制作编曲伴奏
  • 自洽性与Agent的结合
  • DeepSeek-V4-Pro API降价实测
  • 07.训练自己的数据集(上):标注与格式准备
  • Agent 安全性红队测试:如何防止 Prompt Injection 攻击你的智能体?
  • 基于RAG的ChatGPT文件检索工具:从原理到实践
  • 基于LangGraph的多智能体AI内容生成系统XunLong实战指南
  • 智能体的情景记忆
  • NLP文本表示方法对比:词袋、TF-IDF与LLM嵌入
  • 昨天晚上 口头发表政治评论 马桶提示:6 d 心
  • 深度学习实践
  • React Fiber 异步渲染原理讲解
  • 计算机视觉中图像数据预处理与增强技术详解
  • 为什么 Markdown 是大模型更优雅的对话格式?
  • 低功耗IoT自动调制识别:轻量特征+微型神经网络,一文吃透核心理论【附python代码】
  • GOSIM Spotlight 2026 Frontier Creators入围作品正式官宣!
  • Bridgic:轻量级数据集成平台的设计、实践与避坑指南
  • 「一文搞懂 Material Design:Toolbar 到 CollapsingToolbar 全攻略」
  • nli-MiniLM2-L6-H768在软件测试中的应用:自动化生成测试用例与断言
  • MAF快速入门()给Agent Skill添加脚本执行能力
  • C++面试题自用-持续更新
  • Save Image as Type终极指南:如何在Chrome中一键转换图片格式
  • Java开发者如何用LangChain4j构建企业级AI应用:从RAG到智能体
  • 基于T5模型的多语言翻译系统实战指南
  • 机器学习数据准备框架:提升模型效果的工程实践
  • 2026诚信入境旅游服务标杆名录:大陆居民赴台旅游/探险旅游/研学旅行定制/私人高端旅游定制/考古旅游/自驾游/选择指南 - 优质品牌商家
  • 2026中水处理设备标杆名录:安徽污水处理设备厂家/工业废水处理设备/废水处理处理设备/气浮机一体化污水处理设备/选择指南 - 优质品牌商家
  • VM图像处理(1、图像二值化和图像滤波,Sobel提取过程)