Pandas:条件滚动计数

60 投票
5 回答
55026 浏览
提问于 2025-04-18 15:55

我有一个看起来像这样的数据序列:

   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

我找到两个相关的问题,但我不知道怎么把这个信息“写”成数据框中的新列,针对每一行(就像上面那样)。使用rolling_apply效果不好。

相关链接:

按索引计算pandas数据框中的连续事件

在pandas数据框中查找连续段

5 个回答

4

如果你想做同样的事情,但要在两个列上进行筛选,可以使用这个。

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
13

我喜欢@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) 在将数据转换为数据框后,将其应用到你的序列上

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

数据框的输出

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

根据你链接的第二个回答,假设 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
26

一句话解决方案:

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

或者

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

如果你想让计数从1开始的话。

76

我觉得可以把@chrisb和@CodeShaman的解决方案结合起来,这样会更好。因为有人提到,CodeShaman的方案是计算总数,而不是连续的值。

  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

撰写回答