Given a dataframe with a time series like this:
time | event |
---|---|
2020-01-01 12:00:00 | 1 |
2020-01-01 12:00:01 | NaN |
2020-01-01 12:00:02 | 1 |
2020-01-01 12:00:03 | 1 |
2020-01-01 12:00:04 | NaN |
2020-01-01 12:00:05 | NaN |
2020-01-01 12:00:06 | 1 |
2020-01-01 12:00:07 | Nan |
I would like to get a summary-dataframe like:
event_id | time_start | time_stop |
---|---|---|
1 | 2020-01-01 12:00:00 | 2020-01-01 12:00:01 |
2 | 2020-01-01 12:00:02 | 2020-01-01 12:00:04 |
3 | 2020-01-01 12:00:06 | 2020-01-01 12:00:07 |
In a step-by-step approach I think I should first add an empty column 'event_i', then fill in the index of the events (1,2,3,...) Once this works, I can try to create a summary-dataframe. I am already stuck at giving the index to the events.
I could work something out with df.iterrows() but that is not recommended. How can I vectorize this indexing-procedure?
import pandas as pd
import numpy as np
# define mini-dataset as an example
data= {'time': ['2020-01-01 12:00:00', '2020-01-01 12:00:01', '2020-01-01 12:00:02','2020-01-01 12:00:03',
'2020-01-01 12:00:04','2020-01-01 12:00:05', '2020-01-01 12:00:06', '2020-01-01 12:00:07',
'2020-01-01 12:00:08', '2020-01-01 12:00:09','2020-01-01 12:00:10'],
'event': [1,np.nan,1,1,np.nan,np.nan,1,np.nan,1,1,np.nan]}
df = pd.DataFrame(data)
df['time']=pd.to_datetime((df['time']))
# give a sequential number to each event
df['event_i'] = np.nan
# for each event-number, group by and stack: event_id, time_start time_stop
# ...
I think we can do better without cumulative operations and grouping, as in the previous answer. All we need is shifting and comparison, which are easy and can be vectorized:
# transform 1 to True, Nan to False
event = df['event'].notna()
previous = event.shift()
# mark start and stop point by comparing neighbors
start = (event > previous)
stop = (event < previous)
# care about corner cases at the first and last positions
start.iloc[0] = event.iloc[0]
stop.iloc[-1] = event.iloc[-1]
# extract data by prepared indexers
answer = pd.DataFrame({
'time_start': time[start].values,
'time_stop': time[stop].values})