Las pandas miden el tiempo transcurrido cuando la condición es verdadera

Tengo el siguiente dataframe:

dt binary 2016-01-01 00:00:00 False 2016-01-01 00:00:01 False 2016-01-01 00:00:02 False 2016-01-01 00:00:03 False 2016-01-01 00:00:04 True 2016-01-01 00:00:05 True 2016-01-01 00:00:06 True 2016-01-01 00:00:07 False 2016-01-01 00:00:08 False 2016-01-01 00:00:09 True 2016-01-01 00:00:10 True 

Me gustaría sumr el tiempo transcurrido cuando binary es True . Estoy compartiendo mi solución, que la implementa, pero algo me dice que debería haber una manera más fácil ya que es una característica bastante básica de los datos de series de tiempo. Tenga en cuenta que lo más probable es que los datos sean equidistantes, pero no puedo confiar en eso.

 df['binary_grp'] = (df.binary.diff(1) != False).astype(int).cumsum() # Throw away False values df = df[df.binary] groupby = df.groupby('binary_grp') df = pd.DataFrame({'timespan': groupby.dt.last() - groupby.dt.first()}) return df.timespan.sum().seconds / 60.0 

La parte más complicada es probablemente la primera línea. Lo que hace, básicamente asigna un número incrementado a cada bloque consecutivo. Así es como se ven los datos después de eso:

  dt binary binary_grp 2016-01-01 00:00:00 False 1 2016-01-01 00:00:01 False 1 2016-01-01 00:00:02 False 1 2016-01-01 00:00:03 False 1 2016-01-01 00:00:04 True 2 2016-01-01 00:00:05 True 2 2016-01-01 00:00:06 True 2 2016-01-01 00:00:07 False 3 2016-01-01 00:00:08 False 3 2016-01-01 00:00:09 True 4 2016-01-01 00:00:10 True 4 

¿Hay una mejor manera de lograr esto? Supongo que este código es performante, mi preocupación es la legibilidad.

En mi opinión tu solución es buena.

Otra solución:

Comparar los valores de shift ed con ne , obtener grupos por cumsum .

Una vez que el filtrado es posible, apply con diferencia seleccionando con iloc :

 df['binary_grp'] = (df.binary.ne(df.binary.shift())).cumsum() df = df[df.binary] s = df.groupby('binary_grp')['dt'].apply(lambda x: x.iloc[-1] - x.iloc[0]) print (s) binary_grp 2 00:00:02 4 00:00:01 Name: dt, dtype: timedelta64[ns] all_time = s.sum().seconds / 60.0 print (all_time) 0.05 

En su solución, el nuevo DataFrame no es necesario si solo necesita all_time :

 groupby = df.groupby('binary_grp') s = groupby.dt.last() - groupby.dt.first() all_time = s.sum().seconds / 60.0 print (all_time) 0.05 

Pero si es necesario, puede crearlo desde Series s por to_frame :

 df1 = s.to_frame('timestamp') print (df1) timestamp binary_grp 2 00:00:02 4 00:00:01 

IIUC:

Desea encontrar la sum de tiempo en toda la serie donde binary es True .

Sin embargo, tenemos que hacer algunas elecciones o suposiciones.

  dt binary 0 2016-01-01 00:00:00 False 1 2016-01-01 00:00:01 False 2 2016-01-01 00:00:02 False 3 2016-01-01 00:00:03 False 4 2016-01-01 00:00:04 True # <- This where time starts 5 2016-01-01 00:00:05 True 6 2016-01-01 00:00:06 True 7 2016-01-01 00:00:07 False # <- And ends here. So this would 8 2016-01-01 00:00:08 False # be 00:00:07 - 00:00:04 or 3 seconds 9 2016-01-01 00:00:09 True # <- Starts again 10 2016-01-01 00:00:10 True # <- But ends here because # I don't have another Timestamp 

Con esas suposiciones, podemos usar diff , multiplicar y sum

 df.dt.diff().shift(-1).mul(df.binary).sum() Timedelta('0 days 00:00:04') 

Entonces podemos usar este concepto junto con groupby

 # Use xor and cumsum to identify change in True to False and False to True grps = (df.binary ^ df.binary.shift()).cumsum() mask = df.binary.groupby(grps).first() df.dt.diff().shift(-1).groupby(grps).sum()[mask] binary 1 00:00:03 3 00:00:01 Name: dt, dtype: timedelta64[ns] 

O sin la máscara.

 pd.concat([df.dt.diff().shift(-1).groupby(grps).sum(), mask], axis=1) dt binary binary 0 00:00:04 False 1 00:00:03 True 2 00:00:02 False 3 00:00:01 True