对于给定的月份,找出是否至少有一个8天的周期,其中7天标记为TRUE(python)

2024-05-16 01:37:03 发布

您现在位置:Python中文网/ 问答频道 /正文

好的,这是一个很难描述的问题

我正在为一项研究准备一份合规性报告,团队需要知道每个用户是否在8天中的7天内每月至少执行一次特定任务

因此,我需要一种方法:

  1. 对于给定用户和给定月份,在该月份搜索8天期间,其中至少有7天标记为“真”
  2. 如果满足要求,则返回1;如果不满足要求,则返回0

以下是数据结构的一个示例:

import pandas as pd

ids = 1
req_met = ['TRUE', 'TRUE', 'FALSE', 'FALSE', 'TRUE', 'TRUE', 'TRUE', 'FALSE', 'TRUE', 'TRUE', 'TRUE', 'TRUE', 'FALSE', 'FALSE', 'FALSE', 'FALSE', 'TRUE', 'TRUE', 'TRUE', 'FALSE']
date = ['2018-01-01', '2018-01-03', '2018-01-04', '2018-01-05', '2018-01-06', '2018-01-07', '2018-01-08', '2018-01-09', '2018-01-10', '2018-01-11',
'2018-01-12',  '2018-01-13', '2018-01-14', '2018-01-15', '2018-01-16', '2018-01-17', '2018-01-18', '2018-01-19', '2018-01-21', '2018-01-23']

df = pd.DataFrame({'id':ids,
                   'Date':date,
                   'req_met':req_met})
print(df)

    id        Date req_met
0    1  2018-01-01    TRUE
1    1  2018-01-03    TRUE
2    1  2018-01-04   FALSE
3    1  2018-01-05   FALSE
4    1  2018-01-06    TRUE
5    1  2018-01-07    TRUE
6    1  2018-01-08    TRUE
7    1  2018-01-09   FALSE
8    1  2018-01-10    TRUE
9    1  2018-01-11    TRUE
10   1  2018-01-12    TRUE
11   1  2018-01-13    TRUE
12   1  2018-01-14   FALSE
13   1  2018-01-15   FALSE
14   1  2018-01-16   FALSE
15   1  2018-01-17   FALSE
16   1  2018-01-18    TRUE
17   1  2018-01-19    TRUE
18   1  2018-01-21    TRUE
19   1  2018-01-23   FALSE

对于该用户,返回的答案为“1”,因为他们有8天的期限,其中7天为“真”(2018-01-06至2018-01-13)。您可以看到,日期范围并不总是连续的,这增加了复杂性

我期望的输出将是最有效的函数,它可以获取此数据并返回“1”(满足要求)或“0”(未满足要求)

提前感谢您的帮助


Tags: 方法用户idfalsetrueidsdfdate
2条回答

编辑:糟糕,我误解了你对我问题的回答,以为你在确认8天的窗口需要连续。既然不是这样,@ipj的答案很有效

我将把这个留给未来的读者,他们想要类似的东西,但需要连续的窗口

旧的答案是,当我错误地认为对于任何给定的8天窗口,日期必须是连续的

首先,我将用不同的id值连接两个副本:

df1 = pd.DataFrame({'id':ids, 'Date': date, 'req_met': req_met})

df2 = df1.copy()
df2.id = 2

df3 = df1.copy()
df3.id = 3

df = pd.concat([df1, df2, df3]).reset_index(drop=True)
df.Date = pd.to_datetime(df.Date)

现在,为不是连续日期的行(从this answer修改)创建一个掩码:

>>> mask = (df.Date.diff(-1).dt.days == -1) | (df.Date.diff().dt.days == 1)

现在您可以进行滚动求和:

>>> (df[mask].groupby("id").req_met.rolling(8).sum() >= 7).groupby("id").sum()
id
1    1
2    1
3    1
Name: req_met, dtype: int64

这三个都是1,因为我刚刚复制了三次原始数据帧,但这应该适用于任何实际的数据帧。如果您的数据尚未按日期分组,则需要将其添加到groupby

首先将类型转换为booldatetime

df['req_met'] = df['req_met'].replace({'TRUE':True, 'FALSE':False})
df['Date'] = pd.to_datetime(df.Date)

如果缺少日期,并且我们假设用户当天没有记录任何活动,我们需要插入缺少的日期:

df = (df.set_index('Date')
        .groupby('id').req_met
        .resample('D').sum()
        .reset_index()
        )

df['Month'] = df.Date.dt.strftime("%Y-%m")

请注意,resample用于每个id用户,以确保每个用户连续的日历日。 然后使用rolling方法:

df_result = (df.groupby(['id','Month'])
             .rolling(8)['req_met'].sum().ge(7)
             .groupby(['id','Month'])
             .agg({('req_met','max')})
             .reset_index()
             )

结果是:

   id    Month  req_met
0   1  2018-01     True

请注意groupby使用了两次。您可以通过逐步运行代码来检查计算,以完全理解逻辑

相关问题 更多 >