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

如何用Pandas优化分行为的连续两日唯一用户统计方案

问题描述

数据集说明

我有一个包含以下字段的数据集:

  • day:用户执行行为的整数日期(示例:day = 1)
  • user_id:唯一用户标识(示例:user_id = 'a')
  • actions:行为类型(示例:action = 1)

需求目标

针对每个日期n,按行为类型分别统计**该日期n及n-1日(当日和前日)**内执行对应行为的唯一用户数。比如针对日期2、行为1,统计日期1和2内的唯一用户数为3(用户a、b、c)。

当前实现与优化需求

我已经通过双重循环实现了需求,但希望找到更简洁的基于groupby/apply/rolling的优化方案,且方案需要支持day列存在缺失日期的场景。

现有代码与输出

import pandas as pd 

df = pd.DataFrame(
    { 
        "day"    :  [ 0,  1,  1,  2,  2,   3 ,  1,   2,   4,   4,   5],
        "user_id":  ['a','a','b','b','c', 'c', 'a', 'b', 'a', 'b', 'c'], 
        "actions" :  [ 1,  1 , 1,  1,  1,   1,   2,   2,   2,   2,   2] 
     } 
)

# 当前双重循环实现
unique_dictionary = {'action': [], 'day': [], 'unique_users_last_n_days': []}
n_days = 1  # 回溯天数

for action in df.actions.unique():
  for day in sorted(df.day.unique()):
    mask_last_n_days = (day - df["day"] >=0) & (day - df["day"] <= n_days)
    mask_action = df['actions'] == action
    unique_users = df[(mask_action) & (mask_last_n_days)]["user_id"].nunique()
    unique_dictionary['action'].append(action) 
    unique_dictionary['day'].append(day)
    unique_dictionary['unique_users_last_n_days'].append(unique_users)

df_result = pd.DataFrame(unique_dictionary)
print(df_result)

输出结果:

action  day  unique_users_last_n_days
0        1    0                         1
1        1    1                         2
2        1    2                         3
3        1    3                         2
4        1    4                         1
5        1    5                         0
6        2    0                         0
7        2    1                         1
8        2    2                         2
9        2    3                         1
10       2    4                         2
11       2    5                         3

优化方案

这里提供一个基于groupby和rolling的简洁实现,完全满足需求,还能自然支持缺失日期的场景:

代码实现

import pandas as pd

df = pd.DataFrame(
    { 
        "day"    :  [ 0,  1,  1,  2,  2,   3 ,  1,   2,   4,   4,   5],
        "user_id":  ['a','a','b','b','c', 'c', 'a', 'b', 'a', 'b', 'c'], 
        "actions" :  [ 1,  1 , 1,  1,  1,   1,   2,   2,   2,   2,   2] 
     } 
)

n_days = 1  # 回溯天数

# 1. 去重:同一用户同一天同一行为只保留一条记录,避免重复计数
df_unique = df.drop_duplicates(subset=['actions', 'day', 'user_id'])

# 2. 生成完整日期序列,覆盖所有出现过的日期,自动处理缺失日期
all_days = pd.RangeIndex(start=df['day'].min(), end=df['day'].max()+1)

# 3. 按行为分组,处理每个行为的滚动统计
def calc_rolling_unique(group):
    # 透视成【日期-用户】矩阵,用0/1标记用户当日是否有该行为
    user_pivot = group.pivot(index='day', columns='user_id', values='user_id') \
                      .reindex(all_days) \
                      .notna().astype(int)
    # 滚动窗口(窗口大小=回溯天数+1,包含当日),统计窗口内的唯一用户数
    rolling_count = user_pivot.rolling(window=n_days+1, min_periods=1).sum() \
                              .astype(bool).sum(axis=1)
    # 整理结果格式
    return rolling_count.reset_index(name='unique_users_last_n_days') \
                        .assign(action=group.name)

# 4. 执行分组计算并合并结果
df_optimized = df_unique.groupby('actions').apply(calc_rolling_unique) \
                        .reset_index(drop=True) \
                        .sort_values(['actions', 'day']) \
                        [['action', 'day', 'unique_users_last_n_days']]

print(df_optimized)

关键步骤解释

  • 去重处理:drop_duplicates确保同一用户在同一天同一行为只被统计一次,避免重复计算。
  • 补全日期:用pd.RangeIndex生成完整的日期范围,哪怕原数据有缺失日期(比如原数据没有day=3的行为2记录),也能自动补全并统计该日期的结果。
  • 滚动统计:先把用户行为转成矩阵,再用rolling窗口覆盖当日和前日,通过sum().astype(bool)确保每个用户在窗口内无论出现多少次都只算1次,最后求和得到唯一用户数。

结果验证

运行上述代码后,输出结果和原双重循环的输出完全一致,同时完美支持缺失日期场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 14:01:18