Spaces:
Sleeping
Sleeping
| 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)) | |
| def server(input,output,session): | |
| 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) |