{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# PFF Players Missing From Tracking Data\n\nWhile reconciling the PFF data and tracking data, I found two problems:\n\n1. PFF data lists jersey numbers that do not exist in the tracking data for the corresponding play\n2. Tracking data has one game where two teammates have the same jersey number on the same plays\n\nThis notebook identifies those cases and provides [patches](#patches) to fix the source data.\n\nThis data cleaning operation will not cover whether the PFF annotations are valid or not, only cover the cases where the PFF jersey number cannot be conclusively matched to a single jersey number in the tracking data.\n\nI used [this notebook](https://www.kaggle.com/vingkan/finding-missing-players-2021-12-31) to manually view each of the affected plays and determine the correct jersey number present in the tracking data.\n\nFrom looking at the data, here are some possible causes for the mismatches:\n\n- Human error in recording the jersey number\n- Players who changed their jersey number during the season (and NGS and PFF have different ways of choosing)\n\nIn all the examples I looked at, each **missing player** had the same jersey number in the tracking data across all their plays.","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nfrom typing import List\nfrom tqdm.notebook import tqdm\nfrom io import StringIO","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:55:12.089039Z","iopub.execute_input":"2022-01-01T07:55:12.089394Z","iopub.status.idle":"2022-01-01T07:55:12.094480Z","shell.execute_reply.started":"2022-01-01T07:55:12.089356Z","shell.execute_reply":"2022-01-01T07:55:12.093237Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"DIR = \"../input/nfl-big-data-bowl-2022\"\ndf_games = pd.read_csv(f\"{DIR}/games.csv\")\ndf_plays = pd.read_csv(f\"{DIR}/plays.csv\")\ndf_players = pd.read_csv(f\"{DIR}/players.csv\")\ndf_pff = pd.read_csv(f\"{DIR}/PFFScoutingData.csv\")","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:55:12.096625Z","iopub.execute_input":"2022-01-01T07:55:12.096909Z","iopub.status.idle":"2022-01-01T07:55:12.371398Z","shell.execute_reply.started":"2022-01-01T07:55:12.096877Z","shell.execute_reply":"2022-01-01T07:55:12.370741Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"pff_jersey_cols = [\n    \"missedTackler\",\n    \"assistTackler\",\n    \"tackler\",\n    \"gunners\",\n    \"puntRushers\",\n    \"specialTeamsSafeties\",\n    \"vises\",\n]\npff_jersey_out_cols = [\n    \"gameId\",\n    \"playId\",\n    \"teamCode\",\n    \"jerseyNumber\",\n    \"role\",\n]\n\n\ndef split_jersey_list(raw: str, **kwargs) -> List[str]:\n    if pd.isna(raw):\n        return []\n    return raw.split(\"; \")\n\n\ndef get_jersey_part(raw: str, part: int, **kwargs) -> List[str]:\n    if pd.isna(raw):\n        return None\n    return raw.split(\" \")[part]\n\n\ndef get_pff_player_jerseys(df_pff: pd.DataFrame) -> pd.DataFrame:\n    df = df_pff.copy()\n    for col in pff_jersey_cols:\n        df[col] = df[col].apply(split_jersey_list)\n    dfs = []\n    for col in pff_jersey_cols:\n        df[\"role\"] = col\n        df_role = df.explode(col)\n        df_role[\"teamCode\"] = df_role[col].apply(get_jersey_part, args=(0,))\n        df_role[\"jerseyNumber\"] = df_role[col].apply(get_jersey_part, args=(1,))\n        df_role = df_role.dropna(subset=[\"teamCode\", \"jerseyNumber\"])\n        dfs.append(df_role[pff_jersey_out_cols])\n    df_all = pd.concat(dfs)\n    df_all[\"jerseyNumber\"] = df_all[\"jerseyNumber\"].astype(int)\n    return df_all","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:55:12.372514Z","iopub.execute_input":"2022-01-01T07:55:12.373724Z","iopub.status.idle":"2022-01-01T07:55:12.387869Z","shell.execute_reply.started":"2022-01-01T07:55:12.373636Z","shell.execute_reply":"2022-01-01T07:55:12.387062Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_team_code(row: pd.Series, **kwargs) -> str:\n    team = row[\"team\"]\n    if pd.isna(team) or team == \"football\":\n        return None\n    return row[team]\n\n\ndef augment_with_team_code(df: pd.DataFrame) -> pd.DataFrame:\n        df[\"jerseyNumber\"] = df[\"jerseyNumber\"].astype(float)\n        game_to_team_code = df_games \\\n            .rename(columns={\n                \"homeTeamAbbr\": \"home\",\n                \"visitorTeamAbbr\": \"away\",\n            }) \\\n            [[\"gameId\", \"home\", \"away\"]] \\\n            .set_index(\"gameId\")\n        df = df.join(game_to_team_code, on=[\"gameId\"])\n        with pd.option_context(\"mode.chained_assignment\", None):\n            df[\"teamCode\"] = df.apply(get_team_code, axis=\"columns\")\n        return df\n\n\ndef read_tracking_data(seasons: List[int], progress=False, **kwargs) -> pd.DataFrame:\n    readers = [\n        pd.read_csv(\n            f\"{DIR}/tracking{season}.csv\",\n            iterator=True,\n            chunksize=10**5,\n            **kwargs\n        )\n        for season in\n        seasons\n    ]\n    \n    dfs = []\n    for i, reader in enumerate(readers):\n        progress_bar = tqdm(reader, desc=f\"{seasons[i]} Season\") if progress else reader\n        for chunk in progress_bar:\n            dfs.append(chunk)\n            \n    return pd.concat(dfs)","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:55:12.389019Z","iopub.execute_input":"2022-01-01T07:55:12.389758Z","iopub.status.idle":"2022-01-01T07:55:12.407722Z","shell.execute_reply.started":"2022-01-01T07:55:12.389716Z","shell.execute_reply":"2022-01-01T07:55:12.407001Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"SEASONS = [2018, 2019, 2020]\ntracking_cols = [\n    \"gameId\",\n    \"playId\",\n    \"team\",\n    \"jerseyNumber\",\n    \"nflId\",\n    \"displayName\",\n    \"position\",\n]","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:55:12.410982Z","iopub.execute_input":"2022-01-01T07:55:12.412067Z","iopub.status.idle":"2022-01-01T07:55:12.423751Z","shell.execute_reply.started":"2022-01-01T07:55:12.412007Z","shell.execute_reply":"2022-01-01T07:55:12.422749Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_tracking = read_tracking_data(\n    seasons=SEASONS,\n    progress=True,\n    usecols=tracking_cols\n).drop_duplicates().dropna()\nprint(f\"Tracking data contains {len(df_tracking):,d} player-play records.\\n\")\ndf_tracking.head()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:55:12.424949Z","iopub.execute_input":"2022-01-01T07:55:12.425157Z","iopub.status.idle":"2022-01-01T07:57:30.125261Z","shell.execute_reply.started":"2022-01-01T07:55:12.425130Z","shell.execute_reply":"2022-01-01T07:57:30.124464Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_tracking_players = augment_with_team_code(df_tracking)\nprint(f\"Player data from tracking contains {len(df_tracking_players):,d} player-play records.\\n\")\ndf_tracking_players.head()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:30.126476Z","iopub.execute_input":"2022-01-01T07:57:30.127083Z","iopub.status.idle":"2022-01-01T07:57:38.864316Z","shell.execute_reply.started":"2022-01-01T07:57:30.127047Z","shell.execute_reply":"2022-01-01T07:57:38.863292Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"join_cols = [\n    \"gameId\",\n    \"playId\",\n    \"teamCode\",\n    \"jerseyNumber\"\n]\ndf_pff_players = get_pff_player_jerseys(df_pff)\ndf_pff_players_no_roles = df_pff_players.groupby(join_cols)\nprint(f\"PFF data contains {len(df_pff_players):,d} player-play-role records.\\n\")\nprint(f\"PFF data contains {len(df_pff_players_no_roles):,d} player-play records.\\n\")\ndf_pff_players.head()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:38.866128Z","iopub.execute_input":"2022-01-01T07:57:38.866451Z","iopub.status.idle":"2022-01-01T07:57:41.643706Z","shell.execute_reply.started":"2022-01-01T07:57:38.866407Z","shell.execute_reply":"2022-01-01T07:57:41.642904Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_pff_track = df_pff_players.join(df_tracking_players.set_index(join_cols), on=join_cols)\ndf_pff_track_no_roles = df_pff_track.groupby(join_cols)\nprint(f\"Post-join, PFF player role data contains {len(df_pff_track):,d} player-play-role records.\\n\")\nprint(f\"Post-join, PFF player role data contains {len(df_pff_track_no_roles):,d} player-play records.\\n\")\ndf_pff_track.head()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:41.645051Z","iopub.execute_input":"2022-01-01T07:57:41.645282Z","iopub.status.idle":"2022-01-01T07:57:43.428083Z","shell.execute_reply.started":"2022-01-01T07:57:41.645253Z","shell.execute_reply":"2022-01-01T07:57:43.426984Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"play_keys = [\"gameId\", \"playId\"]\nplay_cols = play_keys + [\"specialTeamsPlayType\", \"specialTeamsResult\"]\ndf_pff_track_explosion = df_pff_track \\\n    [join_cols + [\"nflId\"]] \\\n    .drop_duplicates() \\\n    .groupby(join_cols) \\\n    .count() \\\n    .reset_index() \\\n    .join(df_plays[play_cols].set_index(play_keys), on=play_keys) \\\n    .rename(columns={ \"nflId\": \"tracking_matches\" }) \\\n    .query(\"tracking_matches > 1\") \\\n    .sort_values(by=\"tracking_matches\", ascending=False)\nprint(f\"Post-join, {len(df_pff_track_explosion):,d} player-play records had multiple tracking matches.\")\ndf_pff_track_explosion.head()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.430044Z","iopub.execute_input":"2022-01-01T07:57:43.430328Z","iopub.status.idle":"2022-01-01T07:57:43.533414Z","shell.execute_reply.started":"2022-01-01T07:57:43.430299Z","shell.execute_reply":"2022-01-01T07:57:43.532749Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_tracking \\\n    .query(\"team == 'away' and jerseyNumber == 44 and gameId == 2020092004 and playId == 279\")","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.534636Z","iopub.execute_input":"2022-01-01T07:57:43.534908Z","iopub.status.idle":"2022-01-01T07:57:43.583366Z","shell.execute_reply.started":"2022-01-01T07:57:43.534879Z","shell.execute_reply":"2022-01-01T07:57:43.582680Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_missing_from_pff = df_pff_track[df_pff_track[\"nflId\"].isna()]\ndf_missing_from_pff = df_missing_from_pff.join(df_plays[play_cols].set_index(play_keys), on=play_keys)\nwith pd.option_context(\"mode.chained_assignment\", None):\n    df_missing_from_pff[\"season\"] = df_missing_from_pff[\"gameId\"].apply(lambda d: int(str(d)[:4]))\nprint(f\"{len(df_missing_from_pff):,d} PFF player-play-role records are not found in tracking data.\\n\")\ndf_missing_from_pff.head()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.584711Z","iopub.execute_input":"2022-01-01T07:57:43.584941Z","iopub.status.idle":"2022-01-01T07:57:43.622365Z","shell.execute_reply.started":"2022-01-01T07:57:43.584912Z","shell.execute_reply":"2022-01-01T07:57:43.621509Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_tracking_players.query(\"gameId == 2019111711 and playId == 3643 and teamCode == 'LA' and jerseyNumber == 38\")","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.623784Z","iopub.execute_input":"2022-01-01T07:57:43.624049Z","iopub.status.idle":"2022-01-01T07:57:43.685154Z","shell.execute_reply.started":"2022-01-01T07:57:43.624019Z","shell.execute_reply":"2022-01-01T07:57:43.684543Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_missing_from_pff.groupby(\"specialTeamsPlayType\")[\"playId\"].count().reset_index()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.689834Z","iopub.execute_input":"2022-01-01T07:57:43.690844Z","iopub.status.idle":"2022-01-01T07:57:43.703619Z","shell.execute_reply.started":"2022-01-01T07:57:43.690794Z","shell.execute_reply":"2022-01-01T07:57:43.702582Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_missing_from_pff.groupby(\"season\")[\"playId\"].count().reset_index()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.704737Z","iopub.execute_input":"2022-01-01T07:57:43.705035Z","iopub.status.idle":"2022-01-01T07:57:43.721608Z","shell.execute_reply.started":"2022-01-01T07:57:43.704994Z","shell.execute_reply":"2022-01-01T07:57:43.720769Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_missing_from_pff.groupby([\"season\", \"specialTeamsPlayType\"])[\"playId\"].count().reset_index()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.723204Z","iopub.execute_input":"2022-01-01T07:57:43.723943Z","iopub.status.idle":"2022-01-01T07:57:43.743465Z","shell.execute_reply.started":"2022-01-01T07:57:43.723895Z","shell.execute_reply":"2022-01-01T07:57:43.742448Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_missing_from_pff.groupby([\"season\", \"specialTeamsPlayType\", \"role\"])[\"playId\"].count().reset_index()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.744530Z","iopub.execute_input":"2022-01-01T07:57:43.744783Z","iopub.status.idle":"2022-01-01T07:57:43.768063Z","shell.execute_reply.started":"2022-01-01T07:57:43.744744Z","shell.execute_reply":"2022-01-01T07:57:43.767234Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_missing_from_pff.query(\"season == 2020 and specialTeamsPlayType == 'Punt' and role == 'vises'\")","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.769450Z","iopub.execute_input":"2022-01-01T07:57:43.769814Z","iopub.status.idle":"2022-01-01T07:57:43.792041Z","shell.execute_reply.started":"2022-01-01T07:57:43.769779Z","shell.execute_reply":"2022-01-01T07:57:43.791140Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_missing_from_pff \\\n    .query(\"role == 'gunners' or role == 'vises'\") \\\n    .groupby([\"teamCode\", \"jerseyNumber\"]) \\\n    [\"playId\"].count() \\\n    .reset_index() \\\n    .rename(columns={ \"playId\": \"playCount\" }) \\\n    .sort_values(by=\"playCount\", ascending=False)","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.793336Z","iopub.execute_input":"2022-01-01T07:57:43.793564Z","iopub.status.idle":"2022-01-01T07:57:43.814441Z","shell.execute_reply.started":"2022-01-01T07:57:43.793537Z","shell.execute_reply":"2022-01-01T07:57:43.813825Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"out_cols = [\"gameId\", \"playId\", \"teamCode\", \"jerseyNumber\", \"role\"]\nbuffer = StringIO()\ndf_missing_from_pff[out_cols].to_csv(buffer, index=False)","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.815537Z","iopub.execute_input":"2022-01-01T07:57:43.816067Z","iopub.status.idle":"2022-01-01T07:57:43.824938Z","shell.execute_reply.started":"2022-01-01T07:57:43.816037Z","shell.execute_reply":"2022-01-01T07:57:43.824305Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"buffer.getvalue()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.826034Z","iopub.execute_input":"2022-01-01T07:57:43.827068Z","iopub.status.idle":"2022-01-01T07:57:43.838407Z","shell.execute_reply.started":"2022-01-01T07:57:43.827001Z","shell.execute_reply":"2022-01-01T07:57:43.837760Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"raw_missing_players = 'gameId,playId,teamCode,jerseyNumber,role\\n2019111711,3643,LA,38,missedTackler\\n2018120600,1607,TEN,47,tackler\\n2018120600,1607,TEN,47,gunners\\n2018120600,3309,TEN,47,gunners\\n2018122400,542,OAK,14,gunners\\n2019090805,326,NYJ,25,gunners\\n2019090805,666,BUF,29,gunners\\n2019090805,1006,NYJ,23,gunners\\n2019090805,1006,NYJ,25,gunners\\n2019090805,1159,BUF,29,gunners\\n2019090805,1436,NYJ,25,gunners\\n2019090805,1802,NYJ,25,gunners\\n2019090805,2441,NYJ,25,gunners\\n2019090805,3298,NYJ,25,gunners\\n2019090805,3724,NYJ,25,gunners\\n2019090805,4435,BUF,29,gunners\\n2018093011,1761,NO,27,puntRushers\\n2019090805,4435,NYJ,23,puntRushers\\n2019112400,3811,ATL,55,puntRushers\\n2020091312,3370,DAL,59,puntRushers\\n2020091312,3822,DAL,59,puntRushers\\n2020092004,279,BUF,40,puntRushers\\n2020092004,1033,BUF,40,puntRushers\\n2020092004,1353,BUF,40,puntRushers\\n2020092004,1862,BUF,40,puntRushers\\n2020092004,3716,BUF,40,puntRushers\\n2020092708,1811,IND,33,puntRushers\\n2020092708,2190,IND,33,puntRushers\\n2020092708,2556,IND,33,puntRushers\\n2020092708,2665,NYJ,37,puntRushers\\n2020092708,3050,IND,33,puntRushers\\n2020092708,3271,NYJ,37,puntRushers\\n2020092711,2619,DEN,91,puntRushers\\n2020092711,3538,DEN,91,puntRushers\\n2018090903,3713,MIA,30,specialTeamsSafeties\\n2018091613,778,NYG,23,specialTeamsSafeties\\n2018091613,1035,NYG,23,specialTeamsSafeties\\n2018091613,1575,NYG,23,specialTeamsSafeties\\n2018102100,3461,TEN,25,specialTeamsSafeties\\n2019090805,2219,BUF,29,specialTeamsSafeties\\n2019090805,2481,BUF,29,specialTeamsSafeties\\n2019090805,3125,BUF,29,specialTeamsSafeties\\n2019090805,3551,BUF,29,specialTeamsSafeties\\n2019090805,4017,BUF,29,specialTeamsSafeties\\n2019091507,36,SEA,24,specialTeamsSafeties\\n2019091507,1491,SEA,24,specialTeamsSafeties\\n2019091507,2703,SEA,24,specialTeamsSafeties\\n2019091507,3074,SEA,24,specialTeamsSafeties\\n2019091507,3732,SEA,24,specialTeamsSafeties\\n2019111711,54,LA,38,specialTeamsSafeties\\n2019111711,1153,LA,38,specialTeamsSafeties\\n2019111711,1663,LA,38,specialTeamsSafeties\\n2019111711,3643,LA,38,specialTeamsSafeties\\n2020092708,3513,IND,33,specialTeamsSafeties\\n2020100400,40,CAR,20,specialTeamsSafeties\\n2020100400,613,CAR,20,specialTeamsSafeties\\n2020100400,939,CAR,20,specialTeamsSafeties\\n2018090903,1257,MIA,30,vises\\n2018090903,3163,MIA,30,vises\\n2018091613,2608,NYG,23,vises\\n2018120600,1699,TEN,47,vises\\n2018122400,744,OAK,14,vises\\n2018122400,1634,OAK,14,vises\\n2018122400,2467,OAK,14,vises\\n2019090805,1159,NYJ,25,vises\\n2019090805,4435,NYJ,25,vises\\n2019091507,115,SEA,24,vises\\n2019091507,315,SEA,24,vises\\n2019091507,786,SEA,24,vises\\n2019091507,1571,SEA,24,vises\\n2019100700,3613,CLE,25,vises\\n2019111711,1240,LA,38,vises\\n2019111711,2587,LA,38,vises\\n2019111711,2829,LA,38,vises\\n2019111711,3159,LA,38,vises\\n2019111711,3382,LA,38,vises\\n2020100400,169,CAR,20,vises\\n2020100400,698,CAR,20,vises\\n2020100400,1096,CAR,20,vises\\n'\npd.read_csv(StringIO(raw_missing_players))","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.839879Z","iopub.execute_input":"2022-01-01T07:57:43.840375Z","iopub.status.idle":"2022-01-01T07:57:43.864577Z","shell.execute_reply.started":"2022-01-01T07:57:43.840341Z","shell.execute_reply":"2022-01-01T07:57:43.863984Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Patches","metadata":{}},{"cell_type":"markdown","source":"## Raw PFF Patches\n\n```\nrole,pffJersey,trackingJersey\\ntackler,TEN 47,TEN 46\\nmissedTackler,LA 38,LA 21\\ngunners,TEN 47,TEN 46\\ngunners,OAK 14,OAK 38\\ngunners,NYJ 25,NYJ 40\\ngunners,BUF 29,BUF 36\\ngunners,NYJ 23,NYJ 37\\nvises,CAR 20,CAR 41\\nvises,CLE 25,CLE 35\\nvises,LA 38,LA 21\\nvises,MIA 30,MIA 23\\nvises,NYG 23,NYG 37\\nvises,NYJ 25,NYJ 40\\nvises,BUF 29,BUF 36\\nvises,OAK 14,OAK 38\\nvises,SEA 24,SEA 8\\nvises,TEN 47,TEN 46\\npuntRushers,ATL 55,ATL 62\\npuntRushers,DAL 59,DAL 53\\npuntRushers,DEN 91,DEN 58\\npuntRushers,IND 33,IND 36\\npuntRushers,NO 27,NO 36\\npuntRushers,NYJ 23,NYJ 37\\npuntRushers,NYJ 37,NYJ 43\\nspecialTeamsSafeties,BUF 29,BUF 36\\nspecialTeamsSafeties,CAR 20,CAR 41\\nspecialTeamsSafeties,IND 33,IND 36\\nspecialTeamsSafeties,LA 38,LA 21\\nspecialTeamsSafeties,MIA 30,MIA 23\\nspecialTeamsSafeties,NYG 23,NYG 37\\nspecialTeamsSafeties,SEA 24,SEA 8\\nspecialTeamsSafeties,TEN 25,TEN 41\\n\n```\n\n## Tracking Data Patch\n\nIn the 2020 tracking data, both Deon Lacey and Tyler Matakevich on the Buffalo Bills have jersey number 44. In that game, Matakevich was 44 and Lacey should be number 40.\n\n```\ngameId = 2020092004\nteam = away\nnflId = 40657\njerseyNumber = 40 (corrected)\n```","metadata":{}},{"cell_type":"code","source":"raw_pff_patches = 'role,pffJersey,trackingJersey\\ntackler,TEN 47,TEN 46\\nmissedTackler,LA 38,LA 21\\ngunners,TEN 47,TEN 46\\ngunners,OAK 14,OAK 38\\ngunners,NYJ 25,NYJ 40\\ngunners,BUF 29,BUF 36\\ngunners,NYJ 23,NYJ 37\\nvises,CAR 20,CAR 41\\nvises,CLE 25,CLE 35\\nvises,LA 38,LA 21\\nvises,MIA 30,MIA 23\\nvises,NYG 23,NYG 37\\nvises,NYJ 25,NYJ 40\\nvises,BUF 29,BUF 36\\nvises,OAK 14,OAK 38\\nvises,SEA 24,SEA 8\\nvises,TEN 47,TEN 46\\npuntRushers,ATL 55,ATL 62\\npuntRushers,DAL 59,DAL 53\\npuntRushers,DEN 91,DEN 58\\npuntRushers,IND 33,IND 36\\npuntRushers,NO 27,NO 36\\npuntRushers,NYJ 23,NYJ 37\\npuntRushers,NYJ 37,NYJ 43\\nspecialTeamsSafeties,BUF 29,BUF 36\\nspecialTeamsSafeties,CAR 20,CAR 41\\nspecialTeamsSafeties,IND 33,IND 36\\nspecialTeamsSafeties,LA 38,LA 21\\nspecialTeamsSafeties,MIA 30,MIA 23\\nspecialTeamsSafeties,NYG 23,NYG 37\\nspecialTeamsSafeties,SEA 24,SEA 8\\nspecialTeamsSafeties,TEN 25,TEN 41\\n'\ndf_pff_patches = pd.read_csv(StringIO(raw_pff_patches))\ndf_pff_patches.head()","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:44.563758Z","iopub.execute_input":"2022-01-01T07:57:44.566418Z","iopub.status.idle":"2022-01-01T07:57:44.581522Z","shell.execute_reply.started":"2022-01-01T07:57:44.566368Z","shell.execute_reply":"2022-01-01T07:57:44.580681Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def apply_pff_patches(df_pff: pd.DataFrame, df_patches: pd.DataFrame) -> pd.DataFrame:\n    df_out = df_pff.copy()\n    for _, patch in df_patches.iterrows():\n        role = patch[\"role\"]\n        old_jersey = patch[\"pffJersey\"]\n        new_jersey = patch[\"trackingJersey\"]\n        df_out[role] = np.where(\n            df_out[role].str.contains(old_jersey),\n            df_out[role].str.replace(old_jersey, new_jersey),\n            df_out[role]\n        )\n    return df_out\n\n\ndef get_tracking_jersey_patches(df: pd.DataFrame) -> pd.Series:\n    return np.where(\n        (\n            (df[\"gameId\"] == 2020092004)\n            & (df[\"team\"] == \"away\")\n            & (df[\"nflId\"] == 40657)\n        ),\n        40,\n        df[\"jerseyNumber\"]\n    )","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.890656Z","iopub.execute_input":"2022-01-01T07:57:43.891005Z","iopub.status.idle":"2022-01-01T07:57:43.903681Z","shell.execute_reply.started":"2022-01-01T07:57:43.890962Z","shell.execute_reply":"2022-01-01T07:57:43.903084Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_pff_patched = apply_pff_patches(df_pff, df_pff_patches)","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:43.905406Z","iopub.execute_input":"2022-01-01T07:57:43.905986Z","iopub.status.idle":"2022-01-01T07:57:44.477321Z","shell.execute_reply.started":"2022-01-01T07:57:43.905941Z","shell.execute_reply":"2022-01-01T07:57:44.476383Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"df_tracking[\"jerseyNumber\"] = get_tracking_jersey_patches(df_tracking)","metadata":{"execution":{"iopub.status.busy":"2022-01-01T07:57:44.478587Z","iopub.execute_input":"2022-01-01T07:57:44.478887Z","iopub.status.idle":"2022-01-01T07:57:44.562486Z","shell.execute_reply.started":"2022-01-01T07:57:44.478846Z","shell.execute_reply":"2022-01-01T07:57:44.561873Z"},"trusted":true},"execution_count":null,"outputs":[]}]}