In [45]:
from google.colab import drive
drive.mount('/content/drive')
Drive already mounted at /content/drive; to attempt to forcibly remount, call drive.mount("/content/drive", force_remount=True).
In [46]:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
In [47]:
file_path = '/content/drive/MyDrive/Wukong Data Test/adjusted_learning_data_with_summary_contents.csv'

df = pd.read_csv(file_path)
df.head()
Out[47]:
user_id session_date session_index duration content_id correct_rate paid next_session_date exit_flag
0 1 2025-06-01 1 6.4 content_3 0.78 False 2025-06-02 True
1 1 2025-06-02 2 14.8 content_1 0.79 False 2025-06-02 True
2 1 2025-06-02 3 7.1 content_4 0.62 False 2025-06-02 False
3 1 2025-06-02 4 11.9 content_8 0.72 False NaN True
4 2 2025-06-01 1 10.6 content_1 0.75 False 2025-06-01 False
In [48]:
# 将 session_date 转换为日期格式
df['session_date'] = pd.to_datetime(df['session_date'])

# 按 user_id 和 session_date 排序,确保行为顺序正确
df = df.sort_values(['user_id', 'session_date'])
In [49]:
# 计算每个用户下一次 session 的日期(向下取一行)
df['next_session_date'] = df.groupby('user_id')['session_date'].shift(-1)

# 计算当前学习与下一次学习之间的间隔天数
df['days_until_next'] = (df['next_session_date'] - df['session_date']).dt.days

# 判断是否为“次日回访”
df['next_day_retained'] = df['days_until_next'] == 1
In [50]:
# 只保留前5次学习,用于分析免费阶段的留存情况
df_retention = df[df['session_index'] <= 5].copy()

# 示例:判断用户是否至少学习过 5 次
user_stats = df.groupby("user_id").agg({
    'session_index': 'max',
    'paid': 'max'
})
user_stats['high_retention'] = user_stats['session_index'] >= 5
In [51]:
# 按学习次数分组,计算每次后的次留人数和总人数
retention_summary = df_retention.groupby('session_index')['next_day_retained'].agg(['count', 'sum'])

# 计算留存率,并保留3位小数
retention_summary['retention_rate'] = (retention_summary['sum'] / retention_summary['count']).round(3)

# 为绘图做准备,重置索引
retention_plot = retention_summary.reset_index()
In [52]:
# 设置画布大小
plt.figure(figsize=(8, 5))

# 绘制折线图
plt.plot(retention_plot['session_index'], retention_plot['retention_rate'],
         marker='o', linewidth=2, color='orange')

# 添加标题和标签(英文)
plt.title('Next-Day Retention Rate by Session Index', fontsize=14)
plt.xlabel('Session Index (Learning Times)', fontsize=12)
plt.ylabel('Next-Day Retention Rate', fontsize=12)

# 设置 X 轴刻度
plt.xticks(retention_plot['session_index'])

# 设置 Y 轴范围
plt.ylim(0, 1)

# 添加网格和优化布局
plt.grid(True)
plt.tight_layout()

# 显示图像
plt.show()
No description has been provided for this image

图表解读:Next-Day Retention Rate by Session Index¶

本图展示了用户在每一次学习(Session Index)后第二天是否继续回来学习的比例,即“次日留存率”。
横轴代表用户是第几次学习(从第 1 次到第 5 次),纵轴则是对应的次日留存率。

  • 留存率越高,表示该阶段对用户的吸引力越强,用户更愿意继续回来;
  • 留存率逐步下降,尤其在第 5 次学习后显著降低,可能预示着用户对产品逐渐失去兴趣,或预感到即将付费从而退出;
  • 可作为产品优化的重要参考,重点分析第 4、5 次学习的内容体验,设法提升用户在关键节点的留存意愿。
In [53]:
# 只保留退出的记录(exit_flag=True)
exit_df = df[df['exit_flag'] == True].copy()

# 每个内容的退出总人数
exit_count = exit_df.groupby('content_id').size().rename('exit_count')

# 每个内容退出后,次日未回来的人数(即 next_day_retained=False)
exit_lost_count = exit_df[exit_df['next_day_retained'] == False].groupby('content_id').size().rename('exit_and_churn_count')

