按日期范围分组 pandas

Grouping by date range with pandas

我希望按两列分组:user_id 和日期;但是,如果日期足够接近,我希望能够相应地考虑同一组和组的两个条目部分。日期是 m-d-y

user_id     date       val
1           1-1-17     1
2           1-1-17     1
3           1-1-17     1
1           1-1-17     1
1           1-2-17     1
2           1-2-17     1
2           1-10-17    1
3           2-1-17     1

分组将按 user_id 和彼此相差 +/- 3 天的日期进行分组。所以通过求和 val 的组看起来像:

user_id     date       sum(val)
1           1-2-17     3
2           1-2-17     2
2           1-10-17    1
3           1-1-17     1
3           2-1-17     1

有人能想到这可以(稍微)轻松地完成吗?我知道这有一些问题。例如,如果日期无休止地串在一起并且相隔三天,该怎么办。但是我使用的确切数据每人只有 2 个值..

谢谢!

我会将其转换为 datetime 列,然后使用 pd.TimeGrouper:

dates =  pd.to_datetime(df.date, format='%m-%d-%y')
print(dates)
0   2017-01-01
1   2017-01-01
2   2017-01-01
3   2017-01-01
4   2017-01-02
5   2017-01-02
6   2017-01-10
7   2017-02-01
Name: date, dtype: datetime64[ns]

df = (df.assign(date=dates).set_index('date')
        .groupby(['user_id', pd.TimeGrouper('3D')])
        .sum()
        .reset_index())    
print(df)
   user_id       date  val
0        1 2017-01-01    3
1        2 2017-01-01    2
2        2 2017-01-10    1
3        3 2017-01-01    1
4        3 2017-01-31    1

使用pd.Grouper的类似解决方案:

df = (df.assign(date=dates)
        .groupby(['user_id', pd.Grouper(key='date', freq='3D')])
        .sum()
        .reset_index())
print(df)
   user_id       date  val
0        1 2017-01-01    3
1        2 2017-01-01    2
2        2 2017-01-10    1
3        3 2017-01-01    1
4        3 2017-01-31    1

更新:TimeGrouper 将在 pandas 的未来版本中弃用,因此在这种情况下 Grouper 将是首选(感谢提醒,Vaishali!)。

我提出了一个非常丑陋的解决方案,但仍然有效...

df=df.sort_values(['user_id','date'])
df['Key']=df.sort_values(['user_id','date']).groupby('user_id')['date'].diff().dt.days.lt(3).ne(True).cumsum()
df.groupby(['user_id','Key'],as_index=False).agg({'val':'sum','date':'first'})

Out[586]: 
   user_id  Key  val       date
0        1    1    3 2017-01-01
1        2    2    2 2017-01-01
2        2    3    1 2017-01-10
3        3    4    1 2017-01-01
4        3    5    1 2017-02-01