style excelwriter python pandas

excelwriter - pandas python



Pandas: conteo rodante condicional (5)

Basado en la segunda respuesta que vinculó, asumiendo que s es su serie.

df = pd.DataFrame(s) df[''block''] = (df[''col''] != df[''col''].shift(1)).astype(int).cumsum() df[''count''] = df.groupby(''block'').transform(lambda x: range(1, len(x) + 1)) In [88]: df Out[88]: col block count 0 B 1 1 1 B 1 2 2 A 2 1 3 A 2 2 4 A 2 3 5 B 3 1

Tengo una serie que tiene el siguiente aspecto:

col 0 B 1 B 2 A 3 A 4 A 5 B

Es una serie de tiempo, por lo tanto el índice está ordenado por tiempo.

Para cada fila, me gustaría contar cuántas veces ha aparecido el valor consecutivamente, es decir:

Salida:

col count 0 B 1 1 B 2 2 A 1 # Value does not match previous row => reset counter to 1 3 A 2 4 A 3 5 B 1 # Value does not match previous row => reset counter to 1

Encontré 2 preguntas relacionadas, pero no puedo descubrir cómo "escribir" esa información como una nueva columna en el Marco de datos, para cada fila (como se muestra arriba). Usar rolling_apply no funciona bien.

Relacionado:

Contando eventos consecutivos en el marco de datos de pandas por su índice

Encontrar segmentos consecutivos en un marco de datos pandas


Creo que hay una buena manera de combinar la solución de @chrisb y @CodeShaman (como se señaló, la solución de CodeShamans cuenta valores totales y no consecutivos).

df[''count''] = df.groupby((df[''col''] != df[''col''].shift(1)).cumsum()).cumcount()+1 col count 0 B 1 1 B 2 2 A 1 3 A 2 4 A 3 5 B 1


Me gusta la respuesta de @chrisb pero quería compartir mi propia solución, ya que algunas personas pueden encontrarla más legible y más fácil de usar con problemas similares ...

1) Crear una función que utiliza variables estáticas.

def rolling_count(val): if val == rolling_count.previous: rolling_count.count +=1 else: rolling_count.previous = val rolling_count.count = 1 return rolling_count.count rolling_count.count = 0 #static variable rolling_count.previous = None #static variable

2) aplicarlo a su Serie después de convertir a dataframe

df = pd.DataFrame(s) df[''count''] = df[''col''].apply(rolling_count) #new column in dataframe

salida de df

col count 0 B 1 1 B 2 2 A 1 3 A 2 4 A 3 5 B 1


Si desea hacer lo mismo pero filtrar en dos columnas, puede usar esto.

def count_consecutive_items_n_cols(df, col_name_list, output_col): cum_sum_list = [ (df[col_name] != df[col_name].shift(1)).cumsum().tolist() for col_name in col_name_list ] df[output_col] = df.groupby( ["_".join(map(str, x)) for x in zip(*cum_sum_list)] ).cumcount() + 1 return df col_a col_b count 0 1 B 1 1 1 B 2 2 1 A 1 3 2 A 1 4 2 A 2 5 2 B 1


Un trazador de líneas:

df[''count''] = df.groupby(''col'').cumcount()

o

df[''count''] = df.groupby(''col'').cumcount() + 1

Si quieres que los conteos comiencen a 1.