我对Pandas相当陌生,面临着一个相当复杂的问题。由于我的解决方案使用了许多嵌套的迭代循环,我想知道是否有一种更快、更"泛数据"的方法可以做到这一点。
我有一个类似于这个简化版本的事件数据帧:
min sec isDone sessionId
2 40 False 1
2 50 False 1
2 55 False 1
2 58 False 1
3 01 False 1
3 12 True 1
5 0 False 1
5 5 False 1
5 15 False 1
5 30 True 1
5 50 False 1
2 0 False 2
2 10 False 2
2 30 False 2
2 50 True 2
现在我想添加一个列,它包含到"isDone"中的下一个"True"的秒数(最多为一定的秒数(,但只能在同一个"sessionId"中。所有其他值将保持NaN。
在20秒钟内,这看起来像这样:
min sec isDone sessionId secToDone
2 40 False 1 NaN
2 50 False 1 NaN
2 55 False 1 17
2 58 False 1 14
3 01 False 1 11
3 12 True 1 0
5 0 False 1 NaN
5 5 False 1 NaN
5 15 False 1 15
5 30 True 1 0
5 50 False 1 NaN
2 0 False 2 NaN
2 10 False 2 NaN
2 30 False 2 20
2 50 True 2 0
到目前为止,我的解决方案是:
- 遍历sessionId并选择行
- 仅使用此选择中的"True"值构建第二个数据帧df_done
- 在此df_done-Dataframe上迭代,并在"秒"内选择前面的行
- 遍历前面的这些行并写入值
这是我到目前为止的代码(sessionId上的迭代丢失了,因为我目前只测试了一个会话(:
def get_preceding(df_dataset,sec=20):
df_done = df_dataset[(df_dataset['isDone'] == True)]
for row in df_done.itertuples():
done_min = getattr(row, 'minute')
done_sec = getattr(row, 'second')
if done_sec < sec:
pre_min = done_min -1
pre_sec = 60 + done_sec - sec
else:
pre_min = done_min
pre_sec = done_sec - sec
for r in df_dataset.loc[((pre_min == df_dataset['minute']) & (pre_sec <= df_dataset['second'])) | ((pre_min < df_dataset['minute'])&(df_dataset['minute'] < done_min)) | ((df_dataset['minute'] == done_min) & (df_dataset['second'] <= done_sec))].itertuples():
if r['minute'] == done_min:
r['secToDone'] = done_sec - r['second']
if r['minute'] < done_min:
r['secToDone'] = 60 - r['second'] + done_sec + ((done_min - r['minute'] - 1)*60)
但这需要大量的迭代,并且数据帧相当大。所以我的问题是:
有没有一种更快、更"潘达斯式"的方法可以做到这一点?
首先,您需要将分钟和秒组合成合理的东西:
df['t'] = df['min'] * 60 + df.sec
min sec isDone sessionId t
0 2 40 False 1 160
1 2 50 False 1 170
2 2 55 False 1 175
3 2 58 False 1 178
然后,您需要标记True
发生的所有时间:
df['true_t'] = df[df.isDone].t
min sec isDone sessionId t true_t
0 2 40 False 1 160 NaN
1 2 50 False 1 170 NaN
2 2 55 False 1 175 NaN
3 2 58 False 1 178 NaN
4 3 1 False 1 181 NaN
5 3 12 True 1 192 192.0
6 5 0 False 1 300 NaN
现在,groupby的魔力:
df['next_true_t'] = df.groupby('sessionId').true_t.bfill()
min sec isDone sessionId t true_t next_true_t
0 2 40 False 1 160 NaN 192.0
1 2 50 False 1 170 NaN 192.0
2 2 55 False 1 175 NaN 192.0
3 2 58 False 1 178 NaN 192.0
4 3 1 False 1 181 NaN 192.0
5 3 12 True 1 192 192.0 192.0
6 5 0 False 1 300 NaN 330.0
7 5 5 False 1 305 NaN 330.0
8 5 15 False 1 315 NaN 330.0
9 5 30 True 1 330 330.0 330.0
10 5 50 False 1 350 NaN NaN
11 2 0 False 2 120 NaN 170.0
12 2 10 False 2 130 NaN 170.0
13 2 30 False 2 150 NaN 170.0
14 2 50 True 2 170 170.0 170.0
现在,计算diff:很简单
df['diff'] = df.next_true_t - df.t
min sec isDone sessionId t true_t next_true_t diff
0 2 40 False 1 160 NaN 192.0 32.0
1 2 50 False 1 170 NaN 192.0 22.0
2 2 55 False 1 175 NaN 192.0 17.0
3 2 58 False 1 178 NaN 192.0 14.0
4 3 1 False 1 181 NaN 192.0 11.0
5 3 12 True 1 192 192.0 192.0 0.0
6 5 0 False 1 300 NaN 330.0 30.0
7 5 5 False 1 305 NaN 330.0 25.0
8 5 15 False 1 315 NaN 330.0 15.0
9 5 30 True 1 330 330.0 330.0 0.0
10 5 50 False 1 350 NaN NaN NaN
11 2 0 False 2 120 NaN 170.0 50.0
12 2 10 False 2 130 NaN 170.0 40.0
13 2 30 False 2 150 NaN 170.0 20.0
14 2 50 True 2 170 170.0 170.0 0.0
我将由您来决定如何根据秒数来省略值,但这非常简单。