{"cells":[{"metadata":{},"cell_type":"markdown","source":"# 2020 March Madness\nIn this notebook I explore the 2020 Men's and Women's NCAA basketball data. Hopefully you find the analysis and code helpful. Feel free to use any of the helper functions in your code but please reference this as the original source.\n\n![](https://upload.wikimedia.org/wikipedia/en/thumb/2/28/March_Madness_logo.svg/440px-March_Madness_logo.svg.png)"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport matplotlib.pylab as plt\nimport matplotlib as mpl\nfrom matplotlib.patches import Circle, Rectangle, Arc\nimport seaborn as sns\nplt.style.use('seaborn-dark-palette')\nmypal = plt.rcParams['axes.prop_cycle'].by_key()['color'] # Grab the color pal\nimport os\nimport gc\n\nMENS_DIR = '../input/google-cloud-ncaa-march-madness-2020-division-1-mens-tournament'\nWOMENS_DIR = '../input/google-cloud-ncaa-march-madness-2020-division-1-womens-tournament'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Loss Metric & Sample Submission\nLog Loss is the metric we will be evaluated on for the tournament prediction challenge. This metric provides a stronger punishment that are overly confident and wrong."},{"metadata":{"trusted":true},"cell_type":"code","source":"def logloss(true_label, predicted, eps=1e-15):\n    p = np.clip(predicted, eps, 1 - eps)\n    if true_label == 1:\n        return -np.log(p)\n    return -np.log(1 - p)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(f'Confident Wrong Prediction: \\t\\t {logloss(1, 0.01):0.4f}')\nprint(f'Confident Correct Prediction: \\t\\t {logloss(0, 0.01):0.4f}')\nprint(f'Non-Confident Wrong Prediction: \\t {logloss(1, 0.49):0.4f}')\nprint(f'Non-Confident Correct Prediction: \\t {logloss(0, 0.49):0.4f}')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Your submission will have a prediction for every possible combination of tournament teams. Stage 1 (not final) will be graded your score will be based on 2015-2019. It's possible to cheat and get a perfect score.. but don't do that. In Stage 2 you will be graded on the outcomes of the yet to be played 2020 tournament.\n- `ID` is in the format SSSS_XXXX_YYYY, where SSSS is the four digit season number, XXXX is the four-digit TeamID of the lower-ID team, and YYYY is the four-digit TeamID of the higher-ID team. Read more here: https://www.kaggle.com/c/march-madness-analytics-2020/data"},{"metadata":{"trusted":true},"cell_type":"code","source":"Mss = pd.read_csv(f'{MENS_DIR}/MSampleSubmissionStage1_2020.csv')\nWss = pd.read_csv(f'{WOMENS_DIR}/WSampleSubmissionStage1_2020.csv')\nMss.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Team Data\n**MTeams & WTeams**\n\nTeam name and Team ID, first and last D1 Season. Sorting by the `FirstD1Season` column we can see some of the newest teams in D1 basketball. Welcome to D1 Merrimack! Cool mascot.\n![](https://media0.giphy.com/media/Q5G8oHPpDGLb0aaayD/giphy.gif)"},{"metadata":{"trusted":true},"cell_type":"code","source":"MTeams = pd.read_csv(f'{MENS_DIR}/MDataFiles_Stage1/MTeams.csv')\nMTeams.sort_values('FirstD1Season', ascending=False).head(5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Womens' data does not contain years joined :(\nWTeams = pd.read_csv(f'{WOMENS_DIR}/WDataFiles_Stage1/WTeams.csv')\nWTeams.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Seasons Data\n## MSeasons.csv & WSeasons.csv\nThese files identify the different seasons included in the historical data, along with certain season-level properties.\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"MSeasons = pd.read_csv(f'{MENS_DIR}/MDataFiles_Stage1/MSeasons.csv')\nWSeasons = pd.read_csv(f'{WOMENS_DIR}/WDataFiles_Stage1/WSeasons.csv')\nMSeasons.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Tourney Seed Data\n**MNCAATourneySeeds.csv & WNCAATourneySeeds.csv**\nThis file identifies the seeds for all teams in each NCAA® tournament, for all seasons of historical data."},{"metadata":{"trusted":true},"cell_type":"code","source":"MNCAATourneySeeds = pd.read_csv(f'{MENS_DIR}/MDataFiles_Stage1/MNCAATourneySeeds.csv')\nWNCAATourneySeeds = pd.read_csv(f'{WOMENS_DIR}/WDataFiles_Stage1/WNCAATourneySeeds.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# As Lets join this with the teams data to see some of the past matchups\nWNCAATourneySeeds.merge(WTeams, validate='many_to_one').head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Regular Season Results\n**MRegularSeasonCompactResults.csv & WRegularSeasonCompactResults.csv**\n\nThese files identify the game-by-game NCAA® tournament results for all seasons of historical data."},{"metadata":{"trusted":true},"cell_type":"code","source":"MRegularSeasonCompactResults = pd.read_csv(f'{MENS_DIR}/MDataFiles_Stage1/MRegularSeasonCompactResults.csv')\nWRegularSeasonCompactResults = pd.read_csv(f'{WOMENS_DIR}/WDataFiles_Stage1/WRegularSeasonCompactResults.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# We have the team the won, lost and the score.\nMRegularSeasonCompactResults.head(5)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We can join our regular season results on the team names to more clearly identify the games."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Lets Add the winning and losing team names to the results\nMRegularSeasonCompactResults = \\\n    MRegularSeasonCompactResults \\\n    .merge(MTeams[['TeamName', 'TeamID']],\n           left_on='WTeamID',\n           right_on='TeamID',\n           validate='many_to_one') \\\n    .drop('TeamID', axis=1) \\\n    .rename(columns={'TeamName': 'WTeamName'}) \\\n    .merge(MTeams[['TeamName', 'TeamID']],\n           left_on='LTeamID',\n           right_on='TeamID') \\\n    .drop('TeamID', axis=1) \\\n    .rename(columns={'TeamName': 'LTeamName'})\n\nWRegularSeasonCompactResults = \\\n    WRegularSeasonCompactResults \\\n    .merge(WTeams[['TeamName', 'TeamID']],\n           left_on='WTeamID',\n           right_on='TeamID',\n           validate='many_to_one') \\\n    .drop('TeamID', axis=1) \\\n    .rename(columns={'TeamName': 'WTeamName'}) \\\n    .merge(WTeams[['TeamName', 'TeamID']],\n           left_on='LTeamID',\n           right_on='TeamID') \\\n    .drop('TeamID', axis=1) \\\n    .rename(columns={'TeamName': 'LTeamName'})","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"WRegularSeasonCompactResults.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"WRegularSeasonCompactResults['Score_Diff'] = WRegularSeasonCompactResults['WScore'] - WRegularSeasonCompactResults['LScore']\nMRegularSeasonCompactResults['Score_Diff'] = MRegularSeasonCompactResults['WScore'] - MRegularSeasonCompactResults['LScore']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.style.use('fivethirtyeight')\nMRegularSeasonCompactResults['Score_Diff'] \\\n    .plot(kind='hist',\n          bins=90,\n          figsize=(15, 5),\n          label='Mens',\n          alpha=0.5)\nWRegularSeasonCompactResults['Score_Diff'] \\\n    .plot(kind='hist',\n          bins=105,\n          figsize=(15, 5),\n          label='Womens',\n          alpha=0.5)\nplt.title('Score Differential')\nplt.xlim(0,60)\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.style.use('fivethirtyeight')\nWRegularSeasonCompactResults['counter'] = 1\nWRegularSeasonCompactResults.groupby('WTeamName')['counter'] \\\n    .count() \\\n    .sort_values() \\\n    .tail(20) \\\n    .plot(kind='barh',\n          title='Most Winning (Regular Season) Womens Teams',\n          figsize=(15, 8),\n          xlim=(400, 680),\n          color=mypal[0])\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.style.use('fivethirtyeight')\nMRegularSeasonCompactResults['counter'] = 1\nMRegularSeasonCompactResults.groupby('WTeamName')['counter'] \\\n    .count() \\\n    .sort_values() \\\n    .tail(20) \\\n    .plot(kind='barh',\n          title='Most Winning (Regular Season) Mens Teams',\n          figsize=(15, 8),\n          xlim=(600, 920),\n          color=mypal[1])\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Event Data\n\nEach MEvents & WEvents file lists the play-by-play event logs for more than 99.5% of games from that season.\nEach event is assigned to either a team or a single one of the team's players.\nThus if a basket is made by one player and an assist is credited to a second player,\nthat would show up as two separate records. The players are listed by PlayerID within the xPlayers.csv file.\n\nMens Event Files:\n- MEvents2015.csv, MEvents2016.csv, MEvent2017.csv, MEvents2018.csv, MEvents2019.csv\n\nWomens Event Files:\n- WEvents2015.csv, WEvents2016.csv, WEvents2017.csv, WEvents2018.csv, WEvents2019.csv\n\nWe can read in all files and combine into one huge dataframe, one for womens and one for mens."},{"metadata":{"trusted":true},"cell_type":"code","source":"mens_events = []\nfor year in [2015, 2016, 2017, 2018, 2019]:\n    mens_events.append(pd.read_csv(f'{MENS_DIR}/MEvents{year}.csv'))\nMEvents = pd.concat(mens_events)\nprint(MEvents.shape)\nMEvents.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"womens_events = []\nfor year in [2015, 2016, 2017, 2018, 2019]:\n    womens_events.append(pd.read_csv(f'{WOMENS_DIR}/WEvents{year}.csv'))\nWEvents = pd.concat(womens_events)\nprint(WEvents.shape)\nWEvents.head()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true,"_kg_hide-output":true},"cell_type":"code","source":"del mens_events\ndel womens_events\ngc.collect()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Common Event Types"},{"metadata":{"trusted":true},"cell_type":"code","source":"# Event Types\nplt.style.use('fivethirtyeight')\nMEvents['counter'] = 1\nMEvents.groupby('EventType')['counter'] \\\n    .sum() \\\n    .sort_values(ascending=False) \\\n    .plot(kind='bar',\n          figsize=(15, 5),\n         color=mypal[2],\n         title='Event Type Frequency (Mens)')\nplt.xticks(rotation=0)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Event Types\nplt.style.use('fivethirtyeight')\nWEvents['counter'] = 1\nWEvents.groupby('EventType')['counter'] \\\n    .sum() \\\n    .sort_values(ascending=False) \\\n    .plot(kind='bar',\n          figsize=(15, 5),\n         color=mypal[3],\n         title='Event Type Frequency (Womens)')\nplt.xticks(rotation=0)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Area of Event\nWe are told that the `Area` feature describes the 13 \"areas\" of the court, as follows: 1=under basket; 2=in the paint; 3=inside right wing; 4=inside right; 5=inside center; 6=inside left; 7=inside left wing; 8=outside right wing; 9=outside right; 10=outside center; 11=outside left; 12=outside left wing; 13=backcourt.\n\nWe can map these values to their names."},{"metadata":{"trusted":true},"cell_type":"code","source":"area_mapping = {0: np.nan,\n                1: 'under basket',\n                2: 'in the paint',\n                3: 'inside right wing',\n                4: 'inside right',\n                5: 'inside center',\n                6: 'inside left',\n                7: 'inside left wing',\n                8: 'outside right wing',\n                9: 'outside right',\n                10: 'outside center',\n                11: 'outside left',\n                12: 'outside left wing',\n                13: 'backcourt'}\n\nMEvents['Area_Name'] = MEvents['Area'].map(area_mapping)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"MEvents.groupby('Area_Name')['counter'].sum() \\\n    .sort_values() \\\n    .plot(kind='barh',\n          figsize=(15, 8),\n          title='Frequency of Event Area')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(15, 8))\nfor i, d in MEvents.loc[~MEvents['Area_Name'].isna()].groupby('Area_Name'):\n    d.plot(x='X', y='Y', style='.', label=i, ax=ax, title='Visualizing Event Areas')\n    ax.legend()\nplt.legend(bbox_to_anchor=(1.04,1), loc=\"upper left\")\nax.set_xticks([])\nax.set_yticks([])\nax.set_xlabel('')\nax.set_xlim(0, 100)\nax.set_ylim(0, 100)\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Plotting X, Y Data\nThis is some of the most exciting data provided, but after looking there are some things to consider.\n- X, Y points are not available for all games- so this is not a complete sample\n- The X/Y position is provided for fouls, turnovers, and field-goal attempts (either 2-point or 3-point). No X/Y data for other events."},{"metadata":{"trusted":true},"cell_type":"code","source":"# Normalize X, Y positions for court dimentions\n# Court is 50 feet wide and 94 feet end to end.\nMEvents['X_'] = (MEvents['X'] * (94/100))\nMEvents['Y_'] = (MEvents['Y'] * (50/100))\n\nWEvents['X_'] = (WEvents['X'] * (94/100))\nWEvents['Y_'] = (WEvents['Y'] * (50/100))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# NCAA Court Plot Function\nCheck out my notebook here for an example and code for a half court plot:\n\nhttps://www.kaggle.com/robikscube/ncaa-basketball-court-plot-helper-functions"},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_ncaa_full_court(ax=None, three_line='mens', court_color='#dfbb85',\n                           lw=3, lines_color='black', lines_alpha=0.5,\n                           paint_fill='blue', paint_alpha=0.4,\n                           inner_arc=False):\n    \"\"\"\n    Version 2020.2.19\n    Creates NCAA Basketball Court\n    Dimensions are in feet (Court is 97x50 ft)\n    Created by: Rob Mulla / https://github.com/RobMulla\n\n    * Note that this function uses \"feet\" as the unit of measure.\n    * NCAA Data is provided on a x range: 0, 100 and y-range 0 to 100\n    * To plot X/Y positions first convert to feet like this:\n    ```\n    Events['X_'] = (Events['X'] * (94/100))\n    Events['Y_'] = (Events['Y'] * (50/100))\n    ```\n    \n    ax: matplotlib axes if None gets current axes using `plt.gca`\n\n\n    three_line: 'mens', 'womens' or 'both' defines 3 point line plotted\n    court_color : (hex) Color of the court\n    lw : line width\n    lines_color : Color of the lines\n    lines_alpha : transparency of lines\n    paint_fill : Color inside the paint\n    paint_alpha : transparency of the \"paint\"\n    inner_arc : paint the dotted inner arc\n    \"\"\"\n    if ax is None:\n        ax = plt.gca()\n\n    # Create Pathes for Court Lines\n    center_circle = Circle((94/2, 50/2), 6,\n                           linewidth=lw, color=lines_color, lw=lw,\n                           fill=False, alpha=lines_alpha)\n    hoop_left = Circle((5.25, 50/2), 1.5 / 2,\n                       linewidth=lw, color=lines_color, lw=lw,\n                       fill=False, alpha=lines_alpha)\n    hoop_right = Circle((94-5.25, 50/2), 1.5 / 2,\n                        linewidth=lw, color=lines_color, lw=lw,\n                        fill=False, alpha=lines_alpha)\n\n    # Paint - 18 Feet 10 inches which converts to 18.833333 feet - gross!\n    left_paint = Rectangle((0, (50/2)-6), 18.833333, 12,\n                           fill=paint_fill, alpha=paint_alpha,\n                           lw=lw, edgecolor=None)\n    right_paint = Rectangle((94-18.83333, (50/2)-6), 18.833333,\n                            12, fill=paint_fill, alpha=paint_alpha,\n                            lw=lw, edgecolor=None)\n    \n    left_paint_boarder = Rectangle((0, (50/2)-6), 18.833333, 12,\n                           fill=False, alpha=lines_alpha,\n                           lw=lw, edgecolor=lines_color)\n    right_paint_boarder = Rectangle((94-18.83333, (50/2)-6), 18.833333,\n                            12, fill=False, alpha=lines_alpha,\n                            lw=lw, edgecolor=lines_color)\n\n    left_arc = Arc((18.833333, 50/2), 12, 12, theta1=-\n                   90, theta2=90, color=lines_color, lw=lw,\n                   alpha=lines_alpha)\n    right_arc = Arc((94-18.833333, 50/2), 12, 12, theta1=90,\n                    theta2=-90, color=lines_color, lw=lw,\n                    alpha=lines_alpha)\n    \n    leftblock1 = Rectangle((7, (50/2)-6-0.666), 1, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    leftblock2 = Rectangle((7, (50/2)+6), 1, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    ax.add_patch(leftblock1)\n    ax.add_patch(leftblock2)\n    \n    left_l1 = Rectangle((11, (50/2)-6-0.666), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    left_l2 = Rectangle((14, (50/2)-6-0.666), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    left_l3 = Rectangle((17, (50/2)-6-0.666), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    ax.add_patch(left_l1)\n    ax.add_patch(left_l2)\n    ax.add_patch(left_l3)\n    left_l4 = Rectangle((11, (50/2)+6), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    left_l5 = Rectangle((14, (50/2)+6), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    left_l6 = Rectangle((17, (50/2)+6), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    ax.add_patch(left_l4)\n    ax.add_patch(left_l5)\n    ax.add_patch(left_l6)\n    \n    rightblock1 = Rectangle((94-7-1, (50/2)-6-0.666), 1, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    rightblock2 = Rectangle((94-7-1, (50/2)+6), 1, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    ax.add_patch(rightblock1)\n    ax.add_patch(rightblock2)\n\n    right_l1 = Rectangle((94-11, (50/2)-6-0.666), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    right_l2 = Rectangle((94-14, (50/2)-6-0.666), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    right_l3 = Rectangle((94-17, (50/2)-6-0.666), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    ax.add_patch(right_l1)\n    ax.add_patch(right_l2)\n    ax.add_patch(right_l3)\n    right_l4 = Rectangle((94-11, (50/2)+6), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    right_l5 = Rectangle((94-14, (50/2)+6), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    right_l6 = Rectangle((94-17, (50/2)+6), 0.166, 0.666,\n                           fill=True, alpha=lines_alpha,\n                           lw=0, edgecolor=lines_color,\n                           facecolor=lines_color)\n    ax.add_patch(right_l4)\n    ax.add_patch(right_l5)\n    ax.add_patch(right_l6)\n    \n    # 3 Point Line\n    if (three_line == 'mens') | (three_line == 'both'):\n        # 22' 1.75\" distance to center of hoop\n        three_pt_left = Arc((6.25, 50/2), 44.291, 44.291, theta1=-78,\n                            theta2=78, color=lines_color, lw=lw,\n                            alpha=lines_alpha)\n        three_pt_right = Arc((94-6.25, 50/2), 44.291, 44.291,\n                             theta1=180-78, theta2=180+78,\n                             color=lines_color, lw=lw, alpha=lines_alpha)\n\n        # 4.25 feet max to sideline for mens\n        ax.plot((0, 11.25), (3.34, 3.34),\n                color=lines_color, lw=lw, alpha=lines_alpha)\n        ax.plot((0, 11.25), (50-3.34, 50-3.34),\n                color=lines_color, lw=lw, alpha=lines_alpha)\n        ax.plot((94-11.25, 94), (3.34, 3.34),\n                color=lines_color, lw=lw, alpha=lines_alpha)\n        ax.plot((94-11.25, 94), (50-3.34, 50-3.34),\n                color=lines_color, lw=lw, alpha=lines_alpha)\n        ax.add_patch(three_pt_left)\n        ax.add_patch(three_pt_right)\n\n    if (three_line == 'womens') | (three_line == 'both'):\n        # womens 3\n        three_pt_left_w = Arc((6.25, 50/2), 20.75 * 2, 20.75 * 2, theta1=-85,\n                              theta2=85, color=lines_color, lw=lw, alpha=lines_alpha)\n        three_pt_right_w = Arc((94-6.25, 50/2), 20.75 * 2, 20.75 * 2,\n                               theta1=180-85, theta2=180+85,\n                               color=lines_color, lw=lw, alpha=lines_alpha)\n\n        # 4.25 inches max to sideline for mens\n        ax.plot((0, 8.3), (4.25, 4.25), color=lines_color,\n                lw=lw, alpha=lines_alpha)\n        ax.plot((0, 8.3), (50-4.25, 50-4.25),\n                color=lines_color, lw=lw, alpha=lines_alpha)\n        ax.plot((94-8.3, 94), (4.25, 4.25),\n                color=lines_color, lw=lw, alpha=lines_alpha)\n        ax.plot((94-8.3, 94), (50-4.25, 50-4.25),\n                color=lines_color, lw=lw, alpha=lines_alpha)\n\n        ax.add_patch(three_pt_left_w)\n        ax.add_patch(three_pt_right_w)\n\n    # Add Patches\n    ax.add_patch(left_paint)\n    ax.add_patch(left_paint_boarder)\n    ax.add_patch(right_paint)\n    ax.add_patch(right_paint_boarder)\n    ax.add_patch(center_circle)\n    ax.add_patch(hoop_left)\n    ax.add_patch(hoop_right)\n    ax.add_patch(left_arc)\n    ax.add_patch(right_arc)\n    \n    if inner_arc:\n        left_inner_arc = Arc((18.833333, 50/2), 12, 12, theta1=90,\n                             theta2=-90, color=lines_color, lw=lw,\n                       alpha=lines_alpha, ls='--')\n        right_inner_arc = Arc((94-18.833333, 50/2), 12, 12, theta1=-90,\n                        theta2=90, color=lines_color, lw=lw,\n                        alpha=lines_alpha, ls='--')\n        ax.add_patch(left_inner_arc)\n        ax.add_patch(right_inner_arc)\n\n    # Restricted Area Marker\n    restricted_left = Arc((6.25, 50/2), 8, 8, theta1=-90,\n                        theta2=90, color=lines_color, lw=lw,\n                        alpha=lines_alpha)\n    restricted_right = Arc((94-6.25, 50/2), 8, 8,\n                         theta1=180-90, theta2=180+90,\n                         color=lines_color, lw=lw, alpha=lines_alpha)\n    ax.add_patch(restricted_left)\n    ax.add_patch(restricted_right)\n    \n    # Backboards\n    ax.plot((4, 4), ((50/2) - 3, (50/2) + 3),\n            color=lines_color, lw=lw*1.5, alpha=lines_alpha)\n    ax.plot((94-4, 94-4), ((50/2) - 3, (50/2) + 3),\n            color=lines_color, lw=lw*1.5, alpha=lines_alpha)\n    ax.plot((4, 4.6), (50/2, 50/2), color=lines_color,\n            lw=lw, alpha=lines_alpha)\n    ax.plot((94-4, 94-4.6), (50/2, 50/2),\n            color=lines_color, lw=lw, alpha=lines_alpha)\n\n    # Half Court Line\n    ax.axvline(94/2, color=lines_color, lw=lw, alpha=lines_alpha)\n\n    # Boarder\n    boarder = Rectangle((0.3,0.3), 94-0.4, 50-0.4, fill=False, lw=3, color='black', alpha=lines_alpha)\n    ax.add_patch(boarder)\n    \n    # Plot Limit\n    ax.set_xlim(0, 94)\n    ax.set_ylim(0, 50)\n    ax.set_facecolor(court_color)\n    ax.set_xticks([])\n    ax.set_yticks([])\n    ax.set_xlabel('')\n    return ax\n\n\nfig, ax = plt.subplots(figsize=(15, 8.5))\ncreate_ncaa_full_court(ax, three_line='both', paint_alpha=0.4)\nplt.show()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, ax = plt.subplots(figsize=(15, 7.8))\nms = 10\nax = create_ncaa_full_court(ax, paint_alpha=0.1)\nMEvents.query('EventType == \"turnover\"') \\\n    .plot(x='X_', y='Y_', style='X',\n          title='Turnover Locations (Mens)',\n          c='red',\n          alpha=0.3,\n         figsize=(15, 9),\n         label='Steals',\n         ms=ms,\n         ax=ax)\nax.set_xlabel('')\nax.get_legend().remove()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"COURT_COLOR = '#dfbb85'\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 4))\n# Where are 3 pointers made from? (This is really cool)\nWEvents.query('EventType == \"made3\"') \\\n    .plot(x='X_', y='Y_', style='.',\n          color='blue',\n          title='3 Pointers Made (Womens)',\n          alpha=0.01, ax=ax1)\nax1 = create_ncaa_full_court(ax1, lw=0.5, three_line='womens', paint_alpha=0.1)\nax1.set_facecolor(COURT_COLOR)\nWEvents.query('EventType == \"miss3\"') \\\n    .plot(x='X_', y='Y_', style='.',\n          title='3 Pointers Missed (Womens)',\n          color='red',\n          alpha=0.01, ax=ax2)\nax2.set_facecolor(COURT_COLOR)\nax2 = create_ncaa_full_court(ax2, lw=0.5, three_line='womens', paint_alpha=0.1)\nax1.get_legend().remove()\nax2.get_legend().remove()\nax1.set_xticks([])\nax1.set_yticks([])\nax2.set_xticks([])\nax2.set_yticks([])\nax1.set_xlabel('')\nax2.set_xlabel('')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"COURT_COLOR = '#dfbb85'\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 4))\n# Where are 3 pointers made from? (This is really cool)\nWEvents.query('EventType == \"made2\"') \\\n    .plot(x='X_', y='Y_', style='.',\n          color='blue',\n          title='2 Pointers Made (Womens)',\n          alpha=0.01, ax=ax1)\nax1.set_facecolor(COURT_COLOR)\nax1 = create_ncaa_full_court(ax1, lw=0.5, three_line='womens', paint_alpha=0.1)\nWEvents.query('EventType == \"miss2\"') \\\n    .plot(x='X_', y='Y_', style='.',\n          title='2 Pointers Missed (Womens)',\n          color='red',\n          alpha=0.01, ax=ax2)\nax2.set_facecolor(COURT_COLOR)\nax2 = create_ncaa_full_court(ax2, lw=0.5, three_line='womens', paint_alpha=0.1)\nax1.get_legend().remove()\nax2.get_legend().remove()\nax1.set_xticks([])\nax1.set_yticks([])\nax2.set_xticks([])\nax2.set_yticks([])\nax1.set_xlabel('')\nax2.set_xlabel('')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## PlayerIDs\nThere is an issue when trying to read in lines where the player name has a comma. We can use `error_bad_lines` to get past this, but ideally the data would be cleaned to remove the comma or a different delimiter would be used."},{"metadata":{"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"MPlayers = pd.read_csv(f'{MENS_DIR}/MPlayers.csv', error_bad_lines=False)\nWPlayers = pd.read_csv(f'{WOMENS_DIR}/WPlayers.csv')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"MPlayers.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Merge Player name onto events\nMEvents = MEvents.merge(MPlayers,\n              how='left',\n              left_on='EventPlayerID',\n              right_on='PlayerID')\n\nWEvents = WEvents.merge(WPlayers,\n              how='left',\n              left_on='EventPlayerID',\n              right_on='PlayerID')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"# Plotting Specific Players' Made/Missed Shots\nNow that we have player names in the event data, lets single out specific players. Starting with one of the most exciting players of the last decade.\n\n![](https://thenypost.files.wordpress.com/2018/11/zion-williamson-duke-freshman-scouting-comparables.jpg?quality=80&strip=all&w=618&h=410&crop=1)"},{"metadata":{"trusted":true},"cell_type":"code","source":"ms = 10 # Marker Size\nFirstName = 'Zion'\nLastName = 'Williamson'\nfig, ax = plt.subplots(figsize=(15, 8))\nax = create_ncaa_full_court(ax)\nMEvents.query('FirstName == @FirstName and LastName == @LastName and EventType == \"made2\"') \\\n    .plot(x='X_', y='Y_', style='o',\n          title='Shots (Zion Williamson)',\n          alpha=0.5,\n         figsize=(15, 8),\n         label='Made 2',\n         ms=ms,\n         ax=ax)\nplt.legend()\nMEvents.query('FirstName == @FirstName and LastName == @LastName and EventType == \"miss2\"') \\\n    .plot(x='X_', y='Y_', style='X',\n          alpha=0.5, ax=ax,\n         label='Missed 2',\n         ms=ms)\nplt.legend()\nMEvents.query('FirstName == @FirstName and LastName == @LastName and EventType == \"made3\"') \\\n    .plot(x='X_', y='Y_', style='o',\n          c='brown',\n          alpha=0.5,\n         figsize=(15, 8),\n         label='Made 3', ax=ax,\n         ms=ms)\nplt.legend()\nMEvents.query('FirstName == @FirstName and LastName == @LastName and EventType == \"miss3\"') \\\n    .plot(x='X_', y='Y_', style='X',\n          c='green',\n          alpha=0.5, ax=ax,\n         label='Missed 3',\n         ms=ms)\nax.set_xlabel('')\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Next lets look at Katie Lou Samuelson. She is known to be a 3-point shooter. As such, we can see her shots mostly come from outside the 3-point line.\n\n![](https://imagesvc.timeincapp.com/v3/fan/image?url=https://highposthoops.com/wp-content/uploads/getty-images/2018/10/951142340.jpeg?&w=618&h=410&crop=1)"},{"metadata":{"trusted":true},"cell_type":"code","source":"ms = 10 # Marker Size\nFirstName = 'Katie Lou'\nLastName = 'Samuelson'\nfig, ax = plt.subplots(figsize=(15, 8))\nax = create_ncaa_full_court(ax, three_line='womens')\nWEvents.query('FirstName == @FirstName and LastName == @LastName and EventType == \"made2\"') \\\n    .plot(x='X_', y='Y_', style='o',\n          title='Shots (Katie Lou Samuelson)',\n          alpha=0.5,\n         figsize=(15, 8),\n         label='Made 2',\n         ms=ms,\n         ax=ax)\nplt.legend()\nWEvents.query('FirstName == @FirstName and LastName == @LastName and EventType == \"miss2\"') \\\n    .plot(x='X_', y='Y_', style='X',\n          alpha=0.5, ax=ax,\n         label='Missed 2',\n         ms=ms)\nplt.legend()\nWEvents.query('FirstName == @FirstName and LastName == @LastName and EventType == \"made3\"') \\\n    .plot(x='X_', y='Y_', style='o',\n          c='brown',\n          alpha=0.5,\n         figsize=(15, 8),\n         label='Made 3', ax=ax,\n         ms=ms)\nplt.legend()\nWEvents.query('FirstName == @FirstName and LastName == @LastName and EventType == \"miss3\"') \\\n    .plot(x='X_', y='Y_', style='X',\n          c='green',\n          alpha=0.5, ax=ax,\n         label='Missed 3',\n         ms=ms)\nax.set_xlabel('')\nplt.legend()\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Shot Heatmap\nWe can plot a heatmap of where shots occur on the court. Interesting observation when comparing the mens to womens game is that many of the shots for mens come from directly under the hoop, while the hot spots for women shots come more frequently from the left and right of the hoop."},{"metadata":{"trusted":true},"cell_type":"code","source":"N_bins = 100\nshot_events = MEvents.loc[MEvents['EventType'].isin(['miss3','made3','miss2','made2']) & (MEvents['X_'] != 0)]\nfig, ax = plt.subplots(figsize=(15, 7))\nax = create_ncaa_full_court(ax,\n                            paint_alpha=0.0,\n                            three_line='mens',\n                            court_color='black',\n                            lines_color='white')\n_ = plt.hist2d(shot_events['X_'].values + np.random.normal(0, 0.1, shot_events['X_'].shape), # Add Jitter to values for plotting\n           shot_events['Y_'].values + np.random.normal(0, 0.1, shot_events['Y_'].shape),\n           bins=N_bins, norm=mpl.colors.LogNorm(),\n               cmap='plasma')\n\n# Plot a colorbar with label.\ncb = plt.colorbar()\ncb.set_label('Number of shots')\n\nax.set_title('Shot Heatmap (Mens)')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"N_bins = 100\nshot_events = WEvents.loc[WEvents['EventType'].isin(['miss3','made3','miss2','made2']) & (WEvents['X_'] != 0)]\nfig, ax = plt.subplots(figsize=(15, 7))\nax = create_ncaa_full_court(ax, three_line='womens', paint_alpha=0.0,\n                            court_color='black',\n                            lines_color='white')\n_ = plt.hist2d(shot_events['X_'].values + np.random.normal(0, 0.2, shot_events['X_'].shape),\n           shot_events['Y_'].values + np.random.normal(0, 0.2, shot_events['Y_'].shape),\n           bins=N_bins, norm=mpl.colors.LogNorm(),\n               cmap='plasma')\n\n# Plot a colorbar with label.\ncb = plt.colorbar()\ncb.set_label('Number of shots')\n\nax.set_title('Shot Heatmap (Womens)')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"MEvents['PointsScored'] =  0\nMEvents.loc[MEvents['EventType'] == 'made2', 'PointsScored'] = 2\nMEvents.loc[MEvents['EventType'] == 'made3', 'PointsScored'] = 3\nMEvents.loc[MEvents['EventType'] == 'missed2', 'PointsScored'] = 0\nMEvents.loc[MEvents['EventType'] == 'missed3', 'PointsScored'] = 0","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# # Average Points Scored per xy coord\n# avg_pnt_xy = MEvents.loc[MEvents['EventType'].isin(['miss3','made3','miss2','made2']) & (MEvents['X_'] != 0)] \\\n#     .groupby(['X_','Y_'])['PointsScored'].mean().reset_index()\n\n# # .plot(x='X_',y='Y_', style='.')\n# fig, ax = plt.subplots(figsize=(15, 8))\n# ax = sns.scatterplot(data=avg_pnt_xy, x='X_', y='Y_', hue='PointsScored', cmap='coolwarm')\n# ax = create_ncaa_full_court(ax)\n# plt.show()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# avg_made_xy.sort_values('Made')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# avg_made_xy['Made'] / avg_made_xy['Missed']","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# MEvents['Made'] = False\n# MEvents['Made'] = False\n# MEvents.loc[MEvents['EventType'] == 'made2', 'Made'] = True\n# MEvents.loc[MEvents['EventType'] == 'made3', 'Made'] = True\n# MEvents.loc[MEvents['EventType'] == 'missed2', 'Made'] = False\n# MEvents.loc[MEvents['EventType'] == 'missed3', 'Made'] = False\n# MEvents.loc[MEvents['EventType'] == 'made2', 'Missed'] = False\n# MEvents.loc[MEvents['EventType'] == 'made3', 'Missed'] = False\n# MEvents.loc[MEvents['EventType'] == 'missed2', 'Missed'] = True\n# MEvents.loc[MEvents['EventType'] == 'missed3', 'Missed'] = True\n\n# # Average Pct Made per xy coord\n# avg_made_xy = MEvents.loc[MEvents['EventType'].isin(['miss3','made3','miss2','made2']) & (MEvents['X_'] != 0)] \\\n#     .groupby(['X_','Y_'])['Made','Missed'].sum().reset_index()\n\n# # .plot(x='X_',y='Y_', style='.')\n# fig, ax = plt.subplots(figsize=(15, 8))\n# cmap = sns.cubehelix_palette(as_cmap=True)\n# ax = sns.scatterplot(data=avg_made_xy, x='X_', y='Y_', size='Made', cmap='plasma')\n# ax = create_ncaa_full_court(ax, paint_alpha=0)\n# ax.set_title('Number of Shots Made')\n# plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# TODO\n- Half Court Plot\n- Normalize X,Y data to half court"},{"metadata":{},"cell_type":"markdown","source":"# Reference\n1. Court Lines code inspired by code made for plotting the NBA court. http://savvastjortjoglou.com/nba-shot-sharts.html\n2. Official NCAA Basketball Court Dimensions:"},{"metadata":{},"cell_type":"markdown","source":"![](https://og4sg2f1jmu2x9xay48pj5z1-wpengine.netdna-ssl.com/wp-content/uploads/2019/06/NCAA-Mens-and-Womens-Basketball-Court-Diagram-3-point-line-extended-2019.png)"}],"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":4,"nbformat_minor":4}