使用group by检查大熊猫未来行中的条件

时间:2018-10-04 17:27:52

标签: python pandas numpy

以下是我的数据框的外观, Group Signal Value1 Value2 Expected_Output 0 1 0 3 1 NaN 1 1 1 4 2 NaN 2 1 0 7 4 NaN 3 1 0 8 9 1.0 4 1 0 5 3 NaN 5 2 1 3 6 NaN 6 2 1 1 2 1.0 7 2 0 3 4 1.0 是我想要的列。

Group

对于给定的Signal == 1,如果为Value1 < Value2,那么我尝试查看接下来的三行(而不是当前行),并检查是否为Expected_Output。如果该条件成立,那么我在Value < Value2列中返回1。例如,如果出于多种原因满足了Signal == 1条件,因为它位于第5和6行中Group 2的下3行中(Expected_Output),那么我还要返回1 in group by object

我假设np.whereanyshift,{{1}}的正确组合可能是解决方案,但不能完全解决。

NB:-亚历山大指出评论中有冲突。理想情况下,由于前一行中的信号而设置的值将取代给定行中的当前行规则冲突。

2 个答案:

答案 0 :(得分:2)

如果您要检查很多先前的行,则多次移位很快就会变得混乱,但是在这里还算不错:

s = df.groupby('Group').Signal

condition = ((s.shift(1).eq(1) | s.shift(2).eq(1) | s.shift(3).eq(1)) 
                & df.Value1.lt(df.Value2))

df.assign(out=np.where(condition, 1, np.nan))

   Group  Signal  Value1  Value2  out
0      1       0       3       1  NaN
1      1       1       4       2  NaN
2      1       0       7       4  NaN
3      1       0       8       9  1.0
4      1       0       5       3  NaN
5      2       1       3       6  NaN
6      2       1       1       2  1.0
7      2       0       3       4  1.0

如果您担心使用这么多班次的表现,我不会太担心,这里是一百万行的示例:

In [401]: len(df)
Out[401]: 960000

In [402]: %%timeit
     ...: s = df.groupby('Group').Signal
     ...:
     ...: condition = ((s.shift(1).eq(1) | s.shift(2).eq(1) | s.shift(3).eq(1))
     ...:                 & df.Value1.lt(df.Value2))
     ...:
     ...: np.where(condition, 1, np.nan)
     ...:
     ...:
94.5 ms ± 524 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

@Alexander确定了规则中的冲突,以下是使用符合要求的掩码的版本:

s = (df.Signal.mask(df.Signal.eq(0)).groupby(df.Group)
        .ffill(limit=3).mask(df.Signal.eq(1)).fillna(0))

现在,您可以简单地将此列与其他条件一起使用:

np.where((s.eq(1) & df.Value1.lt(df.Value2)).astype(int), 1, np.nan)

array([nan, nan, nan,  1., nan, nan, nan,  1.])

答案 1 :(得分:1)

您可以创建一个与您的条件匹配的索引,然后使用它将预期的输出设置为1。

当规则冲突时,如何处理预期的输出尚不清楚。例如,在第6行上,预期输出将为1,因为它满足了来自第5行的信号标准并适合“随后的三行,其中值1 <值2”。但是,这可能与忽略第一个信号行的规则相冲突。

idx = (df
       .assign(
           grp=df['Signal'].eq(1).cumsum(),
           cond=df.eval('Value1 < Value2'))
       .pipe(lambda df: df[df['grp'] > 0])  # Ignore data preceding first signal.
       .groupby(['Group', 'grp'], as_index=False)
       .apply(lambda df: df.iloc[1:4, :])  # Ignore current row, get rows 1-3. 
       .pipe(lambda df: df[df['cond']])  # Find rows where condition is met.
       .index.get_level_values(1)
)

df['Expected_Output'] = np.nan
df.loc[idx, 'Expected_Output'] = 1

>>> df
   Group  Signal  Value1  Value2  Expected_Output
0      1       0       3       1              NaN
1      1       1       4       2              NaN
2      1       0       7       4              NaN
3      1       0       8       9              1.0
4      1       0       5       3              NaN
5      2       1       3       6              NaN
6      2       1       1       2              NaN  # <<< Intended difference vs. "expected"
7      2       0       3       4              1.0