Help us understand the problem. What is going on with this article?

[pandas] GroupBy Tips

はじめに

KaggleのData Science Bowl 2019に参戦したときに知った、GroupByの便利なTipsをまとめてみました

動作環境

  • Python 3.8.1
  • pandas 0.25.3

使用するデータ

以下の架空のデータを用いる

import pandas as pd

df = pd.DataFrame({
    'name'    : ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Fred', 'George'],
    'state'   : ['NY', 'CA', 'NY', 'CA', 'FL', 'FL', 'NY'],
    'score'   : [4, 3, 5, 10, 1, 0, 7]
})
name state score
0 Alice NY 4
1 Bob CA 3
2 Charlie NY 5
3 David CA 10
4 Eve FL 1
5 Fred FL 0
6 George NY 7

各グループの最初の行を抽出する

first()もしくはhead(1)nth(0)を使う
head(n)は各グループの先頭からn行を取得するので、最初の行がほしい場合はn=1を指定する
nth(n)は各グループのn行目を取得するので、最初の行がほしい場合はn=0を指定する
※Pythonのindexは0から始まる

注意点として、それぞれのメソッドで欠損値がある場合の挙動が異なる
first()は各カラムごとに最初にNaNでない値を取得するので、欠損値が含まれる場合に、別々の行から継ぎはぎしたデータが返ってくるおそれがある
head()は欠損値も含めて、最初からn行を取得する
nth()dropnaの設定値(None, any, all)によって、挙動が異なる

また、first()nth()は出力形式が同じだが、head()は異なる

df.groupby('state').first()
#        name  score
#state
#CA       Bob      3
#FL       Eve      1
#NY     Alice      4

df.groupby('state').head(1)
#    name state  score
#0  Alice    NY      4
#1    Bob    CA      3
#4    Eve    FL      1

df.groupby('state').nth(0)
#        name  score
#state
#CA       Bob      3
#FL       Eve      1
#NY     Alice      4

欠損値がある場合

# データの作成は省略
print(with_nan_df)
#      name state  score
#0    Alice    NY    NaN
#1      Bob    CA    3.0
#2  Charlie    NY    5.0
#3    David    CA   10.0
#4      Eve    FL    NaN
#5     Fred    FL    0.0
#6   George    NY    7.0

with_nan_df.groupby('state').first()
#        name  score
#state
#CA       Bob    3.0
#FL       Eve    0.0
#NY     Alice    5.0
# ⇒EveとAliceのデータが別のデータと継ぎはぎになっている!

with_nan_df.groupby('state').head(1)
#    name state  score
#0  Alice    NY    NaN
#1    Bob    CA    3.0
#4    Eve    FL    NaN
# ⇒NaNはそのまま!

欠損値がある場合の挙動の違いについて、詳しくは以下のページを参照
[pandas]groupbyの最初・最後の行を求めるfirst・last関数の話、headやnthとの違い

各グループの最後の行を抽出する

last()もしくはtail(1)nth(-1)を使う
tail(n)は各グループの後ろからn行を取得するので、最後の行がほしい場合はn=1を指定する
nth(n)は各グループのn行目を取得するので、最後の行がほしい場合はn=-1を指定する
※Pythonはindex=-1で最後のindexを指定できる

注意点として、先ほど最初の行を抽出したとき同様に、それぞれのメソッドで欠損値がある場合の挙動が異なる
last()first()と、tail()head()と同じ挙動をする

df.groupby('state').last()
#         name  score
#state
#CA      David     10
#FL       Fred      0
#NY     George      7

df.groupby('state').tail(1)
#     name state  score
#3   David    CA     10
#5    Fred    FL      0
#6  George    NY      7

df.groupby('state').nth(-1)
#         name  score
#state
#CA      David     10
#FL       Fred      0
#NY     George      7

各グループのサイズを取得する

size()を使う
1つのカラムだけでgroupbyする場合はvalue_counts()でも似た結果が得られるが、複数のカラムに対して各ペアのデータ数を取得する場合には便利

df.groupby('state').size()
#state
#CA    2
#FL    2
#NY    3
#dtype: int64

df['state'].value_counts()
#NY    3
#CA    2
#FL    2
#Name: state, dtype: int64

複数のカラムに対して各ペアのデータ数を取得する

# データの作成は省略
print(team_df)
#      name state  score team
#0    Alice    NY      4    A
#1      Bob    CA      3    A
#2  Charlie    NY      5    A
#3    David    CA     10    A
#4      Eve    FL      1    B
#5     Fred    FL      0    B
#6   George    NY      7    B

team_df.groupby(['state', 'team']).size()
#state  team
#CA     A       2
#FL     B       2
#NY     A       2
#       B       1
#dtype: int64

グループごとにデータをシフトする

groupbyした結果にもshift()は使える

# 結果を見やすくするため、データをstateでソート
df.sort_values('state', inplace=True)
print(df)
#      name state  score
#1      Bob    CA      3
#3    David    CA     10
#4      Eve    FL      1
#5     Fred    FL      0
#0    Alice    NY      4
#2  Charlie    NY      5
#6   George    NY      7

df.groupby('state')['score'].shift()
#1    NaN
#3    3.0
#4    NaN
#5    1.0
#0    NaN
#2    4.0
#6    5.0
#Name: score, dtype: float64

グループごとに累積和をとる

groupbyした結果にapplyを使ってcumsum()を適用する

print(df)
#      name state  score
#1      Bob    CA      3
#3    David    CA     10
#4      Eve    FL      1
#5     Fred    FL      0
#0    Alice    NY      4
#2  Charlie    NY      5
#6   George    NY      7

df.groupby('state').apply(lambda tdf: tdf['score'].cumsum())
#state
#CA     1     3
#       3    13
#FL     4     1
#       5     1
#NY     0     4
#       2     9
#       6    16
#Name: score, dtype: int64

グループごとのカテゴリ数を取得する

例えば以下のプログラムでは、各stateごとに何種類のteamがあるかを取得する

print(team_df)
#      name state  score team
#0    Alice    NY      4    A
#1      Bob    CA      3    A
#2  Charlie    NY      5    A
#3    David    CA     10    A
#4      Eve    FL      1    B
#5     Fred    FL      0    B
#6   George    NY      7    B

team_df.groupby('state')['team'].agg(lambda x: len(x.unique()))
#state
#CA    1
#FL    1
#NY    2
#Name: team, dtype: int64

さいごに

間違いや、もっと良い方法あるよ!って方は教えていただけると嬉しいです

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした