Reputation: 1663
I have the following DataFrame df
, which can be created as follows:
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')
And which looks like this:
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
I want to flag the rows where the is_hit
condition is True
for the first time, such that the expected new column hit_first
would be:
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
How to create this hit_first
column?
Upvotes: 9
Views: 1622
Reputation: 2472
Also this can be done by using simple difference between the series and it's shifted series by 1 period :
df['hit_first'] = df['is_hit']-df['is_hit'].shift()==1
Upvotes: -1
Reputation: 153460
I also, think you can do it this way:
df['is_hit'].astype(int).diff() == 1
Output:
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
Timings:
%timeit df['is_hit'] & (~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()) & 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)
Upvotes: 3
Reputation: 862661
Use Series.shift
chained with &
for bitwise AND
:
df['hit_first'] = df['is_hit'].ne(df['is_hit'].shift()) & 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
Upvotes: 3
Reputation: 1422
My suggestion:
df['hit_first'] = df['is_hit'] & (~df['is_hit']).shift(1)
Upvotes: 11