質問: pd.DataFrame({'id': ['111', '222', '333'], '購入日': ['1/5', '2/3', '4/1'], '店舗': ['東京', '千葉', '千葉'], '商品': ['A', 'B', 'D'], '売上タイプ': ['売上', '返品', '売上'], 'cnt': [3, 2, 2]}) ``` データ量が2500万件ほどあるので、処理速度も考慮しつつ書きたいと思っております、。(基本のfor文だと遅くなる・・・?)アドバイスいただける方がいらっしゃいましたらお願いいたします。
回答: グループ化した後、それぞれに含まれる「売上タイプ」の個数を `agg()` で調べます。「売上」だけ、もしくは「返品」だけの場合は `1` になりますので、「売上タイプ」の個数が `1` になるレコードを抽出します。 ``` df.iloc[ sum(( df.assign(idx=df.index) .groupby(['id', '購入日', '店舗', '商品', 'cnt'], as_index=False) .agg({ 'idx': lambda x: list(x) if len(list(x)) == 1 else [] }) ).idx, [])].reset_index(drop=True) # 処理結果 id 購入日 店舗 商品 売上タイプ cnt 0 111 1/5 東京 A 売上 3 1 222 2/3 千葉 B 返品 2 2 333 4/1 千葉 D 売上 2 ``` **追記** > > df.iloc[ sum(●●●).idx, [])] > > > ilocは、数値で行と列の位置を指定するものという認識ですが(df.iloc[行, 列])、この部分のコードについて補足して下さい > > > こちらについては処理の途中経過を見て貰うと分かりやすいかと思います。 ``` (df.assign(idx=df.index) .groupby(['id', '購入日', '店舗', '商品', 'cnt'], as_index=False) .agg({ 'idx': lambda x: list(x) if len(list(x)) == 1 else [] })) => id 購入日 店舗 商品 cnt idx 0 111 1/5 東京 A 1 [] 1 111 1/5 東京 A 2 [] 2 111 1/5 東京 A 3 [2] 3 222 2/3 千葉 B 1 [] 4 222 2/3 千葉 B 2 [7] 5 333 3/5 東京 C 1 [] 6 333 4/1 千葉 D 1 [] 7 333 4/1 千葉 D 2 [11] ``` ここで、idx カラムには元のデータフレーム(`df`)のインデックス値が入ります。条件に適合しない行(レコード)の場合は空リスト(`[]`)にしていますが、これは後の `sum()` のためです。 ``` 上述のデータフレーム.idx => [[], [], [2], [], [7], [], [], [11]] sum(上述のデータフレーム.idx, []) => [2, 7, 11] ``` `sum(lst, [])` で「上述のデータフレーム.idx」(リストのリスト)を平坦化(flatten)しています。 ``` df.iloc[上述の sum() の結果].reset_index(drop=True) ``` `sum()` の結果はリストで、これは抽出条件に適合するレコードのインデックス値(元のデータフレームの行番号)になります。このリストを `df.iloc[]` に渡す事によって目的の「行」を選択・抽出しています。