[英]Efficiently count occurrences of a value with groupby and within a date range
我有可以做到這一點的代碼,但我正在使用iterrows()
遍歷 dataframe 的每一行。 考慮到它正在檢查超過 6M 行,需要很長時間來處理。 並希望使用矢量化來加快速度。
我已經研究過使用pd.Grouper
和freq
,但是一直堅持如何使用 2 個數據幀來進行此檢查。
鑒於以下2個數據框:
我想查看df1
中的所有行(按'sid'
和'modtype'
分組):
df1:
sid servid date modtype service
0 123 881 2022-07-05 A1 z
1 456 879 2022-07-02 A2 z
然后在df2
中找到它們,並在df1
中該組的日期后 3 天內計算這些組的出現次數,以計算該組在前 3 天內出現的次數,以及在 3 天內出現的次數后。
df2:
sid servid date modtype
0 123 1234 2022-07-03 A1
1 123 881 2022-07-05 A1
2 123 65781 2022-07-06 A1
3 123 8552 2022-07-30 A1
4 123 3453 2022-07-04 A2
5 123 5681 2022-07-07 A2
6 456 78 2022-07-01 A1
7 456 26744 2022-05-05 A2
8 456 56166 2022-06-29 A2
9 456 56717 2022-06-30 A2
10 456 879 2022-07-02 A2
11 456 56 2022-07-25 A2
因此,基本上,在我在下面提供的樣本集中,我的 output 最終會得到:
sid servid date modtype service cnt_3day_before cnt_3day_after
0 123 881 2022-07-05 A1 z 1 1
1 456 879 2022-07-02 A2 z 2 0
樣本集:
import pandas as pd
data1 = {
'sid':['123','456'],
'servid':['881','879'],
'date':['2022-07-05','2022-07-02'],
'modtype':['A1','A2'],
'service':['z','z']}
df1 = pd.DataFrame(data1)
df1['date'] = pd.to_datetime(df1['date'])
df1 = df1.sort_values(by=['sid','modtype','date'], ascending=[True, True, True]).reset_index(drop=True)
data2 = {
'sid':['123','123','123','123','123','123',
'456','456','456','456','456','456'],
'servid':['1234','3453','881','65781','5681','8552',
'26744','56717','879','56166','56','78'],
'date':['2022-07-03','2022-07-04','2022-07-05','2022-07-06','2022-07-07','2022-07-30',
'2022-05-05','2022-06-30','2022-07-02','2022-06-29','2022-07-25','2022-07-01'],
'modtype':['A1','A2','A1','A1','A2','A1',
'A2','A2','A2','A2','A2','A1']}
df2 = pd.DataFrame(data2)
df2['date'] = pd.to_datetime(df2['date'])
df2 = df2.sort_values(by=['sid','modtype','date'], ascending=[True, True, True]).reset_index(drop=True)
# Merge the dataframes on sid and modtype
keys = ['sid', 'modtype']
s = df2.merge(df1[[*keys, 'date']], on=keys, suffixes=['', '_'])
# Create boolean condtitions as per requirements
s['cnt_3day_after'] = s['date'].between(s['date_'], s['date_'] + pd.DateOffset(days=3), inclusive='right')
s['cnt_3day_before'] = s['date'].between(s['date_'] - pd.DateOffset(days=3), s['date_'], inclusive='left' )
# group the boolean conditions by sid and modtype
# and aggregate with sum to count the number of True values
s = s.groupby(keys)[['cnt_3day_after', 'cnt_3day_before']].sum()
# Join the aggregated counts back with df1
df_out = df1.join(s, on=keys)
print(df_out)
sid servid date modtype service cnt_3day_after cnt_3day_before
0 123 881 2022-07-05 A1 z 1 1
1 456 879 2022-07-02 A2 z 0 2
我認為肯定存在更快的解決方案,但你可以試試這個。 它遍歷df1
中的“查詢”,並為每個query
計算df2
中 3 天前后發生的事件數。 為了計算此類事件的數量,我們首先將sid
和modtype
設置為索引列,然后我們 select 通過索引匹配事件並計算所選事件和查詢之間的時間差,然后我們只計算在 +/- 3 天內發生的事件。 如果您對日期列進行了排序,則可以使用二進制搜索優化這個位置,從而為您提供 O(logN) 而不是 O(N) 復雜度。
df2 = df2.set_index(['sid', 'modtype'])
seconds_in_3days = 3*24*60*60
def before_and_after_3days(query):
dates = df2.loc[tuple(query[['sid', 'modtype']]), 'date']
secs = (dates - query['date']).dt.total_seconds().astype(int)
before = ((-seconds_in_3days <= secs) & (secs < 0)).sum()
after = ((0 < secs) & (secs < seconds_in_3days)).sum()
return before, after
before_after = df1.apply(before_and_after_3days, axis=1)
df1[['cnt_3day_before', 'cnt_3day_after']] = before_after.tolist()
這是一個部分解決方案。 沒有時間做完整的事情。 以后可能有時間。 但我想我會傳遞這個想法,以防它可以幫助你朝着正確的方向前進。
def a(x):
s = x['sid_y'].isna()
if s.all():
return pd.Series([0,0], index=['before','after'])
idx = (~s).idxmax()
nb_before = ((x.loc[idx,'date'] > x['date']) & (x.loc[idx,'date'] - x['date'] <= pd.Timedelta('3D'))).sum()
nb_after = ((x.loc[idx,'date'] < x['date']) & (x['date'] - x.loc[idx,'date'] < pd.Timedelta('3D'))).sum()
return pd.Series([nb_before,nb_after], index=['before','after'])
df2.merge(df1, how='left', on='date').groupby(['sid_x','modtype_x']).apply(a)
結果
before after
sid_x modtype_x
123 A1 1 1
A2 0 0
456 A1 0 0
A2 2 0
你必須弄清楚細節。 就像重命名一樣,合並回你想要的任何結果 dataframe 。 您還需要調整TimeDelta
比較。 我所擁有的不一致,但您可能可以從這里獲取。 IE
x['date'] - x.loc[idx,'date'] < pd.Timedelta('3D')
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.