# 合并数据
exit_analysis = pd.concat([exit_count, exit_lost_count], axis=1).fillna(0)

# 计算退出后的“次日流失率”
exit_analysis['churn_rate_after_exit'] = (exit_analysis['exit_and_churn_count'] / exit_analysis['exit_count']).round(3)

# 重置索引,便于查看
exit_analysis = exit_analysis.reset_index()

# 查看
exit_analysis
Out[53]:
content_id exit_count exit_and_churn_count churn_rate_after_exit
0 content_1 325 240 0.738
1 content_2 274 204 0.745
2 content_3 324 252 0.778
3 content_4 302 211 0.699
4 content_5 270 195 0.722
5 content_6 318 245 0.770
6 content_7 299 213 0.712
7 content_8 284 200 0.704
8 content_9 275 177 0.644
In [54]:
# 按流失率排序,方便看出谁最严重
exit_analysis_sorted = exit_analysis.sort_values(by='churn_rate_after_exit', ascending=False)

# 设置画布大小
plt.figure(figsize=(10, 6))

# 绘制横向条形图
plt.barh(exit_analysis_sorted['content_id'], exit_analysis_sorted['churn_rate_after_exit'], color='tomato')

# 添加标题和标签
plt.title('Next-Day Churn Rate After Exit by Content', fontsize=14)
plt.xlabel('Churn Rate After Exit', fontsize=12)
plt.ylabel('Content ID', fontsize=12)

# 添加网格和布局
plt.grid(axis='x', linestyle='--', alpha=0.7)
plt.tight_layout()

# 显示图像
plt.show()
No description has been provided for this image

图表解读:Next-Day Churn Rate After Exit by Content¶

该图展示了用户在不同内容(Content ID)页面退出后,第二天没有再回来的流失率(Churn Rate)。
横轴为流失率数值,纵轴为内容标识(content_id)。

  • 数值越高,说明用户在该内容界面退出后更可能流失,可能是界面设计、节奏、反馈等方面存在体验问题;
  • 可重点关注顶部流失率高的内容(如 content_3 和 content_6),评估其是否为结算页、引导不明确、学习任务密度大等场景;
  • 对于低流失率的内容,可借鉴其设计策略用于优化其他部分的用户体验。
In [55]:
# 每个模块的总出现次数(不管是否退出)
total_appearance = df['content_id'].value_counts().rename('total_count')

# 合并到之前的退出分析表中
exit_analysis = exit_analysis.merge(total_appearance, on='content_id')

# 计算退出率
exit_analysis['exit_rate'] = (exit_analysis['exit_count'] / exit_analysis['total_count']).round(3)
In [56]:
# 按退出率排序
exit_rate_sorted = exit_analysis.sort_values(by='exit_rate', ascending=False)

# 设置画布大小
plt.figure(figsize=(10, 6))

# 绘制横向条形图
plt.barh(exit_rate_sorted['content_id'], exit_rate_sorted['exit_rate'], color='steelblue')

# 添加标题和标签
plt.title('Exit Rate by Content', fontsize=14)
plt.xlabel('Exit Rate', fontsize=12)
plt.ylabel('Content ID', fontsize=12)

# 添加网格和布局优化
plt.grid(axis='x', linestyle='--', alpha=0.7)
plt.tight_layout()

# 显示图像
plt.show()
No description has been provided for this image

图表解读:不同内容的退出率与次日流失率分析¶

上方两张图分别展示了用户在不同内容页面的:

  • 退出率(Exit Rate):即有多少用户在该内容界面退出了应用;
  • 退出后的次日流失率(Next-Day Churn Rate After Exit):即退出后第二天未再回来的用户占比。

综合解读:

  • 某些内容如 content_3、content_6 显示了 极高的退出率与流失率,可能是结算页或缺乏有效引导、激励机制的界面,用户完成任务后倾向直接关闭;
  • 相反,content_9 的退出率虽然不低,但对应的次日流失率较低,说明这类内容虽易导致退出,但用户仍有回归动力,值得挖掘其设计优点;
  • 重点优化对象应为同时在两图中排名靠前的内容,说明用户在此界面体验不佳、后续回访动力不足,是当前流失的主要来源。

