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.