1
1

Kaggleから学ぶPandasのテクニック

Posted at

KaggleのNotebookで見つけたPandasのテクニックのメモ

元データ:NFL_Predictions

フィルタリング

eventがball_snap, pass_forward, だったら抜き出す、みたいなことを一行でやっている。

plays_pass = plays_pass.loc[plays_pass['event'].isin(['ball_snap','pass_forward','lateral','fumble','fumble_offense_recovered','qb_sack','qb_strip_sack','run'])]

集計

キーでグループ化した中からmin値を求める。aggは使ったことがなかったが、これは便利。あと、集計したあとreset_index()でインデックスを初期化するのも大事なテクニックだと思う。

keys2 = ['gameId','playId']
non_bs3 = non_bs2.groupby(keys2).agg({'action_end_frame': 'min'}).reset_index()

カラムのリネーム

こういう風にいくつものカラムを一気にリネームできるのはやったことがなかった。

pos_qb.rename(columns = {'x': 'qb_x', 'y': 'qb_y'}, inplace = True)

データフレームのマージ

一旦左結合して、数が同じだったら結合する。左結合の場合は、keysが共通でないものはNanになる。そうでない結合はkeysで共通なものだけ結合される。

keys = ['gameId', 'playId', 'nflId']
    b = a.merge(pff_df, on=keys, how='left')
    
    if a.shape[0] != b.shape[0]: # test left join quality, if passes continue with inner
        print('error')
        break
    else:
        b = a.merge(pff_df, on=keys)
左結合(left join)とは? ### キーの指定と結合の操作 ```python keys = ['gameId', 'playId', 'nflId'] b = a.merge(pff_df, on=keys, how='left') ```

このコード行では、aデータフレームとpff_dfデータフレームを、keysで指定された列(gameIdplayIdnflId)に基づいて左結合(left join)しています。

左結合(left join)とは?

左結合は、aデータフレームのすべての行を保持し、pff_dfから一致するキーを持つ行を追加します。一致する行がpff_dfに存在しない場合、pff_dfの列にはNaN(欠損値)が入ります。

仮に、以下のようなデータがあったとします。

aデータフレーム

gameId playId nflId other_columns_a
1 101 1001 ...
2 102 1002 ...
1 103 1003 ...

pff_dfデータフレーム

gameId playId nflId officialPosition pff_positionLinedUp completion_pct interception_pct sack_pct scramble_pct playResult
1 101 1001 ... ... ... ... ... ... ...
1 103 1003 ... ... ... ... ... ... ...

左結合の結果

左結合を行うと、以下のような結果が得られます。

bデータフレーム

gameId playId nflId other_columns_a officialPosition pff_positionLinedUp completion_pct interception_pct sack_pct scramble_pct playResult
1 101 1001 ... ... ... ... ... ... ... ...
2 102 1002 ... NaN NaN NaN NaN NaN NaN NaN
1 103 1003 ... ... ... ... ... ... ... ...

このように、aデータフレームのすべての行が保持され、pff_dfに一致するキーを持つ行のデータが追加されています。pff_dfに一致する行がない場合、その列にはNaNが入ります。

その後の処理

if a.shape[0] != b.shape[0]: # test left join quality, if passes continue with inner
    print('error')
    break
else:
    b = a.merge(pff_df, on=keys)

ここで、左結合後の行数が一致しない場合はエラーを表示して処理を中断します。行数が一致する場合は、再度inner join(内部結合)を行います。内部結合では、両方のデータフレームに共通するキーを持つ行のみが結合されます。

最終的に、結合されたデータフレームをdataに追加しています。

data = pd.concat([data, b], ignore_index=True)

このようにして、各週のデータを読み込み、必要な情報を結合し、最終的なデータセットを構築しています。

重複削除

重複行を削除する。確かにデータを加工していると重複することがあるのかも。

keys2 = ['gameId','playId']
facts = ['completion_pct','interception_pct','playResult']
bs2 = plays_pass.loc[plays_pass['event'] == 'ball_snap'][keys2 + ['frameId'] + facts].drop_duplicates()

ランク付け

例えば距離そのものではなく、相対的に「近いか遠いか」というようなランク付けができる。

# データの用意
dist_qb2 = dist_qb1.merge(pos_qb1, on=['gameId', 'playId', 'frameId'])
dist_qb2['dist_qb'] = ((dist_qb2['qb_x'] - dist_qb2['x'])**2 + (dist_qb2['qb_y'] - dist_qb2['y'])**2)**0.5

# プレイヤーごとの最小距離を計算
dist_qb_plyr_min = dist_qb2.groupby(['gameId', 'playId', 'nflId']).agg({'dist_qb': 'min'}).reset_index()

# ランクの計算
dist_qb_plyr_min['prox_rank'] = dist_qb_plyr_min.groupby(['gameId', 'playId'])['dist_qb'].rank()

# 最も近いプレイヤーを特定
prox1 = dist_qb_plyr_min.loc[dist_qb_plyr_min['prox_rank'] == 1][['gameId', 'playId', 'dist_qb']].copy()
prox1.rename(columns={'dist_qb': 'prox1'}, inplace=True)
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1