次のデータフレームdfがあります。
data={'id':[1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2],
'value':[2,2,3,2,2,2,3,3,3,3,1,4,1,1,1,4,4,1,1,1,1,1]}
df=pd.DataFrame.from_dict(data)
df
Out[8]:
id value
0 1 2
1 1 2
2 1 3
3 1 2
4 1 2
5 1 2
6 1 3
7 1 3
8 1 3
9 1 3
10 2 1
11 2 4
12 2 1
13 2 1
14 2 1
15 2 4
16 2 4
17 2 1
18 2 1
19 2 1
20 2 1
21 2 1
必要なのは、IDレベル(df.groupby ['id'])で、同じ値が3回以上連続して表示される場合に識別することです。
上記に対して次の結果が必要です。
df
Out[12]:
id value flag
0 1 2 0
1 1 2 0
2 1 3 0
3 1 2 1
4 1 2 1
5 1 2 1
6 1 3 1
7 1 3 1
8 1 3 1
9 1 3 1
10 2 1 0
11 2 4 0
12 2 1 1
13 2 1 1
14 2 1 1
15 2 4 0
16 2 4 0
17 2 1 1
18 2 1 1
19 2 1 1
20 2 1 1
21 2 1 1
pandas rolling.meanを使用してgroupbyとlambdaのバリエーションを試し、ローリング期間の平均が「値」と比較される場所を特定しました。これらが同じ場合、これはフラグを示します。しかし、これには、フラグを付けようとしている値と平均するさまざまな値が含まれる可能性があるなど、いくつかの問題があります。また、作成したローリング平均のすべての値に「フラグを付ける」方法を理解できません。初期フラグ。ここを参照してください。これはフラグの「右側」を識別しますが、ローリング平均長の以前の値を入力する必要があります。ここにある私のコードを参照してください:
test=df.copy()
test['rma']=test.groupby('id')['value'].transform(lambda x: x.rolling(min_periods=3,window=3).mean())
test['flag']=np.where(test.rma==test.value,1,0)
そして結果はここにあります:
test
Out[61]:
id value rma flag
0 1 2 NaN 0
1 1 2 NaN 0
2 1 3 2.333333 0
3 1 2 2.333333 0
4 1 2 2.333333 0
5 1 2 2.000000 1
6 1 3 2.333333 0
7 1 3 2.666667 0
8 1 3 3.000000 1
9 1 3 3.000000 1
10 2 1 NaN 0
11 2 4 NaN 0
12 2 1 2.000000 0
13 2 1 2.000000 0
14 2 1 1.000000 1
15 2 4 2.000000 0
16 2 4 3.000000 0
17 2 1 3.000000 0
18 2 1 2.000000 0
19 2 1 1.000000 1
20 2 1 1.000000 1
21 2 1 1.000000 1
私が行方不明のものを見るのを待つことができません!ありがとう
これを試すことができます。 1)df.value.diff().ne(0).cumsum()
を使用して追加のグループ変数を作成し、値の変更を示します。 2)transform('size')
を使用してグループサイズを計算し、3つと比較すると、必要なflag
列が得られます。
_df['flag'] = df.value.groupby([df.id, df.value.diff().ne(0).cumsum()]).transform('size').ge(3).astype(int)
df
_
故障:
1)diff
is not equal to zero(これは文字通りdf.value.diff().ne(0)
が意味する)条件True
値が変更されるたびに:
_df.value.diff().ne(0)
#0 True
#1 False
#2 True
#3 True
#4 False
#5 False
#6 True
#7 False
#8 False
#9 False
#10 True
#11 True
#12 True
#13 False
#14 False
#15 True
#16 False
#17 True
#18 False
#19 False
#20 False
#21 False
#Name: value, dtype: bool
_
2)次に、cumsum
は、IDの降順でないシーケンスを提供します。各IDは同じ値を持つ連続したチャンクを示します。ブール値を合計する場合、True
は1つと見なされ、False
ゼロと見なされます:
_df.value.diff().ne(0).cumsum()
#0 1
#1 1
#2 2
#3 3
#4 3
#5 3
#6 4
#7 4
#8 4
#9 4
#10 5
#11 6
#12 7
#13 7
#14 7
#15 8
#16 8
#17 9
#18 9
#19 9
#20 9
#21 9
#Name: value, dtype: int64
_
3)id
列と組み合わせると、データフレームをグループ化し、グループサイズを計算して、flag
列を取得できます。
より堅牢なソリューションについてはEDIT2を参照
同じ結果ですが、少し高速です:
_labels = (df.value != df.value.shift()).cumsum()
df['flag'] = (labels.map(labels.value_counts()) >= 3).astype(int)
id value flag
0 1 2 0
1 1 2 0
2 1 3 0
3 1 2 1
4 1 2 1
5 1 2 1
6 1 3 1
7 1 3 1
8 1 3 1
9 1 3 1
10 2 1 0
11 2 4 0
12 2 1 1
13 2 1 1
14 2 1 1
15 2 4 0
16 2 4 0
17 2 1 1
18 2 1 1
19 2 1 1
20 2 1 1
21 2 1 1
_
どこ:
df.value != df.value.shift()
は値を変更しますcumsum()
は、同じ値のグループごとに「ラベル」を作成しますlabels.value_counts()
は、各ラベルの出現回数をカウントしますlabels.map(...)
は、上記で計算されたカウントでラベルを置き換えます>= 3
_は、カウント値にブールマスクを作成しますastype(int)
はブール値をintにキャストします私の手では、Psidomsのアプローチの2.1msと比較して、dfは1.03msになります。しかし、私はワンライナーではありません。
編集:
両方のアプローチの混合はさらに高速です
_labels = df.value.diff().ne(0).cumsum()
df['flag'] = (labels.map(labels.value_counts()) >= 3).astype(int)
_
あなたのサンプルdfで911µsを与えます。
EDIT2:@ clg4で指摘されているように、IDの変更を説明する正しいソリューション
_labels = (df.value.diff().ne(0) | df.id.diff().ne(0)).cumsum()
df['flag'] = (labels.map(labels.value_counts()) >= 3).astype(int)
_
Where ... | df.id.diff().ne(0)
は、IDが変更されるラベルをインクリメントします
これは、IDの変更時に同じ値でも機能し(インデックス10の値3でテスト)、1.28ミリ秒かかります
EDIT3:より良い説明
インデックス10の値が3である場合を考えてみましょう。df.id.diff().ne(0)
_data={'id':[1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2],
'value':[2,2,3,2,2,2,3,3,3,3,3,4,1,1,1,4,4,1,1,1,1,1]}
df=pd.DataFrame.from_dict(data)
df['id_diff'] = df.id.diff().ne(0).astype(int)
df['val_diff'] = df.value.diff().ne(0).astype(int)
df['diff_or'] = (df.id.diff().ne(0) | df.value.diff().ne(0)).astype(int)
df['labels'] = df['diff_or'].cumsum()
id value id_diff val_diff diff_or labels
0 1 2 1 1 1 1
1 1 2 0 0 0 1
2 1 3 0 1 1 2
3 1 2 0 1 1 3
4 1 2 0 0 0 3
5 1 2 0 0 0 3
6 1 3 0 1 1 4
7 1 3 0 0 0 4
8 1 3 0 0 0 4
9 1 3 0 0 0 4
>10 2 3 1 | 0 = 1 5 <== label increment
11 2 4 0 1 1 6
12 2 1 0 1 1 7
13 2 1 0 0 0 7
14 2 1 0 0 0 7
15 2 4 0 1 1 8
16 2 4 0 0 0 8
17 2 1 0 1 1 9
18 2 1 0 0 0 9
19 2 1 0 0 0 9
20 2 1 0 0 0 9
21 2 1 0 0 0 9
_
_|
_は演算子「ビット単位OR」であり、要素の1つがTrue
である限り、True
を返します。したがって、IDが変更される値に相違がない場合、_|
_はIDの変更を反映します。それ以外の場合は何も変更されません。 .cumsum()
が実行されると、IDが変更される場所でラベルがインクリメントされるため、インデックス10の値_3
_は、インデックス6〜9の値_3
_とグループ化されません。
#try this simpler version
a= pd.Series([1,1,1,2,3,4,5,5,5,7,8,0,0,0])
b= a.groupby([a.ne(0), a]).transform('size').ge(3).astype('int')
#ge(x) <- x is the number of consecutive repeated values
print b
df=pd.DataFrame.from_dict(
{'id':[1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2],
'value':[2,2,3,2,2,2,3,3,3,3,1,4,1,1,1,4,4,1,1,1,1,1]})
df2 = df.groupby((df['value'].shift() != df['value']).\
cumsum()).filter(lambda x: len(x) >= 3)
df['flag'] = np.where(df.index.isin(df2.index),1,0)