[英]How to group rows within a time period using Python
我有一些事務的DataFrame
。 我想根據item
和time
列值對這些事務進行分組:目標是將彼此相隔1小時的項目分組。 因此,我們在下一次觀察時開始一個新的組,該組不在觀察之前的一小時內(參見DataFrame
B
列start time
)。
這是數據:我想將A
轉換為B
A=
item time result
A 2016-04-18 13:08:25 Y
A 2016-04-18 13:57:05 N
A 2016-04-18 14:00:12 N
A 2016-04-18 23:45:50 Y
A 2016-04-20 16:53:48 Y
A 2016-04-20 17:11:47 N
B 2016-04-18 15:24:48 N
C 2016-04-23 13:20:44 N
C 2016-04-23 14:02:23 Y
B=
item start time end time Ys Ns total count
A 2016-04-18 13:08:25 2016-04-18 14:08:25 1 2 3
A 2016-04-18 23:45:50 2016-04-18 00:45:50 1 0 1
A 2016-04-20 16:53:48 2016-04-20 17:53:48 1 1 2
B 2016-04-18 15:24:48 2016-04-18 16:24:48 0 1 1
C 2016-04-23 13:20:44 2016-04-23 14:20:44 1 1 2
這是我做的:
grouped = A.groupby('item')
A['end'] = (grouped['time'].transform(lambda grp: grp.min()+pd.Timedelta(hours=1)))
A2 = A.loc[(A['time'] <= A['end'])]
這給了我每天一組:第一次交易后1小時內的交易。 所以,我在同一天錯過了其他交易,但與第一次交易相差超過1小時。 我的斗爭是如何獲得這些團體。 然后我可以使用pd.crosstab
從result
列中獲取我想要的詳細信息。
我的另一個想法是按item
和time
排序A
,然后逐行排序。 如果時間在上一行的1小時內,則會添加到該組,否則會創建新組。
1)建立window_end
用於以后使用列.groupby()
並定義.get_windows()
來檢查,每個item
組中,如果一row
適合當前電流1小時的窗口,或者什么都不做,保持初始值。 適用於所有item
組:
df['window_end'] = df.time + pd.Timedelta('1H')
def get_windows(data):
window_end = data.iloc[0].window_end
for index, row in data.iloc[1:].iterrows():
if window_end > row.time:
df.loc[index, 'window_end'] = window_end
else:
window_end = row.window_end
df.groupby('item').apply(lambda x: get_windows(x))
2)使用帶有.groupby()
windows
和item
,並返回.value_counts()
作為transposed
DataFrame
,清理index
,並添加total
:
df = df.groupby(['window_end', 'item']).result.apply(lambda x: x.value_counts().to_frame().T)
df = df.fillna(0).astype(int).reset_index(level=2, drop=True)
df['total'] = df.sum(axis=1)
要得到:
N Y total
window_end item
2016-04-18 14:08:25 A A 2 1 3
2016-04-18 16:24:48 B B 1 0 1
2016-04-19 00:45:50 A A 0 1 1
2016-04-20 17:53:48 A A 1 1 2
2016-04-23 14:20:44 C C 1 1 2
受到Stefan解決方案的啟發(+1)我來到了這個:
B = (A.groupby(['item', A.groupby('item')['time']
.diff().fillna(0).dt.total_seconds()//60//60
],
as_index=False)['time'].min()
)
B[['N','Y']] = (A.groupby(['item', A.groupby('item')['time']
.diff().fillna(0).dt.total_seconds()//60//60
])['result']
.apply(lambda x: x.value_counts().to_frame().T).fillna(0)
.reset_index()[['N','Y']]
)
輸出:
In [178]: B
Out[178]:
item time N Y
0 A 2016-04-18 13:08:25 3.0 1.0
1 A 2016-04-18 23:45:50 0.0 1.0
2 A 2016-04-20 16:53:48 0.0 1.0
3 B 2016-04-18 15:24:48 1.0 0.0
4 C 2016-04-23 13:20:44 1.0 1.0
PS的想法是使用A.groupby('item')['time'].diff().fillna(0).dt.total_seconds()//60//60
作為分組的一部分:
In [179]: A.groupby('item')['time'].diff().fillna(0).dt.total_seconds()//60//60
Out[179]:
0 0.0
1 0.0
2 0.0
3 9.0
4 41.0
5 0.0
6 0.0
7 0.0
8 0.0
Name: time, dtype: float64
import pandas as pd
from StringIO import StringIO
text = """item time result
A 2016-04-18 13:08:25 Y
A 2016-04-18 13:57:05 N
A 2016-04-18 14:00:12 N
A 2016-04-18 23:45:50 Y
A 2016-04-20 16:53:48 Y
A 2016-04-20 17:11:47 N
B 2016-04-18 15:24:48 N
C 2016-04-23 13:20:44 N
C 2016-04-23 14:02:23 Y
"""
df = pd.read_csv(StringIO(text), delimiter="\s{2,}", parse_dates=[1], engine='python')
我需要創建一些過程函數:
def set_time_group(df):
cur_time = pd.NaT
for index, row in df.iterrows():
if pd.isnull(cur_time):
cur_time = row.time
delta = row.time - cur_time
if delta.seconds / 3600. < 1:
df.loc[index, 'time_ref'] = cur_time
else:
df.loc[index, 'time_ref'] = row.time
cur_time = row.time
return df
def summarize_results(df):
df_ = df.groupby('result').count().iloc[:, 0]
df_.loc['total count'] = df_.sum()
return df_
dfg1 = df.groupby('item').apply(set_time_group)
dfg2 = dfg1.groupby(['item', 'time_ref']).apply(summarize_results)
df_f = dfg2.unstack().fillna(0)
print df_f
result N Y total count
item time_ref
A 2016-04-18 13:08:25 2.0 1.0 3.0
2016-04-18 23:45:50 0.0 1.0 1.0
2016-04-20 16:53:48 1.0 1.0 2.0
B 2016-04-18 15:24:48 1.0 0.0 1.0
C 2016-04-23 13:20:44 1.0 1.0 2.0
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.