Ответ 1
Однострочник:
df['count'] = df.groupby('col').cumcount()
или
df['count'] = df.groupby('col').cumcount() + 1
если вы хотите, чтобы счетчики начинались с 1.
У меня есть серия, которая выглядит следующим образом:
col
0 B
1 B
2 A
3 A
4 A
5 B
Это временной ряд, поэтому индекс упорядочен по времени.
Для каждой строки я хотел бы подсчитать, сколько раз значение появилось последовательно, то есть:
Вывод:
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
Я нашел 2 связанных вопроса, но я не могу понять, как "записать" эту информацию в качестве нового столбца в DataFrame для каждой строки (как указано выше). Использование roll_apply не работает.
Связанный:
Подсчет последовательных событий на pandas данных по их индексу
Поиск последовательных сегментов в кадре данных pandas
Однострочник:
df['count'] = df.groupby('col').cumcount()
или
df['count'] = df.groupby('col').cumcount() + 1
если вы хотите, чтобы счетчики начинались с 1.
Основываясь на втором ответе, который вы связали, если s
- ваша серия.
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
Мне нравится ответ от @chrisb, но я хочу поделиться своим собственным решением, поскольку некоторые люди могут найти его более читаемым и более простым в использовании с аналогичными проблемами....
1) Создайте функцию, которая использует статические переменные
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) примените его к своей серии после преобразования в dataframe
df = pd.DataFrame(s)
df['count'] = df['col'].apply(rolling_count) #new column in dataframe
вывод df
col count
0 B 1
1 B 2
2 A 1
3 A 2
4 A 3
5 B 1
Я думаю, что есть хороший способ объединить решение @chrisb и @CodeShaman (Как было указано, решение CodeShamans учитывает общее количество, а не последовательные значения).
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
Если вы хотите сделать то же самое, но фильтровать по двум столбцам, вы можете использовать это.
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