Skip to content

Instantly share code, notes, and snippets.

@bendominguez0111
Created May 16, 2020 15:11
Show Gist options
  • Save bendominguez0111/03636364560d1b15df73a06ae2929f88 to your computer and use it in GitHub Desktop.
Save bendominguez0111/03636364560d1b15df73a06ae2929f88 to your computer and use it in GitHub Desktop.
Aaron Jones.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
},
"colab": {
"name": "Aaron Jones.ipynb",
"provenance": [],
"include_colab_link": true
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/fantasydatapros/03636364560d1b15df73a06ae2929f88/aaron-jones.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "7_V8X1BICfFK",
"colab_type": "text"
},
"source": [
"# Statistical Deep-Dive into Aaron Jones' 2019 Fantasy Football season"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "0Y1KSGiiCfFO",
"colab_type": "text"
},
"source": [
"In this notebook, we are going to do a quick view at Aaron Jones 2019 Fantasy Football season.\n",
"\n",
"Aaron Jones finished as RB2 on the season, and if you had in your lineup (as I did) you'd know it was quite the rollercoaster of a season. Let's do some stats and try to analyze his performance.\n",
"\n",
"In this notebook, we'll be examining three questions:\n",
"\n",
"1. How did Aaron Jones compare to other top tier RBs?\n",
"\n",
"We know Jones finished #2 on the season in terms of total points, but total points scored does not tell the whole picture. We also want to look at how consistent Jones was throughout the season and compare that to his top-tier counterparts. \n",
"\n",
"2. Did Jamaal Williams' involvement in the running game actually effect Aaron Jones FF performance?\n",
"\n",
"Probably the most frustrating part of having Jones on my lineup this season - did Williams really have an effect on how Jones scored Fantasy Points week to week? I think the consensus amongst fantasy managers is yes - but when looking the statistics the answer is a bit more nuanced. We'll be looking at Williams' usage numbers and finding the correlation to Jones' output and using a p-value to examine the relationship's statistical signifcance.\n",
"\n",
"3. Based off his usage, did Aaron Jones overperform his season and is he due for a regression in touchdowns?\n",
"\n",
"Another hot topic this year was how effective Aaron Jones was with his touches - of course, being too effective with your touches might mean you got a bit lucky at times and are due for a regression in the next season.\n",
"\n",
"So how do we tell if Jones overperformed his season? We'll be looking at play by play data for 2009 - 2018 and building probability distributions for the likelihood of scoring a touchdown when a team is X yards away from the endzone. We'll be doing this for both receiving and rushing touchdowns and then generating an expected TD value for Aaron Jones based on 2019 play-by-play data. If Jones actual TDs are greater than his expected TD we calculated, he may be due for a regression."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "GQNIuaIPCfFT",
"colab_type": "text"
},
"source": [
"![Picture of Aaron Jones](https://www.wearegreenbay.com/wp-content/uploads/sites/70/2019/11/aaron-jones-mvp.jpg?w=2560&h=1440&crop=1 \"Title\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "C2rNbZ2hCfFY",
"colab_type": "text"
},
"source": [
"To start off, let's import some libraries."
]
},
{
"cell_type": "code",
"metadata": {
"id": "NZkkxNywCfFd",
"colab_type": "code",
"colab": {}
},
"source": [
"import time, os\n",
"import pandas as pd\n",
"from scipy.ndimage.filters import gaussian_filter1d\n",
"import numpy as np\n",
"from matplotlib import pyplot as plt"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "tap_aKX8CfF0",
"colab_type": "text"
},
"source": [
"[You can find weekly and yearly data here.](https://www.fantasyfootballdatapros.com/csv_files)"
]
},
{
"cell_type": "code",
"metadata": {
"id": "zL89wIP7CfF4",
"colab_type": "code",
"colab": {}
},
"source": [
"WEEKLY_BASE_URL = 'data/weekly/2019/week{}.csv'\n",
"YEARLY_BASE_URL = 'data/yearly/2019.csv'"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "MnDKhJb9CfGH",
"colab_type": "text"
},
"source": [
"Let's create an empty DataFrame to start out, and iterate over a range containing each number in a NFL season. Let's add a column to keep track of the weeks, and then concatenate these DataFrames all together to get one big DataFrame containing 2019 data."
]
},
{
"cell_type": "code",
"metadata": {
"id": "HVgbhwYKCfGJ",
"colab_type": "code",
"colab": {}
},
"source": [
"def generate_df():\n",
" df = pd.DataFrame()\n",
" for week in range(1, 18):\n",
" weekly_df = pd.read_csv(WEEKLY_BASE_URL.format(week))\n",
" weekly_df['Week'] = week\n",
" df = pd.concat([df, weekly_df])\n",
" return df\n",
"\n",
"df = generate_df()"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "gxAfcZUjCfGW",
"colab_type": "text"
},
"source": [
"Let's get our top 5 PPR running backs for the 2019 season and confirm Aaron Jones is #2."
]
},
{
"cell_type": "code",
"metadata": {
"id": "QP2TJvScCfGb",
"colab_type": "code",
"colab": {},
"outputId": "15011ea8-3869-41d0-aa76-9a88ef02267e"
},
"source": [
"df.loc[df['Pos'] == 'RB'].groupby('Player')['FantasyPoints'].sum().sort_values(ascending=False).head()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"Player\n",
"Christian McCaffrey 442.0\n",
"Aaron Jones 298.3\n",
"Austin Ekeler 288.4\n",
"Ezekiel Elliott 287.4\n",
"Dalvin Cook 276.6\n",
"Name: FantasyPoints, dtype: float64"
]
},
"metadata": {
"tags": []
},
"execution_count": 4
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "vSE7IMKbCfGt",
"colab_type": "text"
},
"source": [
"Here, we `groupby` position, grab the FantasyPoints column and then use a df method known as `describe` to get us back some descriptive statistics about running backs and wide receivers for the 2019 season. Most meaningful here is the mean and std numbers. Standard Deviation for RBs and WRs is about the same for both, but mean WR output was higher incidentally for the 2019 season. This is most likely because these numbers are in PPR."
]
},
{
"cell_type": "code",
"metadata": {
"id": "mRRGVChQCfGw",
"colab_type": "code",
"colab": {},
"outputId": "3aa9506f-6a2d-4af1-a8c1-b034b7eeb8cf"
},
"source": [
"df2019 = df.groupby('Pos')['FantasyPoints'].describe().reset_index()\n",
"#grab WR too for fun\n",
"df2019[(df2019['Pos'] == 'RB') | (df2019['Pos'] == 'WR')]"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Pos</th>\n",
" <th>count</th>\n",
" <th>mean</th>\n",
" <th>std</th>\n",
" <th>min</th>\n",
" <th>25%</th>\n",
" <th>50%</th>\n",
" <th>75%</th>\n",
" <th>max</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>26</th>\n",
" <td>RB</td>\n",
" <td>1145.0</td>\n",
" <td>8.254271</td>\n",
" <td>8.544875</td>\n",
" <td>-2.0</td>\n",
" <td>1.5</td>\n",
" <td>5.6</td>\n",
" <td>12.7</td>\n",
" <td>49.2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>30</th>\n",
" <td>WR</td>\n",
" <td>1241.0</td>\n",
" <td>11.889637</td>\n",
" <td>8.510828</td>\n",
" <td>-2.0</td>\n",
" <td>6.3</td>\n",
" <td>10.8</td>\n",
" <td>16.5</td>\n",
" <td>53.7</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Pos count mean std min 25% 50% 75% max\n",
"26 RB 1145.0 8.254271 8.544875 -2.0 1.5 5.6 12.7 49.2\n",
"30 WR 1241.0 11.889637 8.510828 -2.0 6.3 10.8 16.5 53.7"
]
},
"metadata": {
"tags": []
},
"execution_count": 5
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "hLEOIrybCfG-",
"colab_type": "text"
},
"source": [
"## Question 1: How did Aaron Jones do compared to other top-tier RBs?\n",
"\n",
"Let's actually take our DataFrame and group by player and get Aaron Jones, and describe his data."
]
},
{
"cell_type": "code",
"metadata": {
"id": "lH1Kxj5SCfHA",
"colab_type": "code",
"colab": {},
"outputId": "cff47bbe-176e-4db6-e51a-1a991a692ec2"
},
"source": [
"aj = df.groupby('Player').get_group('Aaron Jones')\n",
"\n",
"#columns we'd like to keep.\n",
"columns = ['Week', 'Tgt', 'Rec', 'ReceivingYds', 'ReceivingTD', 'RushingAtt', 'RushingYds', 'RushingTD', 'FumblesLost', 'FantasyPoints']\n",
"\n",
"#filtering out unneccessary columns\n",
"aj = aj[columns]\n",
"aj.describe()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Week</th>\n",
" <th>Tgt</th>\n",
" <th>Rec</th>\n",
" <th>ReceivingYds</th>\n",
" <th>ReceivingTD</th>\n",
" <th>RushingAtt</th>\n",
" <th>RushingYds</th>\n",
" <th>RushingTD</th>\n",
" <th>FumblesLost</th>\n",
" <th>FantasyPoints</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>count</th>\n",
" <td>16.000000</td>\n",
" <td>16.0000</td>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>mean</th>\n",
" <td>8.875000</td>\n",
" <td>2.8750</td>\n",
" <td>2.250000</td>\n",
" <td>27.437500</td>\n",
" <td>0.187500</td>\n",
" <td>14.750000</td>\n",
" <td>67.750000</td>\n",
" <td>1.000000</td>\n",
" <td>0.125000</td>\n",
" <td>18.643750</td>\n",
" </tr>\n",
" <tr>\n",
" <th>std</th>\n",
" <td>5.188127</td>\n",
" <td>3.4809</td>\n",
" <td>2.886751</td>\n",
" <td>42.963502</td>\n",
" <td>0.543906</td>\n",
" <td>5.053052</td>\n",
" <td>43.646306</td>\n",
" <td>1.264911</td>\n",
" <td>0.341565</td>\n",
" <td>14.214733</td>\n",
" </tr>\n",
" <tr>\n",
" <th>min</th>\n",
" <td>1.000000</td>\n",
" <td>0.0000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>8.000000</td>\n",
" <td>18.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>1.800000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25%</th>\n",
" <td>4.750000</td>\n",
" <td>0.0000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>11.750000</td>\n",
" <td>36.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>3.875000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>50%</th>\n",
" <td>8.500000</td>\n",
" <td>0.0000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>13.000000</td>\n",
" <td>50.500000</td>\n",
" <td>0.500000</td>\n",
" <td>0.000000</td>\n",
" <td>17.450000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>75%</th>\n",
" <td>13.250000</td>\n",
" <td>6.2500</td>\n",
" <td>4.500000</td>\n",
" <td>38.500000</td>\n",
" <td>0.000000</td>\n",
" <td>16.750000</td>\n",
" <td>101.750000</td>\n",
" <td>2.000000</td>\n",
" <td>0.000000</td>\n",
" <td>25.875000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>max</th>\n",
" <td>17.000000</td>\n",
" <td>8.0000</td>\n",
" <td>7.000000</td>\n",
" <td>159.000000</td>\n",
" <td>2.000000</td>\n",
" <td>25.000000</td>\n",
" <td>154.000000</td>\n",
" <td>4.000000</td>\n",
" <td>1.000000</td>\n",
" <td>49.200000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Week Tgt Rec ReceivingYds ReceivingTD RushingAtt \\\n",
"count 16.000000 16.0000 16.000000 16.000000 16.000000 16.000000 \n",
"mean 8.875000 2.8750 2.250000 27.437500 0.187500 14.750000 \n",
"std 5.188127 3.4809 2.886751 42.963502 0.543906 5.053052 \n",
"min 1.000000 0.0000 0.000000 0.000000 0.000000 8.000000 \n",
"25% 4.750000 0.0000 0.000000 0.000000 0.000000 11.750000 \n",
"50% 8.500000 0.0000 0.000000 0.000000 0.000000 13.000000 \n",
"75% 13.250000 6.2500 4.500000 38.500000 0.000000 16.750000 \n",
"max 17.000000 8.0000 7.000000 159.000000 2.000000 25.000000 \n",
"\n",
" RushingYds RushingTD FumblesLost FantasyPoints \n",
"count 16.000000 16.000000 16.000000 16.000000 \n",
"mean 67.750000 1.000000 0.125000 18.643750 \n",
"std 43.646306 1.264911 0.341565 14.214733 \n",
"min 18.000000 0.000000 0.000000 1.800000 \n",
"25% 36.000000 0.000000 0.000000 3.875000 \n",
"50% 50.500000 0.500000 0.000000 17.450000 \n",
"75% 101.750000 2.000000 0.000000 25.875000 \n",
"max 154.000000 4.000000 1.000000 49.200000 "
]
},
"metadata": {
"tags": []
},
"execution_count": 6
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "GJ_ADqrpCfHJ",
"colab_type": "text"
},
"source": [
"These are just regular statistics, but the one we should focus on here is standard deviation. Specifically, std of FantasyPoints. It's about 14.214, which is quite high. One thing I want to emphasize throughout this analysis is that Fantasy Football output is not good enough when making FF decisions. We also want to consider how consistent the player was in getting us Fantasy Football points each week. Let's look at Christian McCaffrey and Derrick Henry, two other top RB's on the season and see how their standard deviation compared to Jones."
]
},
{
"cell_type": "code",
"metadata": {
"id": "w68UU0DNCfHK",
"colab_type": "code",
"colab": {},
"outputId": "7cc33665-7435-4162-94bb-0020ae066f48"
},
"source": [
"mcc = df.groupby('Player').get_group('Christian McCaffrey')\n",
"mcc = mcc[columns]\n",
"mcc.describe()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Week</th>\n",
" <th>Tgt</th>\n",
" <th>Rec</th>\n",
" <th>ReceivingYds</th>\n",
" <th>ReceivingTD</th>\n",
" <th>RushingAtt</th>\n",
" <th>RushingYds</th>\n",
" <th>RushingTD</th>\n",
" <th>FumblesLost</th>\n",
" <th>FantasyPoints</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>count</th>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" <td>16.00000</td>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" <td>16.000000</td>\n",
" <td>16.0</td>\n",
" <td>16.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>mean</th>\n",
" <td>9.125000</td>\n",
" <td>8.000000</td>\n",
" <td>6.687500</td>\n",
" <td>58.93750</td>\n",
" <td>0.125000</td>\n",
" <td>17.937500</td>\n",
" <td>86.687500</td>\n",
" <td>0.937500</td>\n",
" <td>0.0</td>\n",
" <td>27.625000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>std</th>\n",
" <td>5.188127</td>\n",
" <td>4.966555</td>\n",
" <td>4.407853</td>\n",
" <td>38.67552</td>\n",
" <td>0.341565</td>\n",
" <td>5.157115</td>\n",
" <td>46.963417</td>\n",
" <td>0.771902</td>\n",
" <td>0.0</td>\n",
" <td>11.287131</td>\n",
" </tr>\n",
" <tr>\n",
" <th>min</th>\n",
" <td>1.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.00000</td>\n",
" <td>0.000000</td>\n",
" <td>9.000000</td>\n",
" <td>26.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.0</td>\n",
" <td>3.700000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25%</th>\n",
" <td>4.750000</td>\n",
" <td>4.750000</td>\n",
" <td>3.750000</td>\n",
" <td>34.50000</td>\n",
" <td>0.000000</td>\n",
" <td>14.000000</td>\n",
" <td>50.750000</td>\n",
" <td>0.000000</td>\n",
" <td>0.0</td>\n",
" <td>24.075000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>50%</th>\n",
" <td>9.500000</td>\n",
" <td>9.500000</td>\n",
" <td>7.000000</td>\n",
" <td>65.00000</td>\n",
" <td>0.000000</td>\n",
" <td>19.000000</td>\n",
" <td>78.500000</td>\n",
" <td>1.000000</td>\n",
" <td>0.0</td>\n",
" <td>27.200000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>75%</th>\n",
" <td>13.250000</td>\n",
" <td>11.250000</td>\n",
" <td>10.000000</td>\n",
" <td>83.00000</td>\n",
" <td>0.000000</td>\n",
" <td>22.000000</td>\n",
" <td>119.750000</td>\n",
" <td>1.250000</td>\n",
" <td>0.0</td>\n",
" <td>34.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>max</th>\n",
" <td>17.000000</td>\n",
" <td>15.000000</td>\n",
" <td>15.000000</td>\n",
" <td>121.00000</td>\n",
" <td>1.000000</td>\n",
" <td>27.000000</td>\n",
" <td>176.000000</td>\n",
" <td>2.000000</td>\n",
" <td>0.0</td>\n",
" <td>47.700000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Week Tgt Rec ReceivingYds ReceivingTD RushingAtt \\\n",
"count 16.000000 16.000000 16.000000 16.00000 16.000000 16.000000 \n",
"mean 9.125000 8.000000 6.687500 58.93750 0.125000 17.937500 \n",
"std 5.188127 4.966555 4.407853 38.67552 0.341565 5.157115 \n",
"min 1.000000 0.000000 0.000000 0.00000 0.000000 9.000000 \n",
"25% 4.750000 4.750000 3.750000 34.50000 0.000000 14.000000 \n",
"50% 9.500000 9.500000 7.000000 65.00000 0.000000 19.000000 \n",
"75% 13.250000 11.250000 10.000000 83.00000 0.000000 22.000000 \n",
"max 17.000000 15.000000 15.000000 121.00000 1.000000 27.000000 \n",
"\n",
" RushingYds RushingTD FumblesLost FantasyPoints \n",
"count 16.000000 16.000000 16.0 16.000000 \n",
"mean 86.687500 0.937500 0.0 27.625000 \n",
"std 46.963417 0.771902 0.0 11.287131 \n",
"min 26.000000 0.000000 0.0 3.700000 \n",
"25% 50.750000 0.000000 0.0 24.075000 \n",
"50% 78.500000 1.000000 0.0 27.200000 \n",
"75% 119.750000 1.250000 0.0 34.000000 \n",
"max 176.000000 2.000000 0.0 47.700000 "
]
},
"metadata": {
"tags": []
},
"execution_count": 42
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ustRizTfCfHW",
"colab_type": "text"
},
"source": [
"MCC had a lower standard deviation and beat Aaron Jones by about 9 in mean Fantasy Points per game. MCC was a beast this season, we all know this (trust me, I had to play the MCC owner like 3 times this season). Let's look at Henry which makes the decision between Henry and Jones less clear cut."
]
},
{
"cell_type": "code",
"metadata": {
"id": "vWq11bTaCfHY",
"colab_type": "code",
"colab": {},
"outputId": "2ccd983d-f466-4144-885a-fd0856c70b14"
},
"source": [
"henry = df.groupby('Player').get_group('Derrick Henry')\n",
"henry = henry[columns]\n",
"henry.describe()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Week</th>\n",
" <th>Tgt</th>\n",
" <th>Rec</th>\n",
" <th>ReceivingYds</th>\n",
" <th>ReceivingTD</th>\n",
" <th>RushingAtt</th>\n",
" <th>RushingYds</th>\n",
" <th>RushingTD</th>\n",
" <th>FumblesLost</th>\n",
" <th>FantasyPoints</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>count</th>\n",
" <td>15.000000</td>\n",
" <td>15.000000</td>\n",
" <td>15.000000</td>\n",
" <td>15.000000</td>\n",
" <td>15.000000</td>\n",
" <td>15.000000</td>\n",
" <td>15.000000</td>\n",
" <td>15.000000</td>\n",
" <td>15.000000</td>\n",
" <td>15.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>mean</th>\n",
" <td>8.400000</td>\n",
" <td>0.333333</td>\n",
" <td>0.266667</td>\n",
" <td>7.400000</td>\n",
" <td>0.133333</td>\n",
" <td>20.200000</td>\n",
" <td>102.666667</td>\n",
" <td>1.066667</td>\n",
" <td>0.200000</td>\n",
" <td>18.073333</td>\n",
" </tr>\n",
" <tr>\n",
" <th>std</th>\n",
" <td>4.997142</td>\n",
" <td>0.899735</td>\n",
" <td>0.798809</td>\n",
" <td>20.873086</td>\n",
" <td>0.351866</td>\n",
" <td>5.157519</td>\n",
" <td>51.771016</td>\n",
" <td>0.883715</td>\n",
" <td>0.414039</td>\n",
" <td>10.335754</td>\n",
" </tr>\n",
" <tr>\n",
" <th>min</th>\n",
" <td>1.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>13.000000</td>\n",
" <td>28.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>2.800000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>25%</th>\n",
" <td>4.500000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>16.500000</td>\n",
" <td>76.500000</td>\n",
" <td>0.500000</td>\n",
" <td>0.000000</td>\n",
" <td>10.200000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>50%</th>\n",
" <td>8.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>19.000000</td>\n",
" <td>86.000000</td>\n",
" <td>1.000000</td>\n",
" <td>0.000000</td>\n",
" <td>15.000000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>75%</th>\n",
" <td>12.500000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>0.000000</td>\n",
" <td>22.500000</td>\n",
" <td>126.000000</td>\n",
" <td>1.500000</td>\n",
" <td>0.000000</td>\n",
" <td>25.400000</td>\n",
" </tr>\n",
" <tr>\n",
" <th>max</th>\n",
" <td>17.000000</td>\n",
" <td>3.000000</td>\n",
" <td>3.000000</td>\n",
" <td>75.000000</td>\n",
" <td>1.000000</td>\n",
" <td>32.000000</td>\n",
" <td>211.000000</td>\n",
" <td>3.000000</td>\n",
" <td>1.000000</td>\n",
" <td>39.100000</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Week Tgt Rec ReceivingYds ReceivingTD RushingAtt \\\n",
"count 15.000000 15.000000 15.000000 15.000000 15.000000 15.000000 \n",
"mean 8.400000 0.333333 0.266667 7.400000 0.133333 20.200000 \n",
"std 4.997142 0.899735 0.798809 20.873086 0.351866 5.157519 \n",
"min 1.000000 0.000000 0.000000 0.000000 0.000000 13.000000 \n",
"25% 4.500000 0.000000 0.000000 0.000000 0.000000 16.500000 \n",
"50% 8.000000 0.000000 0.000000 0.000000 0.000000 19.000000 \n",
"75% 12.500000 0.000000 0.000000 0.000000 0.000000 22.500000 \n",
"max 17.000000 3.000000 3.000000 75.000000 1.000000 32.000000 \n",
"\n",
" RushingYds RushingTD FumblesLost FantasyPoints \n",
"count 15.000000 15.000000 15.000000 15.000000 \n",
"mean 102.666667 1.066667 0.200000 18.073333 \n",
"std 51.771016 0.883715 0.414039 10.335754 \n",
"min 28.000000 0.000000 0.000000 2.800000 \n",
"25% 76.500000 0.500000 0.000000 10.200000 \n",
"50% 86.000000 1.000000 0.000000 15.000000 \n",
"75% 126.000000 1.500000 0.000000 25.400000 \n",
"max 211.000000 3.000000 1.000000 39.100000 "
]
},
"metadata": {
"tags": []
},
"execution_count": 8
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "vEUEZKbiCfHm",
"colab_type": "text"
},
"source": [
"So as expected, Henry has about less Fantasy Points per game on the season (albeit not by much), but his standard deviation is lower by about 4. That's big. That means that Henry was only a bit less effective than Jones in the games he did play this season, but he was more consistent. When you look at the ratio of the standard deviation to the mean (also known as the coefficient of variation), Jones has a much higher number. This made season a bit more volatile. In general, I would say volatility is bad for Fantasy Football. You want players who you can predict and consistently can get you points week in and week out."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "PpUJj8yLCfHo",
"colab_type": "text"
},
"source": [
"These are fine examples, but let's see how Aaron Jones faired amongst all players in terms of his variation."
]
},
{
"cell_type": "code",
"metadata": {
"id": "LxkGR9HACfHq",
"colab_type": "code",
"colab": {},
"outputId": "75d4180c-a504-4b31-8095-13958851a576"
},
"source": [
"df.groupby('Player')[['FantasyPoints']].std().sort_values(by='FantasyPoints', ascending=False).head()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>FantasyPoints</th>\n",
" </tr>\n",
" <tr>\n",
" <th>Player</th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>Will Fuller</th>\n",
" <td>16.574162</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Aaron Jones</th>\n",
" <td>14.214733</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Mike Evans</th>\n",
" <td>13.876619</td>\n",
" </tr>\n",
" <tr>\n",
" <th>John Ross</th>\n",
" <td>13.086431</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Boston Scott</th>\n",
" <td>12.784303</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" FantasyPoints\n",
"Player \n",
"Will Fuller 16.574162\n",
"Aaron Jones 14.214733\n",
"Mike Evans 13.876619\n",
"John Ross 13.086431\n",
"Boston Scott 12.784303"
]
},
"metadata": {
"tags": []
},
"execution_count": 51
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "7IlLK3r_CfH1",
"colab_type": "text"
},
"source": [
"Aaron Jones finished second on the season in terms of standard deviation (no surprise Will Fuller is number one on this list)."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "5JjdBmAlCfH2",
"colab_type": "text"
},
"source": [
"## Question 2: How did Jamaal Williams' Usage Affect Aaron Jones' Output on the Season?\n",
"\n",
"Now let's see the correlation between Jamaal Williams' usage throughout the season and how that impacted Aaron Jones' Fantasy Football performance. First, we need to grab Williams' stats from the data, and then, we want to join the two tables together as Jamaal is missing some weeks. We only want to include those weeks that both Williams and Jones both played, so we join the tables on the `Week` column and do a left join of the two DataFrames."
]
},
{
"cell_type": "code",
"metadata": {
"id": "gr8AJUBKCfH4",
"colab_type": "code",
"colab": {}
},
"source": [
"#grab jamaal from our data\n",
"jamaal = df.groupby('Player').get_group('Jamaal Williams')\n",
"jamaal = jamaal[columns]\n",
"#join the tables on week\n",
"df = jamaal.set_index('Week').join(aj.set_index('Week'), how='left', lsuffix='_JamaalWilliams', rsuffix='_AaronJones')\n",
"df.reset_index(inplace=True)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "KkDKceSiCfIE",
"colab_type": "text"
},
"source": [
"Let's define usage as rushing attempts + targets."
]
},
{
"cell_type": "code",
"metadata": {
"id": "jncyUrJ9CfIG",
"colab_type": "code",
"colab": {}
},
"source": [
"df['Usage_JamaalWilliams'] = df['RushingAtt_JamaalWilliams'] + df['Tgt_JamaalWilliams']"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "ruW-9KeZCfIP",
"colab_type": "text"
},
"source": [
"From the `scipy` library, let's import a function called `pearsonr` which gives us back both the correaltion coefficient for two variables and also a p-value. A p-value tells us the probability of commiting a Type I Error. Essentially, in stats we have a null-hypothesis - the world in it's current state that we need to disprove using an alternate hypothesis. Our null hypothesis here is that **there is no linear relationship between Jamaal Williams and Aaron Jones' production**. We need to disprove this null hypothesis by setting a significance level, which is the amount of probability of a Type 1 Error we are willing to accept. If our p-value is less than the significance value, we can reject our null hypothesis and say that there is some relationship betwen Jamaal Williams' usage and Aaron Jones' production."
]
},
{
"cell_type": "code",
"metadata": {
"id": "Y_hKGj55CfIT",
"colab_type": "code",
"colab": {},
"outputId": "3b022037-dca0-4ca8-8116-74ca39da83c0"
},
"source": [
"from scipy.stats import pearsonr\n",
"\n",
"alpha = 0.05\n",
"\n",
"p_r = pearsonr(df['FantasyPoints_AaronJones'], df['Usage_JamaalWilliams'])\n",
"print('''\n",
"Out: The relationship between Aaron Jones Fantasy Football output and Jamaal Williams \n",
"has a correlation of {} and a p-value of {} \\n'''.format(p_r[0], p_r[1]))\n",
"\n",
"if alpha > p_r[1]:\n",
" print('Reject the null hypothesis.')\n",
"else:\n",
" print('Do not reject the null hypothesis.')"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"\n",
"Out: The relationship between Aaron Jones Fantasy Football output and Jamaal Williams \n",
"has a correlation of -0.5626766793635043 and a p-value of 0.0568338493039637 \n",
"\n",
"Do not reject the null hypothesis.\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "QUNeSmONCfIi",
"colab_type": "text"
},
"source": [
"We cannot reject the null-hypothesis here that there is no relationship between Williams' usage and Jones' production."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ol5pFSTKCfIk",
"colab_type": "text"
},
"source": [
"## Question 3: Is Jones due for a regression in TD's?\n",
"\n",
"As mentioned in the intro, we are going to be analzying whether or not Aaron Jones overperformed his 2019 season based off the *quality* of his usage. The way we do this is by generating a probability distribution which tells us the probability of scoring a receiving or rushing touchdown when a team is X yards away from the endzone. We are then going to be using this model and turning around and looking at Aaron Jones' actual 2019 play-by-play data and come up with an expected TD number. If Jones' expected numbers exceeded his actual numbers, he underperformed his season and is probably due for a positive regression in TDs in 2020. If his actual number exceed his expected numbers, however, he is probably due for a regression. "
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "zgSI6ZT4CfIn",
"colab_type": "text"
},
"source": [
"First, let's generate a fresh DataFrame using the function we defined above and let's confirm that Aaron Jones is indeed at the top of the list in terms of TDs."
]
},
{
"cell_type": "code",
"metadata": {
"id": "9Klv-JHlCfIq",
"colab_type": "code",
"colab": {},
"outputId": "464c6692-0ea5-4998-d846-bf79d260fff6"
},
"source": [
"df = generate_df()\n",
"\n",
"df.loc[df['Pos'] == 'RB'].groupby('Player')['RushingTD'].sum().sort_values(ascending=False).head()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"Player\n",
"Aaron Jones 16\n",
"Derrick Henry 16\n",
"Christian McCaffrey 15\n",
"Dalvin Cook 13\n",
"Todd Gurley 12\n",
"Name: RushingTD, dtype: int64"
]
},
"metadata": {
"tags": []
},
"execution_count": 12
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "N9S-GLpBCfI1",
"colab_type": "text"
},
"source": [
"Next, let's load in play-by-play data for 2009 to 2018 that we got from Kaggle. [Here's the link to that. Beware: it's a large file at 700MB.](https://www.kaggle.com/maxhorowitz/nflplaybyplay2009to2016/data#NFL%20Play%20by%20Play%202009-2018%20(v5).csv)"
]
},
{
"cell_type": "code",
"metadata": {
"id": "6FymFQP3CfI8",
"colab_type": "code",
"colab": {},
"outputId": "9bce1dc3-3776-4e8b-b163-d6a93cf5d3b2"
},
"source": [
"#timing it just for fun\n",
"start = time.time() \n",
"data = pd.read_csv('data/playbyplay2009_2018.csv')\n",
"end = time.time()\n",
"print(f'{end - start} seconds to load playbyplay2009_2018 data')\n",
"#checking the size just cause\n",
"MB = os.stat('data/playbyplay2009_2018.csv').st_size / 10**6\n",
"print('Filesize:', MB, 'MB')\n",
"print('Number of columns:', data.shape[1])\n",
"print('Number of rows:', data.shape[0])"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"/home/ben/anaconda3/lib/python3.7/site-packages/IPython/core/interactiveshell.py:3063: DtypeWarning: Columns (42,166,167,168,169,174,175,178,179,182,183,188,189,190,191,194,195,203,204,205,218,219,220,231,232,233,238,240,241,249) have mixed types.Specify dtype option on import or set low_memory=False.\n",
" interactivity=interactivity, compiler=compiler, result=result)\n"
],
"name": "stderr"
},
{
"output_type": "stream",
"text": [
"17.27703094482422 seconds to load playbyplay2009_2018 data\n",
"Filesize: 700.397316 MB\n",
"Number of columns: 255\n",
"Number of rows: 449371\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "r_YJMvRiCfJI",
"colab_type": "text"
},
"source": [
"This function here is the function that generates our probability distribution. It takes in an `output_variable`, which is what we are calculating the probability of. Our output variables here is going to be Passing TDs and Rushing TDs. It also takes in a `filter_variable`, which is how we filter our play by play data. For passing TDs, for example, we only want those plays where a pass attempt occured. So we pass in `passing_attempt`. We pass in our `data` as our df we brought in just above, and we use a `smoothing_sigma` to smooth out our distribution a bit."
]
},
{
"cell_type": "code",
"metadata": {
"id": "IP-IdW9FCfJK",
"colab_type": "code",
"colab": {}
},
"source": [
"def generate_prob_based_off_ydline(output_variable, filter_variable, df=data, smoothing_sigma=2):\n",
" \n",
" distance_from_100_yd_line = df['yardline_100']\n",
" output_column = df[output_variable]\n",
" filter_column = df[filter_variable]\n",
" two_point_attempt = df['two_point_attempt']\n",
" \n",
" df_values = {\n",
" 'DistanceFromEndzone': distance_from_100_yd_line,\n",
" output_variable: output_column,\n",
" filter_variable: filter_column,\n",
" 'two_point_attempt': two_point_attempt\n",
" }\n",
" \n",
" df = pd.DataFrame(df_values)\n",
" \n",
" df = df[df[filter_variable] == 1]\n",
" #remove two point plays\n",
" df = df[df['two_point_attempt'] == 0]\n",
" df.drop([filter_variable, 'two_point_attempt'], axis=1, inplace=True)\n",
" \n",
" norm_df = df.groupby('DistanceFromEndzone')[output_variable].value_counts(normalize=True)\n",
" norm_df = pd.DataFrame({'p': norm_df.values.flatten()}, index=norm_df.index)\n",
" norm_df = norm_df[norm_df.index.get_level_values(output_variable) == 1].reset_index()\n",
" \n",
" #smooth out our probabilities\n",
" norm_df['p_smoothed'] = gaussian_filter1d(norm_df['p'], sigma=smoothing_sigma)\n",
" \n",
" norm_df.drop(output_variable, axis=1, inplace=True)\n",
" return norm_df"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "Vh4gcqd_CfJS",
"colab_type": "code",
"colab": {}
},
"source": [
"#define our output and filter columns so we can reference them later\n",
"passing_columns = ['pass_touchdown', 'pass_attempt']\n",
"rushing_columns = ['rush_touchdown', 'rush_attempt']"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "wLU87b7lCfJd",
"colab_type": "text"
},
"source": [
"We've already written a lot of the code, so creating the distribution is as easy as the two lines below."
]
},
{
"cell_type": "code",
"metadata": {
"id": "4L0uiz_cCfJe",
"colab_type": "code",
"colab": {},
"outputId": "7da49183-3b10-4af6-f387-0604fb0d4ff0"
},
"source": [
"passing_df = generate_prob_based_off_ydline(*passing_columns)\n",
"\n",
"passing_df.plot(x='DistanceFromEndzone', y='p_smoothed')"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x7f777e058e90>"
]
},
"metadata": {
"tags": []
},
"execution_count": 16
},
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"tags": [],
"needs_background": "light"
}
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "iHZnC9odCfJp",
"colab_type": "text"
},
"source": [
"The visualization is pretty self-explanatory. As we get further and further away from the goal line, the probability of a passing TD decreases. As you can see below, at our opponents 1 yard line, we have a 44% chance of scoring a passing touchdown. Run `passing_df.tail()` and you'll see that at our own one yard line, we have a very small chance of scoring a passing TD."
]
},
{
"cell_type": "code",
"metadata": {
"id": "TALwJB-3CfJr",
"colab_type": "code",
"colab": {},
"outputId": "9499bc65-626b-40bc-f755-2c6d727afacc"
},
"source": [
"passing_df.head()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>DistanceFromEndzone</th>\n",
" <th>p</th>\n",
" <th>p_smoothed</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1.0</td>\n",
" <td>0.497980</td>\n",
" <td>0.444381</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2.0</td>\n",
" <td>0.441658</td>\n",
" <td>0.429849</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>3.0</td>\n",
" <td>0.415272</td>\n",
" <td>0.404989</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>4.0</td>\n",
" <td>0.347145</td>\n",
" <td>0.375353</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>5.0</td>\n",
" <td>0.360465</td>\n",
" <td>0.344881</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" DistanceFromEndzone p p_smoothed\n",
"0 1.0 0.497980 0.444381\n",
"1 2.0 0.441658 0.429849\n",
"2 3.0 0.415272 0.404989\n",
"3 4.0 0.347145 0.375353\n",
"4 5.0 0.360465 0.344881"
]
},
"metadata": {
"tags": []
},
"execution_count": 17
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "6QodI93kCfJ3",
"colab_type": "text"
},
"source": [
"We repeat the process for rushing touchdowns. We are going to be using these `passing_df` and `rushing_df` vars later in our code. "
]
},
{
"cell_type": "code",
"metadata": {
"id": "y59atu_rCfJ3",
"colab_type": "code",
"colab": {},
"outputId": "896756ae-c7e9-4a9c-fd02-2a5e94e93e5a"
},
"source": [
"rushing_df = generate_prob_based_off_ydline(*rushing_columns)\n",
"\n",
"rushing_df.plot(x='DistanceFromEndzone', y='p_smoothed')"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"<matplotlib.axes._subplots.AxesSubplot at 0x7f777d675110>"
]
},
"metadata": {
"tags": []
},
"execution_count": 18
},
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {
"tags": [],
"needs_background": "light"
}
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "AwAlwYq-CfKA",
"colab_type": "code",
"colab": {},
"outputId": "010a1393-1024-4559-8dc1-5689d11eb6a6"
},
"source": [
"rushing_df.head()"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>DistanceFromEndzone</th>\n",
" <th>p</th>\n",
" <th>p_smoothed</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1.0</td>\n",
" <td>0.540170</td>\n",
" <td>0.416936</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2.0</td>\n",
" <td>0.397287</td>\n",
" <td>0.387459</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>3.0</td>\n",
" <td>0.333700</td>\n",
" <td>0.338159</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>4.0</td>\n",
" <td>0.271493</td>\n",
" <td>0.282243</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>5.0</td>\n",
" <td>0.196447</td>\n",
" <td>0.230087</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" DistanceFromEndzone p p_smoothed\n",
"0 1.0 0.540170 0.416936\n",
"1 2.0 0.397287 0.387459\n",
"2 3.0 0.333700 0.338159\n",
"3 4.0 0.271493 0.282243\n",
"4 5.0 0.196447 0.230087"
]
},
"metadata": {
"tags": []
},
"execution_count": 19
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "InT-4LCCCfKK",
"colab_type": "text"
},
"source": [
"Now, we load in our 2019 playbyplay data. [You can find a link for that here](http://nflsavant.com/about.php) The problem with this data is that it comes from a different source (we don't have 2019 play by play data from Kaggle), and the data is kinda messy. There is no player name columns so we're going to have to do a fair bit of hacking to be able to take a play description column and turn it in to a player name column. "
]
},
{
"cell_type": "code",
"metadata": {
"id": "6Ri39SsJCfKL",
"colab_type": "code",
"colab": {}
},
"source": [
"data2019 = pd.read_csv('data/playbyplay2019.csv')"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "VO1W4BiQCfKV",
"colab_type": "code",
"colab": {},
"outputId": "ec51efd3-d25a-46f2-85ad-2cc58cd61ccd"
},
"source": [
"#Let's see what columns we have to work with in this new data set\n",
"', '.join(data2019.columns)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"'GameId, GameDate, Quarter, Minute, Second, OffenseTeam, DefenseTeam, Down, ToGo, YardLine, Unnamed: 10, SeriesFirstDown, Unnamed: 12, NextScore, Description, TeamWin, Unnamed: 16, Unnamed: 17, SeasonYear, Yards, Formation, PlayType, IsRush, IsPass, IsIncomplete, IsTouchdown, PassType, IsSack, IsChallenge, IsChallengeReversed, Challenger, IsMeasurement, IsInterception, IsFumble, IsPenalty, IsTwoPointConversion, IsTwoPointConversionSuccessful, RushDirection, YardLineFixed, YardLineDirection, IsPenaltyAccepted, PenaltyTeam, IsNoPlay, PenaltyType, PenaltyYards'"
]
},
"metadata": {
"tags": []
},
"execution_count": 21
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "vx6M3MnYCfKe",
"colab_type": "code",
"colab": {}
},
"source": [
"rushing_data2019 = data2019\n",
"\n",
"#splitting player description on hyphen\n",
"def split_on_hyphen(x):\n",
" try:\n",
" val = x.split('-')[1]\n",
" if val.isnumeric():\n",
" return x\n",
" return val\n",
" except IndexError:\n",
" pass\n",
" return x\n",
" \n",
"#splitting on whitespace\n",
"def split_on_whitespace(x):\n",
" try:\n",
" val = x.split()[0]\n",
" return val\n",
" except IndexError:\n",
" pass\n",
" return x\n",
"\n",
"#phrases we do not want in data. Irrelevant data\n",
"bad_phrases = [\n",
" 'NO PLAY', 'REVERSED', 'POINT CONVERSION ATTEMPT', \n",
" 'EXTRA POINT', 'FIELD GOAL', 'TIMEOUT', 'TWO-MINUTE WARNING', 'END QUARTER', 'PUNT',\n",
" 'KNEELS', 'KICKS', 'END GAME'\n",
"]\n",
"\n",
"#remove play descriptions with phrases from bad_phrases list\n",
"for phrase in bad_phrases:\n",
" rushing_data2019 = rushing_data2019[~rushing_data2019['Description'].str.contains(phrase)]\n",
"\n",
"rushing_data2019['Player'] = rushing_data2019['Description'].apply(split_on_hyphen)\n",
"rushing_data2019['Player'] = rushing_data2019['Player'].apply(split_on_whitespace)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "FzIvqQzRCfKm",
"colab_type": "code",
"colab": {}
},
"source": [
"#only include a play if it is a running play and not a two point conversion\n",
"rushing_data2019 = rushing_data2019[(rushing_data2019['IsRush'] == 1) & (rushing_data2019['IsTwoPointConversion'] == 0)]\n",
"rushing_data2019['DistanceFromEndzone'] = 100 - rushing_data2019['YardLine']\n",
"rushing_data2019 = rushing_data2019[['OffenseTeam','DistanceFromEndzone', 'Player', 'Description','IsTouchdown']]"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "mN3mlJJyCfKt",
"colab_type": "code",
"colab": {},
"outputId": "217f76cd-460b-48a4-a500-f14db03dde6d"
},
"source": [
"#rename some columns\n",
"rushing_data2019.rename({\n",
" 'OffenseTeam': 'Tm',\n",
" 'IsTouchdown': 'RushingTD'\n",
"}, axis=1, inplace=True)\n",
"\n",
"\n",
"rushing_data2019.sort_values(by='DistanceFromEndzone').head(15)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Tm</th>\n",
" <th>DistanceFromEndzone</th>\n",
" <th>Player</th>\n",
" <th>Description</th>\n",
" <th>RushingTD</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>10238</th>\n",
" <td>NE</td>\n",
" <td>1</td>\n",
" <td>T.BRADY</td>\n",
" <td>(3:53) 12-T.BRADY UP THE MIDDLE FOR 1 YARD, TO...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5029</th>\n",
" <td>TB</td>\n",
" <td>1</td>\n",
" <td>D.OGUNBOWALE</td>\n",
" <td>(3:27) (NO HUDDLE) 44-D.OGUNBOWALE UP THE MIDD...</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13183</th>\n",
" <td>DET</td>\n",
" <td>1</td>\n",
" <td>K.JOHNSON</td>\n",
" <td>(14:12) 33-K.JOHNSON UP THE MIDDLE FOR 1 YARD,...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13185</th>\n",
" <td>DET</td>\n",
" <td>1</td>\n",
" <td>K.JOHNSON</td>\n",
" <td>(15:00) 33-K.JOHNSON LEFT TACKLE TO PHI 1 FOR ...</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>15848</th>\n",
" <td>CAR</td>\n",
" <td>1</td>\n",
" <td>D.DALEY</td>\n",
" <td>(2:00) 65-D.DALEY REPORTED IN AS ELIGIBLE. 40...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13194</th>\n",
" <td>PHI</td>\n",
" <td>1</td>\n",
" <td>J.HOWARD</td>\n",
" <td>(6:19) 24-J.HOWARD RIGHT TACKLE FOR 1 YARD, TO...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4858</th>\n",
" <td>ATL</td>\n",
" <td>1</td>\n",
" <td>Q.OLLISON</td>\n",
" <td>(1:27) 30-Q.OLLISON RIGHT GUARD TO TB 1 FOR NO...</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4857</th>\n",
" <td>ATL</td>\n",
" <td>1</td>\n",
" <td>Q.OLLISON</td>\n",
" <td>(:50) 30-Q.OLLISON RIGHT TACKLE FOR 1 YARD, TO...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4781</th>\n",
" <td>LA</td>\n",
" <td>1</td>\n",
" <td>T.GURLEY</td>\n",
" <td>(3:33) 30-T.GURLEY UP THE MIDDLE FOR 1 YARD, T...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4594</th>\n",
" <td>BAL</td>\n",
" <td>1</td>\n",
" <td>M.INGRAM</td>\n",
" <td>(4:46) 21-M.INGRAM II UP THE MIDDLE FOR 1 YARD...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13381</th>\n",
" <td>PHI</td>\n",
" <td>1</td>\n",
" <td>H.VAITAI</td>\n",
" <td>(3:16) 72-H.VAITAI REPORTED IN AS ELIGIBLE. 1...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>575</th>\n",
" <td>NO</td>\n",
" <td>1</td>\n",
" <td>L.MURRAY</td>\n",
" <td>(2:59) 28-L.MURRAY LEFT GUARD TO TEN 2 FOR -1 ...</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>13392</th>\n",
" <td>LA</td>\n",
" <td>1</td>\n",
" <td>J.GOFF</td>\n",
" <td>(8:39) 16-J.GOFF UP THE MIDDLE FOR 1 YARD, TOU...</td>\n",
" <td>1</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4423</th>\n",
" <td>CAR</td>\n",
" <td>1</td>\n",
" <td>A.ARMAH</td>\n",
" <td>(10:55) 40-A.ARMAH RIGHT GUARD TO NO 1 FOR NO ...</td>\n",
" <td>0</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5030</th>\n",
" <td>TB</td>\n",
" <td>1</td>\n",
" <td>D.OGUNBOWALE</td>\n",
" <td>(3:47) (NO HUDDLE, SHOTGUN) 44-D.OGUNBOWALE UP...</td>\n",
" <td>0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Tm DistanceFromEndzone Player \\\n",
"10238 NE 1 T.BRADY \n",
"5029 TB 1 D.OGUNBOWALE \n",
"13183 DET 1 K.JOHNSON \n",
"13185 DET 1 K.JOHNSON \n",
"15848 CAR 1 D.DALEY \n",
"13194 PHI 1 J.HOWARD \n",
"4858 ATL 1 Q.OLLISON \n",
"4857 ATL 1 Q.OLLISON \n",
"4781 LA 1 T.GURLEY \n",
"4594 BAL 1 M.INGRAM \n",
"13381 PHI 1 H.VAITAI \n",
"575 NO 1 L.MURRAY \n",
"13392 LA 1 J.GOFF \n",
"4423 CAR 1 A.ARMAH \n",
"5030 TB 1 D.OGUNBOWALE \n",
"\n",
" Description RushingTD \n",
"10238 (3:53) 12-T.BRADY UP THE MIDDLE FOR 1 YARD, TO... 1 \n",
"5029 (3:27) (NO HUDDLE) 44-D.OGUNBOWALE UP THE MIDD... 0 \n",
"13183 (14:12) 33-K.JOHNSON UP THE MIDDLE FOR 1 YARD,... 1 \n",
"13185 (15:00) 33-K.JOHNSON LEFT TACKLE TO PHI 1 FOR ... 0 \n",
"15848 (2:00) 65-D.DALEY REPORTED IN AS ELIGIBLE. 40... 1 \n",
"13194 (6:19) 24-J.HOWARD RIGHT TACKLE FOR 1 YARD, TO... 1 \n",
"4858 (1:27) 30-Q.OLLISON RIGHT GUARD TO TB 1 FOR NO... 0 \n",
"4857 (:50) 30-Q.OLLISON RIGHT TACKLE FOR 1 YARD, TO... 1 \n",
"4781 (3:33) 30-T.GURLEY UP THE MIDDLE FOR 1 YARD, T... 1 \n",
"4594 (4:46) 21-M.INGRAM II UP THE MIDDLE FOR 1 YARD... 1 \n",
"13381 (3:16) 72-H.VAITAI REPORTED IN AS ELIGIBLE. 1... 1 \n",
"575 (2:59) 28-L.MURRAY LEFT GUARD TO TEN 2 FOR -1 ... 0 \n",
"13392 (8:39) 16-J.GOFF UP THE MIDDLE FOR 1 YARD, TOU... 1 \n",
"4423 (10:55) 40-A.ARMAH RIGHT GUARD TO NO 1 FOR NO ... 0 \n",
"5030 (3:47) (NO HUDDLE, SHOTGUN) 44-D.OGUNBOWALE UP... 0 "
]
},
"metadata": {
"tags": []
},
"execution_count": 24
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "UBpyhs7LCfLA",
"colab_type": "text"
},
"source": [
"Now are data is pretty much formatted at this point. We've removed unneccessary columns and should have only rushing plays. We now want to merge our data with the `rushing_df` we created earlier to add in what the probability was of scoring a touchdown for each play based off the distribution we came up with previously."
]
},
{
"cell_type": "code",
"metadata": {
"id": "GE-xD5_wCfLB",
"colab_type": "code",
"colab": {}
},
"source": [
"rushing_df = pd.merge(rushing_data2019, \n",
" rushing_df, \n",
" how='inner', \n",
" left_on=['DistanceFromEndzone'], \n",
" right_on = ['DistanceFromEndzone']\n",
" )"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "M8F36yKlCfLH",
"colab_type": "code",
"colab": {},
"outputId": "f6b46c59-d132-480c-e184-e082dc27d58c"
},
"source": [
"#our final DataFrame before we calculate Expected Touchdowns\n",
"rushing_df.sort_values(by='DistanceFromEndzone', ascending=True).head(5)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Tm</th>\n",
" <th>DistanceFromEndzone</th>\n",
" <th>Player</th>\n",
" <th>Description</th>\n",
" <th>RushingTD</th>\n",
" <th>p</th>\n",
" <th>p_smoothed</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>1919</th>\n",
" <td>ATL</td>\n",
" <td>1</td>\n",
" <td>D.FREEMAN</td>\n",
" <td>(12:13) (SHOTGUN) 24-D.FREEMAN LEFT GUARD TO H...</td>\n",
" <td>0</td>\n",
" <td>0.54017</td>\n",
" <td>0.416936</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1924</th>\n",
" <td>NYG</td>\n",
" <td>1</td>\n",
" <td>W.GALLMAN</td>\n",
" <td>(12:45) 22-W.GALLMAN JR UP THE MIDDLE TO WAS 1...</td>\n",
" <td>0</td>\n",
" <td>0.54017</td>\n",
" <td>0.416936</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1925</th>\n",
" <td>JAX</td>\n",
" <td>1</td>\n",
" <td>L.FOURNETTE</td>\n",
" <td>(10:30) 27-L.FOURNETTE UP THE MIDDLE FOR 1 YAR...</td>\n",
" <td>1</td>\n",
" <td>0.54017</td>\n",
" <td>0.416936</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1926</th>\n",
" <td>KC</td>\n",
" <td>1</td>\n",
" <td>D.WILLIAMS</td>\n",
" <td>(12:12) (SHOTGUN) 31-D.WILLIAMS UP THE MIDDLE ...</td>\n",
" <td>1</td>\n",
" <td>0.54017</td>\n",
" <td>0.416936</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1927</th>\n",
" <td>CLE</td>\n",
" <td>1</td>\n",
" <td>J.MCCRAY</td>\n",
" <td>(2:17) 67-J.MCCRAY REPORTED IN AS ELIGIBLE. 2...</td>\n",
" <td>1</td>\n",
" <td>0.54017</td>\n",
" <td>0.416936</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Tm DistanceFromEndzone Player \\\n",
"1919 ATL 1 D.FREEMAN \n",
"1924 NYG 1 W.GALLMAN \n",
"1925 JAX 1 L.FOURNETTE \n",
"1926 KC 1 D.WILLIAMS \n",
"1927 CLE 1 J.MCCRAY \n",
"\n",
" Description RushingTD p \\\n",
"1919 (12:13) (SHOTGUN) 24-D.FREEMAN LEFT GUARD TO H... 0 0.54017 \n",
"1924 (12:45) 22-W.GALLMAN JR UP THE MIDDLE TO WAS 1... 0 0.54017 \n",
"1925 (10:30) 27-L.FOURNETTE UP THE MIDDLE FOR 1 YAR... 1 0.54017 \n",
"1926 (12:12) (SHOTGUN) 31-D.WILLIAMS UP THE MIDDLE ... 1 0.54017 \n",
"1927 (2:17) 67-J.MCCRAY REPORTED IN AS ELIGIBLE. 2... 1 0.54017 \n",
"\n",
" p_smoothed \n",
"1919 0.416936 \n",
"1924 0.416936 \n",
"1925 0.416936 \n",
"1926 0.416936 \n",
"1927 0.416936 "
]
},
"metadata": {
"tags": []
},
"execution_count": 26
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "9BLBo09DCfLN",
"colab_type": "code",
"colab": {}
},
"source": [
"#Group by player and team and sum the probabilities - that's our expected TD value\n",
"predicted = rushing_df.groupby(['Player','Tm'])[['p']].sum()\n",
"predicted = predicted.rename(columns={'p':'Expected Touchdowns'})\n",
"predicted.reset_index(inplace=True)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"scrolled": true,
"id": "1_Pkh_HKCfLS",
"colab_type": "code",
"colab": {},
"outputId": "299b417e-4a13-47f4-99a8-d9c7c3b93d23"
},
"source": [
"aj_rushing_expect = predicted[predicted['Player'] == 'A.JONES']['Expected Touchdowns'].values[0]\n",
"aj_rushing_expect"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"7.216190853415877"
]
},
"metadata": {
"tags": []
},
"execution_count": 28
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "p4ab8ewdCfLa",
"colab_type": "code",
"colab": {},
"outputId": "45bb07c8-4db9-42f2-a735-1d5100db2aae"
},
"source": [
"#our model is conservative. Nick Chubb underperformed.\n",
"predicted.sort_values(by='Expected Touchdowns', ascending=False).head(10)"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Player</th>\n",
" <th>Tm</th>\n",
" <th>Expected Touchdowns</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>264</th>\n",
" <td>N.CHUBB</td>\n",
" <td>CLE</td>\n",
" <td>9.912851</td>\n",
" </tr>\n",
" <tr>\n",
" <th>132</th>\n",
" <td>E.ELLIOTT</td>\n",
" <td>DAL</td>\n",
" <td>9.413015</td>\n",
" </tr>\n",
" <tr>\n",
" <th>81</th>\n",
" <td>D.COOK</td>\n",
" <td>MIN</td>\n",
" <td>9.126598</td>\n",
" </tr>\n",
" <tr>\n",
" <th>62</th>\n",
" <td>C.MCCAFFREY</td>\n",
" <td>CAR</td>\n",
" <td>9.079737</td>\n",
" </tr>\n",
" <tr>\n",
" <th>105</th>\n",
" <td>D.MONTGOMERY</td>\n",
" <td>CHI</td>\n",
" <td>7.830039</td>\n",
" </tr>\n",
" <tr>\n",
" <th>190</th>\n",
" <td>J.MIXON</td>\n",
" <td>CIN</td>\n",
" <td>7.626816</td>\n",
" </tr>\n",
" <tr>\n",
" <th>231</th>\n",
" <td>L.FOURNETTE</td>\n",
" <td>JAX</td>\n",
" <td>7.626273</td>\n",
" </tr>\n",
" <tr>\n",
" <th>330</th>\n",
" <td>T.GURLEY</td>\n",
" <td>LA</td>\n",
" <td>7.580702</td>\n",
" </tr>\n",
" <tr>\n",
" <th>246</th>\n",
" <td>M.INGRAM</td>\n",
" <td>BAL</td>\n",
" <td>7.423987</td>\n",
" </tr>\n",
" <tr>\n",
" <th>16</th>\n",
" <td>A.JONES</td>\n",
" <td>GB</td>\n",
" <td>7.216191</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Player Tm Expected Touchdowns\n",
"264 N.CHUBB CLE 9.912851\n",
"132 E.ELLIOTT DAL 9.413015\n",
"81 D.COOK MIN 9.126598\n",
"62 C.MCCAFFREY CAR 9.079737\n",
"105 D.MONTGOMERY CHI 7.830039\n",
"190 J.MIXON CIN 7.626816\n",
"231 L.FOURNETTE JAX 7.626273\n",
"330 T.GURLEY LA 7.580702\n",
"246 M.INGRAM BAL 7.423987\n",
"16 A.JONES GB 7.216191"
]
},
"metadata": {
"tags": []
},
"execution_count": 29
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "6hz8AC0JCfLg",
"colab_type": "code",
"colab": {},
"outputId": "38814d7b-dacd-49ef-e372-8fa542989af4"
},
"source": [
"aj_rushing_actual = aj['RushingTD'].sum()\n",
"\n",
"diff = abs(aj_rushing_actual - aj_rushing_expect)\n",
"\n",
"if aj_rushing_expect > aj_rushing_actual:\n",
" print('Aaron Jones underperformed in Rushing TDs by {} TDs'.format(diff))\n",
"else:\n",
" print('Aaron Jones overperformed in Rushing TDs by {} TDs'.format(diff))"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"Aaron Jones overperformed in Rushing TDs by 8.783809146584122 TDs\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "KqebvA4ZCfLm",
"colab_type": "text"
},
"source": [
"As you can see, Aaron Jones **overperformed in rushing TDs** for the 2019 season based off the probability distribution we calculated. Given Aaron Jones' quality and quantitiy of usage throughout the 2019 season, he would have been expected to score around 3 touchdowns on the season. If you owned Aaron Jones, this is not surprising. The fact that the gap is so large though should raise cause for concern and he may be due for a negative regression next season based off these numbers. Let's run (basically) the same analysis for receiving TDs."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "40J_5fHUCfLn",
"colab_type": "text"
},
"source": [
"As you can see, formatting descriptions to names for receivers was a bit more tedious."
]
},
{
"cell_type": "code",
"metadata": {
"id": "lWzo_B2ECfLo",
"colab_type": "code",
"colab": {}
},
"source": [
"passing_data2019 = data2019\n",
"\n",
"def get_receiver(x):\n",
" try:\n",
" val = x.split(' TO ')[1]\n",
" return val\n",
" except IndexError:\n",
" pass\n",
" return x\n",
"\n",
"def remove_run_plays(x):\n",
" if len(x) < 2:\n",
" return np.nan\n",
" return x\n",
"\n",
"def remove_dot(x):\n",
" try:\n",
" if '.' in x:\n",
" if len(x.split('.')) > 2:\n",
" val = '.'.join(x.split('.')[:2])\n",
" return val\n",
" except TypeError:\n",
" pass\n",
" return x\n",
"\n",
"def remove_special(x, char):\n",
" try:\n",
" if char in x:\n",
" val = x.split(char)[0]\n",
" return val\n",
" except TypeError:\n",
" pass\n",
" return x\n",
"\n",
"filters = [get_receiver, \n",
" split_on_hyphen,\n",
" split_on_whitespace, \n",
" remove_run_plays,\n",
" remove_dot,\n",
" lambda x: remove_special(x, ')'),\n",
" lambda x: remove_special(x, ';'),\n",
" lambda x: remove_special(x, ',')]\n",
"\n",
"bad_phrases = bad_phrases + ['SACKED', 'INTERCEPTED']\n",
"\n",
"for phrase in bad_phrases:\n",
" passing_data2019 = passing_data2019[~passing_data2019['Description'].str.contains(phrase)]\n",
" \n",
"passing_data2019['Player'] = passing_data2019['Description']\n",
"\n",
"for filt_func in filters: \n",
" passing_data2019['Player'] = passing_data2019['Player'].apply(filt_func)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "eJa-K71mCfLt",
"colab_type": "code",
"colab": {}
},
"source": [
"passing_data2019 = passing_data2019[(passing_data2019['IsPass'] == 1) & (passing_data2019['IsTwoPointConversion'] == 0)]\n",
"passing_data2019['DistanceFromEndzone'] = 100 - passing_data2019['YardLine']\n",
"passing_data2019 = passing_data2019[['OffenseTeam', 'Player', 'DistanceFromEndzone', 'Description','IsTouchdown', 'IsPass', 'IsTwoPointConversion']]\n",
"passing_data2019.rename({\n",
" 'OffenseTeam': 'Tm',\n",
" 'IsTouchdown': 'ReceivingTD'\n",
"}, axis=1, inplace=True)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "G2xsgWNTCfLx",
"colab_type": "code",
"colab": {}
},
"source": [
"passing_df = pd.merge(passing_data2019, \n",
" passing_df, \n",
" how='inner', \n",
" left_on=['DistanceFromEndzone'], \n",
" right_on = ['DistanceFromEndzone']\n",
" )"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "po2D-xHJCfL2",
"colab_type": "code",
"colab": {}
},
"source": [
"predicted = passing_df.groupby(['Player','Tm'])[['p']].sum()\n",
"\n",
"predicted = predicted.rename(columns={'p':'Expected Touchdowns'})\n",
"\n",
"predicted.reset_index(inplace=True)"
],
"execution_count": 0,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "eWLJdVj1CfL6",
"colab_type": "code",
"colab": {},
"outputId": "a572c72e-4cfa-4054-cbe4-1dfb115bdc4e"
},
"source": [
"aj_receiving_expect = predicted[predicted['Player'] == 'A.JONES']['Expected Touchdowns'].values[0]\n",
"aj_receiving_expect"
],
"execution_count": 0,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"2.987485448911446"
]
},
"metadata": {
"tags": []
},
"execution_count": 39
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "m2VSUUY3CfL-",
"colab_type": "code",
"colab": {},
"outputId": "dd020f75-3705-469e-e97a-2a4070a9dd31"
},
"source": [
"aj_receiving_actual = aj['ReceivingTD'].sum()\n",
"\n",
"diff = abs(aj_receiving_actual - aj_receiving_expect)\n",
"\n",
"if aj_receiving_expect > aj_receiving_actual:\n",
" print('Aaron Jones underperformed in Receiving TDs by {} TDs'.format(diff))\n",
"else:\n",
" print('Aaron Jones overperformed in Receiving TDs by {} TDs'.format(diff))"
],
"execution_count": 0,
"outputs": [
{
"output_type": "stream",
"text": [
"Aaron Jones overperformed in Receiving TDs by 0.012514551088553816 TDs\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "NLe8Pn9tCfMF",
"colab_type": "text"
},
"source": [
"Not as bad for receiving TDs, it seems as though Aaron Jones caught as many receiving TDs as expected. This doesn't make up for the gap in rushing TDs, however."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "QM4QsDjkCfMF",
"colab_type": "text"
},
"source": [
"## Conclusions"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "98TkaR5mCfMG",
"colab_type": "text"
},
"source": [
"1. Aaron Jones had a stellar season and ranked second in RBs for PPR, but that doesn't tell the whole picture. Henry posted almost the same amount of Fantasy Points per game with a lower standard deviation. Points are the name of the game but don't tell the whole picture. You also have to look at how consistently a player is getting you points. Aaron Jones finished second on the season for ALL players in terms of standard deviation. Not a good look if you're looking for consistency.\n",
"2. We found that there was no statistically significant relationship between Jamaal Williams' usage and Aaron Jones' production. Our p-value was close though, and there did seem to be *some* correlation, so take that with a grain of salt.\n",
"3. Lastly, based off the probability distribution we made for both rushing and receiving TDs, it looks like Aaron Jones **really** overperformed in terms of rushing output, and did about just as expected for receiving. One caveat here is that our model was fairly conservative, but Jones still finished 10th in TDs in our model. Don't be surprised to see a negative regression in rushing TDs for Aaron Jones come the 2020 season."
]
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment