计算 Pandas 列中值的 3 个月滚动计数
Posted
技术标签:
【中文标题】计算 Pandas 列中值的 3 个月滚动计数【英文标题】:Calculate 3 Month Rolling Count of values in a column Pandas 【发布时间】:2021-12-08 07:37:28 【问题描述】:我有如下dataframe(这是dataframe的简化版,但逻辑是一样的):
#MONTH = yyyy-mm-dd
MONTH User
0 2021-04-01 A
1 2021-04-01 B
2 2021-05-01 B
3 2021-06-01 A
4 2021-06-01 B
5 2021-07-01 A
6 2021-07-01 B
7 2021-08-01 A
8 2021-08-01 B
我想要的是计算用户是否在 3 个月滚动的基础上处于活动状态。
例如,用户B
如果我们考虑 6 月 (2021-06-01),我们可以看到他在 5 月和 4 月处于活跃状态,因此在 3M 滚动的基础上,他被认为在 6 月处于活跃状态。而同一时间段内的用户A
,在三个月的其中一个月内没有活跃,因此在六月他将不会被视为活跃。
一个期望的输出是有一个列来统计每个月的活跃用户(300 万滚动),例如基于上述数据:
MONTH Active_User_Count
0 2021-04-01 NaN
1 2021-05-01 NaN
2 2021-06-01 1
3 2021-07-01 1
4 2021-08-01 2
我仍在努力了解滚动数据,所以如果有人能在这方面帮助我,那就太好了!提前致谢!
编辑 MONTH
列只有每个月的第一天的值,但当天有多个用户。所以没有 2021-04-30,都是每月的第一天。
【问题讨论】:
MONTH 列是字符串,还是 datetime.date 对象,还是其他? 听起来您正在尝试groupby
"User"
列,计算该用户是否在 3 个月滚动的基础上处于活动状态,然后在 groupby
"MONTH"
上汇总活动用户列?
@JoshuaVoskamp 它是一个日期时间对象
@JoshuaVoskamp 是的,简而言之,这就是我想要实现的目标!
如果您将'MONTH'
列对象类型更改为pandas.Timestamp
(与df['MONTH'] = df['MONTH'].apply(pd.Timestamp)
一样简单),那么您可以使用df['MONTH'].diff()
来比较值
【参考方案1】:
好吧,让我们试试这个。
假设有一个名为df
的pandas.DataFrame
,它有一个MONTH
类型为pandas.Timestamp
的列,以及一个User
列,我们可以groupby
:
import pandas as pd
import numpy as np
df = #[however you got your data here]
df.MONTH = df.MONTH.apply(pd.Timestamp)
例如
>>> df
MONTH User
0 2021-04-01 A
1 2021-04-01 B
2 2021-05-01 B
3 2021-06-01 A
4 2021-06-01 B
5 2021-07-01 A
6 2021-07-01 B
7 2021-08-01 A
8 2021-08-01 B
那么给定上面的内容,让我们做一个DataFrame来保存我们的结果,连续几个月从开始到结束输入DataFrame
,并将活跃用户数列初始化为0:
res = pd.DataFrame(pd.date_range(df.MONTH.min(),df.MONTH.max(),freq='MS'),columns=['MONTH'])
res['Active_User_Count'] = 0
res = res.set_index('MONTH').sort_index()
现在添加值:
for user, frame in df.groupby(by='User'):
# make a helper column, that has an indicator of whether the user
# was active that month (value='both') or not (value='right_only')
frame = frame.merge(
pd.Series(pd.date_range(start=frame.MONTH.min(),\
end=frame.MONTH.max(),\
freq='MS'),\
name='MONTH'),\
on='MONTH',how='outer',indicator=True)\
.set_index('MONTH').sort_index()
# this is where the magic happens;
# categorize the '_merge' results (0 = left_only, 1 = right_only, 2 = both)
# then on a 3-wide rolling window, get the minimum value
# check that it is greater than 1.5 (i.e. all three prev months
# are _merge value 'both')
# if it's not > 1.5, then the user wasn't active for all 3 months
# finally take the result from that rolling.min.apply,
# and funnel into a numpy.where array, which sets
# 'Active_User_Count' of the in-process user frame
# to an array of 1s and 0s
frame['Active_User_Count'] = np.where(
(frame._merge
.astype('category').cat.codes
.rolling(3).min().apply(lambda x: x > 1.5)), 1, 0)
# add the current-user activity into the total result
res.Active_User_Count[frame.index] += frame.Active_User_Count
# some re-formatting
res = res.reset_index().sort_index()
毕竟我们得到了我们的输出:
>>> res
MONTH Active_User_Count
0 2021-04-01 0
1 2021-05-01 0
2 2021-06-01 1
3 2021-07-01 1
4 2021-08-01 2
TL;DR
这里有一个函数来做这件事
import pandas as pd
import numpy as np
def active_users(df):
res = pd.DataFrame(pd.date_range(df.MONTH.min(),\
df.MONTH.max(),\
freq='MS'),\
columns=['MONTH'])
res['Active_User_Count'] = 0
res = res.set_index('MONTH').sort_index()
for user, frame in df.groupby(by='User'):
frame = frame.merge(pd.Series(
pd.date_range(start=frame.MONTH.min(),\
end=frame.MONTH.max(),\
freq='MS'),\
name='MONTH'),\
on='MONTH',\
how='outer',\
indicator=True)\
.set_index('MONTH').sort_index()
frame['Active_User_Count'] = np.where(
(frame._merge
.astype('category')
.cat.codes
.rolling(3).min().apply(lambda x: x > 1.5)), 1, 0)
res.Active_User_Count[frame.index] += frame.Active_User_Count
return res.reset_index().sort_index()
【讨论】:
它不是很漂亮,根据数据帧的大小,它可能无法执行,但它应该是您正在寻找的方向的开始。 这行得通 - 花了一些时间,因为我的数据框相当大,但输出是正确的!我认为这会更容易,非常感谢您的帮助! Re: 花了一些时间,这种方法使用 for 循环来迭代每个唯一用户,这对于大量用户来说效率很低。我确信有更好的方法;我不知道它是什么;)以上是关于计算 Pandas 列中值的 3 个月滚动计数的主要内容,如果未能解决你的问题,请参考以下文章
每月每个类别的分组计数(当前月份与过去几个月的剩余时间)在 pandas 的单独列中
excel中使用CORREL函数计算两个时间序列数据列之间的滚动相关性(Rolling correlations)例如,计算两种商品销售额之间的3个月的滚动相关性