Pandas 在满足一定条件的情况下,熊猫统计过去N天内发生的次数

Pandas 在满足一定条件的情况下,熊猫统计过去N天内发生的次数,pandas,Pandas,假设我有下面的数据框,并且我想计算过去14天内同一类别中出现“True”的次数,我该怎么做?例如,以下dataframe将生成一个值为0,1,1,0,2,0,1,0的列 Date Category has_egg 2017-01-01 Lunch True 2017-01-02 Lunch True 2017-01-02 Lunch False 2017-01-02 Dinner True 2017-01-12 Lu

假设我有下面的数据框,并且我想计算过去14天内同一类别中出现“True”的次数,我该怎么做?例如,以下dataframe将生成一个值为0,1,1,0,2,0,1,0的列

Date         Category   has_egg
2017-01-01   Lunch      True
2017-01-02   Lunch      True 
2017-01-02   Lunch      False
2017-01-02   Dinner     True
2017-01-12   Lunch      False
2017-01-13   Breakfast  False  
2017-01-13   Dinner     False
2017-02-04   Lunch      True
我尝试使用GROUPBY,但无法理解确切的命令

df.groupby("Category").has_egg.count_number_of_True(time_delta(-14d)) ?

我认为只要将
重采样
滚动
groupby
相结合,就可以得到一个非常通用的解决方案。(请注意,下面的代码假设您的索引是正确的python/datetime。如果不是,您需要首先使用
pd.将其转换为\u datetime

resample
行只是纠正了一个事实,即每个日期/类别可以有多行或少行。然后,您可以以非常简单的方式使用
rolling

以下是部分输出:

Lunch     Lunch     2017-01-01      1.0
                    2017-01-02      2.0
                    . . .

                    2017-01-14      2.0
                    2017-01-15      1.0
                    2017-01-16      0.0
或者,为了简洁起见,以下是每周的情况:

df.groupby('Category').resample('w').sum().fillna(0).\
   groupby('Category').rolling(2,min_periods=1).sum()

                                has_egg
Category  Category  Date               
Breakfast Breakfast 2017-01-15      0.0
Dinner    Dinner    2017-01-08      1.0
                    2017-01-15      1.0
Lunch     Lunch     2017-01-01      1.0
                    2017-01-08      2.0
                    2017-01-15      1.0
                    2017-01-22      0.0
                    2017-01-29      0.0
                    2017-02-05      1.0
我认为这种方法应该非常快,但内存效率不高,因为它将数据扩展到每个日期/类别组合的一行。如果内存是一个问题,那么您应该考虑一些替代方法(可能会有点慢,也不那么优雅,所以我不会担心,除非您的数据相当大)


另请注意:我相信如果您对一个唯一的日期/类别有多个真值,即使您的示例数据不包括该情况,该代码也应该可以正常工作。如果能够处理这一点对您来说很重要,那么您可能需要编辑样本数据。这可能不是一种有效的方法,但您可以做的是,迭代每一行,构建一个符合要求的
掩码或其他
数据框,并对它们进行计数,以更新到新的

# converting to pandas datetime
df['Date'] = pd.to_datetime(df['Date']).dt.date
print(df)
结果
df
为:

         Date   Category has_egg
0  2017-01-01      Lunch    True
1  2017-01-02      Lunch    True
2  2017-01-02      Lunch   False
3  2017-01-02     Dinner    True
4  2017-01-12      Lunch   False
5  2017-01-13  Breakfast   False
6  2017-01-13     Dinner   False
7  2017-02-04      Lunch    True
现在,遍历每一行,查找满足所有要求的行并求和:

for index, row in df.iterrows():
    mask = (df.Category == row.Category) & (df.Date > (row.Date - pd.Timedelta(days=14))) & (df.Date < row.Date) & (df.has_egg == True)
    df.loc[index, 'values'] = sum(mask) # insert to the new column

print(df)

您是否认为
时间增量(-14d)
从每个日期中减去组的最大日期,如果少于14天,则进行比较?对我来说,它返回
[真,真,真,假,真,假,假]
。你是如何得到你的结果的?我认为这是正确的解决方案,因为这产生了原始问题的答案。我不确定是否有更好的方法可以做到这一点,因为在3000行的df上运行3分钟需要80个内核。@ArMonk我不是在批评这个答案,因为我认为它的代码编写得很好(我给出了+1),而是在笔记本电脑上快速计时我的代码(比80个内核少得多)它的计时时间为16.1ms,相当于10000x左右的加速。这并不奇怪,因为这是矢量化代码与循环的比较。毕竟我没有勾选这是一个公认的答案,因为这段代码似乎无法在更大的数据集上运行,因此我无法继续分析。谢谢你的回答,我认为这是一个可能的解决方案,但它产生了一个单独的df,与原始问题所要求的略有不同。@JohnE我同意我所做的不是有效的,这是有效的方法+谢谢你的支持。@opensource谢谢!有两个好的,但不同的答案是不会有伤害的@约翰:谢谢你通过一些测试指出了效率!它总是好的和有用的。如果您做出更改并让用户知道,可能他可以将此作为可接受的答案(我不知道它是如何工作的,所以我使用的可能是),这样它将在将来帮助其他人处理类似问题和大量数据。@开源问题始终由发布问题的人决定。就我而言,现在的情况是100%的好,但我很感激这次投票。你的答案更接近海报的要求,我不想在这一点上再做我的答案
for index, row in df.iterrows():
    mask = (df.Category == row.Category) & (df.Date > (row.Date - pd.Timedelta(days=14))) & (df.Date < row.Date) & (df.has_egg == True)
    df.loc[index, 'values'] = sum(mask) # insert to the new column

print(df)
         Date   Category has_egg  values
0  2017-01-01      Lunch    True     0.0
1  2017-01-02      Lunch    True     1.0
2  2017-01-02      Lunch   False     1.0
3  2017-01-02     Dinner    True     0.0
4  2017-01-12      Lunch   False     2.0
5  2017-01-13  Breakfast   False     0.0
6  2017-01-13     Dinner   False     1.0
7  2017-02-04      Lunch    True     0.0