Determine el valor medio de ‘datos’ donde el número más alto de CONTINUOUS cond = True

Tengo un Dataframe de pandas con una columna ‘data’ y ‘cond’ (- ition). Necesito el valor medio (de la columna de datos) de las filas con el mayor número de objetos CONTINUOUS True en ‘cond’.

Example DataFrame: cond data 0 True 0.20 1 False 0.30 2 True 0.90 3 True 1.20 4 True 2.30 5 False 0.75 6 True 0.80 Result = 1.466, which is the mean value of row-indexes 2:4 with 3 True 

No pude encontrar una solución “vectorizada” con un método groupby o pivot. Así que escribí una función que hace un bucle en las filas. Desafortunadamente, esto toma aproximadamente una hora para 1 millón de líneas, lo cual es demasiado largo. Desafortunadamente, la decoración de @jit no reduce la duración de forma considerable.

Los datos que quiero analizar provienen de un proyecto de monitoreo durante un año y tengo cada 3 horas un DataFrame con un millón de filas. Por lo tanto, alrededor de 3000 tales archivos.

Una solución eficiente sería muy importante. También estoy muy agradecido por una solución en numpy.

Aquí hay un enfoque basado en NumPy –

 # Extract the relevant cond column as a 1D NumPy array and pad with False at # either ends, as later on we would try to find the start (rising edge) # and stop (falling edge) for each interval of True values arr = np.concatenate(([False],df.cond.values,[False])) # Determine the rising and falling edges as start and stop start = np.nonzero(arr[1:] > arr[:-1])[0] stop = np.nonzero(arr[1:] < arr[:-1])[0] # Get the interval lengths and determine the largest interval ID maxID = (stop - start).argmax() # With maxID get max interval range and thus get mean on the second col out = df.data.iloc[start[maxID]:stop[maxID]].mean() 

Prueba de tiempo de ejecución

Enfoques según funciones.

 def pandas_based(df): # @ayhan's soln res = df['data'].groupby((df['cond'] != df['cond'].shift()).\ cumsum()).agg(['count', 'mean']) return res[res['count'] == res['count'].max()] def numpy_based(df): arr = np.concatenate(([False],df.cond.values,[False])) start = np.nonzero(arr[1:] > arr[:-1])[0] stop = np.nonzero(arr[1:] < arr[:-1])[0] maxID = (stop - start).argmax() return df.data.iloc[start[maxID]:stop[maxID]].mean() 

Tiempos -

 In [208]: # Setup dataframe ...: N = 1000 # Datasize ...: df = pd.DataFrame(np.random.rand(N),columns=['data']) ...: df['cond'] = np.random.rand(N)>0.3 # To have 70% True values ...: In [209]: %timeit pandas_based(df) 100 loops, best of 3: 2.61 ms per loop In [210]: %timeit numpy_based(df) 1000 loops, best of 3: 215 µs per loop In [211]: # Setup dataframe ...: N = 10000 # Datasize ...: df = pd.DataFrame(np.random.rand(N),columns=['data']) ...: df['cond'] = np.random.rand(N)>0.3 # To have 70% True values ...: In [212]: %timeit pandas_based(df) 100 loops, best of 3: 4.12 ms per loop In [213]: %timeit numpy_based(df) 1000 loops, best of 3: 331 µs per loop 

Utilizando el enfoque de Cálculo del número de valores iguales consecutivos específicos de una forma vectorizada en pandas :

 df['data'].groupby((df['cond'] != df['cond'].shift()).cumsum()).agg(['count', 'mean'])[lambda x: x['count']==x['count'].max()] Out: count mean cond 3 3 1.466667 

La indexación por una llamada requiere 0.18.0, para versiones anteriores, puede hacer:

 res = df['data'].groupby((df['cond'] != df['cond'].shift()).cumsum()).agg(['count', 'mean']) res[res['count'] == res['count'].max()] Out: count mean cond 3 3 1.466667 

Cómo funciona:

La primera parte, df['cond'] != df['cond'].shift() devuelve una matriz booleana:

 df['cond'] != df['cond'].shift() Out: 0 True 1 True 2 True 3 False 4 False 5 True 6 True Name: cond, dtype: bool 

Entonces, el valor es Falso siempre que la fila sea la misma que la anterior. Eso significa que si tomas la sum acumulada, estas filas (las consecutivas) tendrán el mismo número:

 (df['cond'] != df['cond'].shift()).cumsum() Out: 0 1 1 2 2 3 3 3 4 3 5 4 6 5 Name: cond, dtype: int32 

Dado que groupby acepta cualquier serie para agrupar (no es necesario pasar una columna, puede pasar una lista arbitraria), esto se puede usar para agrupar los resultados. .agg(['count', 'mean'] parte solo da los conteos y medios respectivos para cada grupo y al final selecciona el que tiene el conteo más alto.

Tenga en cuenta que esto también agruparía las Falsas consecutivas. Si solo desea considerar las True consecutivas, puede cambiar la serie de agrupación a:

 ((df['cond'] != df['cond'].shift()) | (df['cond'] != True)).cumsum() 

Como queremos que sea Falso cuando la condición es Verdadera, la condición “no es igual a la fila que se encuentra debajo O no es Verdadera” Así que la línea original cambiaría a:

 df['data'].groupby(((df['cond'] != df['cond'].shift()) | (df['cond'] != True)).cumsum()).agg(['count', 'mean'])[lambda x: x['count']==x['count'].max()]