Проверка состояния в будущих строках в пандах с группировкой по - PullRequest
0 голосов
/ 04 октября 2018

Вот как выглядит мой фрейм данных, а Expected_Output - мой желаемый столбец.

  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.Если это условие истинно, тогда я возвращаю 1 в столбце Expected_Output.Если, например, условие Value < Value2 удовлетворяется по нескольким причинам, так как оно находится в 3 следующих строках от Signal == 1 в обеих строках 5 и 6 (Group 2), то я также возвращаю 1 в Expected_Output.

Я предполагаю, что правильная комбинация group by object, np.where, any, shift могла бы быть решением, но я не могу этого достичь.

Примечание: - Александр указал на конфликт в комментариях.В идеале значение, устанавливаемое из-за сигнала в предыдущей строке, заменит текущий конфликт правил строки в данной строке.

Ответы [ 2 ]

0 голосов
/ 04 октября 2018

Вы можете создать индекс, который соответствует вашим критериям, а затем использовать его для установки ожидаемого результата на 1.

Не ясно, как обрабатывать ожидаемый результат, когда правила конфликтуют.Например, в строке 6 ожидаемый результат будет равен 1, поскольку он удовлетворяет критериям сигнала из строки пять и соответствует «последующим трем строкам, где значение 1 <значение 2».Однако это может противоречить правилу, согласно которому первая строка сигнала игнорируется. </p>

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
0 голосов
/ 04 октября 2018

Если вы собираетесь проверять много предыдущих строк, несколько смен могут быстро стать грязными, но здесь это не так уж плохо:

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

Если вы 'Что касается эффективности использования такого количества смен, я бы не стал сильно беспокоиться, вот пример на 1 миллион строк:

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)

@ Александр определил конфликт в правилах, вотверсия с использованием маски, которая соответствует этому требованию:

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.])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...