You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Pandas中计算接触特定课程后剩余的唯一用户数

高效处理千万级数据:计算课程接触后的学生流失与剩余统计

首先得说,1300万行用for循环肯定行不通——Python的循环在大数据量下效率太低了,得用pandas的向量化分组操作来解决,这能把速度提升几个数量级。下面直接给你可运行的方案,分两种常见的业务场景:


场景1:接触课程后完全不再有任何行为(即最后一次记录就是该课程)

这种场景下,流失学生指的是:某个学生的最后一次平台行为就是接触该课程,之后再也没出现过。

代码实现

import pandas as pd

# 第一步:确保timestamp是datetime类型(如果不是的话先转换)
df['timestamp'] = pd.to_datetime(df['timestamp'])

# 第二步:按用户和时间戳排序,保证行为的时间顺序正确
df_sorted = df.sort_values(by=['userid', 'timestamp']).reset_index(drop=True)

# 第三步:标记每个用户的最后一条行为记录
df_sorted['is_last_behavior'] = df_sorted.groupby('userid')['timestamp'].transform(
    lambda x: x == x.max()
)

# 第四步:统计核心指标
# 1. 每个课程的总接触学生数(去重)
total_users_per_lecture = df_sorted.groupby('lecture_id')['userid'].nunique()
# 2. 每个课程对应的流失学生数(最后一次行为是该课程的学生)
churned_users_per_lecture = df_sorted[df_sorted['is_last_behavior']]['lecture_id'].value_counts()

# 第五步:合并成统计DataFrame,填充缺失值(有些课程没有流失学生)
stats_df = pd.DataFrame({
    'total_users': total_users_per_lecture,
    'churned_users': churned_users_per_lecture.reindex(total_users_per_lecture.index, fill_value=0)
})

# 计算剩余学生数
stats_df['remaining_users'] = stats_df['total_users'] - stats_df['churned_users']

# 重置索引,让lecture_id成为普通列
stats_df = stats_df.reset_index()

场景2:接触课程后不再接触其他课程(但可能重复刷该课程)

如果你的“流失”定义是:学生接触某课程后,再也没接触过其他课程(但可以继续刷该课程),那需要调整逻辑,关注学生的「首次接触课程顺序」:

代码实现

import pandas as pd

# 预处理:转换时间戳+排序
df['timestamp'] = pd.to_datetime(df['timestamp'])
df_sorted = df.sort_values(by=['userid', 'timestamp'])

# 获取每个用户首次接触各课程的时间
user_first_contact = df_sorted.groupby(['userid', 'lecture_id'])['timestamp'].min().reset_index()

# 对每个用户,按首次接触时间排序,标记最后一个首次接触的课程
user_first_contact_sorted = user_first_contact.sort_values(by=['userid', 'timestamp'])
user_first_contact_sorted['is_last_new_lecture'] = user_first_contact_sorted.groupby('userid')['timestamp'].transform(
    lambda x: x == x.max()
)

# 统计指标
total_users = user_first_contact_sorted.groupby('lecture_id')['userid'].nunique()
churned_users = user_first_contact_sorted[user_first_contact_sorted['is_last_new_lecture']].groupby('lecture_id')['userid'].nunique()

# 合并统计结果
stats_df = pd.DataFrame({
    'total_users': total_users,
    'churned_users': churned_users.reindex(total_users.index, fill_value=0)
})
stats_df['remaining_users'] = stats_df['total_users'] - stats_df['churned_users']
stats_df = stats_df.reset_index()

为什么这比for循环快?

所有操作都是pandas的向量化分组/变换,底层用C语言实现,避开了Python循环的性能损耗。1300万行三列的数据,只要你的内存足够(大概几百MB以内),几分钟就能跑完,完全不会出现循环卡死的情况。

如果内存实在吃紧,可以考虑用dask.dataframe做分块处理,但上面的方案对大部分普通机器已经够用了。

内容的提问来源于stack exchange,提问作者Chad Coleman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:29:55