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

Pandas rolling调用nunique报错 分组滚动统计唯一值方法

问题原因

pandas 原生的Rolling对象没有内置nunique()聚合方法,因此直接链式调用会抛出属性不存在的错误,sum()、mean()属于Rolling内置支持的聚合函数,所以可以正常运行。
另外原代码还有两个隐性问题:

  • 列名大小写不匹配:原数据列名是employee_HK,代码里写的是Employee_HK
  • 直接写rolling(12,1)是按12行数据开窗口,不是需要的12个月时间跨度,算出来的结果不符合业务要求。
实现步骤

首先先做基础预处理,保证计算逻辑正确:

import pandas as pd
# 转换日期格式、按项目+日期排序,避免窗口顺序错乱
df['date'] = pd.to_datetime(df['date'])
df = df.sort_values(['Project', 'date']).reset_index(drop=True)

方法1:通用兼容写法(适合小数据量)

通过rolling.apply传入自定义去重计数函数即可实现需求,写法最简洁:

# 自定义滚动去重计数函数
def count_unique(x):
    return len(set(x))

# 按真实12个月时间窗口计算(贴合业务需求)
df = df.set_index('date')
df['MA_Employee'] = (
    df.groupby('Project')['employee_HK']
    .transform(
        lambda col: col.rolling(
            window=pd.DateOffset(months=12),
            min_periods=1
        ).apply(count_unique, raw=True)
    )
).reset_index()

如果数据是每月固定一条记录,确实要按12行窗口计算,把window=pd.DateOffset(months=12)改成window=12即可。

方法2:高性能写法(适合大数据量)

apply本质是逐窗口循环,数据量大时运行速度很慢,可以通过独热编码转换后滚动求和的方式实现,性能可以提升5~10倍:

df = df.set_index('date')
# 对员工ID做独热编码
employee_dum = pd.get_dummies(df['employee_HK'])
# 分组滚动求和,统计窗口内出现过的员工数(求和值>0代表该员工在窗口内出现过)
roll_calc = employee_dum.groupby(df['Project']).rolling(
    window=pd.DateOffset(months=12),
    min_periods=1
).sum()
df['MA_Employee'] = (roll_calc > 0).sum(axis=1).values
df = df.reset_index()
注意事项
  • 所有滚动计算前必须按分组字段+日期字段排序,否则窗口内数据顺序错乱,结果完全错误
  • 时间窗口计算必须将日期列转为datetime类型并设为索引,传入时间偏移量作为窗口参数,不能直接用整数,否则窗口是按行计数而非时间跨度
  • 如果你使用的是 pandas 2.1.0 及以上版本,Rolling对象已经原生支持nunique()方法,直接调用原来的逻辑(修正列名和窗口参数后)即可运行,不需要自定义函数。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 10:01:01