CoolFace
Apppublic

TJStatsApps/pitch_plot_select_mlb

sourceHugging Facemitupdated 1y agoView on Hugging Face
1likes
pitching_summary_api.ipynb1939 linesDownload Raw Back to root
1{2 "cells": [3  {4   "cell_type": "code",5   "execution_count": 1,6   "metadata": {},7   "outputs": [8    {9     "name": "stderr",10     "output_type": "stream",11     "text": [12      "C:\\Users\\thoma\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python39\\site-packages\\dask\\dataframe\\_pyarrow_compat.py:21: UserWarning: You are using pyarrow version 12.0.0 which is known to be insecure. See https://www.cve.org/CVERecord?id=CVE-2023-47248 for further details. Please upgrade to pyarrow>=14.0.1 or install pyarrow-hotfix to patch your current version.\n",13      "  warnings.warn(\n"14     ]15    }16   ],17   "source": [18    "import polars as pl\n",19    "import numpy as np\n",20    "import pandas as pd\n",21    "import api_scraper\n",22    "scrape = api_scraper.MLB_Scrape()\n",23    "from functions import df_update\n",24    "from functions import pitch_summary_functions\n",25    "update = df_update.df_update()\n",26    "from stuff_model import feature_engineering as fe\n",27    "from stuff_model import stuff_apply\n",28    "import requests\n",29    "import joblib\n",30    "from matplotlib.gridspec import GridSpec\n",31    "\n",32    "colour_palette = ['#FFB000','#648FFF','#785EF0',\n",33    "                  '#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']"34   ]35  },36  {37   "cell_type": "code",38   "execution_count": 2,39   "metadata": {},40   "outputs": [41    {42     "name": "stdout",43     "output_type": "stream",44     "text": [45      "http://statsapi.mlb.com/api/v1/people/682243?hydrate=stats(type=gameLog,season=2024,startDate=2024-01-01,endDate=2024-12-31,sportId=1,gameType=[R]),hydrations\n",46      "This May Take a While. Progress Bar shows Completion of Data Retrieval.\n"47     ]48    },49    {50     "name": "stderr",51     "output_type": "stream",52     "text": [53      "Processing: 100%|██████████| 31/31 [00:06<00:00,  5.00iteration/s]\n"54     ]55    },56    {57     "name": "stdout",58     "output_type": "stream",59     "text": [60      "Converting Data to Dataframe.\n"61     ]62    },63    {64     "name": "stderr",65     "output_type": "stream",66     "text": [67      "C:\\Users\\thoma\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python39\\site-packages\\sklearn\\base.py:445: UserWarning: X does not have valid feature names, but KNeighborsClassifier was fitted with feature names\n",68      "  warnings.warn(\n",69      "C:\\Users\\thoma\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python39\\site-packages\\sklearn\\base.py:445: UserWarning: X does not have valid feature names, but RandomForestClassifier was fitted with feature names\n",70      "  warnings.warn(\n",71      "C:\\Users\\thoma\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python39\\site-packages\\sklearn\\base.py:445: UserWarning: X does not have valid feature names, but KNeighborsClassifier was fitted with feature names\n",72      "  warnings.warn(\n",73      "C:\\Users\\thoma\\AppData\\Local\\Packages\\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\\LocalCache\\local-packages\\Python39\\site-packages\\sklearn\\base.py:445: UserWarning: X does not have valid feature names, but RobustScaler was fitted with feature names\n",74      "  warnings.warn(\n"75     ]76    }77   ],78   "source": [79    "year_input = 2024\n",80    "sport_id = 1\n",81    "\n",82    "df_pitcher_info = scrape.get_players(sport_id=sport_id, season=year_input).filter(\n",83    "    pl.col(\"position\").is_in(['P'])).sort(\"name\")\n",84    "\n",85    "player_input = 682243\n",86    "start_date = '2024-01-01'\n",87    "end_date = '2024-12-31'\n",88    "\n",89    "df_pitcher_info_select = df_pitcher_info.filter(pl.col(\"player_id\") == player_input)\n",90    "pitcher_id = df_pitcher_info_select.select(\"player_id\").to_numpy()[0][0]\n",91    "game_list  = scrape.get_player_games_list(sport_id = sport_id,\n",92    "                                          season = year_input, \n",93    "                                          player_id = pitcher_id,\n",94    "                                          start_date = start_date,\n",95    "                                          end_date = end_date)\n",96    "\n",97    "data_list = scrape.get_data(game_list_input = game_list[:])\n",98    "df = stuff_apply.stuff_apply(fe.feature_engineering(update.update(scrape.get_data_df(data_list = data_list).filter(\n",99    "                    (pl.col(\"pitcher_id\") == pitcher_id)&\n",100    "                    (pl.col(\"is_pitch\") == True))))).with_columns(\n",101    "        pl.col('pitch_type').count().over('pitch_type').alias('pitch_count')\n",102    "    )"103   ]104  },105  {106   "cell_type": "code",107   "execution_count": 4,108   "metadata": {},109   "outputs": [],110   "source": [111    "df.write_csv('pitcher_data_sample.csv')"112   ]113  },114  {115   "cell_type": "code",116   "execution_count": 3,117   "metadata": {},118   "outputs": [],119   "source": [120    "# import pandas as pd\n",121    "# import numpy as np\n",122    "# import json\n",123    "# from  matplotlib.ticker import FuncFormatter\n",124    "# from matplotlib.ticker import MaxNLocator\n",125    "# import math\n",126    "# from matplotlib.patches import Ellipse\n",127    "# import matplotlib.transforms as transforms\n",128    "# import matplotlib.colors\n",129    "# import matplotlib.colors as mcolors\n",130    "# import seaborn as sns\n",131    "# import matplotlib.pyplot as plt\n",132    "# import requests\n",133    "# import polars as pl\n",134    "# from PIL import Image\n",135    "# import requests\n",136    "# from io import BytesIO\n",137    "# from matplotlib.offsetbox import OffsetImage, AnnotationBbox\n",138    "\n",139    "# ### PITCH COLOURS ###\n",140    "\n",141    "# # Dictionary to map pitch types to their corresponding colors and names\n",142    "# pitch_colours = {\n",143    "#     ## Fastballs ##\n",144    "#     'FF': {'colour': '#FF007D', 'name': '4-Seam Fastball'},\n",145    "#     'FA': {'colour': '#FF007D', 'name': 'Fastball'},\n",146    "#     'SI': {'colour': '#98165D', 'name': 'Sinker'},\n",147    "#     'FC': {'colour': '#BE5FA0', 'name': 'Cutter'},\n",148    "\n",149    "#     ## Offspeed ##\n",150    "#     'CH': {'colour': '#F79E70', 'name': 'Changeup'},\n",151    "#     'FS': {'colour': '#FE6100', 'name': 'Splitter'},\n",152    "#     'SC': {'colour': '#F08223', 'name': 'Screwball'},\n",153    "#     'FO': {'colour': '#FFB000', 'name': 'Forkball'},\n",154    "\n",155    "#     ## Sliders ##\n",156    "#     'SL': {'colour': '#67E18D', 'name': 'Slider'},\n",157    "#     'ST': {'colour': '#1BB999', 'name': 'Sweeper'},\n",158    "#     'SV': {'colour': '#376748', 'name': 'Slurve'},\n",159    "\n",160    "#     ## Curveballs ##\n",161    "#     'KC': {'colour': '#311D8B', 'name': 'Knuckle Curve'},\n",162    "#     'CU': {'colour': '#3025CE', 'name': 'Curveball'},\n",163    "#     'CS': {'colour': '#274BFC', 'name': 'Slow Curve'},\n",164    "#     'EP': {'colour': '#648FFF', 'name': 'Eephus'},\n",165    "\n",166    "#     ## Others ##\n",167    "#     'KN': {'colour': '#867A08', 'name': 'Knuckleball'},\n",168    "#     'PO': {'colour': '#472C30', 'name': 'Pitch Out'},\n",169    "#     'UN': {'colour': '#9C8975', 'name': 'Unknown'},\n",170    "# }\n",171    "\n",172    "# # Create dictionaries for pitch types and their attributes\n",173    "# dict_colour = {key: value['colour'] for key, value in pitch_colours.items()}\n",174    "# dict_pitch = {key: value['name'] for key, value in pitch_colours.items()}\n",175    "# dict_pitch_desc_type = {value['name']: key for key, value in pitch_colours.items()}\n",176    "# dict_pitch_desc_type.update({'Four-Seam Fastball':'FF'})\n",177    "# dict_pitch_desc_type.update({'All':'All'})\n",178    "# dict_pitch_name = {value['name']: value['colour'] for key, value in pitch_colours.items()}\n",179    "# dict_pitch_name.update({'Four-Seam Fastball':'#FF007D'})\n",180    "\n",181    "# font_properties = {'family': 'calibi', 'size': 12}\n",182    "# font_properties_titles = {'family': 'calibi', 'size': 20}\n",183    "# font_properties_axes = {'family': 'calibi', 'size': 16}\n",184    "     \n",185    "# cmap_sum = matplotlib.colors.LinearSegmentedColormap.from_list(\"\", ['#648FFF','#FFFFFF','#FFB000',])\n",186    "\n",187    "# ### FANGRAPHS STATS DICT ###\n",188    "# fangraphs_stats_dict = {'IP':{'table_header':'$\\\\bf{IP}$','format':'.1f',} ,\n",189    "#  'TBF':{'table_header':'$\\\\bf{PA}$','format':'.0f',} ,\n",190    "#  'AVG':{'table_header':'$\\\\bf{AVG}$','format':'.3f',} ,\n",191    "#  'K/9':{'table_header':'$\\\\bf{K\\/9}$','format':'.2f',} ,\n",192    "#  'BB/9':{'table_header':'$\\\\bf{BB\\/9}$','format':'.2f',} ,\n",193    "#  'K/BB':{'table_header':'$\\\\bf{K\\/BB}$','format':'.2f',} ,\n",194    "#  'HR/9':{'table_header':'$\\\\bf{HR\\/9}$','format':'.2f',} ,\n",195    "#  'K%':{'table_header':'$\\\\bf{K\\%}$','format':'.1%',} ,\n",196    "#  'BB%':{'table_header':'$\\\\bf{BB\\%}$','format':'.1%',} ,\n",197    "#  'K-BB%':{'table_header':'$\\\\bf{K-BB\\%}$','format':'.1%',} ,\n",198    "#  'WHIP':{'table_header':'$\\\\bf{WHIP}$','format':'.2f',} ,\n",199    "#  'BABIP':{'table_header':'$\\\\bf{BABIP}$','format':'.3f',} ,\n",200    "#  'LOB%':{'table_header':'$\\\\bf{LOB\\%}$','format':'.1%',} ,\n",201    "#  'xFIP':{'table_header':'$\\\\bf{xFIP}$','format':'.2f',} ,\n",202    "#  'FIP':{'table_header':'$\\\\bf{FIP}$','format':'.2f',} ,\n",203    "#  'H':{'table_header':'$\\\\bf{H}$','format':'.0f',} ,\n",204    "#  '2B':{'table_header':'$\\\\bf{2B}$','format':'.0f',} ,\n",205    "#  '3B':{'table_header':'$\\\\bf{3B}$','format':'.0f',} ,\n",206    "#  'R':{'table_header':'$\\\\bf{R}$','format':'.0f',} ,\n",207    "#  'ER':{'table_header':'$\\\\bf{ER}$','format':'.0f',} ,\n",208    "#  'HR':{'table_header':'$\\\\bf{HR}$','format':'.0f',} ,\n",209    "#  'BB':{'table_header':'$\\\\bf{BB}$','format':'.0f',} ,\n",210    "#  'IBB':{'table_header':'$\\\\bf{IBB}$','format':'.0f',} ,\n",211    "#  'HBP':{'table_header':'$\\\\bf{HBP}$','format':'.0f',} ,\n",212    "#  'SO':{'table_header':'$\\\\bf{SO}$','format':'.0f',} ,\n",213    "#  'OBP':{'table_header':'$\\\\bf{OBP}$','format':'.0f',} ,\n",214    "#  'SLG':{'table_header':'$\\\\bf{SLG}$','format':'.0f',} ,\n",215    "#  'ERA':{'table_header':'$\\\\bf{ERA}$','format':'.2f',} ,\n",216    "#  'wOBA':{'table_header':'$\\\\bf{wOBA}$','format':'.3f',} ,\n",217    "#  'G':{'table_header':'$\\\\bf{G}$','format':'.0f',} }\n",218    "\n",219    "# ### GET COLOURS ###\n",220    "# def get_color(value, normalize, cmap_sum):\n",221    "#     \"\"\"\n",222    "#     Get the color corresponding to a value based on a colormap and normalization.\n",223    "\n",224    "#     Parameters\n",225    "#     ----------\n",226    "#     value : float\n",227    "#         The value to be mapped to a color.\n",228    "#     normalize : matplotlib.colors.Normalize\n",229    "#         The normalization function to scale the value.\n",230    "#     cmap_sum : matplotlib.colors.Colormap\n",231    "#         The colormap to use for mapping the value to a color.\n",232    "\n",233    "#     Returns\n",234    "#     -------\n",235    "#     str\n",236    "#         The hexadecimal color code corresponding to the value.\n",237    "#     \"\"\"\n",238    "#     color = cmap_sum(normalize(value))\n",239    "#     return mcolors.to_hex(color)\n",240    "\n",241    "# ### PITCH ELLIPSE ###\n",242    "# def confidence_ellipse(x, y, ax, n_std=3.0, facecolor='none', **kwargs):\n",243    "#     \"\"\"\n",244    "#     Create a plot of the covariance confidence ellipse of *x* and *y*.\n",245    "\n",246    "#     Parameters\n",247    "#     ----------\n",248    "#     x, y : array-like, shape (n, )\n",249    "#         Input data.\n",250    "\n",251    "#     ax : matplotlib.axes.Axes\n",252    "#         The axes object to draw the ellipse into.\n",253    "\n",254    "#     n_std : float\n",255    "#         The number of standard deviations to determine the ellipse's radiuses.\n",256    "\n",257    "#     **kwargs\n",258    "#         Forwarded to `~matplotlib.patches.Ellipse`\n",259    "\n",260    "#     Returns\n",261    "#     -------\n",262    "#     matplotlib.patches.Ellipse\n",263    "#     \"\"\"\n",264    "    \n",265    "#     if len(x) != len(y):\n",266    "#         raise ValueError(\"x and y must be the same size\")\n",267    "#     try:\n",268    "#         cov = np.cov(x, y)\n",269    "#         pearson = cov[0, 1]/np.sqrt(cov[0, 0] * cov[1, 1])\n",270    "#         # Using a special case to obtain the eigenvalues of this\n",271    "#         # two-dimensional dataset.\n",272    "#         ell_radius_x = np.sqrt(1 + pearson)\n",273    "#         ell_radius_y = np.sqrt(1 - pearson)\n",274    "#         ellipse = Ellipse((0, 0), width=ell_radius_x * 2, height=ell_radius_y * 2,\n",275    "#                         facecolor=facecolor,linewidth=2,linestyle='--', **kwargs)\n",276    "        \n",277    "\n",278    "#         # Calculating the standard deviation of x from\n",279    "#         # the squareroot of the variance and multiplying\n",280    "#         # with the given number of standard deviations.\n",281    "#         scale_x = np.sqrt(cov[0, 0]) * n_std\n",282    "#         mean_x = x.mean()\n",283    "        \n",284    "\n",285    "#         # calculating the standard deviation of y ...\n",286    "#         scale_y = np.sqrt(cov[1, 1]) * n_std\n",287    "#         mean_y = y.mean()\n",288    "        \n",289    "\n",290    "#         transf = transforms.Affine2D() \\\n",291    "#             .rotate_deg(45) \\\n",292    "#             .scale(scale_x, scale_y) \\\n",293    "#             .translate(mean_x, mean_y)\n",294    "        \n",295    "        \n",296    "\n",297    "#         ellipse.set_transform(transf + ax.transData)\n",298    "#     except ValueError:\n",299    "#          return    \n",300    "        \n",301    "#     return ax.add_patch(ellipse)     \n",302    "# ### VELOCITY KDES ###\n",303    "# def velocity_kdes(df: pl.DataFrame,\n",304    "#                     ax: plt.Axes,\n",305    "#                     gs: GridSpec,\n",306    "#                     gs_x: list,\n",307    "#                     gs_y: list,\n",308    "#                     fig: plt.Figure):\n",309    "#     \"\"\"\n",310    "#     Plot the velocity KDEs for different pitch types.\n",311    "\n",312    "#     Parameters\n",313    "#     ----------\n",314    "#     df : pl.DataFrame\n",315    "#         The DataFrame containing pitch data.\n",316    "#     ax : plt.Axes\n",317    "#         The axis to plot on.\n",318    "#     gs : GridSpec\n",319    "#         The GridSpec for the subplot layout.\n",320    "#     gs_x : list\n",321    "#         The x-coordinates for the GridSpec.\n",322    "#     gs_y : list\n",323    "#         The y-coordinates for the GridSpec.\n",324    "#     fig : plt.Figure\n",325    "#         The figure to plot on.\n",326    "#     \"\"\"\n",327    "#     # Join the original DataFrame on 'pitch_type' with sorted counts to reorder\n",328    "#     items_in_order = (df\n",329    "#                         .sort(\"pitch_count\", descending=True)['pitch_type']\n",330    "#                         .unique(maintain_order=True)\n",331    "#                         .to_numpy()\n",332    "#                         )\n",333    "\n",334    "#     # Create the inner subplot inside the outer subplot\n",335    "#     import matplotlib.gridspec as gridspec\n",336    "#     ax.axis('off')\n",337    "#     ax.set_title('Pitch Velocity Distribution', fontdict={'family': 'calibi', 'size': 20})\n",338    "\n",339    "#     inner_grid_1 = gridspec.GridSpecFromSubplotSpec(len(items_in_order), 1, subplot_spec=gs[gs_x[0]:gs_x[-1], gs_y[0]:gs_y[-1]])\n",340    "#     ax_top = [fig.add_subplot(inner) for inner in inner_grid_1]\n",341    "\n",342    "#     for idx, i in enumerate(items_in_order):\n",343    "#         pitch_data = df.filter(pl.col('pitch_type') == i)['start_speed']\n",344    "#         if np.unique(pitch_data).size == 1:  # Check if all values are the same\n",345    "#             ax_top[idx].plot([np.unique(pitch_data), np.unique(pitch_data)], [0, 1], linewidth=4,\n",346    "#                                 color=dict_colour[i], zorder=20)\n",347    "#         else:\n",348    "#             sns.kdeplot(pitch_data, ax=ax_top[idx], fill=True,\n",349    "#                         clip=(pitch_data.min(), pitch_data.max()),\n",350    "#                         color=dict_colour[i])\n",351    "#         ax_top[idx].set_xlim(math.floor(df['start_speed'].min() / 5) * 5, math.ceil(df['start_speed'].max() / 5) * 5)\n",352    "#         ax_top[idx].set_xlabel('')\n",353    "#         ax_top[idx].set_ylabel('')\n",354    "#         if idx < len(items_in_order) - 1:\n",355    "#             ax_top[idx].spines['top'].set_visible(False)\n",356    "#             ax_top[idx].spines['right'].set_visible(False)\n",357    "#             ax_top[idx].spines['left'].set_visible(False)\n",358    "#             ax_top[idx].tick_params(axis='x', colors='none')\n",359    "\n",360    "#         ax_top[idx].set_xticks(range(math.floor(df['start_speed'].min() / 5) * 5, math.ceil(df['start_speed'].max() / 5) * 5, 5))\n",361    "#         ax_top[idx].set_yticks([])\n",362    "#         ax_top[idx].grid(axis='x', linestyle='--')\n",363    "#         ax_top[idx].text(-0.01, 0.5, i, transform=ax_top[idx].transAxes,\n",364    "#                             fontsize=14, va='center', ha='right')\n",365    "\n",366    "#     ax_top[-1].spines['top'].set_visible(False)\n",367    "#     ax_top[-1].spines['right'].set_visible(False)\n",368    "#     ax_top[-1].spines['left'].set_visible(False)\n",369    "#     ax_top[-1].set_xticks(list(range(math.floor(df['start_speed'].min() / 5) * 5, math.ceil(df['start_speed'].max() / 5) * 5, 5)))\n",370    "#     ax_top[-1].set_xlabel('Velocity (mph)')\n",371    "\n",372    "\n",373    "# ### TJ STUFF+ ROLLING ###\n",374    "# def tj_stuff_roling(df: pl.DataFrame, window: int, ax: plt.Axes):\n",375    "#     \"\"\"\n",376    "#     Plot the rolling average of tjStuff+ for different pitch types.\n",377    "\n",378    "#     Parameters\n",379    "#     ----------\n",380    "#     df : pl.DataFrame\n",381    "#         The DataFrame containing pitch data.\n",382    "#     window : int\n",383    "#         The window size for calculating the rolling average.\n",384    "#     ax : plt.Axes\n",385    "#         The axis to plot on.\n",386    "#     \"\"\"\n",387    "#     # Join the original DataFrame on 'pitch_type' with sorted counts to reorder\n",388    "#     items_in_order = (\n",389    "#         df.sort(\"pitch_count\", descending=True)['pitch_type']\n",390    "#         .unique(maintain_order=True)\n",391    "#         .to_numpy()\n",392    "#     )\n",393    "\n",394    "#     # Plot the rolling average for each pitch type\n",395    "#     for i in items_in_order:\n",396    "#         if max(df.filter(pl.col('pitch_type') == i)['pitch_count']) >= window:\n",397    "#             sns.lineplot(\n",398    "#                 x=range(1, max(df.filter(pl.col('pitch_type') == i)['pitch_count']) + 1),\n",399    "#                 y=df.filter(pl.col('pitch_type') == i)['tj_stuff_plus'].rolling_mean(window),\n",400    "#                 color=dict_colour[i],\n",401    "#                 ax=ax,\n",402    "#                 linewidth=3\n",403    "#             )\n",404    "\n",405    "#     # Adjust x-axis limits to start from 1\n",406    "#     ax.set_xlim(window, max(df['pitch_count']))\n",407    "#     ax.set_ylim(70, 130)\n",408    "#     ax.set_xlabel('Pitches', fontdict=font_properties_axes)\n",409    "#     ax.set_ylabel('tjStuff+', fontdict=font_properties_axes)\n",410    "#     ax.set_title(f\"{window} Pitch Rolling tjStuff+\", fontdict=font_properties_titles)\n",411    "#     ax.xaxis.set_major_locator(MaxNLocator(integer=True))\n",412    "\n",413    "\n",414    "# ### TJ STUFF+ ROLLING ###\n",415    "# def tj_stuff_roling_game(df: pl.DataFrame, window: int, ax: plt.Axes):\n",416    "#     \"\"\"\n",417    "#     Plot the rolling average of tjStuff+ for different pitch types over games.\n",418    "\n",419    "#     Parameters\n",420    "#     ----------\n",421    "#     df : pl.DataFrame\n",422    "#         The DataFrame containing pitch data.\n",423    "#     window : int\n",424    "#         The window size for calculating the rolling average.\n",425    "#     ax : plt.Axes\n",426    "#         The axis to plot on.\n",427    "#     \"\"\"\n",428    "#     # Map game_id to sequential numbers\n",429    "#     date_to_number = {date: i + 1 for i, date in enumerate(df['game_id'].unique(maintain_order=True))}\n",430    "\n",431    "#     # Add a column with the sequential game numbers\n",432    "#     df_plot = df.with_columns(\n",433    "#         pl.col(\"game_id\").map_elements(lambda x: date_to_number.get(x, x)).alias(\"start_number\")\n",434    "#     )\n",435    "\n",436    "#     # Group by relevant columns and calculate mean tj_stuff_plus\n",437    "#     plot_game_roll = df_plot.group_by(['start_number', 'game_id', 'game_date', 'pitch_type', 'pitch_description']).agg(\n",438    "#         pl.col('tj_stuff_plus').mean().alias('tj_stuff_plus')\n",439    "#     ).sort('start_number', descending=False)\n",440    "\n",441    "#     # Get the list of pitch types ordered by frequency\n",442    "#     sorted_value_counts = df['pitch_type'].value_counts().sort('count', descending=True)\n",443    "#     items_in_order = sorted_value_counts['pitch_type'].to_list()\n",444    "\n",445    "#     # Plot the rolling average for each pitch type\n",446    "#     for i in items_in_order:\n",447    "#         df_item = plot_game_roll.filter(pl.col('pitch_type') == i)\n",448    "#         df_item = df_item.with_columns(\n",449    "#             pl.col(\"start_number\").cast(pl.Int64)\n",450    "#         ).join(\n",451    "#             pl.DataFrame({\"start_number\": list(date_to_number.values())}),\n",452    "#             on=\"start_number\",\n",453    "#             how=\"outer\"\n",454    "#         ).sort(\"start_number_right\").with_columns([\n",455    "#             pl.col(\"start_number\").fill_null(strategy=\"forward\").fill_null(strategy=\"backward\"),\n",456    "#             pl.col(\"tj_stuff_plus\").fill_null(strategy=\"forward\").fill_null(strategy=\"backward\"),\n",457    "#             pl.col(\"pitch_type\").fill_null(strategy=\"forward\").fill_null(strategy=\"backward\"),\n",458    "#             pl.col(\"pitch_description\").fill_null(strategy=\"forward\").fill_null(strategy=\"backward\")\n",459    "#         ])\n",460    "\n",461    "#         sns.lineplot(x=range(1, max(df_item['start_number_right']) + 1),\n",462    "#                      y=df_item.filter(pl.col('pitch_type') == i)['tj_stuff_plus'].rolling_mean(window),\n",463    "#                      color=dict_colour[i],\n",464    "#                      ax=ax, linewidth=3)\n",465    "\n",466    "#         # Highlight missing game data points\n",467    "#         for n in range(len(df_item)):\n",468    "#             if df_item['game_id'].is_null()[n]:\n",469    "#                 sns.scatterplot(x=[df_item['start_number_right'][n]],\n",470    "#                                 y=[df_item['tj_stuff_plus'][n]],\n",471    "#                                 color='white',\n",472    "#                                 ec='black',\n",473    "#                                 ax=ax,\n",474    "#                                 zorder=100)\n",475    "\n",476    "#     # Adjust x-axis limits to start from 1\n",477    "#     ax.set_xlim(window, max(df_item['start_number']))\n",478    "#     ax.set_ylim(70, 130)\n",479    "#     ax.set_xlabel('Games', fontdict=font_properties_axes)\n",480    "#     ax.set_ylabel('tjStuff+', fontdict=font_properties_axes)\n",481    "#     ax.set_title(f\"{window} Game Rolling tjStuff+\", fontdict=font_properties_titles)\n",482    "#     ax.xaxis.set_major_locator(MaxNLocator(integer=True))\n",483    "\n",484    "\n",485    "# def break_plot(df: pl.DataFrame, ax: plt.Axes):\n",486    "#     \"\"\"\n",487    "#     Plot the pitch breaks for different pitch types.\n",488    "\n",489    "#     Parameters\n",490    "#     ----------\n",491    "#     df : pl.DataFrame\n",492    "#         The DataFrame containing pitch data.\n",493    "#     ax : plt.Axes\n",494    "#         The axis to plot on.\n",495    "#     \"\"\"\n",496    "#     # Get unique pitch types sorted by pitch count\n",497    "#     label_labels = df.sort(by=['pitch_count', 'pitch_type'], descending=[False, True])['pitch_type'].unique(maintain_order=True).to_numpy()\n",498    "\n",499    "#     # Plot confidence ellipses for each pitch type\n",500    "#     for idx, label in enumerate(label_labels):\n",501    "#         subset = df.filter(pl.col('pitch_type') == label)\n",502    "#         if len(subset) > 4:\n",503    "#             try:\n",504    "#                 confidence_ellipse(subset['hb'], subset['ivb'], ax=ax, edgecolor=dict_colour[label], n_std=2, facecolor=dict_colour[label], alpha=0.2)\n",505    "#             except ValueError:\n",506    "#                 return\n",507    "\n",508    "#     # Plot scatter plot for pitch breaks\n",509    "#     if df['pitcher_hand'][0] == 'R':\n",510    "#         sns.scatterplot(ax=ax, x=df['hb'], y=df['ivb'] * 1, hue=df['pitch_type'], palette=dict_colour, ec='black', alpha=1, zorder=2)\n",511    "#     if df['pitcher_hand'][0] == 'L':\n",512    "#         sns.scatterplot(ax=ax, x=df['hb'], y=df['ivb'] * 1, hue=df['pitch_type'], palette=dict_colour, ec='black', alpha=1, zorder=2)\n",513    "\n",514    "#     # Set axis limits\n",515    "#     ax.set_xlim((-25, 25))\n",516    "#     ax.set_ylim((-25, 25))\n",517    "\n",518    "#     # Add horizontal and vertical lines\n",519    "#     ax.hlines(y=0, xmin=-50, xmax=50, color=colour_palette[8], alpha=0.5, linestyles='--', zorder=1)\n",520    "#     ax.vlines(x=0, ymin=-50, ymax=50, color=colour_palette[8], alpha=0.5, linestyles='--', zorder=1)\n",521    "\n",522    "#     # Set axis labels and title\n",523    "#     ax.set_xlabel('Horizontal Break (in)', fontdict=font_properties_axes)\n",524    "#     ax.set_ylabel('Induced Vertical Break (in)', fontdict=font_properties_axes)\n",525    "#     ax.set_title(\"Pitch Breaks\", fontdict=font_properties_titles)\n",526    "\n",527    "#     # Remove legend\n",528    "#     ax.get_legend().remove()\n",529    "\n",530    "#     # Set tick labels\n",531    "#     ax.set_xticklabels(ax.get_xticks(), fontdict=font_properties)\n",532    "#     ax.set_yticklabels(ax.get_yticks(), fontdict=font_properties)\n",533    "\n",534    "#     # Add text annotations for glove side and arm side\n",535    "#     if df['pitcher_hand'][0] == 'R':\n",536    "#         ax.text(-24.5, -24.5, s='← Glove Side', fontstyle='italic', ha='left', va='bottom',\n",537    "#                 bbox=dict(facecolor='white', edgecolor='black'), fontsize=12, zorder=3)\n",538    "#         ax.text(24.5, -24.5, s='Arm Side →', fontstyle='italic', ha='right', va='bottom',\n",539    "#                 bbox=dict(facecolor='white', edgecolor='black'), fontsize=12, zorder=3)\n",540    "#     if df['pitcher_hand'][0] == 'L':\n",541    "#         ax.invert_xaxis()\n",542    "#         ax.text(24.5, -24.5, s='← Arm Side', fontstyle='italic', ha='left', va='bottom',\n",543    "#                 bbox=dict(facecolor='white', edgecolor='black'), fontsize=12, zorder=3)\n",544    "#         ax.text(-24.5, -24.5, s='Glove Side →', fontstyle='italic', ha='right', va='bottom',\n",545    "#                 bbox=dict(facecolor='white', edgecolor='black'), fontsize=12, zorder=3)\n",546    "\n",547    "#     # Set aspect ratio and format axis ticks\n",548    "#     ax.set_aspect('equal', adjustable='box')\n",549    "#     ax.xaxis.set_major_formatter(FuncFormatter(lambda x, _: int(x)))\n",550    "#     ax.yaxis.set_major_formatter(FuncFormatter(lambda x, _: int(x)))\n",551    "\n",552    "# # DEFINE STRIKE ZONE\n",553    "# strike_zone = pl.DataFrame({\n",554    "#     'PlateLocSide': [-0.9, -0.9, 0.9, 0.9, -0.9],\n",555    "#     'PlateLocHeight': [1.5, 3.5, 3.5, 1.5, 1.5]\n",556    "# })\n",557    "\n",558    "# ### STRIKE ZONE ###\n",559    "# def draw_line(axis, alpha_spot=1, catcher_p=True):\n",560    "#     \"\"\"\n",561    "#     Draw the strike zone and home plate on the given axis.\n",562    "\n",563    "#     Parameters\n",564    "#     ----------\n",565    "#     axis : matplotlib.axes.Axes\n",566    "#         The axis to draw the strike zone on.\n",567    "#     alpha_spot : float, optional\n",568    "#         The transparency level of the lines (default is 1).\n",569    "#     catcher_p : bool, optional\n",570    "#         Whether to draw the catcher's perspective (default is True).\n",571    "#     \"\"\"\n",572    "#     # Draw the strike zone\n",573    "#     axis.plot(strike_zone['PlateLocSide'].to_list(), strike_zone['PlateLocHeight'].to_list(), \n",574    "#               color='black', linewidth=1.3, zorder=3, alpha=alpha_spot)\n",575    "\n",576    "#     if catcher_p:\n",577    "#         # Draw home plate from catcher's perspective\n",578    "#         axis.plot([-0.708, 0.708], [0.15, 0.15], color='black', linewidth=1, alpha=alpha_spot, zorder=1)\n",579    "#         axis.plot([-0.708, -0.708], [0.15, 0.3], color='black', linewidth=1, alpha=alpha_spot, zorder=1)\n",580    "#         axis.plot([-0.708, 0], [0.3, 0.5], color='black', linewidth=1, alpha=alpha_spot, zorder=1)\n",581    "#         axis.plot([0, 0.708], [0.5, 0.3], color='black', linewidth=1, alpha=alpha_spot, zorder=1)\n",582    "#         axis.plot([0.708, 0.708], [0.3, 0.15], color='black', linewidth=1, alpha=alpha_spot, zorder=1)\n",583    "#     else:\n",584    "#         # Draw home plate from pitcher's perspective\n",585    "#         axis.plot([-0.708, 0.708], [0.4, 0.4], color='black', linewidth=1, alpha=alpha_spot, zorder=1)\n",586    "#         axis.plot([-0.708, -0.9], [0.4, -0.1], color='black', linewidth=1, alpha=alpha_spot, zorder=1)\n",587    "#         axis.plot([-0.9, 0], [-0.1, -0.35], color='black', linewidth=1, alpha=alpha_spot, zorder=1)\n",588    "#         axis.plot([0, 0.9], [-0.35, -0.1], color='black', linewidth=1, alpha=alpha_spot, zorder=1)\n",589    "#         axis.plot([0.9, 0.708], [-0.1, 0.4], color='black', linewidth=1, alpha=alpha_spot, zorder=1)\n",590    "\n",591    "# def location_plot(df: pl.DataFrame, ax: plt.Axes, hand: str):\n",592    "#     \"\"\"\n",593    "#     Plot the pitch locations for different pitch types against a specific batter hand.\n",594    "\n",595    "#     Parameters\n",596    "#     ----------\n",597    "#     df : pl.DataFrame\n",598    "#         The DataFrame containing pitch data.\n",599    "#     ax : plt.Axes\n",600    "#         The axis to plot on.\n",601    "#     hand : str\n",602    "#         The batter hand ('L' for left-handed, 'R' for right-handed).\n",603    "#     \"\"\"\n",604    "#     # Get unique pitch types sorted by pitch count\n",605    "#     label_labels = df.sort(by=['pitch_count', 'pitch_type'], descending=[False, True])['pitch_type'].unique(maintain_order=True).to_numpy()\n",606    "\n",607    "#     # Plot confidence ellipses for each pitch type\n",608    "#     for label in label_labels:\n",609    "#         subset = df.filter((pl.col('pitch_type') == label) & (pl.col('batter_hand') == hand))\n",610    "#         if len(subset) >= 5:\n",611    "#             confidence_ellipse(subset['px'], subset['pz'], ax=ax, edgecolor=dict_colour[label], n_std=1.5, facecolor=dict_colour[label], alpha=0.3)\n",612    "\n",613    "#     # Group pitch locations by pitch type and calculate mean values\n",614    "#     pitch_location_group = (\n",615    "#         df.filter(pl.col(\"batter_hand\") == hand)\n",616    "#         .group_by(\"pitch_type\")\n",617    "#         .agg([\n",618    "#             pl.col(\"start_speed\").count().alias(\"pitches\"),\n",619    "#             pl.col(\"px\").mean().alias(\"px\"),\n",620    "#             pl.col(\"pz\").mean().alias(\"pz\")\n",621    "#         ])\n",622    "#     )\n",623    "\n",624    "#     # Calculate pitch percentages\n",625    "#     total_pitches = pitch_location_group['pitches'].sum()\n",626    "#     pitch_location_group = pitch_location_group.with_columns(\n",627    "#         (pl.col(\"pitches\") / total_pitches).alias(\"pitch_percent\")\n",628    "#     )\n",629    "\n",630    "#     # Plot pitch locations\n",631    "#     sns.scatterplot(ax=ax, x=pitch_location_group['px'], y=pitch_location_group['pz'],\n",632    "#                     hue=pitch_location_group['pitch_type'], palette=dict_colour, ec='black',\n",633    "#                     s=pitch_location_group['pitch_percent'] * 750, linewidth=2, zorder=2)\n",634    "\n",635    "#     # Customize plot appearance\n",636    "#     ax.axis('square')\n",637    "#     draw_line(ax, alpha_spot=0.75, catcher_p=False)\n",638    "#     ax.axis('off')\n",639    "#     ax.set_xlim((-2.75, 2.75))\n",640    "#     ax.set_ylim((-0.5, 5))\n",641    "#     if len(pitch_location_group['px']) > 0:\n",642    "#         ax.get_legend().remove()\n",643    "#     ax.grid(False)\n",644    "#     ax.set_title(f\"Pitch Locations vs {hand}HB\\n{pitch_location_group['pitches'].sum()} Pitches\", fontdict=font_properties_titles)\n",645    "\n",646    "\n",647    "# def summary_table(df: pl.DataFrame, ax: plt.Axes):\n",648    "#     \"\"\"\n",649    "#     Create a summary table of pitch data.\n",650    "\n",651    "#     Parameters\n",652    "#     ----------\n",653    "#     df : pl.DataFrame\n",654    "#         The DataFrame containing pitch data.\n",655    "#     ax : plt.Axes\n",656    "#         The axis to plot the table on.\n",657    "#     \"\"\"\n",658    "#     # Aggregate pitch data by pitch description\n",659    "#     df_agg = df.group_by(\"pitch_description\").agg(\n",660    "#         pl.col('is_pitch').sum().alias('count'),\n",661    "#         (pl.col('is_pitch').sum() / df.select(pl.col('is_pitch').sum())).alias('count_percent'),\n",662    "#         pl.col('start_speed').mean().alias('start_speed'),\n",663    "#         pl.col('ivb').mean().alias('ivb'),\n",664    "#         pl.col('hb').mean().alias('hb'),\n",665    "#         pl.col('spin_rate').mean().alias('spin_rate'),\n",666    "#         pl.col('vaa').mean().alias('vaa'),\n",667    "#         pl.col('haa').mean().alias('haa'),\n",668    "#         pl.col('x0').mean().alias('x0'),\n",669    "#         pl.col('z0').mean().alias('z0'),\n",670    "#         pl.col('extension').mean().alias('extension'),\n",671    "#         (((pl.col('spin_direction').mean() + 180) % 360 // 30) + \n",672    "#         (((pl.col('spin_direction').mean() + 180) % 360 % 30 / 30 / 100 * 60).round(2) * 10).round(0) // 1.5 / 4)\n",673    "#         .cast(pl.Float64).map_elements(lambda x: f\"{int(x)}:{int((x % 1) * 60):02d}\", return_dtype=pl.Utf8).alias('clock_time'),\n",674    "#         pl.col('tj_stuff_plus').mean().alias('tj_stuff_plus'),\n",675    "#         pl.col('pitch_grade').mean().alias('pitch_grade'),\n",676    "#         (pl.col('in_zone').sum() / pl.col('is_pitch').sum()).alias('zone_percent'),\n",677    "#         (pl.col('ozone_swing').sum() / pl.col('out_zone').sum()).alias('chase_percent'),\n",678    "#         (pl.col('whiffs').sum() / pl.col('swings').sum()).alias('whiff_percent'),\n",679    "#         (pl.col('woba_pred_contact').sum() / pl.col('bip').sum()).alias('xwobacon')\n",680    "#     ).sort(\"count\", descending=True)\n",681    "\n",682    "#     # Aggregate all pitch data\n",683    "#     df_agg_all = df.group_by(pl.lit(\"All\").alias(\"pitch_description\")).agg(\n",684    "#         pl.col('is_pitch').sum().alias('count'),\n",685    "#         (pl.col('is_pitch').sum() / df.select(pl.col('is_pitch').sum())).alias('count_percent'),\n",686    "#         pl.lit(None).alias('start_speed'),\n",687    "#         pl.lit(None).alias('ivb'),\n",688    "#         pl.lit(None).alias('hb'),\n",689    "#         pl.lit(None).alias('spin_rate'),\n",690    "#         pl.lit(None).alias('vaa'),\n",691    "#         pl.lit(None).alias('haa'),\n",692    "#         pl.lit(None).alias('x0'),\n",693    "#         pl.lit(None).alias('z0'),\n",694    "#         pl.col('extension').mean().alias('extension'),\n",695    "#         pl.lit(None).alias('clock_time'),\n",696    "#         pl.col('tj_stuff_plus').mean().alias('tj_stuff_plus'),\n",697    "#         pl.lit(None).alias('pitch_grade'),\n",698    "#         (pl.col('in_zone').sum() / pl.col('is_pitch').sum()).alias('zone_percent'),\n",699    "#         (pl.col('ozone_swing').sum() / pl.col('out_zone').sum()).alias('chase_percent'),\n",700    "#         (pl.col('whiffs').sum() / pl.col('swings').sum()).alias('whiff_percent'),\n",701    "#         (pl.col('woba_pred_contact').sum() / pl.col('bip').sum()).alias('xwobacon')\n",702    "#     )\n",703    "\n",704    "#     # Concatenate aggregated data\n",705    "#     df_agg = pl.concat([df_agg, df_agg_all]).fill_nan(None)\n",706    "\n",707    "#     # Load statcast pitch summary data\n",708    "#     statcast_pitch_summary = pl.read_csv('functions/statcast_2024_grouped.csv')\n",709    "\n",710    "#     # Create table\n",711    "#     table = ax.table(cellText=df_agg.fill_nan('—').fill_null('—').to_numpy(), colLabels=df_agg.columns, cellLoc='center',\n",712    "#                      colWidths=[2.3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], bbox=[0.0, 0, 1, 0.8])\n",713    "\n",714    "#     # Set table properties\n",715    "#     min_font_size = 14\n",716    "#     table.auto_set_font_size(False)\n",717    "#     table.set_fontsize(min_font_size)\n",718    "#     table.scale(1, 0.5)\n",719    "\n",720    "#     # Set font size for values\n",721    "#     min_font_size = 18\n",722    "#     for i in range(len(df_agg) + 1):\n",723    "#         for j in range(len(df_agg.columns)):\n",724    "#             if i > 0:  # Skip the header row\n",725    "#                 cell = table.get_celld()[i, j]\n",726    "#                 cell.set_fontsize(min_font_size)\n",727    "\n",728    "#     # Define color maps\n",729    "#     cmap_sum = mcolors.LinearSegmentedColormap.from_list(\"\", ['#648FFF', '#FFFFFF', '#FFB000'])\n",730    "#     cmap_sum_r = mcolors.LinearSegmentedColormap.from_list(\"\", ['#FFB000', '#FFFFFF', '#648FFF'])\n",731    "\n",732    "#     # Update table cells with colors and text properties\n",733    "#     for i in range(len(df_agg)):\n",734    "#         pitch_check = dict_pitch_desc_type[df_agg['pitch_description'][i]]\n",735    "#         cell_text = table.get_celld()[(i + 1, 0)].get_text().get_text()\n",736    "\n",737    "#         if cell_text != 'All':\n",738    "#             table.get_celld()[(i + 1, 0)].set_facecolor(dict_pitch_name[cell_text])\n",739    "#             text_props = {'color': '#000000', 'fontweight': 'bold'} if cell_text in ['Split-Finger', 'Slider', 'Changeup'] else {'color': '#ffffff', 'fontweight': 'bold'}\n",740    "#             table.get_celld()[(i + 1, 0)].set_text_props(**text_props)\n",741    "#             if cell_text == 'Four-Seam Fastball':\n",742    "#                 table.get_celld()[(i + 1, 0)].get_text().set_text('4-Seam')\n",743    "\n",744    "#         select_df = statcast_pitch_summary.filter(statcast_pitch_summary['pitch_type'] == pitch_check)\n",745    "\n",746    "#         # Apply color to specific columns based on normalized values\n",747    "#         columns_to_color = [(3, 'release_speed', 0.95, 1.05), (11, 'release_extension', 0.9, 1.1), (13, None, 80, 120), \n",748    "#                             (14, None, 30, 70), (15, 'in_zone_rate', 0.7, 1.3), (16, 'chase_rate', 0.7, 1.3), \n",749    "#                             (17, 'whiff_rate', 0.7, 1.3), (18, 'xwoba', 0.7, 1.3)]\n",750    "\n",751    "#         for col, stat, vmin_factor, vmax_factor in columns_to_color:\n",752    "#             cell_value = table.get_celld()[(i + 1, col)].get_text().get_text()\n",753    "#             if cell_value != '—':\n",754    "#                 vmin = select_df[stat].mean() * vmin_factor if stat else vmin_factor\n",755    "#                 vmax = select_df[stat].mean() * vmax_factor if stat else vmax_factor\n",756    "#                 normalize = mcolors.Normalize(vmin=vmin, vmax=vmax)\n",757    "#                 cmap = cmap_sum if col != 18 else cmap_sum_r\n",758    "#                 table.get_celld()[(i + 1, col)].set_facecolor(get_color(float(cell_value.strip('%')), normalize, cmap))\n",759    "\n",760    "#     # Set header text properties\n",761    "#     table.get_celld()[(len(df_agg), 0)].set_text_props(color='#000000', fontweight='bold')\n",762    "\n",763    "#     # Update column names\n",764    "#     new_column_names = ['$\\\\bf{Pitch\\\\ Name}$', '$\\\\bf{Count}$', '$\\\\bf{Pitch\\\\%}$', '$\\\\bf{Velocity}$', '$\\\\bf{iVB}$', \n",765    "#                         '$\\\\bf{HB}$', '$\\\\bf{Spin}$', '$\\\\bf{VAA}$', '$\\\\bf{HAA}$', '$\\\\bf{vRel}$', '$\\\\bf{hRel}$', \n",766    "#                         '$\\\\bf{Ext.}$', '$\\\\bf{Axis}$', '$\\\\bf{tjStuff+}$', '$\\\\bf{Grade}$', '$\\\\bf{Zone\\\\%}$', \n",767    "#                         '$\\\\bf{Chase\\\\%}$', '$\\\\bf{Whiff\\\\%}$', '$\\\\bf{xwOBA}$\\n$\\\\bf{Contact}$']\n",768    "\n",769    "#     for i, col_name in enumerate(new_column_names):\n",770    "#         table.get_celld()[(0, i)].get_text().set_text(col_name)\n",771    "\n",772    "#     # Format cell values\n",773    "#     def format_cells(columns, fmt):\n",774    "#         for col in columns:\n",775    "#             col_idx = df_agg.columns.index(col)\n",776    "#             for row in range(1, len(df_agg) + 1):\n",777    "#                 cell_value = table.get_celld()[(row, col_idx)].get_text().get_text()\n",778    "#                 if cell_value != '—':\n",779    "#                     table.get_celld()[(row, col_idx)].get_text().set_text(fmt.format(float(cell_value.strip('%'))))\n",780    "\n",781    "#     format_cells(['start_speed', 'ivb', 'hb', 'vaa', 'haa', 'z0', 'x0', 'extension'], '{:,.1f}')\n",782    "#     format_cells(['xwobacon'], '{:,.3f}')\n",783    "#     format_cells(['count_percent', 'zone_percent', 'chase_percent', 'whiff_percent'], '{:,.1%}')\n",784    "#     format_cells(['tj_stuff_plus', 'pitch_grade', 'spin_rate'], '{:,.0f}')\n",785    "\n",786    "#     # Create legend for pitch types\n",787    "#     items_in_order = (df.sort(\"pitch_count\", descending=True)['pitch_type'].unique(maintain_order=True).to_numpy())\n",788    "#     colour_pitches = [dict_colour[x] for x in items_in_order]\n",789    "#     label = [dict_pitch[x] for x in items_in_order]\n",790    "#     handles = [plt.scatter([], [], color=color, marker='o', s=100) for color in colour_pitches]\n",791    "#     if len(label) > 5:\n",792    "#         ax.legend(handles, label, bbox_to_anchor=(0.1, 0.81, 0.8, 0.14), ncol=5,\n",793    "#                   fancybox=True, loc='lower center', fontsize=16, framealpha=1.0, markerscale=1.7, prop={'family': 'calibi', 'size': 16})\n",794    "#     else:\n",795    "#         ax.legend(handles, label, bbox_to_anchor=(0.1, 0.81, 0.8, 0.14), ncol=5,\n",796    "#                   fancybox=True, loc='lower center', fontsize=20, framealpha=1.0, markerscale=2, prop={'family': 'calibi', 'size': 20})\n",797    "#     ax.axis('off')\n",798    "\n",799    "# def plot_footer(ax:plt.Axes):\n",800    "#    # Add footer text\n",801    "#     ax.text(0, 1, 'By: @TJStats', ha='left', va='top', fontsize=24)\n",802    "#     ax.text(0.5, 0.25, \n",803    "#                 '''\n",804    "#                 Colour Coding Compares to League Average By Pitch\n",805    "#                    tjStuff+ calculates the Expected Run Value (xRV) of a pitch regardless of type\n",806    "#                    tjStuff+ is normally distributed, where 100 is the mean and Standard Deviation is 10\n",807    "#                    Pitch Grade scales tjStuff+ to the traditional 20-80 Scouting Scale for a given pitch type\n",808    "#                    ''', \n",809    "#                   ha='center', va='bottom', fontsize=16)\n",810    "#     ax.text(1, 1, 'Data: MLB, Fangraphs\\nImages: MLB, ESPN', ha='right', va='top', fontsize=24)\n",811    "#     ax.axis('off')\n",812    "\n",813    "\n",814    "# # Function to get an image from a URL and display it on the given axis\n",815    "# def player_headshot(player_input: str, ax: plt.Axes):\n",816    "#     # Construct the URL for the player's headshot image\n",817    "#     url = f'https://img.mlbstatic.com/mlb-photos/image/'\\\n",818    "#           f'upload/d_people:generic:headshot:67:current.png'\\\n",819    "#           f'/w_640,q_auto:best/v1/people/{player_input}/headshot/silo/current.png'\n",820    "\n",821    "#     # Send a GET request to the URL\n",822    "#     response = requests.get(url)\n",823    "\n",824    "#     # Open the image from the response content\n",825    "#     img = Image.open(BytesIO(response.content))\n",826    "\n",827    "\n",828    "#     # Display the image on the axis\n",829    "#     ax.set_xlim(0, 1.3)\n",830    "#     ax.set_ylim(0, 1)\n",831    "#     ax.imshow(img, extent=[0, 1, 0, 1], origin='upper')\n",832    "\n",833    "#     # Turn off the axis\n",834    "#     ax.axis('off')\n",835    "\n",836    "\n",837    "# def player_bio(pitcher_id: str, ax: plt.Axes):\n",838    "#     # Construct the URL to fetch player data\n",839    "#     url = f\"https://statsapi.mlb.com/api/v1/people?personIds={pitcher_id}&hydrate=currentTeam\"\n",840    "\n",841    "#     # Send a GET request to the URL and parse the JSON response\n",842    "#     data = requests.get(url).json()\n",843    "\n",844    "#     # Extract player information from the JSON data\n",845    "#     player_name = data['people'][0]['fullName']\n",846    "#     pitcher_hand = data['people'][0]['pitchHand']['code']\n",847    "#     age = data['people'][0]['currentAge']\n",848    "#     height = data['people'][0]['height']\n",849    "#     weight = data['people'][0]['weight']\n",850    "\n",851    "#     # Display the player's name, handedness, age, height, and weight on the axis\n",852    "#     ax.text(0.5, 1, f'{player_name}', va='top', ha='center', fontsize=56)\n",853    "#     ax.text(0.5, 0.7, f'{pitcher_hand}HP, Age:{age}, {height}/{weight}', va='top', ha='center', fontsize=30)\n",854    "#     ax.text(0.5, 0.45, f'Season Pitching Summary', va='top', ha='center', fontsize=40)\n",855    "#     ax.text(0.5, 0.20, f'2024 MLB Season', va='top', ha='center', fontsize=30, fontstyle='italic')\n",856    "\n",857    "#     # Turn off the axis\n",858    "#     ax.axis('off')\n",859    "\n",860    "\n",861    "# def plot_logo(pitcher_id: str, ax: plt.Axes):\n",862    "# # List of MLB teams and their corresponding ESPN logo URLs\n",863    "#     mlb_teams = [\n",864    "#         {\"team\": \"AZ\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/ari.png&h=500&w=500\"},\n",865    "#         {\"team\": \"ATL\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/atl.png&h=500&w=500\"},\n",866    "#         {\"team\": \"BAL\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/bal.png&h=500&w=500\"},\n",867    "#         {\"team\": \"BOS\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/bos.png&h=500&w=500\"},\n",868    "#         {\"team\": \"CHC\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/chc.png&h=500&w=500\"},\n",869    "#         {\"team\": \"CWS\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/chw.png&h=500&w=500\"},\n",870    "#         {\"team\": \"CIN\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/cin.png&h=500&w=500\"},\n",871    "#         {\"team\": \"CLE\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/cle.png&h=500&w=500\"},\n",872    "#         {\"team\": \"COL\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/col.png&h=500&w=500\"},\n",873    "#         {\"team\": \"DET\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/det.png&h=500&w=500\"},\n",874    "#         {\"team\": \"HOU\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/hou.png&h=500&w=500\"},\n",875    "#         {\"team\": \"KC\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/kc.png&h=500&w=500\"},\n",876    "#         {\"team\": \"LAA\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/laa.png&h=500&w=500\"},\n",877    "#         {\"team\": \"LAD\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/lad.png&h=500&w=500\"},\n",878    "#         {\"team\": \"MIA\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/mia.png&h=500&w=500\"},\n",879    "#         {\"team\": \"MIL\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/mil.png&h=500&w=500\"},\n",880    "#         {\"team\": \"MIN\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/min.png&h=500&w=500\"},\n",881    "#         {\"team\": \"NYM\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/nym.png&h=500&w=500\"},\n",882    "#         {\"team\": \"NYY\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/nyy.png&h=500&w=500\"},\n",883    "#         {\"team\": \"OAK\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/oak.png&h=500&w=500\"},\n",884    "#         {\"team\": \"PHI\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/phi.png&h=500&w=500\"},\n",885    "#         {\"team\": \"PIT\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/pit.png&h=500&w=500\"},\n",886    "#         {\"team\": \"SD\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/sd.png&h=500&w=500\"},\n",887    "#         {\"team\": \"SF\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/sf.png&h=500&w=500\"},\n",888    "#         {\"team\": \"SEA\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/sea.png&h=500&w=500\"},\n",889    "#         {\"team\": \"STL\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/stl.png&h=500&w=500\"},\n",890    "#         {\"team\": \"TB\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/tb.png&h=500&w=500\"},\n",891    "#         {\"team\": \"TEX\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/tex.png&h=500&w=500\"},\n",892    "#         {\"team\": \"TOR\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/tor.png&h=500&w=500\"},\n",893    "#         {\"team\": \"WSH\", \"logo_url\": \"https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/wsh.png&h=500&w=500\"}\n",894    "#     ]\n",895    "\n",896    "#     # Create a DataFrame from the list of dictionaries\n",897    "#     df_image = pd.DataFrame(mlb_teams)\n",898    "#     image_dict = df_image.set_index('team')['logo_url'].to_dict()    \n",899    "    \n",900    "#     # Construct the URL to fetch player data\n",901    "#     url = f\"https://statsapi.mlb.com/api/v1/people?personIds={pitcher_id}&hydrate=currentTeam\"\n",902    "\n",903    "#     # Send a GET request to the URL and parse the JSON response\n",904    "#     data = requests.get(url).json()\n",905    "\n",906    "#     # Construct the URL to fetch team data\n",907    "#     url_team = 'https://statsapi.mlb.com/' + data['people'][0]['currentTeam']['link']\n",908    "\n",909    "#     # Send a GET request to the team URL and parse the JSON response\n",910    "#     data_team = requests.get(url_team).json()\n",911    "\n",912    "#     # Extract the team abbreviation\n",913    "#     team_abb = data_team['teams'][0]['abbreviation']\n",914    "\n",915    "#     # Get the logo URL from the image dictionary using the team abbreviation\n",916    "#     logo_url = image_dict[team_abb]\n",917    "\n",918    "#     # Send a GET request to the logo URL\n",919    "#     response = requests.get(logo_url)\n",920    "\n",921    "#     # Open the image from the response content\n",922    "#     img = Image.open(BytesIO(response.content))\n",923    "\n",924    "#     # Display the image on the axis\n",925    "#     ax.set_xlim(0, 1.3)\n",926    "#     ax.set_ylim(0, 1)\n",927    "#     ax.imshow(img, extent=[0.3, 1.3, 0, 1], origin='upper')\n",928    "\n",929    "#     # Turn off the axis\n",930    "#     ax.axis('off')\n",931    "\n",932    "# splits = {\n",933    "#     'All':0,\n",934    "#     'LHH':13,\n",935    "#     'RHH':14,\n",936    "# }\n",937    "\n",938    "# splits_title = {\n",939    "\n",940    "#     'All':'',\n",941    "#     'LHH':' vs LHH',\n",942    "#     'RHH':' vs RHH',\n",943    "\n",944    "# }\n",945    "\n",946    "\n",947    "# def fangraphs_pitching_leaderboards(season: int,\n",948    "#                                     split: str,\n",949    "#                                     start_date: str = '2024-01-01',\n",950    "#                                     end_date: str = '2024-12-31'):\n",951    "#     \"\"\"\n",952    "#     Fetch pitching leaderboards data from Fangraphs.\n",953    "\n",954    "#     Parameters\n",955    "#     ----------\n",956    "#     season : int\n",957    "#         The season year.\n",958    "#     split : str\n",959    "#         The split type (e.g., 'All', 'LHH', 'RHH').\n",960    "#     start_date : str, optional\n",961    "#         The start date for the data (default is '2024-01-01').\n",962    "#     end_date : str, optional\n",963    "#         The end date for the data (default is '2024-12-31').\n",964    "\n",965    "#     Returns\n",966    "#     -------\n",967    "#     pl.DataFrame\n",968    "#         The DataFrame containing the pitching leaderboards data.\n",969    "#     \"\"\"\n",970    "#     url = f\"\"\"\n",971    "#            https://www.fangraphs.com/api/leaders/major-league/data?age=&pos=all&stats=pit&lg=all&season={season}&season1={season}\n",972    "#            &startdate={start_date}&enddate={end_date}&ind=0&qual=0&type=8&month={splits[split]}&pageitems=500000\n",973    "#            \"\"\"\n",974    "\n",975    "#     data = requests.get(url).json()\n",976    "#     df = pl.DataFrame(data=data['data'], infer_schema_length=1000)\n",977    "#     return df\n",978    "\n",979    "                                   \n",980    "# def fangraphs_table(ax: plt.Axes,\n",981    "#                     season: int,\n",982    "#                     split: str,\n",983    "#                     start_date: str = '2024-01-01',\n",984    "#                     end_date: str = '2024-12-31'):\n",985    "#     \"\"\"\n",986    "#     Create a table of Fangraphs pitching leaderboards data for a specific player.\n",987    "\n",988    "#     Parameters\n",989    "#     ----------\n",990    "#     ax : plt.Axes\n",991    "#         The axis to plot the table on.\n",992    "#     season : int\n",993    "#         The season year.\n",994    "#     split : str\n",995    "#         The split type (e.g., 'All', 'LHH', 'RHH').\n",996    "#     start_date : str, optional\n",997    "#         The start date for the data (default is '2024-01-01').\n",998    "#     end_date : str, optional\n",999    "#         The end date for the data (default is '2024-12-31').\n",1000    "#     \"\"\"\n",1001    "#     # Fetch Fangraphs pitching leaderboards data\n",1002    "#     df_fangraphs = fangraphs_pitching_leaderboards(season=season,\n",1003    "#                                                    split=split,\n",1004    "#                                                    start_date=start_date,\n",1005    "#                                                    end_date=end_date).filter(pl.col('xMLBAMID') == player_input)\n",1006    "    \n",1007    "#     # Select relevant columns for the table\n",1008    "#     plot_table = df_fangraphs.select(['IP', 'WHIP', 'ERA', 'TBF', 'FIP', 'K%', 'BB%', 'K-BB%'])\n",1009    "\n",1010    "#     # Format table values\n",1011    "#     plot_table_values = [format(plot_table[x][0], fangraphs_stats_dict[x]['format']) if plot_table[x][0] != '---' else '---' for x in plot_table.columns]\n",1012    "    \n",1013    "#     # Create the table\n",1014    "#     table_fg = ax.table(cellText=[plot_table_values], colLabels=plot_table.columns, cellLoc='center',\n",1015    "#                         bbox=[0.0, 0.1, 1, 0.7])\n",1016    "\n",1017    "#     # Set font size for the table\n",1018    "#     min_font_size = 20\n",1019    "#     table_fg.set_fontsize(min_font_size)\n",1020    "\n",1021    "#     # Update column names with formatted headers\n",1022    "#     new_column_names = [fangraphs_stats_dict[col]['table_header'] for col in plot_table.columns]\n",1023    "#     for i, col_name in enumerate(new_column_names):\n",1024    "#         table_fg.get_celld()[(0, i)].get_text().set_text(col_name)\n",1025    "\n",1026    "#     # Set header text properties\n",1027    "#     ax.text(0.5, 0.9, f'{start_date} to {end_date}{splits_title[split]}', va='bottom', ha='center',\n",1028    "#             fontsize=36, fontstyle='italic')\n",1029    "#     ax.axis('off')"1030   ]1031  },1032  {1033   "cell_type": "code",1034   "execution_count": 4,1035   "metadata": {},1036   "outputs": [],1037   "source": [1038    "### PITCH COLOURS ###\n",1039    "\n",1040    "# Dictionary to map pitch types to their corresponding colors and names\n",1041    "pitch_colours = {\n",1042    "    ## Fastballs ##\n",1043    "    'FF': {'colour': '#FF007D', 'name': '4-Seam Fastball'},\n",1044    "    'FA': {'colour': '#FF007D', 'name': 'Fastball'},\n",1045    "    'SI': {'colour': '#98165D', 'name': 'Sinker'},\n",1046    "    'FC': {'colour': '#BE5FA0', 'name': 'Cutter'},\n",1047    "\n",1048    "    ## Offspeed ##\n",1049    "    'CH': {'colour': '#F79E70', 'name': 'Changeup'},\n",1050    "    'FS': {'colour': '#FE6100', 'name': 'Splitter'},\n",1051    "    'SC': {'colour': '#F08223', 'name': 'Screwball'},\n",1052    "    'FO': {'colour': '#FFB000', 'name': 'Forkball'},\n",1053    "\n",1054    "    ## Sliders ##\n",1055    "    'SL': {'colour': '#67E18D', 'name': 'Slider'},\n",1056    "    'ST': {'colour': '#1BB999', 'name': 'Sweeper'},\n",1057    "    'SV': {'colour': '#376748', 'name': 'Slurve'},\n",1058    "\n",1059    "    ## Curveballs ##\n",1060    "    'KC': {'colour': '#311D8B', 'name': 'Knuckle Curve'},\n",1061    "    'CU': {'colour': '#3025CE', 'name': 'Curveball'},\n",1062    "    'CS': {'colour': '#274BFC', 'name': 'Slow Curve'},\n",1063    "    'EP': {'colour': '#648FFF', 'name': 'Eephus'},\n",1064    "\n",1065    "    ## Others ##\n",1066    "    'KN': {'colour': '#867A08', 'name': 'Knuckleball'},\n",1067    "    'PO': {'colour': '#472C30', 'name': 'Pitch Out'},\n",1068    "    'UN': {'colour': '#9C8975', 'name': 'Unknown'},\n",1069    "}\n",1070    "\n",1071    "# Create dictionaries for pitch types and their attributes\n",1072    "dict_colour = {key: value['colour'] for key, value in pitch_colours.items()}\n",1073    "dict_pitch = {key: value['name'] for key, value in pitch_colours.items()}\n",1074    "dict_pitch_desc_type = {value['name']: key for key, value in pitch_colours.items()}\n",1075    "dict_pitch_desc_type.update({'Four-Seam Fastball':'FF'})\n",1076    "dict_pitch_desc_type.update({'All':'All'})\n",1077    "dict_pitch_name = {value['name']: value['colour'] for key, value in pitch_colours.items()}\n",1078    "dict_pitch_name.update({'Four-Seam Fastball':'#FF007D'})"1079   ]1080  },1081  {1082   "cell_type": "code",1083   "execution_count": 5,1084   "metadata": {},1085   "outputs": [],1086   "source": [1087    "import polars as pl\n",1088    "import numpy as np\n",1089    "import pandas as pd\n",1090    "import api_scraper\n",1091    "scrape = api_scraper.MLB_Scrape()\n",1092    "from functions import df_update\n",1093    "from functions import pitch_summary_functions\n",1094    "update = df_update.df_update()\n",1095    "from stuff_model import feature_engineering as fe\n",1096    "from stuff_model import stuff_apply\n",1097    "import requests\n",1098    "import joblib\n",1099    "from matplotlib.gridspec import GridSpec\n",1100    "from shiny import App, reactive, ui, render\n",1101    "from shiny.ui import h2, tags\n",1102    "import matplotlib.pyplot as plt\n",1103    "import matplotlib.gridspec as gridspec\n",1104    "import seaborn as sns\n",1105    "from functions.pitch_summary_functions import *\n",1106    "from shiny import App, reactive, ui, render\n",1107    "from shiny.ui import h2, tags"1108   ]1109  },1110  {1111   "cell_type": "code",1112   "execution_count": 6,1113   "metadata": {},1114   "outputs": [],1115   "source": [1116    "import lightgbm"1117   ]1118  },1119  {1120   "cell_type": "code",1121   "execution_count": 7,1122   "metadata": {},1123   "outputs": [1124    {1125     "data": {1126      "text/plain": [1127       "<module 'lightgbm.libpath' from 'C:\\\\Users\\\\thoma\\\\AppData\\\\Local\\\\Packages\\\\PythonSoftwareFoundation.Python.3.9_qbz5n2kfra8p0\\\\LocalCache\\\\local-packages\\\\Python39\\\\site-packages\\\\lightgbm\\\\libpath.py'>"1128      ]1129     },1130     "execution_count": 7,1131     "metadata": {},1132     "output_type": "execute_result"1133    }1134   ],1135   "source": [1136    "lightgbm.libpath"1137   ]1138  },1139  {1140   "cell_type": "code",1141   "execution_count": 8,1142   "metadata": {},1143   "outputs": [1144    {1145     "data": {1146      "text/plain": [1147       "['Throws',\n",1148       " 'xMLBAMID',\n",1149       " 'season',\n",1150       " 'Name',\n",1151       " 'Team',\n",1152       " 'Age',\n",1153       " 'AgeR',\n",1154       " 'W',\n",1155       " 'L',\n",1156       " 'ERA',\n",1157       " 'G',\n",1158       " 'GS',\n",1159       " 'QS',\n",1160       " 'CG',\n",1161       " 'ShO',\n",1162       " 'SV',\n",1163       " 'HLD',\n",1164       " 'BS',\n",1165       " 'IP',\n",1166       " 'TBF',\n",1167       " 'H',\n",1168       " 'R',\n",1169       " 'ER',\n",1170       " 'HR',\n",1171       " 'BB',\n",1172       " 'IBB',\n",1173       " 'HBP',\n",1174       " 'WP',\n",1175       " 'BK',\n",1176       " 'SO',\n",1177       " 'K/9',\n",1178       " 'BB/9',\n",1179       " 'H/9',\n",1180       " 'K/BB',\n",1181       " 'IFH%',\n",1182       " 'BUH%',\n",1183       " 'TTO%',\n",1184       " 'GB',\n",1185       " 'FB',\n",1186       " 'LD',\n",1187       " 'IFFB',\n",1188       " 'IFH',\n",1189       " 'BU',\n",1190       " 'BUH',\n",1191       " 'K%',\n",1192       " 'BB%',\n",1193       " 'K-BB%',\n",1194       " 'SIERA',\n",1195       " 'HR/9',\n",1196       " 'AVG',\n",1197       " 'WHIP',\n",1198       " 'BABIP',\n",1199       " 'LOB%',\n",1200       " 'FIP',\n",

Showing the first 1,200 of 1939 lines. Download the file for the rest.