2024_spray / ev_angle.py
nesticot's picture
Upload 13 files
30629a5 verified
raw
history blame contribute delete
No virus
11 kB
from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui
import datasets
from datasets import load_dataset
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy.stats import gaussian_kde
import matplotlib
from matplotlib.ticker import MaxNLocator
from matplotlib.gridspec import GridSpec
from scipy.stats import zscore
import math
import matplotlib
from adjustText import adjust_text
import matplotlib.ticker as mtick
from shinywidgets import output_widget, render_widget
import pandas as pd
from configure import base_url
import shinyswatch
from matplotlib.pyplot import text
### Import Datasets
dataset = load_dataset('nesticot/mlb_data', data_files=['mlb_pitch_data_2023.csv' ])
dataset_train = dataset['train']
exit_velo_df = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)
colour_palette = ['#FFB000','#648FFF','#785EF0',
'#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']
#exit_velo_df = pd.read_csv('exit_velo_df.csv',index_col=[0])
conditions = [
(exit_velo_df['launch_speed'].isna()),
(exit_velo_df['launch_speed']*1.5 - exit_velo_df['launch_angle'] >= 117 ) & (exit_velo_df['launch_speed'] + exit_velo_df['launch_angle'] >= 124) & (exit_velo_df['launch_speed'] > 98) & (exit_velo_df['launch_angle'] >= 8) & (exit_velo_df['launch_angle'] <= 50)
]
choices = [False,True]
exit_velo_df['barrel'] = np.select(conditions, choices, default=np.nan)
test_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()
test_df = test_df.set_index('batter_id')
#test_df = test_df[test_df.pitcher == 'Chris Bassitt'].append(test_df[test_df.pitcher != 'Chris Bassitt'])
batter_dict = test_df['batter_name'].to_dict()
colour_palette = ['#FFB000','#648FFF','#785EF0',
'#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']
angle_ev_list_df = pd.read_csv('angle_ev_list_df.csv')
ev_ranges = list(np.arange(97.5,130,0.1))
angle_ranges = list(range(8,51))
#print
def server(input,output,session):
@output
@render.plot(alt="A histogram")
@reactive.event(input.go, ignore_none=False)
def plot():
data_df = exit_velo_df[exit_velo_df.batter_id==int(input.id())]
#pitch_list = exit_velo_df_small.pitch_type.unique()
sns.set_theme(style="whitegrid", palette="pastel")
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
if input.plot_id() == 'dist':
sns.histplot(x=data_df.launch_angle,y=data_df.launch_speed,cbar=colour_palette,binwidth=(5,2.5),ax=ax,cbar_kws=dict(shrink=.75,label='Count'),binrange=(
(math.floor((min(data_df.launch_angle.dropna())/5))*5,math.ceil((max(data_df.launch_angle.dropna())/5))*5),(math.floor((min(data_df.launch_speed.dropna())/5))*5,math.ceil((max(data_df.launch_speed.dropna())/5))*5)))
if input.plot_id() == 'scatter':
sns.scatterplot(x=data_df.launch_angle,y=data_df.launch_speed,color=colour_palette[1])
ax.set_xlim(math.floor((min(data_df.launch_angle.dropna())/10))*10,math.ceil((max(data_df.launch_angle.dropna())/10))*10)
#ticks=np.arange(revels.values.min(),revels.values.max()+1 )
sns.lineplot(x=angle_ev_list_df.launch_angle,y=angle_ev_list_df.launch_speed,color=colour_palette[0])
ax.vlines(x=angle_ev_list_df.launch_angle[0],ymin=angle_ev_list_df.launch_speed[0],ymax=ev_ranges[-1],color=colour_palette[0])
ax.vlines(x=angle_ev_list_df.launch_angle[len(angle_ev_list_df)-1],ymin=angle_ev_list_df.launch_speed[len(angle_ev_list_df)-1],ymax=ev_ranges[-1],color=colour_palette[0])
groundball = f'{sum(data_df.launch_angle.dropna()<=10)/len(data_df.launch_angle.dropna()):.1%}'
linedrive = f'{sum((data_df.launch_angle.dropna()<=25) & (data_df.launch_angle.dropna()>10))/len(data_df.launch_angle.dropna()):.1%}'
flyball = f'{sum((data_df.launch_angle.dropna()<=50) & (data_df.launch_angle.dropna()>25))/len(data_df.launch_angle.dropna()):.1%}'
popup = f'{sum(data_df.launch_angle.dropna()>50)/len(data_df.launch_angle.dropna()):.1%}'
percentages_list = [groundball,linedrive,flyball,popup]
hard_hit_percent = f'{sum(data_df.launch_speed.dropna()>=95)/len(data_df.launch_speed.dropna()):.1%}'
barrel_percentage = f'{data_df.barrel.dropna().sum()/len(data_df.launch_angle.dropna()):.1%}'
plt.text(x=27, y=math.ceil((max(data_df.launch_speed.dropna())/5))*5+5-3, s=f'Barrel% {barrel_percentage}',ha='left',bbox=dict(facecolor='white',alpha=0.8, edgecolor=colour_palette[4], pad=5))
sample_dates = np.array([math.floor((min(data_df.launch_angle.dropna())/10))*10,10,25,50])
sample_text = [f'Groundball ({groundball})',f'Line Drive ({linedrive})',f'Fly Ball ({flyball})',f'Pop-up ({popup})']
hard_hit_dates = [95]
hard_hit_text = [f'Hard Hit% ({hard_hit_percent})']
#sample_dates = mdates.date2num(sample_dates)
plt.hlines(y=hard_hit_dates,xmin=math.floor((min(data_df.launch_angle.dropna())/10))*10, xmax=math.ceil((max(data_df.launch_angle.dropna())/10))*10, color = colour_palette[4],linestyles='--')
plt.vlines(x=sample_dates, ymin=0, ymax=130, color = colour_palette[3],linestyles='--')
# ax.vlines(x=10,ymin=0,ymax=ev_ranges[-1],color=colour_palette[3],linestyles='--')
# ax.vlines(x=25,ymin=0,ymax=ev_ranges[-1],color=colour_palette[3],linestyles='--')
# ax.vlines(x=50,ymin=0,ymax=ev_ranges[-1],color=colour_palette[3],linestyles='--')
for i, x in enumerate(hard_hit_dates):
text(math.ceil((max(data_df.launch_angle.dropna())/10))*10-2.5, x+1.25,hard_hit_text[i], rotation=0, ha='right',
bbox=dict(facecolor='white',alpha=0.5, edgecolor=colour_palette[4], pad=5))
for i, x in enumerate(sample_dates):
text(x+0.75, (math.floor((min(data_df.launch_speed.dropna())/5))*5)+1,sample_text[i], rotation=90, verticalalignment='bottom',
bbox=dict(facecolor='white',alpha=0.5, edgecolor=colour_palette[3], pad=5))
#ax.vlines(x=math.floor((min(data_df.launch_angle.dropna())/10))*10+1,ymin=0,ymax=ev_ranges[-1],color=colour_palette[3],linestyles='--')
ax.set_xlim((math.floor((min(data_df.launch_angle.dropna())/10))*10,math.ceil((max(data_df.launch_angle.dropna())/10))*10))
ax.set_ylim((math.floor((min(data_df.launch_speed.dropna())/5))*5,math.ceil((max(data_df.launch_speed.dropna())/5))*5+5))
# ax.set_xlim(-90,90)
# ax.set_ylim(0,125)
ax.set_title(f'MLB - {data_df.batter_name.unique()[0]} Launch Angle vs EV Plot', fontsize=18,fontname='Century Gothic',)
#vals = ax.get_yticks()
ax.set_xlabel('Launch Angle', fontsize=16,fontname='Century Gothic')
ax.set_ylabel('Exit Velocity', fontsize=16,fontname='Century Gothic')
ax.fill_between(angle_ev_list_df.launch_angle, 130, angle_ev_list_df.launch_speed, interpolate=True, color=colour_palette[3],alpha=0.1,label='Barrel')
#fig.colorbar(plot_dist, ax=ax)
#fig.colorbar(plot_dist)
#fig.axes[0].invert_yaxis()
ax.legend(fontsize='16',loc='upper left')
fig.text(x=0.03,y=0.02,s='By: @TJStats')
fig.text(x=1-0.03,y=0.02,s='Data: MLB',ha='right')
# fig.text(x=0.25,y=0.02,s='Data: MLB',ha='right')
# fig.text(x=0.25,y=0.02,s='Data: MLB',ha='right')
# fig.text(x=0.25,y=0.02,s='Data: MLB',ha='right')
#cbar = plt.colorbar()
#fig.subplots_adjust(wspace=.02, hspace=.02)
#ax.xaxis.set_major_formatter(FuncFormatter(lambda x, _: int(x)))
fig.set_facecolor('white')
fig.tight_layout()
#matplotlib.rcParams["figure.dpi"] = 300
# ax.set_xlim(input.n(),exit_velo_df_small.pitch.max())
#ax.axis('off')
#fig.set_facecolor('white')
#fig.tight_layout()
#ax.hist(exit_velo_df[exit_velo_df.pitcher_id==int(input.id())]['pitch_velocity'],input.n(),density=True)
#plt.show()
#return g
# This is a shiny.App object. It must be named `app`.
# fig, ax = plt.subplots()
#print(input.pitcher_id())
# print(input)
# plt.hist(x=exit_velo_df[exit_velo_df.pitcher_id==input.x()]['pitch_velocity'])
# plt.show()
ev_angle = App(ui.page_fluid(
ui.tags.base(href=base_url),
ui.tags.div(
{"style": "width:90%;margin: 0 auto;max-width: 1600px;"},
ui.tags.style(
"""
h4 {
margin-top: 1em;font-size:35px;
}
h2{
font-size:25px;
}
"""
),
shinyswatch.theme.simplex(),
ui.tags.h4("TJStats"),
ui.tags.i("Baseball Analytics and Visualizations"),
ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),
ui.navset_tab(
ui.nav_control(
ui.a(
"Home",
href="home/"
),
),
ui.nav_menu(
"Batter Charts",
ui.nav_control(
ui.a(
"Batting Rolling",
href="rolling_batter/"
),
ui.a(
"Spray & Damage",
href="spray/"
),
ui.a(
"Decision Value",
href="decision_value/"
),
# ui.a(
# "Damage Model",
# href="damage_model/"
# ),
ui.a(
"Batter Scatter",
href="batter_scatter/"
),
# ui.a(
# "EV vs LA Plot",
# href="ev_angle/"
# ),
ui.a(
"Statcast Compare",
href="statcast_compare/"
)
),
),
ui.nav_menu(
"Pitcher Charts",
ui.nav_control(
ui.a(
"Pitcher Rolling",
href="rolling_pitcher/"
),
ui.a(
"Pitcher Summary",
href="pitching_summary_graphic_new/"
),
ui.a(
"Pitcher Scatter",
href="pitcher_scatter/"
)
),
)),ui.row(
ui.layout_sidebar(
ui.panel_sidebar(
ui.input_select("id", "Select Batter",batter_dict,width=1),
ui.input_select("plot_id", "Select Plot",{'scatter':'Scatter Plot','dist':'Distribution Plot'},width=1),
ui.input_action_button("go", "Generate",class_="btn-primary",
)),
ui.panel_main(
ui.output_plot("plot",height = "1000px",width="1000px")
),
)),)),server)