Отмечать только первую строку, в которой выполняется условие во фрейме данных

#python #pandas #dataframe

#python #pandas #фрейм данных

Вопрос:

У меня есть следующий фрейм данных df , который может быть создан следующим образом:

 date_today = datetime.now().date()
days = pd.date_range(date_today, date_today   timedelta(19), freq='D')
x = np.arange(0,2*np.pi,0.1*np.pi)   # start,stop,step
y = np.sin(x)
df = pd.DataFrame({'dates': days, 'vals': y, 'is_hit': abs(y)>0.9})
df = df.set_index('dates')
  

И которая выглядит следующим образом:

             is_hit          vals
dates                           
2019-03-27   False  0.000000e 00
2019-03-28   False  3.090170e-01
2019-03-29   False  5.877853e-01
2019-03-30   False  8.090170e-01
2019-03-31    True  9.510565e-01
2019-04-01    True  1.000000e 00
2019-04-02    True  9.510565e-01
2019-04-03   False  8.090170e-01
2019-04-04   False  5.877853e-01
2019-04-05   False  3.090170e-01
2019-04-06   False  1.224647e-16
2019-04-07   False -3.090170e-01
2019-04-08   False -5.877853e-01
2019-04-09   False -8.090170e-01
2019-04-10    True -9.510565e-01
2019-04-11    True -1.000000e 00
2019-04-12    True -9.510565e-01
2019-04-13   False -8.090170e-01
2019-04-14   False -5.877853e-01
2019-04-15   False -3.090170e-01
  

Я хочу впервые пометить строки, в которых выполняется is_hit условие True , таким образом, чтобы ожидаемый новый столбец hit_first был:

            is_hit          vals  hit_first
dates                                      
2019-03-27   False  0.000000e 00      False
2019-03-28   False  3.090170e-01      False
2019-03-29   False  5.877853e-01      False
2019-03-30   False  8.090170e-01      False
2019-03-31    True  9.510565e-01       True
2019-04-01    True  1.000000e 00      False
2019-04-02    True  9.510565e-01      False
2019-04-03   False  8.090170e-01      False
2019-04-04   False  5.877853e-01      False
2019-04-05   False  3.090170e-01      False
2019-04-06   False  1.224647e-16      False
2019-04-07   False -3.090170e-01      False
2019-04-08   False -5.877853e-01      False
2019-04-09   False -8.090170e-01      False
2019-04-10    True -9.510565e-01       True
2019-04-11    True -1.000000e 00      False
2019-04-12    True -9.510565e-01      False
2019-04-13   False -8.090170e-01      False
2019-04-14   False -5.877853e-01      False
2019-04-15   False -3.090170e-01      False
  

Как создать этот hit_first столбец?

Ответ №1:

Мое предложение:

 df['hit_first'] = df['is_hit'] amp; (~df['is_hit']).shift(1)
  

Ответ №2:

Используйте Series.shift chained with amp; для побитового AND :

 df['hit_first'] = df['is_hit'].ne(df['is_hit'].shift()) amp; df['is_hit']
print (df)
                    vals  is_hit  hit_first
dates                                      
2019-03-27  0.000000e 00   False      False
2019-03-28  3.090170e-01   False      False
2019-03-29  5.877853e-01   False      False
2019-03-30  8.090170e-01   False      False
2019-03-31  9.510565e-01    True       True
2019-04-01  1.000000e 00    True      False
2019-04-02  9.510565e-01    True      False
2019-04-03  8.090170e-01   False      False
2019-04-04  5.877853e-01   False      False
2019-04-05  3.090170e-01   False      False
2019-04-06  1.224647e-16   False      False
2019-04-07 -3.090170e-01   False      False
2019-04-08 -5.877853e-01   False      False
2019-04-09 -8.090170e-01   False      False
2019-04-10 -9.510565e-01    True       True
2019-04-11 -1.000000e 00    True      False
2019-04-12 -9.510565e-01    True      False
2019-04-13 -8.090170e-01   False      False
2019-04-14 -5.877853e-01   False      False
2019-04-15 -3.090170e-01   False      False
  

Ответ №3:

Я также думаю, что вы можете сделать это таким образом:

 df['is_hit'].astype(int).diff() == 1
  

Вывод:

 dates
2019-03-27    False
2019-03-28    False
2019-03-29    False
2019-03-30    False
2019-03-31     True
2019-04-01    False
2019-04-02    False
2019-04-03    False
2019-04-04    False
2019-04-05    False
2019-04-06    False
2019-04-07    False
2019-04-08    False
2019-04-09    False
2019-04-10     True
2019-04-11    False
2019-04-12    False
2019-04-13    False
2019-04-14    False
2019-04-15    False
Name: is_hit, dtype: bool
  

Тайминги:

 %timeit df['is_hit'] amp; (~df['is_hit']).shift(1)
1.13 ms ± 5.63 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df['is_hit'].ne(df['is_hit'].shift()) amp; df['is_hit']
908 µs ± 9.53 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit df['is_hit'].astype(int).diff() == 1
689 µs ± 8.24 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
  

Ответ №4:

Также это можно сделать, используя простую разницу между серией и ее сдвинутой серией на 1 период :

 df['hit_first'] = df['is_hit']-df['is_hit'].shift()==1
  

Комментарии:

1. Да, я понял. Спасибо 🙂

2. Хотя этот код может отвечать на вопрос, предоставление дополнительного контекста относительно того, как и / или почему он решает проблему, улучшило бы долгосрочную ценность ответа.