{
 "metadata": {
  "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.8.8-final"
  },
  "orig_nbformat": 2,
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3.8.8 64-bit",
   "metadata": {
    "interpreter": {
     "hash": "b3ba2566441a7c06988d0923437866b63cedc61552a5af99d1f4fb67d367b25f"
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2,
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 184,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "#from pandas.api.types import CategoricalDtype\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "import altair as alt\n",
    "\n",
    "from sklearn.preprocessing import MinMaxScaler\n",
    "\n",
    "from ipywidgets import interact, interactive, fixed, interact_manual\n",
    "import ipywidgets as widgets"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "years = [2020]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = pd.DataFrame()\n",
    "for i in years:  \n",
    "    i_data = pd.read_csv('https://github.com/guga31bb/nflfastR-data/blob/master/data/' \\\n",
    "                         'play_by_play_' + str(i) + '.csv.gz?raw=True',\n",
    "                         compression='gzip', low_memory=False)\n",
    "    data = data.append(i_data, sort=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = data.drop(columns=data.columns[data.columns.str.contains('epa') | data.columns.str.contains('wp')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('home') | data.columns.str.contains('away')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('tackle') | data.columns.str.contains('fumble')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('punt') | data.columns.str.contains('kick')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('xyac') | data.columns.str.contains('prob')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('stadium')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('blocked')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('extra')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('drive')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('fantasy')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('field_goal')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('jersey')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('lateral')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('pass_def')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('qb_hit')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('return')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('run')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('rush')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('two')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('_down_')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('end_')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('_post')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('series_')])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('td_')])\n",
    "data = data.drop(index=data.index[data.play_type_nfl != 'PASS'])\n",
    "data = data.drop(columns=data.columns[data.columns.str.contains('penalty')])\n",
    "data = data.drop(columns=['cp', 'cpoe', 'ep', 'pass_attempt', 'pass_oe', 'sp', 'touchback', 'weather'])\n",
    "#data = data.drop(columns=data.columns[data.columns.str.contains('end_')])\n",
    "#data = data.drop(columns=['cp', 'cpoe'])\n",
    "#data.columns[data.columns.str.contains('end_')]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "(18983, 95)"
      ]
     },
     "metadata": {},
     "execution_count": 5
    }
   ],
   "source": [
    "data.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "Index(['aborted_play', 'air_yards', 'complete_pass', 'defteam',\n",
       "       'defteam_score', 'defteam_timeouts_remaining', 'desc', 'div_game',\n",
       "       'down', 'first_down', 'game_date', 'game_half', 'game_id',\n",
       "       'game_seconds_remaining', 'goal_to_go', 'half_seconds_remaining', 'id',\n",
       "       'incomplete_pass', 'interception', 'interception_player_id',\n",
       "       'interception_player_name', 'location', 'name', 'nfl_api_id',\n",
       "       'no_huddle', 'old_game_id', 'order_sequence', 'out_of_bounds', 'pass',\n",
       "       'pass_length', 'pass_location', 'pass_touchdown', 'passer', 'passer_id',\n",
       "       'passer_player_id', 'passer_player_name', 'passing_yards', 'play',\n",
       "       'play_clock', 'play_deleted', 'play_id', 'play_type', 'play_type_nfl',\n",
       "       'posteam', 'posteam_score', 'posteam_timeouts_remaining',\n",
       "       'posteam_type', 'qb_dropback', 'qb_kneel', 'qb_scramble', 'qb_spike',\n",
       "       'qtr', 'quarter_end', 'quarter_seconds_remaining', 'receiver',\n",
       "       'receiver_id', 'receiver_player_id', 'receiver_player_name',\n",
       "       'receiving_yards', 'replay_or_challenge', 'replay_or_challenge_result',\n",
       "       'result', 'roof', 'sack', 'safety', 'score_differential', 'season',\n",
       "       'season_type', 'series', 'shotgun', 'side_of_field', 'special',\n",
       "       'special_teams_play', 'spread_line', 'st_play_type', 'start_time',\n",
       "       'success', 'surface', 'temp', 'time', 'time_of_day', 'timeout',\n",
       "       'timeout_team', 'total', 'total_line', 'touchdown', 'week', 'wind',\n",
       "       'xpass', 'yardline_100', 'yards_after_catch', 'yards_gained', 'ydsnet',\n",
       "       'ydstogo', 'yrdln'],\n",
       "      dtype='object')"
      ]
     },
     "metadata": {},
     "execution_count": 6
    }
   ],
   "source": [
    "data.columns"
   ]
  },
  {
   "source": [
    "# Seaborn Violin Plot"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "# https://stackoverflow.com/questions/40372030/pandas-round-to-the-nearest-n\n",
    "def custom_round(x, base=5):\n",
    "  return (x // base) * base"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_completions_violin(week='ALL', team='ALL'):\n",
    "  if (team == 'ALL') and (week == 'ALL'):\n",
    "    plot_subdata = data\n",
    "  elif (team == 'ALL'):\n",
    "    plot_subdata = data[(data.week == int(week))]\n",
    "  elif (week == 'ALL'):\n",
    "    plot_subdata = data[(data.posteam == team)]\n",
    "  else:\n",
    "    plot_subdata = data[(data.week == int(week)) & (data.posteam == team)]\n",
    "\n",
    "  completions = int(sum(plot_subdata.complete_pass))\n",
    "  total_passes = plot_subdata.shape[0]\n",
    "  \n",
    "  if plot_subdata.shape[0] == 0:\n",
    "    return None\n",
    "\n",
    "  plt.figure(figsize=(15, 5))\n",
    "  \n",
    "  ax = sns.violinplot(data=plot_subdata,\n",
    "                      x='air_yards', y='pass_location', order=['left', 'middle', 'right'],\n",
    "                      hue='complete_pass', split=True, hue_order=[1.0, 0.0],\n",
    "                      inner='quartile',\n",
    "                      orient='h',\n",
    "                      palette={1.0: '#1479FC', 0.0: '0.85'},\n",
    "                      linewidth=0.5)\n",
    "  \n",
    "  ax.xaxis.set_major_locator(plt.MultipleLocator(10))\n",
    "  ax.xaxis.set_minor_locator(plt.MultipleLocator(5))\n",
    "  ax.grid(b=True, which='minor', axis='x', linewidth=0.5)\n",
    "\n",
    "  sns.set_theme(rc={\n",
    "      'axes.facecolor': '#196F0C',\n",
    "      'figure.facecolor': '#196F0C',\n",
    "\n",
    "      'axes.labelcolor': 'white',\n",
    "      'axes.titlecolor': 'white',\n",
    "      'axes.titlesize': 20,\n",
    "\n",
    "      'xtick.color': 'white',\n",
    "      'ytick.color': 'white',\n",
    "  })\n",
    "  plt.setp(ax.collections, edgecolor='black')\n",
    "\n",
    "  plt.xlabel('Air Yards (from LOS)')\n",
    "  plt.ylabel('Pass Location')\n",
    "  plt.title(f'Passing success ({completions}/{total_passes})')\n",
    "  plt.legend(title = 'Completions')\n",
    "  \n",
    "  plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "weeks = data.week.unique()\n",
    "teams = data.posteam.unique()\n",
    "teams.sort()\n",
    "\n",
    "weeks = np.append(weeks, 'ALL')\n",
    "teams = np.append(teams, 'ALL')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": "interactive(children=(Dropdown(description='week', options=('1', '2', '3', '4', '5', '6', '7', '8', '9', '10',…",
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "3073dd28a789473383582006c6ac3112"
      }
     },
     "metadata": {}
    }
   ],
   "source": [
    "@interact\n",
    "def f(week=weeks, team=teams):\n",
    "    return plot_completions_violin(week, team)"
   ]
  },
  {
   "source": [
    "# Matplotlib Plot"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [],
   "source": [
    "#one_game = data[(data.week == 12) & (data.posteam == 'KC')].copy()\n",
    "def prepare_passing_accuracy_data(one_game):\n",
    "    one_game['air_yards_rounded'] = custom_round(one_game.air_yards)\n",
    "    one_game = one_game.groupby(by=['air_yards_rounded', 'pass_location'], as_index=False).agg(\n",
    "        {\n",
    "            'complete_pass': ['sum', 'count']     \n",
    "        }\n",
    "    )\n",
    "\n",
    "    one_game.columns = ['air_yards_rounded', 'location', 'completions', 'passes']\n",
    "    one_game['location_int'] = one_game.location.replace({'left': 2, 'middle': 1, 'right': 0})\n",
    "    one_game['location'] = one_game.location.str.capitalize()\n",
    "    one_game['accuracy'] = one_game.completions / one_game.passes\n",
    "    one_game['text'] = one_game.completions.astype('int64').astype(str) + '/' + one_game.passes.astype(str)\n",
    "    one_game = one_game.sort_values(by=['location_int'])\n",
    "    return one_game"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "def normalize_passes_size(passes):\n",
    "    max_passes = passes.max()\n",
    "    if max_passes > 1000:\n",
    "        min_range, max_range = (1000, 7500)\n",
    "    elif max_passes > 50:\n",
    "        min_range, max_range = (1000, 6000)\n",
    "    else:\n",
    "        min_range, max_range = (500,  5000)\n",
    "\n",
    "    return (max_range - min_range) * (passes - passes.min()) / (passes.max() - passes.min()) + min_range    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "0    7500.000000\n",
       "1    1117.234469\n",
       "2    1013.026052\n",
       "3    1000.000000\n",
       "dtype: float64"
      ]
     },
     "metadata": {},
     "execution_count": 31
    }
   ],
   "source": [
    "normalize_passes_size(pd.Series([10000,200, 40, 20]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_completions_matplotlib(week='ALL', team='ALL'):\n",
    "    if (team == 'ALL') and (week == 'ALL'):\n",
    "        one_game = data\n",
    "    elif (team == 'ALL'):\n",
    "        one_game = data[(data.week == int(week))]\n",
    "    elif (week == 'ALL'):\n",
    "        one_game = data[(data.posteam == team)]\n",
    "    else:\n",
    "        one_game = data[(data.week == int(week)) & (data.posteam == team)]\n",
    "    \n",
    "    one_game = prepare_passing_accuracy_data(one_game)\n",
    "    max_passes = one_game.passes.max()\n",
    "    total_completions = int(sum(one_game.completions))\n",
    "    total_passes = int(sum(one_game.passes))\n",
    "\n",
    "    fig, ax = plt.subplots(1,1,figsize=(20,10))\n",
    "\n",
    "    # sns.set_theme(rc={\n",
    "    #     'axes.facecolor': '#196F0C',\n",
    "    #     'figure.facecolor': '#19300C',\n",
    "\n",
    "    #     'axes.labelcolor': 'white',\n",
    "    #     'axes.titlecolor': 'white',\n",
    "        \n",
    "    #     'xtick.color': 'white',\n",
    "    #     'ytick.color': 'white',\n",
    "    # })\n",
    "\n",
    "    plt.xlim(-20, 70)\n",
    "    plt.ylim(-1, 3)\n",
    "    plt.xticks(fontsize=20, color='white')\n",
    "    plt.yticks(fontsize=20, color='white')\n",
    "    \n",
    "    ax.xaxis.set_major_locator(plt.MultipleLocator(10))\n",
    "    ax.xaxis.set_minor_locator(plt.MultipleLocator(5))\n",
    "    ax.grid(b=True, which='both', axis='x', linewidth=2.5)\n",
    "    #ax.grid(b=True, which='minor', axis='x', linewidth=1)\n",
    "    ax.grid(b=False, which='both', axis='y')\n",
    "    ax.set_axisbelow(True)\n",
    "\n",
    "    # if max_passes > 50:\n",
    "    #     one_game['passes_size'] = ((6000 - 500) * one_game.passes / max_passes) + 500\n",
    "    # else:\n",
    "    #     one_game['passes_size'] = ((5000 - 500) * one_game.passes / max_passes) + 500\n",
    "    one_game['passes_size'] = normalize_passes_size(one_game.passes)\n",
    "\n",
    "    plt.scatter(x=one_game.air_yards_rounded,\n",
    "                y=one_game.location, \n",
    "                s=one_game.passes_size,\n",
    "                c=one_game.accuracy,\n",
    "                cmap='Oranges',\n",
    "                edgecolors='black')\n",
    "\n",
    "    for x, y, text in zip(one_game.air_yards_rounded, one_game.location, one_game.text):\n",
    "        plt.text(x=x, y=y, s=text, \n",
    "                ha = 'center', va = 'center',\n",
    "                fontsize=15)\n",
    "\n",
    "    fig.set_facecolor('#196F0C')\n",
    "    ax.set_facecolor('#196F0C')\n",
    "    #ax.set_labelcolor('white')\n",
    "    #ax.set_titlecolor('white')\n",
    "\n",
    "    plt.xlabel('Air yards (from LOS)', fontsize=15, color='white')\n",
    "    plt.ylabel('')\n",
    "    plt.title(f'Pass success for Week={week} and Team={team} ({total_completions}/{total_passes})', fontsize=30, color='white')\n",
    "\n",
    "    plt.show()\n",
    "    plt.rcParams.update(plt.rcParamsDefault)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": "interactive(children=(Dropdown(description='week', options=('1', '2', '3', '4', '5', '6', '7', '8', '9', '10',…",
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "a92c9d62e5d44f6581d005bfc7f29dfc"
      }
     },
     "metadata": {}
    }
   ],
   "source": [
    "#plot_completions_matplotlib(5, 'KC')\n",
    "@interact\n",
    "def f(week=weeks, team=teams):\n",
    "    return plot_completions_matplotlib(week, team)"
   ]
  },
  {
   "source": [
    "# Altair Plot"
   ],
   "cell_type": "markdown",
   "metadata": {}
  },
  {
   "cell_type": "code",
   "execution_count": 118,
   "metadata": {},
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/html": "\n<div id=\"altair-viz-926bbe039a02443198407e778fe25e4c\"></div>\n<script type=\"text/javascript\">\n  (function(spec, embedOpt){\n    let outputDiv = document.currentScript.previousElementSibling;\n    if (outputDiv.id !== \"altair-viz-926bbe039a02443198407e778fe25e4c\") {\n      outputDiv = document.getElementById(\"altair-viz-926bbe039a02443198407e778fe25e4c\");\n    }\n    const paths = {\n      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.8.1?noext\",\n      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n    };\n\n    function loadScript(lib) {\n      return new Promise(function(resolve, reject) {\n        var s = document.createElement('script');\n        s.src = paths[lib];\n        s.async = true;\n        s.onload = () => resolve(paths[lib]);\n        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n        document.getElementsByTagName(\"head\")[0].appendChild(s);\n      });\n    }\n\n    function showError(err) {\n      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n      throw err;\n    }\n\n    function displayChart(vegaEmbed) {\n      vegaEmbed(outputDiv, spec, embedOpt)\n        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n    }\n\n    if(typeof define === \"function\" && define.amd) {\n      requirejs.config({paths});\n      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n    } else if (typeof vegaEmbed === \"function\") {\n      displayChart(vegaEmbed);\n    } else {\n      loadScript(\"vega\")\n        .then(() => loadScript(\"vega-lite\"))\n        .then(() => loadScript(\"vega-embed\"))\n        .catch(showError)\n        .then(() => displayChart(vegaEmbed));\n    }\n  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300, \"fill\": \"#19500C\"}, \"axis\": {\"labelColor\": \"white\", \"labelFontSize\": 20, \"titleColor\": \"white\", \"titleFontSize\": 30}, \"background\": \"#196F0C\", \"scale\": {\"maxSize\": 6000}, \"title\": {\"color\": \"white\", \"fontSize\": 30}}, \"layer\": [{\"mark\": \"circle\", \"encoding\": {\"color\": {\"type\": \"quantitative\", \"field\": \"accuracy\", \"legend\": {\"title\": \"Accuracy\"}, \"scale\": {\"scheme\": \"lightgreyred\"}}, \"size\": {\"type\": \"quantitative\", \"field\": \"passes\", \"legend\": null}, \"x\": {\"type\": \"quantitative\", \"axis\": {\"values\": [-20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65]}, \"field\": \"air_yards_rounded\", \"scale\": {\"domain\": [-20, 70]}, \"title\": \"Air Yards from Line of Scrimmage\"}, \"y\": {\"type\": \"nominal\", \"field\": \"location\", \"title\": null}}}, {\"mark\": {\"type\": \"text\", \"align\": \"center\", \"baseline\": \"middle\", \"color\": \"black\", \"fontSize\": 10}, \"encoding\": {\"text\": {\"type\": \"nominal\", \"field\": \"text\"}, \"x\": {\"type\": \"quantitative\", \"field\": \"air_yards_rounded\"}, \"y\": {\"type\": \"nominal\", \"field\": \"location\"}}}], \"data\": {\"name\": \"data-1e8fedc68cf790e4dfe305b3aa026616\"}, \"height\": 500, \"title\": \"Pass success for Week=ALL and Team=KC (502/753)\", \"width\": 1000, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.8.1.json\", \"datasets\": {\"data-1e8fedc68cf790e4dfe305b3aa026616\": [{\"air_yards_rounded\": 15.0, \"location\": \"Right\", \"completions\": 13.0, \"passes\": 22, \"location_int\": 0, \"accuracy\": 0.5909090909090909, \"text\": \"13/22\"}, {\"air_yards_rounded\": 45.0, \"location\": \"Right\", \"completions\": 1.0, \"passes\": 1, \"location_int\": 0, \"accuracy\": 1.0, \"text\": \"1/1\"}, {\"air_yards_rounded\": 40.0, \"location\": \"Right\", \"completions\": 1.0, \"passes\": 3, \"location_int\": 0, \"accuracy\": 0.3333333333333333, \"text\": \"1/3\"}, {\"air_yards_rounded\": 35.0, \"location\": \"Right\", \"completions\": 1.0, \"passes\": 5, \"location_int\": 0, \"accuracy\": 0.2, \"text\": \"1/5\"}, {\"air_yards_rounded\": 30.0, \"location\": \"Right\", \"completions\": 1.0, \"passes\": 3, \"location_int\": 0, \"accuracy\": 0.3333333333333333, \"text\": \"1/3\"}, {\"air_yards_rounded\": 25.0, \"location\": \"Right\", \"completions\": 6.0, \"passes\": 10, \"location_int\": 0, \"accuracy\": 0.6, \"text\": \"6/10\"}, {\"air_yards_rounded\": 20.0, \"location\": \"Right\", \"completions\": 5.0, \"passes\": 19, \"location_int\": 0, \"accuracy\": 0.2631578947368421, \"text\": \"5/19\"}, {\"air_yards_rounded\": 50.0, \"location\": \"Right\", \"completions\": 1.0, \"passes\": 1, \"location_int\": 0, \"accuracy\": 1.0, \"text\": \"1/1\"}, {\"air_yards_rounded\": 10.0, \"location\": \"Right\", \"completions\": 28.0, \"passes\": 47, \"location_int\": 0, \"accuracy\": 0.5957446808510638, \"text\": \"28/47\"}, {\"air_yards_rounded\": 5.0, \"location\": \"Right\", \"completions\": 48.0, \"passes\": 71, \"location_int\": 0, \"accuracy\": 0.676056338028169, \"text\": \"48/71\"}, {\"air_yards_rounded\": 55.0, \"location\": \"Right\", \"completions\": 0.0, \"passes\": 1, \"location_int\": 0, \"accuracy\": 0.0, \"text\": \"0/1\"}, {\"air_yards_rounded\": -10.0, \"location\": \"Right\", \"completions\": 5.0, \"passes\": 7, \"location_int\": 0, \"accuracy\": 0.7142857142857143, \"text\": \"5/7\"}, {\"air_yards_rounded\": 0.0, \"location\": \"Right\", \"completions\": 55.0, \"passes\": 81, \"location_int\": 0, \"accuracy\": 0.6790123456790124, \"text\": \"55/81\"}, {\"air_yards_rounded\": -5.0, \"location\": \"Right\", \"completions\": 28.0, \"passes\": 32, \"location_int\": 0, \"accuracy\": 0.875, \"text\": \"28/32\"}, {\"air_yards_rounded\": 10.0, \"location\": \"Middle\", \"completions\": 23.0, \"passes\": 36, \"location_int\": 1, \"accuracy\": 0.6388888888888888, \"text\": \"23/36\"}, {\"air_yards_rounded\": 15.0, \"location\": \"Middle\", \"completions\": 6.0, \"passes\": 8, \"location_int\": 1, \"accuracy\": 0.75, \"text\": \"6/8\"}, {\"air_yards_rounded\": 40.0, \"location\": \"Middle\", \"completions\": 1.0, \"passes\": 3, \"location_int\": 1, \"accuracy\": 0.3333333333333333, \"text\": \"1/3\"}, {\"air_yards_rounded\": 20.0, \"location\": \"Middle\", \"completions\": 2.0, \"passes\": 4, \"location_int\": 1, \"accuracy\": 0.5, \"text\": \"2/4\"}, {\"air_yards_rounded\": 0.0, \"location\": \"Middle\", \"completions\": 21.0, \"passes\": 33, \"location_int\": 1, \"accuracy\": 0.6363636363636364, \"text\": \"21/33\"}, {\"air_yards_rounded\": 25.0, \"location\": \"Middle\", \"completions\": 1.0, \"passes\": 3, \"location_int\": 1, \"accuracy\": 0.3333333333333333, \"text\": \"1/3\"}, {\"air_yards_rounded\": 5.0, \"location\": \"Middle\", \"completions\": 44.0, \"passes\": 54, \"location_int\": 1, \"accuracy\": 0.8148148148148148, \"text\": \"44/54\"}, {\"air_yards_rounded\": -5.0, \"location\": \"Middle\", \"completions\": 17.0, \"passes\": 17, \"location_int\": 1, \"accuracy\": 1.0, \"text\": \"17/17\"}, {\"air_yards_rounded\": 30.0, \"location\": \"Middle\", \"completions\": 1.0, \"passes\": 3, \"location_int\": 1, \"accuracy\": 0.3333333333333333, \"text\": \"1/3\"}, {\"air_yards_rounded\": 10.0, \"location\": \"Left\", \"completions\": 14.0, \"passes\": 25, \"location_int\": 2, \"accuracy\": 0.56, \"text\": \"14/25\"}, {\"air_yards_rounded\": -5.0, \"location\": \"Left\", \"completions\": 46.0, \"passes\": 54, \"location_int\": 2, \"accuracy\": 0.8518518518518519, \"text\": \"46/54\"}, {\"air_yards_rounded\": 40.0, \"location\": \"Left\", \"completions\": 0.0, \"passes\": 4, \"location_int\": 2, \"accuracy\": 0.0, \"text\": \"0/4\"}, {\"air_yards_rounded\": 0.0, \"location\": \"Left\", \"completions\": 61.0, \"passes\": 76, \"location_int\": 2, \"accuracy\": 0.8026315789473685, \"text\": \"61/76\"}, {\"air_yards_rounded\": 30.0, \"location\": \"Left\", \"completions\": 1.0, \"passes\": 4, \"location_int\": 2, \"accuracy\": 0.25, \"text\": \"1/4\"}, {\"air_yards_rounded\": 25.0, \"location\": \"Left\", \"completions\": 2.0, \"passes\": 7, \"location_int\": 2, \"accuracy\": 0.2857142857142857, \"text\": \"2/7\"}, {\"air_yards_rounded\": 20.0, \"location\": \"Left\", \"completions\": 1.0, \"passes\": 6, \"location_int\": 2, \"accuracy\": 0.16666666666666666, \"text\": \"1/6\"}, {\"air_yards_rounded\": 15.0, \"location\": \"Left\", \"completions\": 15.0, \"passes\": 22, \"location_int\": 2, \"accuracy\": 0.6818181818181818, \"text\": \"15/22\"}, {\"air_yards_rounded\": 5.0, \"location\": \"Left\", \"completions\": 46.0, \"passes\": 76, \"location_int\": 2, \"accuracy\": 0.6052631578947368, \"text\": \"46/76\"}, {\"air_yards_rounded\": 35.0, \"location\": \"Left\", \"completions\": 1.0, \"passes\": 6, \"location_int\": 2, \"accuracy\": 0.16666666666666666, \"text\": \"1/6\"}, {\"air_yards_rounded\": -10.0, \"location\": \"Left\", \"completions\": 6.0, \"passes\": 9, \"location_int\": 2, \"accuracy\": 0.6666666666666666, \"text\": \"6/9\"}]}}, {\"mode\": \"vega-lite\"});\n</script>",
      "text/plain": [
       "alt.LayerChart(...)"
      ]
     },
     "metadata": {},
     "execution_count": 118
    }
   ],
   "source": [
    "team = 'KC'\n",
    "week = 'ALL'\n",
    "if (team == 'ALL') and (week == 'ALL'):\n",
    "    one_game = data\n",
    "elif (team == 'ALL'):\n",
    "    one_game = data[(data.week == int(week))]\n",
    "elif (week == 'ALL'):\n",
    "    one_game = data[(data.posteam == team)]\n",
    "else:\n",
    "    one_game = data[(data.week == int(week)) & (data.posteam == team)]\n",
    "\n",
    "one_game = prepare_passing_accuracy_data(one_game)\n",
    "total_completions = int(sum(one_game.completions))\n",
    "total_passes = int(sum(one_game.passes))\n",
    "\n",
    "points = alt.Chart(one_game).mark_circle().encode(\n",
    "    alt.X('air_yards_rounded', title='Air Yards from Line of Scrimmage', \n",
    "          axis=alt.Axis(values=list(range(-20,70,5))),\n",
    "          scale=alt.Scale(domain=[-20, 70])),\n",
    "    alt.Y('location', title=None),\n",
    "    alt.Size('passes', legend=None),\n",
    "    alt.Color('accuracy', scale=alt.Scale(scheme='lightgreyred'), legend=alt.Legend(title='Accuracy')),\n",
    ")\n",
    "\n",
    "text = alt.Chart(one_game).mark_text(\n",
    "    align='center',\n",
    "    baseline='middle',\n",
    "    fontSize=10,\n",
    "    color='black'\n",
    ").encode(\n",
    "    x='air_yards_rounded',\n",
    "    y='location',\n",
    "    text='text'\n",
    ")\n",
    "\n",
    "alt.layer(\n",
    "    points, \n",
    "    text\n",
    ").properties(\n",
    "    title=f'Pass success for Week={week} and Team={team} ({total_completions}/{total_passes})',\n",
    "    #subtitle='here',\n",
    "    width=1000,\n",
    "    height=500\n",
    ").configure(\n",
    "    background='#196F0C'\n",
    ").configure_axis(\n",
    "    labelColor='white',\n",
    "    labelFontSize=20,\n",
    "    titleColor='white',\n",
    "    titleFontSize=30,\n",
    ").configure_scale(\n",
    "    maxSize=6000\n",
    ").configure_title(\n",
    "    color='white',\n",
    "    fontSize=30\n",
    ").configure_view(\n",
    "    fill='#19500C'\n",
    ")#.interactive()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 205,
   "metadata": {},
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "(1093, 9)"
      ]
     },
     "metadata": {},
     "execution_count": 205
    }
   ],
   "source": [
    "season = data.copy()\n",
    "season['air_yards_rounded'] = custom_round(season.air_yards)\n",
    "season = season.groupby(by=['posteam', 'air_yards_rounded', 'pass_location'], as_index=False).agg(\n",
    "    {\n",
    "        'complete_pass': ['sum', 'count']     \n",
    "    }\n",
    ")\n",
    "\n",
    "season.columns = ['team', 'air_yards_rounded', 'location', 'completions', 'passes']\n",
    "season['location_int'] = season.location.replace({'left': 2, 'middle': 1, 'right': 0})\n",
    "season['location'] = season.location.str.capitalize()\n",
    "season['accuracy'] = season.completions / season.passes\n",
    "season['text'] = season.completions.astype('int64').astype(str) + '/' + season.passes.astype(str)\n",
    "season = season.sort_values(by=['location_int'])\n",
    "season = season.reset_index()\n",
    "\n",
    "season_2020_url = 'season_2020.json'\n",
    "season.to_json(season_2020_url, orient='records')\n",
    "season.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 208,
   "metadata": {},
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/html": "\n<div id=\"altair-viz-a3b9d9795a264fdc812d8ee0f3ace26e\"></div>\n<script type=\"text/javascript\">\n  (function(spec, embedOpt){\n    let outputDiv = document.currentScript.previousElementSibling;\n    if (outputDiv.id !== \"altair-viz-a3b9d9795a264fdc812d8ee0f3ace26e\") {\n      outputDiv = document.getElementById(\"altair-viz-a3b9d9795a264fdc812d8ee0f3ace26e\");\n    }\n    const paths = {\n      \"vega\": \"https://cdn.jsdelivr.net/npm//vega@5?noext\",\n      \"vega-lib\": \"https://cdn.jsdelivr.net/npm//vega-lib?noext\",\n      \"vega-lite\": \"https://cdn.jsdelivr.net/npm//vega-lite@4.8.1?noext\",\n      \"vega-embed\": \"https://cdn.jsdelivr.net/npm//vega-embed@6?noext\",\n    };\n\n    function loadScript(lib) {\n      return new Promise(function(resolve, reject) {\n        var s = document.createElement('script');\n        s.src = paths[lib];\n        s.async = true;\n        s.onload = () => resolve(paths[lib]);\n        s.onerror = () => reject(`Error loading script: ${paths[lib]}`);\n        document.getElementsByTagName(\"head\")[0].appendChild(s);\n      });\n    }\n\n    function showError(err) {\n      outputDiv.innerHTML = `<div class=\"error\" style=\"color:red;\">${err}</div>`;\n      throw err;\n    }\n\n    function displayChart(vegaEmbed) {\n      vegaEmbed(outputDiv, spec, embedOpt)\n        .catch(err => showError(`Javascript Error: ${err.message}<br>This usually means there's a typo in your chart specification. See the javascript console for the full traceback.`));\n    }\n\n    if(typeof define === \"function\" && define.amd) {\n      requirejs.config({paths});\n      require([\"vega-embed\"], displayChart, err => showError(`Error loading script: ${err.message}`));\n    } else if (typeof vegaEmbed === \"function\") {\n      displayChart(vegaEmbed);\n    } else {\n      loadScript(\"vega\")\n        .then(() => loadScript(\"vega-lite\"))\n        .then(() => loadScript(\"vega-embed\"))\n        .catch(showError)\n        .then(() => displayChart(vegaEmbed));\n    }\n  })({\"config\": {\"view\": {\"continuousWidth\": 400, \"continuousHeight\": 300, \"fill\": \"#19500C\"}, \"axis\": {\"labelColor\": \"white\", \"labelFontSize\": 20, \"titleColor\": \"white\", \"titleFontSize\": 30}, \"background\": \"#196F0C\", \"scale\": {\"maxSize\": 6000}, \"title\": {\"anchor\": \"start\", \"color\": \"white\", \"fontSize\": 30, \"subtitleColor\": \"white\", \"subtitleFontSize\": 15}}, \"layer\": [{\"mark\": \"circle\", \"encoding\": {\"color\": {\"type\": \"quantitative\", \"field\": \"accuracy\", \"legend\": {\"title\": \"Accuracy\"}, \"scale\": {\"scheme\": \"lightgreyred\"}}, \"size\": {\"type\": \"quantitative\", \"field\": \"passes\", \"legend\": null}, \"tooltip\": {\"type\": \"nominal\", \"field\": \"text\"}, \"x\": {\"type\": \"quantitative\", \"axis\": {\"values\": [-20, -15, -10, -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65]}, \"field\": \"air_yards_rounded\", \"scale\": {\"domain\": [-20, 70]}, \"title\": \"Air Yards from Line of Scrimmage\"}, \"y\": {\"type\": \"nominal\", \"field\": \"location\", \"title\": null}}, \"selection\": {\"selector063\": {\"type\": \"single\", \"fields\": [\"team\"], \"bind\": {\"input\": \"select\", \"options\": [\"ARI\", \"ATL\", \"BAL\", \"BUF\", \"CAR\", \"CHI\", \"CIN\", \"CLE\", \"DAL\", \"DEN\", \"DET\", \"GB\", \"HOU\", \"IND\", \"JAX\", \"KC\", \"LA\", \"LAC\", \"LV\", \"MIA\", \"MIN\", \"NE\", \"NO\", \"NYG\", \"NYJ\", \"PHI\", \"PIT\", \"SEA\", \"SF\", \"TB\", \"TEN\", \"WAS\"], \"name\": \"Team: \"}, \"init\": {\"team\": \"NE\"}}}}, {\"mark\": {\"type\": \"text\", \"align\": \"center\", \"baseline\": \"middle\", \"color\": \"black\", \"fontSize\": 10}, \"encoding\": {\"text\": {\"type\": \"nominal\", \"field\": \"text\"}, \"x\": {\"type\": \"quantitative\", \"field\": \"air_yards_rounded\"}, \"y\": {\"type\": \"nominal\", \"field\": \"location\"}}}], \"data\": {\"url\": \"https://raw.githubusercontent.com/javendano585/NFL_Data/main/season_2020.json\"}, \"height\": 500, \"title\": {\"text\": \"Pass success for 2020 Season\"}, \"transform\": [{\"filter\": {\"selection\": \"selector063\"}}], \"width\": 1000, \"$schema\": \"https://vega.github.io/schema/vega-lite/v4.8.1.json\"}, {\"mode\": \"vega-lite\"});\n</script>",
      "text/plain": [
       "alt.LayerChart(...)"
      ]
     },
     "metadata": {},
     "execution_count": 208
    }
   ],
   "source": [
    "input_dropdown = alt.binding_select(options=np.sort(season.team.unique()), name='Team: ')\n",
    "selection = alt.selection_single(fields=['team'], bind=input_dropdown, init={'team': 'NE'})\n",
    "github_season_2020_url = 'https://raw.githubusercontent.com/javendano585/NFL_Data/main/season_2020.json'\n",
    "\n",
    "points = alt.Chart(github_season_2020_url).mark_circle().encode(\n",
    "    alt.X('air_yards_rounded:Q', title='Air Yards from Line of Scrimmage', \n",
    "          axis=alt.Axis(values=list(range(-20,70,5))),\n",
    "          scale=alt.Scale(domain=[-20, 70])),\n",
    "    alt.Y('location:N', title=None),\n",
    "    alt.Size('passes:Q', legend=None),\n",
    "    alt.Color('accuracy:Q', scale=alt.Scale(scheme='lightgreyred'), legend=alt.Legend(title='Accuracy')),\n",
    "    tooltip='text:N'\n",
    ")\n",
    "\n",
    "text = alt.Chart(github_season_2020_url).mark_text(\n",
    "    align='center',\n",
    "    baseline='middle',\n",
    "    fontSize=10,\n",
    "    color='black'\n",
    ").encode(\n",
    "    x='air_yards_rounded:Q',\n",
    "    y='location:N',\n",
    "    text='text:N'\n",
    ")\n",
    "\n",
    "passing_chart = alt.layer(\n",
    "    points, \n",
    "    text\n",
    ").properties(\n",
    "    title={\n",
    "      \"text\": 'Pass success for 2020 Season', \n",
    "      #\"subtitle\": f'Accuracy = {(total_completions / total_passes):.3f}% ({total_completions}/{total_passes})'\n",
    "    },\n",
    "    width=1000,\n",
    "    height=500\n",
    ").configure(\n",
    "    background='#196F0C'\n",
    ").configure_axis(\n",
    "    labelColor='white',\n",
    "    labelFontSize=20,\n",
    "    titleColor='white',\n",
    "    titleFontSize=30,\n",
    ").configure_scale(\n",
    "    maxSize=6000\n",
    ").configure_title(\n",
    "    anchor='start',\n",
    "    color='white',\n",
    "    fontSize=30,\n",
    "    subtitleFontSize=15,\n",
    "    subtitleColor='white'\n",
    ").configure_view(\n",
    "    fill='#19500C'\n",
    ").add_selection(\n",
    "    selection\n",
    ").transform_filter(\n",
    "    selection\n",
    ")#.interactive()\n",
    "\n",
    "passing_chart.save('Passing_2020.html')\n",
    "passing_chart"
   ]
  }
 ]
}