Posted at

pandasで連続値をカウントする

More than 1 year has passed since last update.

昨日書いた処理ににていますが、グループごとに連続する値をカウントしてみたいという要件があったとき、pandasには一発でやる機能がありません。

どのようにするか、stack overflowに答えがありましたが、2次的解説。

df = pd.DataFrame({'group' : [1,1,2,3,3,3,3,2,2,4,4,4] , 'value' : [1,2,1,1,3,2,4,2,1,3,2,1]} )

group value
0 1 1
1 1 2
2 2 1
3 3 1
4 3 3
5 3 2
6 3 4
7 2 2
8 2 1
9 4 3
10 4 2
11 4 1


cumcountじゃね?

こういうデータに対して、cumcount()を使うと以下のようになってしまいます。

df['new'] = df.groupby('group').cumcount()

group value new
0 1 1 0
1 1 2 1
2 2 1 0
3 3 1 0
4 3 3 1
5 3 2 2
6 3 4 3
7 2 2 1
8 2 1 2
9 4 3 0
10 4 2 1
11 4 1 2

これでいい場合もあると思いますが、group2を扱う際、連続するという条件を加味すると、これではまずいことがわかります。


ちょっと気持ち悪いけど

so(Counting consecutive positive value in Python array)に書いてあったのは、以下のようにshiftとcumsumを使う方法です。ちょっと回りくどい気もしますが、わかりやすいです。

例はarrayでという質問でしたが、この回答はpandas DataFrameかSeriesに対して有効です。

簡単にいうと、シーケンスの変わり目にフラグを立てて、cumsomで階段状の累積インデックスを振っていきます。これで元のグループ(要素)とは関係なく連続値を再グループ化できますので、groupbyしてcumcount()で終わり。

y = df['group']

df['new'] = y.groupby((y != y.shift()).cumsum()).cumcount() + 1

group value new
0 1 1 1
1 1 2 2
2 2 1 1
3 3 1 1
4 3 3 2
5 3 2 3
6 3 4 4
7 2 2 1
8 2 1 2
9 4 3 1
10 4 2 2
11 4 1 3

groupbyする前にグループ化したりとか、少々二度手間感がありますが、そもそもやろうとしていることが違うので、そんなに無駄ではないでしょうね。