Pandas 在满足一定条件的情况下,熊猫统计过去N天内发生的次数
假设我有下面的数据框,并且我想计算过去14天内同一类别中出现“True”的次数,我该怎么做?例如,以下dataframe将生成一个值为0,1,1,0,2,0,1,0的列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
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