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

如何按组统计每行日期前n天内的唯一值数量

问题描述

现有如下pandas DataFrame:

groupIddatevalue
12023-01-01A
12023-01-05B
12023-01-17C
22023-01-01A
22023-01-20B
32023-01-01A
32023-01-10B
32023-01-12C

需求:按groupId分组,统计每组中当前行date的最近14天内的唯一value数量,期望结果如下:

groupIddatevaluenewColumn
12023-01-01A1
12023-01-05B2
12023-01-17C2
22023-01-01A1
22023-01-20B1
32023-01-01A1
32023-01-10B2
32023-01-12C3

尝试过groupby(...).rolling('14d').nunique(),但该方法对字符串/对象类型的value字段无法正确统计唯一值。

生成测试DataFrame的代码:

import pandas as pd

df = pd.DataFrame(
{
 'groupId': [1, 1, 1, 2, 2, 3, 3, 3],
 'date': ['2023-01-01', '2023-01-05', '2023-01-17', '2023-01-01', '2023-01-20', '2023-01-01', '2023-01-10', '2023-01-12'],
 'value': ['A', 'B', 'C', 'A', 'B', 'A', 'B', 'C'],
}
)
解决方案

方法1:分组后用apply遍历行筛选日期范围

先将date列转为datetime类型,按groupId分组后,对每组内的每行筛选出当前日期前14天内的记录,统计唯一value数量:

# 转换日期类型
df['date'] = pd.to_datetime(df['date'])

def count_unique_last14d(group):
    group['newColumn'] = group.apply(
        lambda row: group.loc[
            (group['date'] >= row['date'] - pd.Timedelta(days=14)) & 
            (group['date'] <= row['date']), 
            'value'
        ].nunique(),
        axis=1
    )
    return group

df_result = df.groupby('groupId').apply(count_unique_last14d).reset_index(drop=True)
print(df_result)

逻辑直观,适合小数据量场景,大数据量下效率较低。

方法2:自连接+日期过滤+分组统计

通过自连接匹配同组所有行,过滤出日期差在14天内的记录,再按原始行索引统计唯一值:

df['date'] = pd.to_datetime(df['date'])
# 添加索引列用于后续匹配
df['idx'] = df.index

# 同groupId的行两两配对
merged = df.merge(df, on='groupId', suffixes=('', '_other'))

# 保留当前日期前14天内的记录
filtered = merged[merged['date'] >= merged['date_other'] - pd.Timedelta(days=14)]

# 按原始行索引分组统计唯一value数
unique_counts = filtered.groupby('idx')['value_other'].nunique().reset_index(name='newColumn')

# 合并回原DataFrame
df_result = df.merge(unique_counts, on='idx').drop('idx', axis=1)
print(df_result)

利用矢量化操作,比apply效率更高,适合大数据量场景。

方法3:rolling结合自定义聚合函数

将字符串类型的value转为编码,再用自定义函数实现rolling窗口内的唯一值统计:

df['date'] = pd.to_datetime(df['date'])
# 将value转为类别编码
df['value_cat'] = df['value'].astype('category').cat.codes

def nunique_rolling(arr):
    return len(pd.unique(arr))

# 分组后应用rolling窗口+自定义聚合函数
df['newColumn'] = df.groupby('groupId').rolling('14d', on='date')['value_cat'].apply(nunique_rolling).reset_index(0, drop=True)

# 删除临时编码列
df = df.drop('value_cat', axis=1)
print(df)

保留rolling的使用逻辑,绕开原方法对字符串类型的限制。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 01:25:26