TJStatsApps/2025_decision_value
0
1from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui2import datasets3from datasets import load_dataset4import pandas as pd5import numpy as np6import matplotlib.pyplot as plt7import seaborn as sns8import numpy as np9from scipy.stats import gaussian_kde10import matplotlib11from matplotlib.ticker import MaxNLocator12from matplotlib.gridspec import GridSpec13from scipy.stats import zscore14import math15import matplotlib16from adjustText import adjust_text17import matplotlib.ticker as mtick18from shinywidgets import output_widget, render_widget19import pandas as pd20from configure import base_url21import shinyswatch22import inflect23from matplotlib.pyplot import text24 25def percentile(n):26 def percentile_(x):27 return np.nanpercentile(x, n)28 percentile_.__name__ = 'percentile_%s' % n29 return percentile_30 31colour_palette = ['#FFB000','#648FFF','#785EF0',32 '#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']33 34 35 36print('Starting Everything:')37# exit_velo_df = milb_a_ev_df.append([triple_a_ev_df,double_a_ev_df,a_high_a_ev_df,single_a_ev_df]).reset_index(drop=True)38# player_df_all = mlb_a_player_df.append([triple_a_player_df,double_a_player_df,a_high_a_player_df,single_a_player_df]).reset_index(drop=True)39# exit_velo_df = pd.read_csv('exit_velo_df_all.csv',index_col=[0])40# player_df_all = pd.read_csv('player_df_all.csv',index_col=[0])41 42# pa_df = pd.read_csv('pa_df_all.csv',index_col=[0])43# pa_df_full_na = pa_df.dropna()44 45### Import Datasets46dataset = load_dataset('nesticot/mlb_data', data_files=['mlb_pitch_data_2023.csv',47 ])48dataset_train = dataset['train']49exit_velo_df_mlb = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)50#print(df_2023)51exit_velo_df_mlb['level'] = 'MLB'52 53# ### Import Datasets54# dataset = load_dataset('nesticot/mlb_data', data_files=['aaa_pitch_data_2023.csv',55# ])56# dataset_train = dataset['train']57# exit_velo_df_aaa = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)58# #print(df_2023)59# exit_velo_df_aaa['level'] = 'AAA'60 61# ### Import Datasets62# dataset = load_dataset('nesticot/mlb_data', data_files=['aa_pitch_data_2023.csv',63# ])64# dataset_train = dataset['train']65# exit_velo_df_aa = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)66# #print(df_2023)67# exit_velo_df_aa['level'] = 'AA'68 69# ### Import Datasets70# dataset = load_dataset('nesticot/mlb_data', data_files=['high_a_pitch_data_2023.csv',71# ])72# dataset_train = dataset['train']73# exit_velo_df_ha = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)74# #print(df_2023)75# exit_velo_df_ha['level'] = 'A+'76 77# ### Import Datasets78# dataset = load_dataset('nesticot/mlb_data', data_files=['a_pitch_data_2023.csv',79# ])80# dataset_train = dataset['train']81# exit_velo_df_a = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)82# #print(df_2023)83# exit_velo_df_a['level'] = 'A'84 85# exit_velo_df = pd.concat([exit_velo_df_mlb,exit_velo_df_aaa,exit_velo_df_aa,exit_velo_df_ha,exit_velo_df_a])86exit_velo_df = pd.concat([exit_velo_df_mlb])87# exit_velo_df_copy = exit_velo_df.copy()88 89# exit_velo_df = exit_velo_df_copy.copy()90 91end_codes = ['strikeout', 'field_out', 'single', 'walk', 'hit_by_pitch',92 'double', 'sac_fly', 'force_out', 'home_run',93 'grounded_into_double_play', 'fielders_choice', 'field_error',94 'triple', 'sac_bunt', 'double_play', 'intent_walk',95 'fielders_choice_out', 'strikeout_double_play',96 'sac_fly_double_play', 'catcher_interf', 'other_out']97 98 99 100exit_velo_df['pa'] = exit_velo_df.event_type.isin(end_codes)101#exit_velo_df['pa'] = 1102exit_velo_df['k'] = exit_velo_df.event_type.isin(list(filter(None, [x if 'strikeout' in x else '' for x in exit_velo_df.event_type.fillna('None').unique()])))103exit_velo_df['bb'] = exit_velo_df.event_type.isin(list(filter(None, [x if 'walk' in x else '' for x in exit_velo_df.event_type.fillna('None').unique()])))104 105#exit_velo_df['k_minus_bb'] = exit_velo_df['k'].astype(np.float32)-exit_velo_df['bb'].astype(np.float32)106exit_velo_df['bb_minus_k'] = exit_velo_df['bb'].astype(np.float32)-exit_velo_df['k'].astype(np.float32)107 108 109 110exit_velo_df = exit_velo_df.drop_duplicates(subset=['play_id'])111 112 113 114swing_codes = ['Swinging Strike', 'In play, no out', 115 'Foul', 'In play, out(s)', 116 'In play, run(s)', 'Swinging Strike (Blocked)',117 'Foul Bunt','Foul Tip', 'Missed Bunt','Foul Pitchout','Swinging Pitchout']118 119swings_in = ['Swinging Strike', 'In play, no out', 120 'Foul', 'In play, out(s)', 121 'In play, run(s)', 'Swinging Strike (Blocked)',122 'Foul Bunt','Foul Tip', 'Missed Bunt','Foul Pitchout','Swinging Pitchout'] 123 124swing_strike_codes = ['Swinging Strike',125 'Swinging Strike (Blocked)','Missed Bunt','Foul Tip','Swinging Pitchout']126 127 128contact_codes = ['In play, no out', 129 'Foul', 'In play, out(s)', 130 'In play, run(s)', 131 'Foul Bunt']132 133codes_in = ['In play, out(s)',134'Swinging Strike',135'Ball',136'Foul',137'In play, no out',138'Called Strike',139'Foul Tip',140'In play, run(s)',141'Hit By Pitch',142'Ball In Dirt',143'Pitchout',144'Swinging Strike (Blocked)',145'Foul Bunt',146'Missed Bunt',147'Foul Pitchout',148'Intent Ball',149'Swinging Pitchout']150 151exit_velo_df['in_zone'] = exit_velo_df['zone'] < 10152 153 154exit_velo_df = exit_velo_df.drop_duplicates(subset=['play_id'])155 156exit_velo_df_codes = exit_velo_df[exit_velo_df.play_description.isin(codes_in)].dropna(subset=['in_zone'])157 158exit_velo_df_codes['bip'] = ~exit_velo_df_codes.launch_speed.isna()159conditions = [160 (exit_velo_df_codes['launch_speed'].isna()),161 (exit_velo_df_codes['launch_speed']*1.5 - exit_velo_df_codes['launch_angle'] >= 117 ) & (exit_velo_df_codes['launch_speed'] + exit_velo_df_codes['launch_angle'] >= 124) & (exit_velo_df_codes['launch_speed'] > 98) & (exit_velo_df_codes['launch_angle'] >= 8) & (exit_velo_df_codes['launch_angle'] <= 50)162]163 164choices = [False,True]165exit_velo_df_codes['barrel'] = np.select(conditions, choices, default=np.nan)166 167conditions_ss = [168 (exit_velo_df_codes['launch_angle'].isna()),169 (exit_velo_df_codes['launch_angle'] >= 8 ) * (exit_velo_df_codes['launch_angle'] <= 32 ) 170]171 172choices_ss = [False,True]173exit_velo_df_codes['sweet_spot'] = np.select(conditions_ss, choices_ss, default=np.nan)174 175 176conditions_hh = [177 (exit_velo_df_codes['launch_speed'].isna()),178 (exit_velo_df_codes['launch_speed'] >= 94.5 ) 179]180 181choices_hh = [False,True]182exit_velo_df_codes['hard_hit'] = np.select(conditions_hh, choices_hh, default=np.nan)183 184 185conditions_tb = [186 (exit_velo_df_codes['event_type']=='single'),187 (exit_velo_df_codes['event_type']=='double'),188 (exit_velo_df_codes['event_type']=='triple'),189 (exit_velo_df_codes['event_type']=='home_run'),190]191 192choices_tb = [1,2,3,4]193 194exit_velo_df_codes['tb'] = np.select(conditions_tb, choices_tb, default=np.nan)195 196conditions_woba = [197 (exit_velo_df_codes['event_type']=='walk'),198 (exit_velo_df_codes['event_type']=='hit_by_pitch'), 199 (exit_velo_df_codes['event_type']=='single'),200 (exit_velo_df_codes['event_type']=='double'),201 (exit_velo_df_codes['event_type']=='triple'),202 (exit_velo_df_codes['event_type']=='home_run'),203]204 205choices_woba = [0.705,206 0.688,207 0.897,208 1.233,209 1.612,210 2.013]211 212exit_velo_df_codes['woba'] = np.select(conditions_woba, choices_woba, default=np.nan)213 214 215woba_codes = ['strikeout', 'field_out', 'single', 'walk', 'hit_by_pitch',216 'double', 'sac_fly', 'force_out', 'home_run',217 'grounded_into_double_play', 'fielders_choice', 'field_error',218 'triple', 'sac_bunt', 'double_play',219 'fielders_choice_out', 'strikeout_double_play',220 'sac_fly_double_play', 'other_out']221 222 223 224 225 226conditions_woba_code = [227 (exit_velo_df_codes['event_type'].isin(woba_codes))228]229 230choices_woba_code = [1]231 232exit_velo_df_codes['woba_codes'] = np.select(conditions_woba_code, choices_woba_code, default=np.nan)233 234 235#exit_velo_df_codes['barrel'] = (exit_velo_df_codes.launch_speed >= 98) & (exit_velo_df_codes.launch_angle >= (26 - (-98 + exit_velo_df_codes.launch_speed))) & (exit_velo_df_codes.launch_angle <= 30 + (-98 + exit_velo_df_codes.launch_speed)) & (exit_velo_df_codes.launch_angle >= 8) & (exit_velo_df_codes.launch_angle <= 50)236 237 238 239 240 241#exit_velo_df_codes['barrel'] = (exit_velo_df_codes.launch_speed >= 98) & (exit_velo_df_codes.launch_angle >= (26 - (-98 + exit_velo_df_codes.launch_speed))) & (exit_velo_df_codes.launch_angle <= 30 + (-98 + exit_velo_df_codes.launch_speed)) & (exit_velo_df_codes.launch_angle >= 8) & (exit_velo_df_codes.launch_angle <= 50)242exit_velo_df_codes['pitches'] = 1243exit_velo_df_codes['whiffs'] = [1 if ((x == 'S')|(x == 'W')|(x =='T')) else 0 for x in exit_velo_df_codes.play_code]244exit_velo_df_codes['csw'] = [1 if ((x == 'S')|(x == 'W')|(x =='T')|(x == 'C')) else 0 for x in exit_velo_df_codes.play_code]245exit_velo_df_codes['swings'] = [1 if x in swings_in else 0 for x in exit_velo_df_codes.play_description]246 247exit_velo_df_codes['out_zone'] = exit_velo_df_codes.in_zone == False248exit_velo_df_codes['zone_swing'] = (exit_velo_df_codes.in_zone == True)&(exit_velo_df_codes.swings == 1)249exit_velo_df_codes['zone_contact'] = (exit_velo_df_codes.in_zone == True)&(exit_velo_df_codes.swings == 1)&(exit_velo_df_codes.whiffs == 0)250exit_velo_df_codes['ozone_swing'] = (exit_velo_df_codes.in_zone==False)&(exit_velo_df_codes.swings == 1)251exit_velo_df_codes['ozone_contact'] = (exit_velo_df_codes.in_zone==False)&(exit_velo_df_codes.swings == 1)&(exit_velo_df_codes.whiffs == 0)252 253 254 255exit_velo_df_codes_summ = exit_velo_df_codes.groupby(['batter_id','batter_name','level']).agg(256 pa = ('pa','sum'), 257 k = ('k','sum'), 258 bb = ('bb','sum'), 259 bb_minus_k = ('bb_minus_k','sum'), 260 csw = ('csw','sum'), 261 bip = ('bip','sum'),262 tb = ('tb','sum'),263 woba = ('woba','sum'),264 woba_codes = ('woba_codes','sum'),265 hard_hit = ('hard_hit','sum'), 266 barrel = ('barrel','sum'), 267 sweet_spot = ('sweet_spot','sum'), 268 max_launch_speed = ('launch_speed','max'),269 launch_speed_90 = ('launch_speed',percentile(90)),270 launch_speed = ('launch_speed','mean'),271 launch_angle = ('launch_angle','mean'),272 pitches = ('pitches','sum'),273 swings = ('swings','sum'),274 in_zone = ('in_zone','sum'),275 out_zone = ('out_zone','sum'),276 whiffs = ('whiffs','sum'),277 zone_swing = ('zone_swing','sum'),278 zone_contact = ('zone_contact','sum'),279 ozone_swing = ('ozone_swing','sum'),280 ozone_contact = ('ozone_contact','sum'),281 ).reset_index()282 283#exit_velo_df_codes_summ['out_zone'] = ~exit_velo_df_codes_summ.in_zone284#bip_min_input = int(input())285#bip_min = min(bip_min_input,50)286#exit_velo_df_codes_summ = exit_velo_df_codes_summ[exit_velo_df_codes_summ.balls_in_play>=bip_min]287 288 289exit_velo_df_codes_summ['k_percent'] = [exit_velo_df_codes_summ.k[x]/exit_velo_df_codes_summ.pa[x] if exit_velo_df_codes_summ.pa[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]290exit_velo_df_codes_summ['bb_percent'] =[exit_velo_df_codes_summ.bb[x]/exit_velo_df_codes_summ.pa[x] if exit_velo_df_codes_summ.pa[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]291exit_velo_df_codes_summ['bb_minus_k_percent'] =[exit_velo_df_codes_summ.bb_minus_k[x]/exit_velo_df_codes_summ.pa[x] if exit_velo_df_codes_summ.pa[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]292 293exit_velo_df_codes_summ['csw_percent'] =[exit_velo_df_codes_summ.csw[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]294 295 296exit_velo_df_codes_summ['sweet_spot_percent'] = [exit_velo_df_codes_summ.sweet_spot[x]/exit_velo_df_codes_summ.bip[x] if exit_velo_df_codes_summ.bip[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]297 298exit_velo_df_codes_summ['woba_percent'] = [exit_velo_df_codes_summ.woba[x]/exit_velo_df_codes_summ.woba_codes[x] if exit_velo_df_codes_summ.woba_codes[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]299#exit_velo_df_codes_summ['hard_hit_percent'] = [exit_velo_df_codes_summ.sweet_spot[x]/exit_velo_df_codes_summ.bip[x] if exit_velo_df_codes_summ.bip[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]300exit_velo_df_codes_summ['hard_hit_percent'] = [exit_velo_df_codes_summ.hard_hit[x]/exit_velo_df_codes_summ.bip[x] if exit_velo_df_codes_summ.bip[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]301 302 303exit_velo_df_codes_summ['barrel_percent'] = [exit_velo_df_codes_summ.barrel[x]/exit_velo_df_codes_summ.bip[x] if exit_velo_df_codes_summ.bip[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]304 305exit_velo_df_codes_summ['zone_contact_percent'] = [exit_velo_df_codes_summ.zone_contact[x]/exit_velo_df_codes_summ.zone_swing[x] if exit_velo_df_codes_summ.zone_swing[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]306 307exit_velo_df_codes_summ['zone_swing_percent'] = [exit_velo_df_codes_summ.zone_swing[x]/exit_velo_df_codes_summ.in_zone[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]308 309exit_velo_df_codes_summ['zone_percent'] = [exit_velo_df_codes_summ.in_zone[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]310 311exit_velo_df_codes_summ['chase_percent'] = [exit_velo_df_codes_summ.ozone_swing[x]/(exit_velo_df_codes_summ.pitches[x] - exit_velo_df_codes_summ.in_zone[x]) if (exit_velo_df_codes_summ.pitches[x]- exit_velo_df_codes_summ.in_zone[x]) != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]312 313exit_velo_df_codes_summ['chase_contact'] = [exit_velo_df_codes_summ.ozone_contact[x]/exit_velo_df_codes_summ.ozone_swing[x] if exit_velo_df_codes_summ.ozone_swing[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]314 315exit_velo_df_codes_summ['swing_percent'] = [exit_velo_df_codes_summ.swings[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]316 317exit_velo_df_codes_summ['whiff_rate'] = [exit_velo_df_codes_summ.whiffs[x]/exit_velo_df_codes_summ.swings[x] if exit_velo_df_codes_summ.swings[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]318 319exit_velo_df_codes_summ['swstr_rate'] = [exit_velo_df_codes_summ.whiffs[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]320 321exit_velo_df_codes_summ = exit_velo_df_codes_summ.dropna(subset=['bip'])322 323woba_list = ['woba']324pa_list = ['k','bb','bb_minus_k']325balls_in_play_list = ['hard_hit','launch_speed','launch_speed_90','launch_angle','barrel','sweet_spot']326pitches_list = ['zone_percent','swing_percent','sw_str','csw']327swings_list = ['whiff_percent']328in_zone_pitches_list = ['zone_swing']329in_zone_swings_list = ['zone_contact']330out_zone_pitches_list = ['chase_percent']331out_zone_swings_list = ['chase_contact']332 333plot_dict = {334'k':{'x_axis':'Plate Appearances','y_axis':'K%','title':'K%','x_value':'k','x_range':[0.0,0.1,0.2,0.3,0.4],'percent':True,'percentile_label':'k_percent','flip_p':True,'percentile':False,'avg_adjust':False},335'bb':{'x_axis':'Plate Appearances','y_axis':'BB%','title':'BB%','x_value':'bb','x_range':[0.0,0.1,0.2,0.3],'percent':True,'percentile_label':'bb_percent','flip_p':False,'percentile':False,'avg_adjust':False},336'bb_minus_k':{'x_axis':'Plate Appearances','y_axis':'BB-K%','title':'BB-K%','x_value':'bb_minus_k','x_range':[-0.3,-0.2,-0.1,0,0.1,0.2],'percent':True,'percentile_label':'bb_minus_k_percent','flip_p':False,'percentile':False,'avg_adjust':False},337'csw':{'x_axis':'Pitches','y_axis':'CSW%','title':'CSW%','x_value':'csw','x_range':[.2,.25,.3,.35,.4],'percent':True,'percentile_label':'csw_percent','flip_p':True,'percentile':False,'avg_adjust':False},338'woba':{'x_axis':'wOBA PA','y_axis':'wOBA','title':'wOBA','x_value':'woba','x_range':[.20,.30,.40,.50],'percent':False,'percentile_label':'woba_percent','flip_p':False,'percentile':False,'avg_adjust':True},339'launch_speed':{'x_axis':'Balls In Play','y_axis':'Exit Velocity','title':'Exit Velocity','x_value':'launch_speed','x_range':[85,90,95,100],'percent':False,'percentile_label':'launch_speed','flip_p':False,'percentile':False,'avg_adjust':False},340'launch_speed_90':{'x_axis':'Balls In Play','y_axis':'90th Percentile Exit Velocity','title':'90th Percentile Exit Velocity','x_value':'launch_speed','x_range':[95,100,105,110,115],'percent':False,'percentile_label':'launch_speed_90','flip_p':False,'percentile':True,'avg_adjust':False}, 341'hard_hit':{'x_axis':'Balls In Play','y_axis':'HardHit%','title':'HardHit%','x_value':'hard_hit','x_range':[0.2,0.3,0.4,0.5,0.6,0.7],'percent':True,'percentile_label':'hard_hit_percent','flip_p':False,'percentile':False,'avg_adjust':False},342'sweet_spot':{'x_axis':'Balls In Play','y_axis':'SweetSpot%','title':'SweetSpot%','x_value':'sweet_spot','x_range':[0.2,0.3,0.4,0.5],'percent':True,'percentile_label':'sweet_spot_percent','flip_p':False,'percentile':False,'avg_adjust':False},343'launch_angle':{'x_axis':'Balls In Play','y_axis':'Launch Angle','title':'Launch Angle','x_value':'launch_angle','x_range':[-20,-10,0,10,20],'percent':False,'percentile_label':'launch_angle','flip_p':False,'percentile':False,'avg_adjust':False},344'barrel':{'x_axis':'Balls In Play','y_axis':'Barrel%','title':'Barrel%','x_value':'barrel','x_range':[0,0.05,0.10,.15,.20,.25,.30],'percent':True,'percentile_label':'barrel_percent','flip_p':False,'percentile':False,'avg_adjust':False},345'zone_percent':{'x_axis':'Pitches','y_axis':'Zone%','title':'Zone%','x_value':'in_zone','x_range':[0.3,0.4,0.5,0.6,0.7],'percent':True,'percentile_label':'zone_percent','flip_p':False,'percentile':False,'avg_adjust':False},346'swing_percent':{'x_axis':'Pitches','y_axis':'Swing%','title':'Swing%','x_value':'swings','x_range':[0.2,0.3,0.4,0.5,0.6,0.7,0.8],'percent':True,'percentile_label':'swing_percent','flip_p':False,'percentile':False,'avg_adjust':False},347'whiff_percent':{'x_axis':'Swings','y_axis':'Whiff%','title':'Whiff%','x_value':'whiffs','x_range':[0.0,0.1,0.2,0.3,0.4,0.5],'percent':True,'percentile_label':'whiff_rate','flip_p':True,'percentile':False,'avg_adjust':False},348'sw_str':{'x_axis':'Pitches','y_axis':'SwStr%','title':'SwStr%','x_value':'whiffs','x_range':[0.0,0.05,0.1,0.15,0.2,0.25],'percent':True,'percentile_label':'swstr_rate','flip_p':True,'percentile':False,'avg_adjust':False},349'zone_swing':{'x_axis':'In-Zone Pitches','y_axis':'Z-Swing%','title':'Z-Swing%','x_value':'zone_swing','x_range':[0.3,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1],'percent':True,'percentile_label':'zone_swing_percent','flip_p':False,'percentile':False,'avg_adjust':False},350'zone_contact':{'x_axis':'In-Zone Swings','y_axis':'Z-Contact%','title':'Z-Contact%','x_value':'zone_contact','x_range':[0.5,0.6,0.7,0.8,0.9,1],'percent':True,'percentile_label':'zone_contact_percent','flip_p':False,'percentile':False,'avg_adjust':False},351'chase_percent':{'x_axis':'Out-of-Zone Pitches','y_axis':'O-Swing%','title':'O-Swing%','x_value':'ozone_swing','x_range':[0.0,0.1,0.2,0.3,0.4,0.5],'percent':True,'percentile_label':'chase_percent','flip_p':True,'percentile':False,'avg_adjust':False},352'chase_contact':{'x_axis':'Out-of-Zone Swings','y_axis':'O-Contact%','title':'O-Contact%','x_value':'ozone_contact','x_range':[0.2,0.3,0.4,0.5,0.6,0.7,0.8],'percent':True,'percentile_label':'chase_contact','flip_p':False,'percentile':False,'avg_adjust':False},}353 354 355 356 357test_df = exit_velo_df.sort_values(by='batter_name').drop_duplicates(subset='batter_id').reset_index(drop=True)[['batter_id','batter_name']]#['pitcher'].to_dict()358test_df = test_df.dropna()359test_df['batter_id'] = test_df['batter_id'].astype(int)360test_df = test_df.set_index('batter_id')361#test_df = test_df[test_df.pitcher == 'Chris Bassitt'].append(test_df[test_df.pitcher != 'Chris Bassitt'])362 363batter_dict = test_df['batter_name'].to_dict()364 365level_dict = {'MLB':'MLB','AAA':'AAA','AA':'AA','A+':'A+','A':'A'}366 367plot_dict_small = {368'k':'K%',369'bb':'BB%',370'csw':'CSW%',371'launch_speed':'Exit Velocity',372'launch_speed_90':'90th Percentile Exit Velocity',373'sweet_spot':'SweetSpot%',374'launch_angle':'Launch Angle',375'zone_percent':'Zone%',376'barrel':'Barrel%',377'swing_percent':'Swing%',378'whiff_percent':'Whiff%',379'sw_str':'SwStr%',380'zone_swing':'Z-Swing%',381'zone_contact':'Z-Contact%',382'chase_percent':'O-Swing%',383'chase_contact':'O-Contact%',}384 385 386def server(input,output,session):387 388 @output389 @render.plot(alt="A histogram")390 @reactive.event(input.go, ignore_none=False)391 def plot():392 # np.random.seed(19680801)393 # x = 100 + 15 * np.random.randn(437)394 # fig, ax = plt.subplots()395 # ax.hist(x, input.n(), density=True)396 # return fig397 sns.set_theme(style="whitegrid", palette="pastel")398 if input.id() is "":399 fig = plt.figure(figsize=(12, 12))400 fig.text(s='Please Select a Pitcher',x=0.5,y=0.5)401 return402 403 swing_min = int(input.n())404 fig, ax = plt.subplots(1, 1, figsize=(10, 10))405 406 fig.set_facecolor('white')407 #ax.set_facecolor('white')408 #fig.patch.set_facecolor('lightblue')409 410 print(input.stat_id())411 412 if input.stat_id() in pa_list:413 print('we hAVE MADE IT TO THIS PART OF THE CODE')414 415 416 if input.stat_id() in pa_list:417 elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.pa==1)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]418 divisor_x = 'pa'419 print('this is short')420 print(elly_zone_df)421 422 423 if input.stat_id() in balls_in_play_list:424 elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.bip)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]425 divisor_x = 'bip'426 #print('this is short')427 428 if input.stat_id() in balls_in_play_list:429 elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.bip)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]430 divisor_x = 'bip'431 print('this is short')432 433 if input.stat_id() in pitches_list:434 elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.pitches == 1)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]435 divisor_x = 'pitches'436 437 if input.stat_id() in swings_list:438 elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.swings == 1)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]439 divisor_x = 'swings'440 441 442 if input.stat_id() in in_zone_pitches_list:443 elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.in_zone)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]444 divisor_x = 'in_zone'445 446 447 if input.stat_id() in in_zone_swings_list:448 elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.zone_swing)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]449 divisor_x = 'zone_swing'450 451 452 if input.stat_id() in out_zone_pitches_list:453 elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.in_zone == False)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]454 divisor_x = 'out_zone'455 456 457 if input.stat_id() in out_zone_swings_list:458 elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.ozone_swing)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]459 divisor_x = 'ozone_swing'460 461 # penguins = sns.load_dataset("penguins")462 # sns.histplot(data=penguins, x="flipper_length_mm")463 # print('we made it here:')464 # print(int(input.id()))465 # print(input.stat_id())466 # print(input.level_id())467 # print(exit_velo_df_codes[(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())])468 # print(exit_velo_df_codes.columns)469 # print(elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum())470 471 exit_velo_df_codes_summ_new = exit_velo_df_codes_summ.copy()472 exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.set_index('batter_id','batter_name','level')473 exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new[divisor_x] >= int(input.n())]474 exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.level==input.level_id()]475 476 exit_velo_df_codes_summ_rank = exit_velo_df_codes_summ_new.rank(method='max',ascending=False)477 exit_velo_df_codes_summ_rank.columns = exit_velo_df_codes_summ_rank.columns+['_rank']478 479 exit_velo_df_codes_summ_rank_percent = exit_velo_df_codes_summ_new.rank(pct=True)480 exit_velo_df_codes_summ_rank_percent.columns = exit_velo_df_codes_summ_rank_percent.columns+['_percent']481 482 483 exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.reset_index()484 exit_velo_df_codes_summ_rank = exit_velo_df_codes_summ_rank.reset_index()485 exit_velo_df_codes_summ_rank_percent = exit_velo_df_codes_summ_rank_percent.reset_index()486 print('Table columns:')487 488 exit_velo_df_codes_summ_new.batter_id = exit_velo_df_codes_summ_new.batter_id.astype(int)489 exit_velo_df_codes_summ_rank.batter_id = exit_velo_df_codes_summ_rank.batter_id.astype(int)490 exit_velo_df_codes_summ_rank_percent.batter_id = exit_velo_df_codes_summ_rank_percent.batter_id.astype(int)491 492 print('Table columns2:')493 exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.merge(exit_velo_df_codes_summ_rank,left_on=['batter_id'],right_on=['batter_id'],how='left',suffixes=['','_rank'])494 495 exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.merge(exit_velo_df_codes_summ_rank_percent,left_on=['batter_id'],right_on=['batter_id'],how='left',suffixes=['','_percent'])496 497 498 print(exit_velo_df_codes_summ_new)499 print(exit_velo_df_codes_summ_rank)500 print(exit_velo_df_codes_summ_rank_percent)501 502 503 504 505 #sns.scatterplot(x=data_df.launch_speed_90,y=data_df.zone_contact,color=colour_palette[0],s=75,label=int(input.id()))506 507 exit_velo_df_codes_summ_new_select = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.batter_id == int(input.id())].reset_index(drop=True)508 print('whiffing')509 print(exit_velo_df_codes)510 print('Player _df:')511 print(exit_velo_df_codes_summ_new_select)512 513 if len(exit_velo_df_codes_summ_new_select) < 1:514 ax.text(x=0.5,y=0.5,s='Please Select Different Parameters to Produce a plot',fontsize=18,ha='center')515 return 516 517 p = inflect.engine()518 519 exit_velo_df_codes_summ_new_select = exit_velo_df_codes_summ_new_select.loc[:,~exit_velo_df_codes_summ_new_select.columns.duplicated(keep='last')].copy()520 print('Table for the player:')521 print(list(exit_velo_df_codes_summ_new_select.columns))522 print(plot_dict[input.stat_id()]["percentile_label"])523 print(plot_dict[input.stat_id()]["percentile_label"]+'_percent')524 print(exit_velo_df_codes_summ_new_select)525 print(1*plot_dict[input.stat_id()]["flip_p"])526 print(round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))527 print((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)528 529 # print(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+'_percent'])530 531 if plot_dict[input.stat_id()]['percent']:532 label_1=f'{input.level_id()} Average {exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].sum()/exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][divisor_x].sum():.1%}'533 label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1%} ({p.ordinal(abs(int((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)))} Percentile)'534 #label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1%}'535 ax.yaxis.set_major_formatter(mtick.PercentFormatter(1))536 537 else:538 label_1=f'{input.level_id()} Average {exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].sum()/exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][divisor_x].sum():.1f}'539 label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1f} ({p.ordinal(abs(int((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)))} Percentile)'540 #label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1f}'541 #ax.yaxis.set_major_formatter(mtick.int)542 543 544 if plot_dict[input.stat_id()]['percentile']:545 label_1=f'{input.level_id()} Average {exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].quantile(0.9):.1f}'546 label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].quantile(0.9):.1f} ({p.ordinal(abs(int((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)))} Percentile)'547 #label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1%}'548 #ax.yaxis.set_major_formatter(mtick.int)549 550 551 print(plot_dict[input.stat_id()]["x_value"])552 print(divisor_x)553 554 # exit_velo_df_codes_summ_new = exit_velo_df_codes_summ.copy()555 # exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.balls_in_play >= int(input.n())]556 # exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.level==input.level_id()]557 558 559 print('this is here:')560 print(exit_velo_df_codes_summ_new.head())561 print(exit_velo_df_codes_summ_new.columns)562 563 564 if plot_dict[input.stat_id()]["flip_p"] == False:565 ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[2],linestyle='dotted',alpha=0.5)566 ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[3],linestyle='dotted',alpha=0.5)567 ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[4],linestyle='dotted',alpha=0.5)568 ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[5],linestyle='dotted',alpha=0.5)569 570 571 hard_hit_dates = [(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),572 (exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),573 (exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),574 (exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1)]575 hard_hit_text = ['90th %','75th %','25th %','10th %']576 for i, x in enumerate(hard_hit_dates):577 text(min(input.n()+input.n()/100,+input.n()+1), x ,hard_hit_text[i], rotation=0, ha='left',578 bbox=dict(facecolor='white',alpha=0.5, edgecolor=colour_palette[2+i], pad=2))579 580 581 582 if plot_dict[input.stat_id()]["flip_p"] == True:583 ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[2],linestyle='dotted',alpha=0.5)584 ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[3],linestyle='dotted',alpha=0.5)585 ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[4],linestyle='dotted',alpha=0.5)586 ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[5],linestyle='dotted',alpha=0.5)587 588 hard_hit_dates = [(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),589 (exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),590 (exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),591 (exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1)]592 hard_hit_text = ['10th %','25th %','75th %','90th %']593 for i, x in enumerate(hard_hit_dates):594 text(min(input.n()+input.n()/100,input.n()+input.n()+3), x ,hard_hit_text[i], rotation=0, ha='left',595 bbox=dict(facecolor='white',alpha=0.5, edgecolor=colour_palette[2+i], pad=2))596 597 598 599 600 601 602 if plot_dict[input.stat_id()]["percentile"] == False:603 ax.hlines(y=exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].sum()/exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][divisor_x].sum(),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[1],linestyle='-.',label=label_1)604 605 ax.hlines(y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum(),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[0],linestyle='--',label=label_2)606 607 sns.lineplot(x=range(1,len(elly_zone_df)+1),y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].fillna(0).rolling(window=swing_min).sum()/swing_min,color=colour_palette[0],linewidth=3,ax=ax)608 609 610 611 if plot_dict[input.stat_id()]["percentile"] == True:612 613 ax.hlines(y=exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[1],linestyle='-.',label=label_1)614 615 ax.hlines(y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].fillna(0).quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[0],linestyle='--',label=label_2)616 617 sns.lineplot(x=range(1,len(elly_zone_df)+1),y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].fillna(0).rolling(window=swing_min).quantile(0.9),color=colour_palette[0],linewidth=3,ax=ax)618 619 620 #ax.set_xlim(input.n(),exit_velo_df_small.pitch.max())621 #plt.yticks([0,0.2,0.4,0.6,0.8,1])622 #ax.set_ylim(math.floor((min(exit_velo_df_codes_summ.zone_contact)/5)*100)*5/100,1)623 ax.set_xlim(math.floor(swing_min),len(elly_zone_df))624 ax.set_title(f'{batter_dict[int(input.id())]} - {input.level_id()} - {swing_min} {plot_dict[input.stat_id()]["x_axis"]} Rolling {plot_dict[input.stat_id()]["title"]}', fontsize=16,fontname='Century Gothic',)625 #vals = ax.get_yticks()626 ax.set_xlabel(plot_dict[input.stat_id()]['x_axis'], fontsize=16,fontname='Century Gothic')627 ax.set_ylabel(plot_dict[input.stat_id()]['y_axis'], fontsize=16,fontname='Century Gothic')628 629 #fig.axes[0].invert_yaxis()630 631 #fig.subplots_adjust(wspace=.02, hspace=.02)632 #ax.xaxis.set_major_formatter(FuncFormatter(lambda x, _: int(x)))633 ax.set_yticks(plot_dict[input.stat_id()]["x_range"])634 #fig.colorbar(plot_dist, ax=ax)635 #fig.colorbar(plot_dist)636 #fig.axes[0].invert_yaxis()637 ax.legend(fontsize='16')638 fig.text(x=0.03,y=0.02,s='By: @TJStats',fontname='Century Gothic')639 fig.text(x=1-0.03,y=0.02,s='Data: MLB',ha='right',fontname='Century Gothic')640 fig.tight_layout()641 642 643rolling_batter = App(ui.page_fluid(644 ui.tags.base(href=base_url), 645 ui.tags.div(646 {"style": "width:90%;margin: 0 auto;max-width: 1600px;"},647 ui.tags.style(648 """649 h4 {650 margin-top: 1em;font-size:35px;651 }652 h2{653 font-size:25px;654 }655 """656 ),657 shinyswatch.theme.simplex(),658 ui.tags.h4("TJStats"),659 ui.tags.i("Baseball Analytics and Visualizations"),660 ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),661 ui.navset_tab(662 ui.nav_control(663 ui.a(664 "Home",665 href="home/"666 ),667 ),668 ui.nav_menu(669 "Batter Charts",670 ui.nav_control(671 ui.a(672 "Batting Rolling",673 href="rolling_batter/"674 ),675 ui.a(676 "Spray & Damage",677 href="spray/"678 ),679 ui.a(680 "Decision Value",681 href="decision_value/"682 ),683 # ui.a(684 # "Damage Model",685 # href="damage_model/"686 # ),687 ui.a(688 "Batter Scatter",689 href="batter_scatter/"690 ),691 # ui.a(692 # "EV vs LA Plot",693 # href="ev_angle/"694 # ),695 ui.a(696 "Statcast Compare",697 href="statcast_compare/"698 )699 ),700 ),701 ui.nav_menu(702 "Pitcher Charts",703 ui.nav_control(704 ui.a(705 "Pitcher Rolling",706 href="rolling_pitcher/"707 ),708 ui.a(709 "Pitcher Summary",710 href="pitching_summary_graphic_new/"711 ),712 ui.a(713 "Pitcher Scatter",714 href="pitcher_scatter/"715 )716 ),717 )),ui.row(718 ui.layout_sidebar(719 720 ui.panel_sidebar(721 ui.input_select("id", "Select Pitcher",batter_dict,selected=675911,width=1,size=1,selectize=True),722 ui.input_select("level_id", "Select Level",level_dict,width=1,size=1),723 ui.input_select("stat_id", "Select Stat",plot_dict_small,width=1,size=1),724 ui.input_numeric("n", "Rolling Window Size", value=50),725 ui.input_action_button("go", "Generate",class_="btn-primary"),726 ui.output_table("result")727 ),728 729 ui.panel_main(730 ui.output_plot("plot",height = "1000px",width="1000px")731 ),732 )),)),server)