Digamos que tengo pd.Series como a continuación
s = pd.Series([False, True, False,True,True,True,False, False]) 0 False 1 True 2 False 3 True 4 True 5 True 6 False 7 False dtype: bool Quiero saber cuánto tiempo es la secuencia True más larga, en este ejemplo, es 3.
Lo intenté de una manera estúpida.
s_list = s.tolist() count = 0 max_count = 0 for item in s_list: if item: count +=1 else: if count>max_count: max_count = count count = 0 print(max_count) Imprimirá 3 , pero en una Series de todo True , imprimirá 0
Opción 1
Use la serie misma para enmascarar la suma acumulada de la negación. Luego usa value_counts
(~s).cumsum()[s].value_counts().max() 3explicación
(~s).cumsum() es una forma bastante estándar de producir distintos grupos True / False
0 1 1 1 2 2 3 2 4 2 5 2 6 3 7 4 dtype: int64 Pero puedes ver que el grupo que nos importa está representado por los 2 y hay cuatro de ellos. Eso es porque el grupo es iniciado por el primer False (que se convierte en True con (~s) ). Por lo tanto, enmascaramos esta suma acumulativa con la máscara booleana con la que comenzamos.
(~s).cumsum()[s] 1 1 3 2 4 2 5 2 dtype: int64 Ahora vemos aparecer los tres 2 y solo tenemos que usar un método para extraerlos. value_counts y max .
opcion 2
Utilice factorize y bincount
a = s.values b = pd.factorize((~a).cumsum())[0] np.bincount(b[a]).max() 3 explicación
Esta es una explicación similar a la de la opción 1. La principal diferencia está en cómo encontré el máximo. Uso pd.factorize para tokenizar los valores en números enteros que van desde 0 hasta el número total de valores únicos. Dados los valores reales que teníamos en (~a).cumsum() , no necesitábamos estrictamente esta parte. Lo usé porque es una herramienta de propósito general que podría usarse en nombres de grupos arbitrarios.
Después pd.factorize , uso esos valores enteros en np.bincount que acumula el número total de veces que se usa cada entero. Entonces toma el máximo.
Opción 3
Como se indica en la explicación de la opción 2, esto también funciona:
a = s.values np.bincount((~a).cumsum()[a]).max() 3Creo que esto podría funcionar
pd.Series(s.index[~s].values).diff().max()-1 Out[57]: 3.0También fuera de los pandas podemos volver a python groupby
from itertools import groupby max([len(list(group)) for key, group in groupby(s.tolist())]) Out[73]: 3Actualizar :
from itertools import compress max(list(compress([len(list(group)) for key, group in groupby(s.tolist())],[key for key, group in groupby(s.tolist())]))) Out[84]: 3Editar : como mencionó piRSquared, mi solución anterior necesita agregar dos False al principio y al final de la serie. piRSquared amablemente dio una respuesta basada en eso.
(np.diff(np.flatnonzero(np.append(True, np.append(~s.values, True)))) - 1).max()Mi juicio original es
(np.diff(s.where(~s).dropna().index.values) - 1).max() ( Esto no dará la respuesta correcta si el True más largo comienza al principio o termina al final, como lo indica piRSquared. Utilice la solución anterior proporcionada por piRSquared. Este trabajo solo sirve como explicación).
Explicación:
Esto encuentra los índices de las partes False y al encontrar los espacios entre los índices de False , podemos saber las True más largas .
s.where(s == False).dropna().index.values encuentra todos los índices de False
array([0, 2, 6, 7]) Sabemos que los True viven entre los False . Por lo tanto, podemos usar np.diff para encontrar las brechas entre estos índices.
array([2, 4, 1]) Menos 1 al final, ya que True s se encuentra entre estos índices.
Encuentre el máximo de la diferencia.