Pandas—若一行中满足条件,则在不迭代的情况下将值添加到前面的行中



我对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

到目前为止,我的解决方案是:

  1. 遍历sessionId并选择行
  2. 仅使用此选择中的"True"值构建第二个数据帧df_done
  3. 在此df_done-Dataframe上迭代,并在"秒"内选择前面的行
  4. 遍历前面的这些行并写入值

这是我到目前为止的代码(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

我将由您来决定如何根据秒数来省略值,但这非常简单。

最新更新