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
で指定された列(gameId
、playId
、nflId
)に基づいて左結合(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)