Подтвердить что ты не робот

Pandas: условное количество прокатки

У меня есть серия, которая выглядит следующим образом:

   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

4b9b3361

Ответ 1

Однострочник:

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

или

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

если вы хотите, чтобы счетчики начинались с 1.

Ответ 2

Основываясь на втором ответе, который вы связали, если 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

Ответ 3

Мне нравится ответ от @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

Ответ 4

Я думаю, что есть хороший способ объединить решение @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

Ответ 5

Если вы хотите сделать то же самое, но фильтровать по двум столбцам, вы можете использовать это.

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