建议结合产品逻辑,确认这些内容是否为:

  • 任务中断点;
  • 结算/奖励页;
  • 新用户引导失败区域;
  • 存在加载延迟或互动反馈较差的问题区域。
In [57]:
# 按 correct_rate 分桶
df_exit_rate = df.copy()
df_exit_rate['correct_bin'] = pd.cut(df_exit_rate['correct_rate'],
                                     bins=[0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
                                     include_lowest=True)

# 计算每个正确率区间的退出率
exit_by_correct = df_exit_rate.groupby('correct_bin')['exit_flag'].mean().round(3).reset_index()
exit_by_correct.rename(columns={'exit_flag': 'exit_rate'}, inplace=True)

# 显示结果表
exit_by_correct
/tmp/ipython-input-57-490851674.py:8: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning.
  exit_by_correct = df_exit_rate.groupby('correct_bin')['exit_flag'].mean().round(3).reset_index()
Out[57]:
correct_bin exit_rate
0 (0.499, 0.6] 0.818
1 (0.6, 0.7] 0.805
2 (0.7, 0.8] 0.820
3 (0.8, 0.9] 0.789
4 (0.9, 1.0] 0.828
In [58]:
# 正确率区间 vs 退出率
plt.figure(figsize=(8, 5))
bars = plt.bar(exit_by_correct['correct_bin'].astype(str),
               exit_by_correct['exit_rate'],
               color='royalblue')

plt.title('Exit Rate by Correct Rate Interval', fontsize=14)
plt.xlabel('Correct Rate Interval', fontsize=12)
plt.ylabel('Exit Rate', fontsize=12)
plt.ylim(0, 1)
plt.grid(axis='y', linestyle='--', alpha=0.7)

# 添加柱子上的数值标签
for bar in bars:
    height = bar.get_height()
    plt.text(bar.get_x() + bar.get_width()/2, height + 0.02,
             f'{height:.2f}', ha='center', va='bottom', fontsize=10)

plt.tight_layout()
plt.show()
No description has been provided for this image

图表解读:正确率区间与退出率的关系¶

本图展示了不同正确率区间下的用户退出率(Exit Rate),用于探究“表现好坏”是否影响用户是否退出应用。

解读要点:

  • 各区间的退出率整体 差异不大,均在 0.79 ~ 0.83 之间;
  • 表现最好的区间 (0.9, 1.0] 并未显示出更高的留存,反而退出率略高;
  • 正确率最低的区间 (0.499, 0.6] 退出率为 0.82,说明低表现用户也没有更明显的流失倾向;
  • 唯一小幅下降的是 (0.8, 0.9] 区间的用户,可能是在“表现良好但未达满分”阶段形成了继续学习的动机。

初步结论: 正确率与退出率之间 不构成强关联,因此产品设计上不应仅基于“答对题多不多”来判断用户是否可能流失。需要考虑其他维度(如学习时间、引导设计、内容兴趣匹配度)来进一步细化流失原因。

In [59]:
# 将正确率按区间分桶(例如 0.5~0.6, 0.6~0.7, ..., 0.9~1.0)
df_retention_rate = df.copy()
df_retention_rate['correct_bin'] = pd.cut(df_retention_rate['correct_rate'],
                    bins=[0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
                    include_lowest=True)

# 对每个正确率区间,计算 next_day_retained 的均值
correct_retention = df_retention_rate.groupby('correct_bin')['next_day_retained'].mean().round(3).reset_index()
correct_retention.rename(columns={'next_day_retained': 'avg_next_day_retention'}, inplace=True)

# 显示结果
correct_retention
/tmp/ipython-input-59-135271496.py:8: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning.
  correct_retention = df_retention_rate.groupby('correct_bin')['next_day_retained'].mean().round(3).reset_index()
Out[59]:
correct_bin avg_next_day_retention
0 (0.499, 0.6] 0.244
1 (0.6, 0.7] 0.230
2 (0.7, 0.8] 0.220
3 (0.8, 0.9] 0.195
4 (0.9, 1.0] 0.231
In [60]:
# 正确率 vs 次日留存率
plt.figure(figsize=(8, 5))
bars = plt.bar(correct_retention['correct_bin'].astype(str),
               correct_retention['avg_next_day_retention'],
               color='mediumseagreen')

plt.title('Next-Day Retention Rate by Correct Rate', fontsize=14)
plt.xlabel('Correct Rate Interval', fontsize=12)
plt.ylabel('Next-Day Retention Rate', fontsize=12)
plt.ylim(0, 1)
plt.grid(axis='y', linestyle='--', alpha=0.7)

# 添加数值标签
for bar in bars:
    height = bar.get_height()
    plt.text(bar.get_x() + bar.get_width()/2, height + 0.02,
             f'{height:.2f}', ha='center', va='bottom', fontsize=10)

plt.tight_layout()
plt.show()
No description has been provided for this image

图表解读:正确率区间与次日留存率的关系¶

本图展示了用户在不同正确率区间下的次日留存率(Next-Day Retention Rate),用于分析“答题表现”是否会影响用户是否会在次日继续使用产品。

解读要点:

  • 整体留存率均较低,波动范围在 0.20 到 0.24 之间;
  • 正确率较高(如 0.9 以上)的用户并未表现出显著更高的留存率;
  • 正确率在 (0.8, 0.9] 区间的用户反而次日留存最低,仅为 20%;
  • 正确率最低的 (0.499, 0.6] 区间反而略高,为 24%。

初步结论: 本图显示,正确率并非决定留存的关键因素。这提示我们,用户是否留下可能更多受到外部动机、学习体验或产品节奏等影响,而不仅仅是“玩得好不好”。

In [61]:
# 先为每个用户添加 “是否最终付费” 标签(只保留1条记录)
user_paid = df.groupby('user_id')['paid'].max().reset_index().rename(columns={'paid': 'final_paid'})

# 计算每位用户的平均正确率
user_correct = df.groupby('user_id')['correct_rate'].mean().reset_index().rename(columns={'correct_rate': 'avg_correct_rate'})

# 合并两者
correct_vs_paid = pd.merge(user_correct, user_paid, on='user_id')

# 将正确率按区间分桶
correct_vs_paid['correct_bin'] = pd.cut(correct_vs_paid['avg_correct_rate'],
                                        bins=[0.5, 0.6, 0.7, 0.8, 0.9, 1.0],
                                        include_lowest=True)

# 每个区间内的付费率
paid_by_correct = correct_vs_paid.groupby('correct_bin')['final_paid'].mean().round(3).reset_index()
paid_by_correct.rename(columns={'final_paid': 'paid_rate'}, inplace=True)

# 显示结果
paid_by_correct
/tmp/ipython-input-61-1343709252.py:16: FutureWarning: The default of observed=False is deprecated and will be changed to True in a future version of pandas. Pass observed=False to retain current behavior or observed=True to adopt the future default and silence this warning.
  paid_by_correct = correct_vs_paid.groupby('correct_bin')['final_paid'].mean().round(3).reset_index()
Out[61]:
correct_bin paid_rate
0 (0.499, 0.6] 0.109
1 (0.6, 0.7] 0.174
2 (0.7, 0.8] 0.249
3 (0.8, 0.9] 0.191
4 (0.9, 1.0] 0.066
In [62]:
# 平均正确率 vs 最终付费率
plt.figure(figsize=(8, 5))
bars = plt.bar(paid_by_correct['correct_bin'].astype(str),
               paid_by_correct['paid_rate'],
               color='salmon')

plt.title('Final Paid Rate by Avg Correct Rate', fontsize=14)
plt.xlabel('Avg Correct Rate Interval (per user)', fontsize=12)
plt.ylabel('Paid Rate', fontsize=12)
plt.ylim(0, 1)
plt.grid(axis='y', linestyle='--', alpha=0.7)

# 添加数值标签
for bar in bars:
    height = bar.get_height()
    plt.text(bar.get_x() + bar.get_width()/2, height + 0.02,
             f'{height:.2f}', ha='center', va='bottom', fontsize=10)

plt.tight_layout()
plt.show()
No description has been provided for this image

图表解读:用户平均正确率与最终付费率的关系¶

该图展示了用户在不同平均正确率区间下的最终付费比例(Paid Rate),旨在探究学习表现与付费行为之间的关联。

解读要点:

  • 平均正确率在 0.7 到 0.8 区间的用户付费率最高,达到 25%,显著高于其他区间;
  • 正确率较低的用户(0.499–0.6)付费率为 11%,仍具一定意愿尝试;
  • 值得注意的是,正确率最高区间(0.9–1.0)用户的付费率最低,仅为 7%,出现反向趋势。

初步结论: 学习表现最优的用户未必是最具商业价值的用户。中等难度带来的“挑战感”可能更易驱动转化,而答题过于轻松可能导致用户缺乏继续付费的动力。后续可进一步分析此类用户的学习路径和停留时间。

In [63]:
# 按 content_id 分组,计算平均学习时长、退出率、留存率
content_stats = df.groupby('content_id').agg(
    avg_duration=('duration', 'mean'),
    exit_rate=('exit_flag', 'mean'),
    retention_rate=('next_day_retained', 'mean')
).round(3).reset_index()

# 查看结果
content_stats.sort_values(by='avg_duration', ascending=False)
Out[63]:
content_id avg_duration exit_rate retention_rate
7 content_8 9.194 0.791 0.234
3 content_4 9.064 0.774 0.233
1 content_2 9.036 0.753 0.192
2 content_3 8.876 0.913 0.203
8 content_9 8.849 0.743 0.265
6 content_7 8.834 0.793 0.228
4 content_5 8.812 0.754 0.209
5 content_6 8.679 0.893 0.205
0 content_1 8.677 0.898 0.235
In [64]:
# 不同内容的平均学习时长 vs 退出率
plt.figure(figsize=(8, 5))
sns.scatterplot(data=content_stats,
                x='avg_duration',
                y='exit_rate',
                hue='content_id',
                s=100)

plt.title('Avg Duration vs Exit Rate by Content')
plt.xlabel('Average Duration (minutes)')
plt.ylabel('Exit Rate')
plt.grid(True)
plt.tight_layout()
plt.show()
No description has been provided for this image

图表解读:不同内容的平均学习时长与退出率关系¶

该图为每个 content_id(内容界面)对应的平均学习时长(横轴)与退出率(纵轴)的散点图,帮助我们理解:不同内容页面的停留时间是否与用户退出行为有关。

解读要点:

  • content_1、content_3、content_6 三个点聚集在高退出率(接近 0.9)但停留时长较短的位置(约 8.7 分钟),这些通常为结算类页面,完成任务后用户易自然流失;
  • 其余内容点分布较为分散,说明有的内容虽然平均停留时间长,但并未显著降低退出率(例如 content_8);
  • 整体上,学习时长与退出率并无显著线性相关关系,但可以将高退出率内容单独识别并重点优化。

产品优化建议:

  • 针对高退出率、高停留时间的内容,需重点审查是否存在“任务完成即离开”的机制;
  • 可通过在这些页面加入推荐、引导式交互或奖励提示,延长用户生命周期。
In [65]:
# 不同章节的平均学习时长 vs 次日留存率
plt.figure(figsize=(8, 5))
sns.scatterplot(data=content_stats,
                x='avg_duration',
                y='retention_rate',
                hue='content_id',
                s=100)

plt.title('Avg Duration vs Next-Day Retention Rate by Content')
plt.xlabel('Average Duration (minutes)')
plt.ylabel('Retention Rate')
plt.grid(True)
plt.tight_layout()
plt.show()
No description has been provided for this image

图表解读:不同内容的平均学习时长与次日留存率关系¶

该图展示了每个 content_id(内容页面)对应的平均学习时长(横轴)与次日留存率(纵轴)的散点图,目的是分析:用户在某个内容页面上停留越久,是否更可能次日继续使用产品。

解读要点:

  • 整体来看,学习时长与次留存之间并没有明显的线性正相关;
  • 部分内容页面如 content_9 表现突出:停留时间适中但留存率高,可能是体验设计良好、激励机制有效;
  • 相反,content_2 的停留时长很长但次日留存很低,提示用户虽在页面停留较久,但转化为持续使用意愿较弱,值得关注其内容质量或引导设计;
  • content_6 与 content_3 同时处于低停留与低留存的区域,是优先改进对象。

产品优化建议:

  • 对于“高停留-低留存”页面,建议引入更多激励或强化引导机制,避免用户“耗时但无留存”;
  • 对于“高留存”页面(如 content_9),建议复用其设计逻辑或交互机制,提升其他内容效果。
In [66]:
# 查看退出率分布
content_stats['exit_rate'].describe()
Out[66]:
exit_rate
count 9.000000
mean 0.812444
std 0.068928
min 0.743000
25% 0.754000
50% 0.791000
75% 0.893000
max 0.913000

In [67]:
# 查看平均学习时长分布
content_stats['avg_duration'].describe()
Out[67]:
avg_duration
count 9.000000
mean 8.891222
std 0.174993
min 8.677000
25% 8.812000
50% 8.849000
75% 9.036000
max 9.194000

In [68]:
# 按退出率降序查看前3名
content_stats.sort_values('exit_rate', ascending=False).head(3)
Out[68]:
content_id avg_duration exit_rate retention_rate
2 content_3 8.876 0.913 0.203
0 content_1 8.677 0.898 0.235
5 content_6 8.679 0.893 0.205
In [69]:
# 筛选退出率高于 0.78,学习时长大于 8.9 的 (太难花了太多时间不想继续)
suspicious = content_stats[
    (content_stats['exit_rate'] > 0.78) &
    (content_stats['avg_duration'] > 8.9)
]
suspicious
Out[69]:
content_id avg_duration exit_rate retention_rate
7 content_8 9.194 0.791 0.234
In [70]:
# 添加可疑标记列
content_stats['is_suspicious'] = (
    (content_stats['exit_rate'] > 0.78) &
    (content_stats['avg_duration'] > 8.9)
)

plt.figure(figsize=(8, 5))
sns.scatterplot(data=content_stats,
                x='avg_duration',
                y='exit_rate',
                hue='is_suspicious',
                style='content_id',
                s=100)

plt.title('Avg Duration vs Exit Rate by Content')
plt.xlabel('Average Duration')
plt.ylabel('Exit Rate')
plt.grid(True)
plt.tight_layout()
plt.show()
No description has been provided for this image

图表解读:平均停留时长与退出率(含是否异常)¶

该图可视化了各个内容页的平均停留时长(横轴)与退出率(纵轴),并用颜色和图形标记哪些页面为疑似异常页(如结算页、快速跳出页等)。

图例说明:

  • 蓝色点:正常内容页(False)
  • 橙色点:疑似异常页(True),在此图中仅有一个内容页为异常页;
  • 点的形状代表 content_id,便于跨图追踪。

洞察要点:

  • 大多数正常页面在平均时长为 8.7 ~ 9.2 分钟之间,退出率集中在 0.74 ~ 0.90 区间;
  • 异常页面(橙色点)处于右边:具有较长平均停留时长(约 9.2 分钟)和较高退出率(约 0.79),可能是结算页或其他逻辑终止页;
  • 正常页面中,如 content_3 停留时间长但退出率很高,可能是体验问题页;
  • content_9、content_5 等页面退出率低、停留时间适中,表现优秀。

建议操作:

  • 对异常页面做逻辑归类处理,在分析中剔除或单独看待;
  • 对“高退出-长停留”的正常页进行进一步调研,识别是否存在用户卡顿、任务中断等问题;
  • 借鉴低退出率页面的设计机制,改善整体页面表现。
In [71]:
# 按用户聚合生成用户画像
user_summary = df.groupby("user_id").agg({
    "duration": "mean",               # 平均学习时长
    "correct_rate": "mean",             # 平均正确率
    "exit_flag": "mean",              # 平均退出率
    "next_session_date": "count",          # 学习次数
    "paid": "max"                  # 是否最终付费
}).rename(columns={
    "duration": "avg_duration",
    "correct_rate": "avg_correct_rate",
    "exit_flag": "exit_rate",
    "next_session_date": "session_count",
    "paid": "paid"
}).reset_index()

# 显示前几行
user_summary.head()
Out[71]:
user_id avg_duration avg_correct_rate exit_rate session_count paid
0 1 10.050 0.7275 0.750000 3 False
1 2 9.600 0.8200 0.666667 2 False
2 3 4.800 0.6600 1.000000 0 False
3 4 9.675 0.7475 0.750000 3 False
4 5 8.750 0.7900 1.000000 1 False
In [72]:
# 简单定义:学习次数 >= 3 视为高留存
user_summary['high_retention'] = user_summary['session_count'] >= 3
In [73]:
# 可视化高留存 vs 流失的特征对比
# 设置画布为 1 行 2 列
fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# 第一张图:Correct Rate
sns.boxplot(data=user_summary, x='high_retention', y='avg_correct_rate', ax=axes[0])
axes[0].set_title('Correct Rate by Retention')
axes[0].set_xlabel('High Retention')
axes[0].set_ylabel('Average Correct Rate')

# 第二张图:Avg Duration
sns.boxplot(data=user_summary, x='high_retention', y='avg_duration', ax=axes[1])
axes[1].set_title('Avg Duration by Retention')
axes[1].set_xlabel('High Retention')
axes[1].set_ylabel('Average Duration')

# 布局优化
plt.tight_layout()
plt.show()
No description has been provided for this image

图表解读:高留存用户的行为特征¶

该图包含两个箱线图(Boxplot):

  1. 左图:平均正确率(Average Correct Rate)与是否高留存
  2. 右图:平均停留时长(Average Duration)与是否高留存

左图观察:

  • 高留存用户(True)与低留存用户(False)在平均正确率上中位数相近;
  • 但高留存用户分布更集中,说明其答题行为更稳定;
  • 部分低留存用户的正确率波动大,存在极低或极高的异常点。

右图观察:

  • 高留存用户的平均停留时长整体略高于低留存群体,分布集中;
  • 低留存用户中存在大量短停留时长的用户(<5分钟),可能是体验较差或快速流失用户;
  • 同时也有一些异常点显示低留存群体中存在少量停留很久但依然流失的个体。

初步结论:

  • 平均停留时长比正确率更能区分是否高留存用户;
  • 为提高次留率,可优化内容结构和引导机制,延长用户首次停留时间;
  • 针对低停留高正确的流失用户,可能存在内容难度不均、跳转机制不合理的问题,应做后续排查。

建议:

  • 针对低时长流失用户设置提示或引导,鼓励完成更多内容;
  • 考虑引入“进阶挑战”机制,吸引正确率高但快流失的用户持续参与。
In [79]:
# 选择特征列
X = user_summary[['avg_duration', 'avg_correct_rate', 'exit_rate']]
y = user_summary['high_retention']

# 拆分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)

# 训练逻辑回归
model = LogisticRegression()
model.fit(X_train, y_train)

# 查看特征重要性(系数)
coefficients = pd.DataFrame({
    'Feature': X.columns,
    'Coefficient': model.coef_[0]
})
coefficients.sort_values(by='Coefficient', ascending=False)
Out[79]:
Feature Coefficient
1 avg_correct_rate 0.517502
0 avg_duration 0.066054
2 exit_rate -2.943294

特征重要性分析:逻辑回归模型结果¶

使用逻辑回归模型对用户是否为高留存群体进行建模,选取的三个自变量是:

  • avg_duration(用户平均停留时长)
  • avg_correct_rate(用户平均正确率)
  • exit_rate(用户退出率)

模型训练后提取的系数如下:

特征名称 系数 (Coefficient)
avg_correct_rate 0.518
avg_duration 0.066
exit_rate -2.943

结论解释:

  • exit_rate 的权重最大(-2.943),说明在本模型中,退出率是影响用户高留存概率的最关键因素,且呈强烈负相关,即退出率越高,用户越不可能次日留存;
  • avg_correct_rate 的权重次之(0.518),正确率仍是次日留存的重要正向指标,体现用户答题体验质量对留存的促进作用;
  • avg_duration 的权重最小(0.066),虽对留存仍有一定影响,但作用相对较弱;

结合前面的箱线图和可视化分析,说明 用户退出行为(exit_rate)和答题体验质量(avg_correct_rate) 对于次日留存的影响最为显著。


建议方向:

  • 降低用户在内容页中的退出率,可通过优化内容结构、减少干扰因素、提升引导性来实现;
  • 强化内容引导与教学设计,提升用户答题正确率;
  • 在前5次免费体验中适当安排难易度,避免过难或过简导致的用户流失;
  • 对于答题正确率高但留存不佳的用户,结合退出率进一步个性化推送内容或互动机制。