Spaces:
Running
Running
import pandas as pd | |
import numpy as np | |
import requests | |
import math | |
import matplotlib.pyplot as plt | |
import seaborn as sns | |
import matplotlib.patches as patches | |
import matplotlib.colors as mcolors | |
import matplotlib | |
import inflect | |
infl = inflect.engine() | |
from matplotlib.offsetbox import (OffsetImage, AnnotationBbox) | |
from matplotlib.colors import Normalize | |
from matplotlib.ticker import FuncFormatter | |
import matplotlib.ticker as mtick | |
from matplotlib.colors import Normalize | |
import urllib | |
import urllib.request | |
import urllib.error | |
from urllib.error import HTTPError | |
import time | |
from shinywidgets import output_widget, render_widget | |
import shinyswatch | |
from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui | |
column_list = ['woba_percent', | |
'xwoba_percent', | |
'barrel_percent', | |
'sweet_spot_percent', | |
'hard_hit_percent', | |
'launch_speed', | |
'launch_speed_90', | |
'max_launch_speed', | |
'k_percent', | |
'bb_percent', | |
'swing_percent', | |
'whiff_rate', | |
'zone_swing_percent', | |
'zone_contact_percent', | |
'chase_percent', | |
'chase_contact'] | |
column_list_pitch = ['pitches','bip','woba_percent_contact','whiff_rate','chase_percent'] | |
import joblib | |
loaded_model = joblib.load('joblib_model/barrel_model.joblib') | |
in_zone_model = joblib.load('joblib_model/in_zone_model_knn_20240410.joblib') | |
stat_plot_dict = {'woba_percent':{'name':'wOBA','format':'.3f','flip':False}, | |
'xwoba_percent':{'name':'xwOBA','format':'.3f','flip':False}, | |
'woba_percent_contact':{'name':'wOBACON','format':'.3f','flip':False}, | |
'xwoba_percent_contact':{'name':'xwOBACON','format':'.3f','flip':False}, | |
'barrel_percent':{'name':'Barrel%','format':'.1%','flip':False}, | |
'max_launch_speed':{'name':'Max EV','format':'.1f','flip':False}, | |
'launch_speed_90':{'name':'90th% EV','format':'.1f','flip':False}, | |
'launch_speed':{'name':'Avg EV','format':'.1f','flip':False}, | |
'sweet_spot_percent':{'name':'SwSpot%','format':'.1%','flip':False}, | |
'hard_hit_percent':{'name':'HardHit%','format':'.1%','flip':False}, | |
'k_percent':{'name':'K%','format':'.1%','flip':True}, | |
'bb_percent':{'name':'BB%','format':'.1%','flip':False}, | |
'zone_contact_percent':{'name':'Z-Contact%','format':'.1%','flip':False}, | |
'zone_swing_percent':{'name':'Z-Swing%','format':'.1%','flip':False}, | |
'zone_percent':{'name':'Zone%','format':'.1%','flip':False}, | |
'chase_percent':{'name':'O-Swing%','format':'.1%','flip':True}, | |
'chase_contact':{'name':'O-Contact%','format':'.1%','flip':False}, | |
'swing_percent':{'name':'Swing%','format':'.1%','flip':False}, | |
'whiff_rate':{'name':'Whiff%','format':'.1%','flip':True}, | |
'bip':{'name':'Balls in Play','format':'.0f','flip':False}, | |
'pitches':{'name':'Pitches','format':'.0f','flip':False},} | |
stat_plot_dict_rolling = {'woba_percent':{'name':'wOBA','format':'.3f','flip':False,'y':'woba','div':'woba_codes','y_min':0.2,'y_max':0.6,'x_label':'wOBA PA','form':'3f'}, | |
'xwoba_percent':{'name':'xwOBA','format':'.3f','flip':False,'y':'xwoba','div':'woba_codes','y_min':0.2,'y_max':0.6,'x_label':'xwOBA PA','form':'3f'}, | |
'k_percent':{'name':'K%','format':'.1%','flip':True,'y':'k','div':'pa','y_min':0.0,'y_max':0.4,'x_label':'PA','form':'1%'}, | |
'bb_percent':{'name':'BB%','format':'.1%','flip':False,'y':'bb','div':'pa','y_min':0.0,'y_max':0.3,'x_label':'PA','form':'1%'}, | |
'zone_contact_percent':{'name':'Z-Contact%','format':'.1%','flip':False,'y':'zone_contact','div':'zone_swing','y_min':0.6,'y_max':1.0,'x_label':'In-Zone Swings','form':'1%'}, | |
'zone_swing_percent':{'name':'Z-Swing%','format':'.1%','flip':False,'y':'zone_swing','div':'in_zone','y_min':0.5,'y_max':1.0,'x_label':'In-Zone Pitches','form':'1%'}, | |
'zone_percent':{'name':'Zone%','format':'.1%','flip':False,'y':'in_zone','div':'pitches','y_min':0.3,'y_max':0.7,'x_label':'Pitches','form':'1%'}, | |
'chase_percent':{'name':'O-Swing%','format':'.1%','flip':True,'y':'ozone_swing','div':'out_zone','y_min':0.1,'y_max':0.4,'x_label':'Out-of-Zone Pitches','form':'1%'}, | |
'chase_contact':{'name':'O-Contact%','format':'.1%','flip':False,'y':'ozone_contact','div':'ozone_swing','y_min':0.4,'y_max':0.8,'x_label':'Out-of-Zone Swings','form':'1%'}, | |
'swing_percent':{'name':'Swing%','format':'.1%','flip':False,'y':'swings','div':'pitches','y_min':0.3,'y_max':0.7,'x_label':'Pitches','form':'1%'}, | |
'whiff_rate':{'name':'Whiff%','format':'.1%','flip':True,'y':'whiffs','div':'swings','y_min':0.0,'y_max':0.5,'x_label':'Swings','form':'1%'},} | |
cmap_sum = matplotlib.colors.LinearSegmentedColormap.from_list("", ["#0C7BDC","#FFFFFF","#FFB000"]) | |
cmap_sum_r = matplotlib.colors.LinearSegmentedColormap.from_list("", ["#FFB000","#FFFFFF","#0C7BDC",]) | |
cmap_sum.set_bad(color='#C7C7C7', alpha=1.0) | |
cmap_sum_r.set_bad(color='#C7C7C7', alpha=1.0) | |
from batting_update import df_update,df_update_summ_avg,df_update_summ,df_summ_batter_pitch_up,df_summ_changes,df_summ_filter_out | |
def percentile(n): | |
def percentile_(x): | |
return np.nanpercentile(x, n) | |
percentile_.__name__ = 'percentile_%s' % n | |
return percentile_ | |
print('Reading A') | |
### Import Datasets | |
from datasets import load_dataset | |
dataset = load_dataset('nesticot/mlb_data', data_files=['aaa_pitch_data_2024.csv' ]) | |
dataset_train = dataset['train'] | |
df_a = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True) | |
# from api_scraper import MLB_Scrape | |
# from datetime import datetime, timedelta | |
# mlb_stats = MLB_Scrape() | |
# schedule_spring = mlb_stats.get_schedule(year_input=2024, | |
# sport_id=11, | |
# start_date='2024-01-01', | |
# end_date='2024-12-31', | |
# final=False, | |
# regular=True, | |
# spring=False) | |
# schedule_spring = schedule_spring.drop_duplicates(subset=['game_id']) | |
# schedule_spring = schedule_spring[(schedule_spring['date']==(datetime.today() - timedelta(hours=8)).date())] | |
# data = mlb_stats.get_data(schedule_spring.game_id[:].values) | |
# df_2023_new = mlb_stats.get_data_df(data_list = data) | |
# df_a = pd.concat([df_a,df_2023_new]) | |
# df_a = df_a.drop_duplicates(subset=['play_id'],keep='last') | |
import datetime | |
date_string = df_a['game_date'].min() | |
date_min = datetime.datetime.strptime(date_string, "%Y-%m-%d").date() | |
sport_id_input = 11 | |
print('Reading A') | |
df_a_update = df_update(df_a) | |
#df_a_update['batter_id'] = df_a_update['batter_id'].astype(int) | |
df_a_update['batter_name'] = df_a_update['batter_name'].str.strip(' ') | |
df_a_update['bip'] = df_a_update['bip'].replace({'0':False,'False':False,'True':True}) | |
choices_woba = [0.696, | |
0.726, | |
0.883, | |
1.244, | |
1.569, | |
2.004] | |
woba_codes = ['strikeout', 'field_out', 'single', 'walk', 'hit_by_pitch', | |
'double', 'sac_fly', 'force_out', 'home_run', | |
'grounded_into_double_play', 'fielders_choice', 'field_error', | |
'triple', 'sac_bunt', 'double_play', | |
'fielders_choice_out', 'strikeout_double_play', | |
'sac_fly_double_play', 'other_out'] | |
df_a_update['bip_div'] = ~df_a_update.launch_speed.isna() | |
# df_dom_update['bip_div'] = ~df_dom_update.launch_speed.isna() | |
df_a_update['average'] = 'average' | |
#df_dom_update['average'] = 'average' | |
#df_u['is_pitch'] | |
df_summ_a_update = df_summ_changes(df_update_summ(df_a_update)).set_index(['batter_id','batter_name']) | |
# df_summ_dom_update = df_summ_changes(df_update_summ(df_dom_update)).set_index(['batter_id','batter_name']) | |
df_summ_avg_a_update = df_summ_changes(df_update_summ_avg(df_a_update)).set_index(['average']) | |
# df_summ_avg_dom_update = df_summ_changes(df_update_summ_avg(df_dom_update)).set_index(['average']) | |
stat_roll_dict = dict(zip(stat_plot_dict_rolling.keys(), | |
[stat_plot_dict_rolling[x]['name'] for x in stat_plot_dict_rolling])) | |
df_a_update['batter_id'] = df_a_update['batter_id'].astype(float).astype(int) | |
a_player_dict = df_a_update.drop_duplicates( | |
'batter_id')[['batter_id','batter_name']].sort_values(by='batter_name').set_index('batter_id').to_dict()['batter_name'] | |
# dom_player_dict = df_summ_dom_update.reset_index().drop_duplicates( | |
# 'batter_id')[['batter_id','batter_name']].sort_values(by='batter_name').set_index('batter_id').to_dict()['batter_name'] | |
import api_scraper | |
mlb_stats = api_scraper.MLB_Scrape() | |
def get_color(value, vmin, vmax, cmap_name=cmap_sum): | |
# Normalize the value within the range [0, 1] | |
normalized_value = (value - vmin) / (vmax - vmin) | |
# Get the colormap | |
cmap = plt.get_cmap(cmap_name) | |
# Map the normalized value to a color in the colormap | |
color = cmap(normalized_value) | |
# Convert the color from RGBA to hexadecimal format | |
hex_color = mcolors.rgb2hex(color) | |
return hex_color | |
def server(input, output, session): | |
def test(): | |
# @reactive.Effect | |
return ui.input_select("player_id", "Select Batter",a_player_dict,selectize=True) | |
# if input.my_tabs() == 'LIDOM': | |
# return ui.input_select("player_id", "Select Batter",dom_player_dict,selectize=True) | |
def a_plot(): | |
### Iniput data for the level | |
#time.sleep(2) | |
df_update = df_a_update.copy() | |
df_summ_update = df_summ_a_update.copy() | |
df_summ_avg_update = df_summ_avg_a_update.copy() | |
if len(input.player_id()) < 1: | |
fig, ax = plt.subplots(1,1,figsize=(10,10)) | |
ax.text(s='Please Select a Batter',x=0.5,y=0.5, ha='center') | |
ax.axis('off') | |
return fig | |
batter_select = int(input.player_id()) | |
df_roll = df_update[df_update['batter_id']==batter_select] | |
if len(df_roll) == 0: | |
fig, ax = plt.subplots(1,1,figsize=(10,10)) | |
ax.text(s='Card is Generating',x=0.5,y=0.5, ha='center') | |
ax.axis('off') | |
return fig | |
df_summ_filter = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select,date_min=date_min)[0] | |
df_summ_filter_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select,date_min=date_min)[1] | |
df_summ_player = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select,date_min=date_min)[2] | |
df_summ_player_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select,date_min=date_min)[3] | |
df_summ_batter_pitch = df_summ_batter_pitch_up(df= df_update).set_index(['batter_id','batter_name','pitch_category']) | |
df_summ_batter_pitch_pct = df_summ_batter_pitch.loc[df_summ_filter.index.get_level_values(0)] | |
df_summ_batter_pitch_pct = df_summ_batter_pitch_pct[df_summ_batter_pitch_pct['pitches']>0] | |
df_summ_batter_pitch_pct_rank = df_summ_batter_pitch_pct.groupby(level='pitch_category').apply(lambda x: x.rank(pct=True)).xs(batter_select,level=0) | |
df_summ_batter_pitch_pct_rank['pitch_count'] = df_summ_batter_pitch_pct_rank.index.get_level_values(1).map(df_summ_batter_pitch.xs(batter_select,level=0).reset_index().set_index('pitch_category')['pitches'].to_dict()) | |
df_summ_batter_pitch_pct_rank = df_summ_batter_pitch_pct_rank.sort_values('pitch_count',ascending=False) | |
#df_summ_batter_pitch_pct_rank = df_summ_batter_pitch_pct_rank.dropna() | |
def rolling_plot(stat='k_percent',window_width=100,ax=0,df_r=df_roll,df_r_summ_avg=pd.DataFrame(),stat_plot_dict_rolling=stat_plot_dict_rolling): | |
plot = sns.lineplot(x=range(window_width,len(df_r[df_r[stat_plot_dict_rolling[stat]['div']]>0])+1), | |
y=df_r[df_r[stat_plot_dict_rolling[stat]['div']]==1].fillna(0).rolling(window=window_width)[stat_plot_dict_rolling[stat]['y']].sum().dropna()/window_width, | |
ax=ax, | |
color="#FFB000", | |
zorder=10) | |
# ["#0C7BDC","#FFFFFF","#FFB000"]) | |
ax.set_xlim(window_width,len(df_r[df_r[stat_plot_dict_rolling[stat]['div']]==1])) | |
ax.set_xlabel(stat_plot_dict_rolling[stat]['x_label'],fontsize=8) | |
ax.set_ylabel(stat_plot_dict_rolling[stat]['name'],fontsize=8) | |
ax.hlines(df_r_summ_avg[stat_plot_dict_rolling[stat]['y']]/df_r_summ_avg[stat_plot_dict_rolling[stat]['div']], | |
xmin=window_width, | |
xmax=len(df_r[df_r[stat_plot_dict_rolling[stat]['div']]==1]), | |
color="#0C7BDC",linestyles='-.') | |
ax.hlines(sum(df_r[stat_plot_dict_rolling[stat]['y']].dropna())/sum(df_r[stat_plot_dict_rolling[stat]['div']].dropna()), | |
xmin=window_width, | |
xmax=len(df_r[df_r[stat_plot_dict_rolling[stat]['div']]==1]), | |
color="#FFB000",linestyles='--') | |
#print(sum(df_r[stat_plot_dict_rolling[stat]['y']].dropna())/sum(df_r[stat_plot_dict_rolling[stat]['div']].dropna())) | |
ax.tick_params(axis='x', labelsize=8) # Set x-axis ticks size | |
ax.tick_params(axis='y', labelsize=8) # Set y-axis ticks size | |
ax.set_title(f"{window_width} {stat_plot_dict_rolling[stat]['x_label']} Rolling {stat_plot_dict_rolling[stat]['name']}",fontsize=8) | |
ax.set_ylim(stat_plot_dict_rolling[stat]['y_min'],stat_plot_dict_rolling[stat]['y_max']) | |
ax.grid(True,alpha=0.2) | |
if stat_plot_dict_rolling[stat]['form'] == '3f': | |
ax.yaxis.set_major_formatter(mtick.StrMethodFormatter('{x:.3f}')) | |
elif stat_plot_dict_rolling[stat]['form'] == '1f': | |
ax.yaxis.set_major_formatter(mtick.StrMethodFormatter('{x:.1f}')) | |
elif stat_plot_dict_rolling[stat]['form'] == '1%': | |
ax.yaxis.set_major_formatter(mtick.PercentFormatter(1)) | |
return plot | |
dict_level = {1:'MLB', | |
11:'MiLB AAA', | |
12:'MiLB AA', | |
13:'MiLB High-A', | |
14:'MiLB A'} | |
def plot_card(sport_id_input=sport_id_input, | |
batter_select=batter_select, | |
df_roll=df_roll, | |
df_summ_player=df_summ_player, | |
df_summ_update = df_summ_update, | |
df_summ_batter_pitch_pct=df_summ_batter_pitch_pct, | |
): | |
#player_df = get_players(sport_id=sport_id_input) | |
mlb_teams = mlb_stats.get_teams() | |
team_logos = pd.read_csv('team_logos.csv') | |
if sport_id_input == 1: | |
player_bio = requests.get(f'https://statsapi.mlb.com/api/v1/people?personIds={batter_select}&appContext=majorLeague&hydrate=currentTeam').json() | |
else: | |
player_bio = requests.get(f'https://statsapi.mlb.com/api/v1/people?personIds={batter_select}&appContext=minorLeague&hydrate=currentTeam').json() | |
fig = plt.figure(figsize=(10, 10))#,dpi=600) | |
plt.rcParams.update({'figure.autolayout': True}) | |
fig.set_facecolor('white') | |
sns.set_theme(style="whitegrid", palette="pastel") | |
from matplotlib.gridspec import GridSpec | |
gs = GridSpec(5, 5, width_ratios=[0.2,1,1,1,0.2], height_ratios=[0.6,0.05,0.15,.30,0.025]) | |
gs.update(hspace=0.4, wspace=0.5) | |
# gs.update(left=0.1,right=0.9,top=0.97,bottom=0.03,wspace=0.3,hspace=0.09) | |
# ax1 = plt.subplot(4,1,1) | |
# ax2 = plt.subplot(2,2,2) | |
# ax3 = plt.subplot(2,2,3) | |
# ax4 = plt.subplot(4,1,4) | |
#ax2 = plt.subplot(3,3,2) | |
# Add subplots to the grid | |
ax = fig.add_subplot(gs[0, :]) | |
#ax1 = fig.add_subplot(gs[2, 0]) | |
# ax2 = fig.add_subplot(gs[2, :]) # Subplot at the top-right position | |
# fig, ax = plt.subplots(1,1,figsize=(10,12)) | |
ax.axis('off') | |
width = 0.08 | |
height = width*2.45 | |
if df_summ_player['launch_speed'].isna().values[0]: | |
df_summ_player['sweet_spot_percent'] = np.nan | |
df_summ_player['barrel_percent'] = np.nan | |
df_summ_player['hard_hit_percent'] = np.nan | |
df_summ_player['xwoba_percent'] = np.nan | |
df_summ_player['woba_percent_contact'] = np.nan | |
if df_summ_player_pct['launch_speed'].isna().values[0]: | |
df_summ_player_pct['sweet_spot_percent'] = np.nan | |
df_summ_player_pct['barrel_percent'] = np.nan | |
df_summ_player_pct['hard_hit_percent'] = np.nan | |
df_summ_player_pct['xwoba_percent'] = np.nan | |
df_summ_player['woba_percent_contact'] = np.nan | |
if df_summ_batter_pitch['launch_speed'].isna().values[0]: | |
df_summ_batter_pitch['woba_percent_contact'] = np.nan | |
# x = 0.1 | |
# y = 0.9 | |
for cat in range(len(column_list)): | |
# if cat < len(column_list)/2: | |
x_adjust, y_adjust =(0.85/7*8)*cat/8+0.075 - (0.85/7*8)*math.floor((cat)/8), 0.45-math.floor((cat)/8)/3.2 | |
# else: | |
# x_adjust, y_adjust = (cat-len(column_list)/2)*(1.7/(math.ceil((len(column_list)-1))))+0.1, 0.5 | |
#print( x_adjust, y_adjust) | |
if sum(df_summ_player[column_list[cat]].isna()) < 1: | |
print(f'{df_summ_player[column_list[cat]].values[0]:{stat_plot_dict[column_list[cat]]["format"]}}') | |
ax.text(s = f'{df_summ_player[column_list[cat]].values[0]:{stat_plot_dict[column_list[cat]]["format"]}}'.format().strip(), | |
x = x_adjust, | |
y = y_adjust, | |
color='black', | |
#bbox=dict(facecolor='none', edgecolor='black', pad=10.0), | |
fontsize = 16, | |
ha='center', | |
va='center') | |
if stat_plot_dict[column_list[cat]]['flip']: | |
bbox = patches.Rectangle((x_adjust- width/2,y_adjust- height/2), width, height, linewidth=1,edgecolor='black', | |
facecolor = get_color(df_summ_player_pct[column_list[cat]].values[0],0,1,cmap_name=cmap_sum_r)) | |
ax.add_patch(bbox) | |
else: | |
bbox = patches.Rectangle((x_adjust- width/2,y_adjust- height/2), width, height, linewidth=1,edgecolor='black', | |
facecolor = get_color(df_summ_player_pct[column_list[cat]].values[0],0,1,cmap_name=cmap_sum)) | |
ax.add_patch(bbox) | |
else: | |
print(f'{df_summ_player[column_list[cat]].values[0]:{stat_plot_dict[column_list[cat]]["format"]}}') | |
ax.text(s = f'{df_summ_player[column_list[cat]].fillna("N/A").values[0]}', | |
x = x_adjust, | |
y = y_adjust, | |
color='black', | |
#bbox=dict(facecolor='none', edgecolor='black', pad=10.0), | |
fontsize = 14, | |
ha='center', | |
va='center') | |
if stat_plot_dict[column_list[cat]]['flip']: | |
bbox = patches.Rectangle((x_adjust- width/2,y_adjust- height/2), width, height, linewidth=1,edgecolor='black', | |
facecolor = get_color(df_summ_player_pct[column_list[cat]].values[0],0,1,cmap_name=cmap_sum_r)) | |
ax.add_patch(bbox) | |
else: | |
bbox = patches.Rectangle((x_adjust- width/2,y_adjust- height/2), width, height, linewidth=1,edgecolor='black', | |
facecolor = get_color(df_summ_player_pct[column_list[cat]].values[0],0,1,cmap_name=cmap_sum)) | |
ax.add_patch(bbox) | |
ax.text(s = stat_plot_dict[column_list[cat]]['name'], | |
x = x_adjust, | |
y = y_adjust-0.14, | |
color='black', | |
#bbox=dict(facecolor='none', edgecolor='black', pad=10.0), | |
fontsize = 12, | |
ha='center', | |
va='center') | |
ax.text(s = f"{player_bio['people'][0]['fullName']}", | |
x = 0.5, | |
y = 0.95, | |
color='black', | |
#bbox=dict(facecolor='none', edgecolor='black', pad=10.0), | |
fontsize = 28, | |
ha='center', | |
va='center') | |
if 'parentOrgId' in player_bio['people'][0]['currentTeam']: | |
ax.text(s = f"{player_bio['people'][0]['primaryPosition']['abbreviation']}, {mlb_teams[mlb_teams['team_id'] == player_bio['people'][0]['currentTeam']['parentOrgId']]['franchise'].values[0]}", | |
x = 0.5, | |
y = 0.85, | |
color='black', | |
#bbox=dict(facecolor='none', edgecolor='black', pad=10.0), | |
fontsize = 14, | |
ha='center', | |
va='center') | |
else: ax.text(s = f"{player_bio['people'][0]['primaryPosition']['abbreviation']}, {player_bio['people'][0]['currentTeam']['name']}", | |
x = 0.5, | |
y = 0.85, | |
color='black', | |
#bbox=dict(facecolor='none', edgecolor='black', pad=10.0), | |
fontsize = 14, | |
ha='center', | |
va='center') | |
ax.text(s = | |
f"B/T: {player_bio['people'][0]['batSide']['code']}/" | |
f"{player_bio['people'][0]['pitchHand']['code']} " | |
f"{player_bio['people'][0]['height']}/" | |
f"{player_bio['people'][0]['weight']}", | |
x = 0.5, | |
y = 0.785, | |
color='black', | |
#bbox=dict(facecolor='none', edgecolor='black', pad=10.0), | |
fontsize = 14, | |
ha='center', | |
va='center') | |
ax.text(s = | |
f"DOB: {player_bio['people'][0]['birthDate']} " | |
f"Age: {player_bio['people'][0]['currentAge']}", | |
x = 0.5, | |
y = 0.72, | |
color='black', | |
#bbox=dict(facecolor='none', edgecolor='black', pad=10.0), | |
fontsize = 14, | |
ha='center', | |
va='center') | |
if sport_id_input == 1: | |
try: | |
url = f'https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_213,q_auto:best/v1/people/{batter_select}/headshot/67/current.png' | |
test_mage = plt.imread(url) | |
except urllib.error.HTTPError as err: | |
url = f'https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_213,q_auto:best/v1/people/1/headshot/67/current.png' | |
else: | |
try: | |
url = f'https://img.mlbstatic.com/mlb-photos/image/upload/c_fill,g_auto/w_180/v1/people/{batter_select}/headshot/milb/current.png' | |
test_mage = plt.imread(url) | |
except urllib.error.HTTPError as err: | |
url = f'https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_213,q_auto:best/v1/people/1/headshot/67/current.png' | |
im = plt.imread(url) | |
# response = requests.get(url) | |
# im = Image.open(BytesIO(response.content), cmap='viridis') | |
# im = plt.imread(np.array(PIL.Image.open(urllib.request.urlopen(url)))) | |
# ax = fig.add_axes([0,0,1,0.85], anchor='C', zorder=1) | |
imagebox = OffsetImage(im, zoom = 0.3) | |
ab = AnnotationBbox(imagebox, (0.125, 0.8), frameon = False) | |
ax.add_artist(ab) | |
if 'parentOrgId' in player_bio['people'][0]['currentTeam']: | |
url = team_logos[team_logos['id'] == player_bio['people'][0]['currentTeam']['parentOrgId']]['imageLink'].values[0] | |
im = plt.imread(url) | |
# response = requests.get(url) | |
# im = Image.open(BytesIO(response.content)) | |
# im = plt.imread(team_logos[team_logos['id'] == player_bio['people'][0]['currentTeam']['parentOrgId']]['imageLink'].values[0]) | |
# ax = fig.add_axes([0,0,1,0.85], anchor='C', zorder=1) | |
imagebox = OffsetImage(im, zoom = 0.225) | |
ab = AnnotationBbox(imagebox, (0.875, 0.8), frameon = False) | |
ax.add_artist(ab) | |
else: | |
url = team_logos[team_logos['id'] == player_bio['people'][0]['currentTeam']['id']]['imageLink'].values[0] | |
im = plt.imread(team_logos[team_logos['id'] == player_bio['people'][0]['currentTeam']['id']]['imageLink'].values[0]) | |
# im = plt.imread(url) | |
# response = requests.get(url) | |
# im = Image.open(BytesIO(response.content)) | |
#im = plt.imread(team_logos[team_logos['id'] == player_bio['people'][0]['currentTeam']['parentOrgId']]['imageLink'].values[0]) | |
# ax = fig.add_axes([0,0,1,0.85], anchor='C', zorder=1) | |
imagebox = OffsetImage(im, zoom = 0.225) | |
ab = AnnotationBbox(imagebox, (0.875, 0.8), frameon = False) | |
ax.add_artist(ab) | |
ax.text(s = f'2024 {dict_level[sport_id_input]} Metrics', | |
x = 0.5, | |
y = 0.62, | |
color='black', | |
#bbox=dict(facecolor='none', edgecolor='black', pad=10.0), | |
fontsize = 20, | |
ha='center', | |
va='center') | |
df_plot = df_summ_batter_pitch[column_list_pitch].xs([batter_select,df_summ_update.xs(batter_select,level=0).index[0]]).sort_values('pitches',ascending=False).fillna('—') | |
df_plot = df_plot[df_plot['pitches'] > 0] | |
df_plot_pct = df_summ_batter_pitch_pct[column_list_pitch].xs([batter_select,df_summ_update.xs(batter_select,level=0).index[0]]).sort_values('pitches',ascending=False)#.dropna() | |
value = 1 | |
# Normalize the value | |
colormap = plt.get_cmap(cmap_sum) | |
colormap_r = plt.get_cmap(cmap_sum_r) | |
norm = Normalize(vmin=0, vmax=1) | |
col_5_colour = [colormap_r(norm(x)) for x in list((df_summ_batter_pitch_pct_rank['chase_percent']))] | |
col_4_colour = [colormap_r(norm(x)) for x in list((df_summ_batter_pitch_pct_rank['whiff_rate']))] | |
col_3_colour = [colormap(norm(x)) for x in list((df_summ_batter_pitch_pct_rank['woba_percent_contact']))] | |
col_2_colour = ['white']*len(df_summ_batter_pitch_pct_rank) | |
col_1_colour = ['white']*len(df_summ_batter_pitch_pct_rank) | |
colour_df = pd.DataFrame(data=[col_1_colour,col_2_colour,col_3_colour,col_4_colour,col_5_colour]).T.values | |
ax_table = fig.add_subplot(gs[2, 1:-1]) | |
ax_table.axis('off') | |
print(colour_df) | |
print(df_plot) | |
table = ax_table.table(cellText=df_plot.values, colLabels=[stat_plot_dict[x]['name'] for x in df_plot.columns],rowLabels=df_plot.index, cellLoc='center', | |
bbox=[0.13, 0.0, 0.79, 1],colWidths=[0.1]*len(df_plot.columns), | |
loc='center',cellColours=colour_df) | |
ax_table.text(x=0.5,y=1.1,s='Metrics By Pitch Type',ha='center',fontdict={ 'size': 12},fontname='arial') | |
w, h = table[0,1].get_width(), table[0,1].get_height() | |
cell_i = table.add_cell(0, -1, w,h, text='Pitch Type') | |
cell_i.get_text().set_horizontalalignment('left') | |
min_font_size = 12 | |
if len(df_plot) >3: | |
min_font_size = 10 | |
# Set table properties | |
table.auto_set_font_size(False) | |
table.set_fontsize(min_font_size) | |
#table.set_fontname('arial') | |
table.scale(1, len(df_plot)*0.3) | |
int_list = ['pitches','bip'] | |
for fl in int_list: | |
# Subset of column names | |
subset_columns = [fl] | |
# Get the list of column indices | |
column_indices = [df_plot.columns.get_loc(col) for col in subset_columns] | |
# # print(column_indices) | |
for row_l in range(1,len(df_plot)+1): | |
# print(row_l) | |
if table.get_celld()[(row_l,column_indices[0])].get_text().get_text() != '—': | |
# print() | |
# print(fl) | |
table.get_celld()[(row_l,column_indices[0])].get_text().set_text('{:,.0f}'.format(float(table.get_celld()[(row_l,column_indices[0])].get_text().get_text().strip('%')))) | |
float_3_list = ['woba_percent_contact'] | |
for fl in float_3_list: | |
# Subset of column names | |
subset_columns = [fl] | |
# Get the list of column indices | |
column_indices = [df_plot.columns.get_loc(col) for col in subset_columns] | |
# # print(column_indices) | |
for row_l in range(1,len(df_plot)+1): | |
# print(row_l) | |
if table.get_celld()[(row_l,column_indices[0])].get_text().get_text() != '—': | |
# print() | |
# print(fl) | |
table.get_celld()[(row_l,column_indices[0])].get_text().set_text('{:,.3f}'.format(float(table.get_celld()[(row_l,column_indices[0])].get_text().get_text().strip('%')))) | |
percent_list = ['whiff_rate','chase_percent'] | |
for fl in percent_list: | |
# Subset of column names | |
subset_columns = [fl] | |
# Get the list of column indices | |
column_indices = [df_plot.columns.get_loc(col) for col in subset_columns] | |
# # print(column_indices) | |
for row_l in range(1,len(df_plot)+1): | |
# print(row_l) | |
if table.get_celld()[(row_l,column_indices[0])].get_text().get_text() != '—': | |
# print(fl) | |
table.get_celld()[(row_l,column_indices[0])].get_text().set_text('{:,.1%}'.format(float(table.get_celld()[(row_l,column_indices[0])].get_text().get_text().strip('%')))) | |
stat_1 = input.stat_1() | |
window_width_1 = input.window_1() | |
stat_2 = input.stat_2() | |
window_width_2 = input.window_2() | |
stat_3 = input.stat_3() | |
window_width_3 = input.window_3() | |
inset_ax = ax = fig.add_subplot(gs[3, 1]) | |
rolling_plot(stat=stat_1,window_width=window_width_1,ax=inset_ax,df_r=df_roll,df_r_summ_avg=df_summ_avg_update) | |
inset_ax = ax = fig.add_subplot(gs[3, 2]) | |
rolling_plot(stat=stat_2,window_width=window_width_2,ax=inset_ax,df_r=df_roll,df_r_summ_avg=df_summ_avg_update) | |
inset_ax = ax = fig.add_subplot(gs[3, 3]) | |
rolling_plot(stat=stat_3,window_width=window_width_3,ax=inset_ax,df_r=df_roll,df_r_summ_avg=df_summ_avg_update) | |
ax_bot = ax = fig.add_subplot(gs[4, :]) | |
ax_bot.text(x=0.05,y=-0.5,s='By: @TJStats',ha='left',fontdict={ 'size': 14},fontname='arial') | |
ax_bot.text(x=1-0.05,y=-0.5,s='Data: MLB',ha='right',fontdict={ 'size': 14},fontname='arial') | |
ax_bot.axis('off') | |
ax_cbar = fig.add_subplot(gs[1,1:-1]) | |
cb = matplotlib.colorbar.ColorbarBase(ax_cbar, orientation='horizontal', | |
cmap=cmap_sum) | |
#ax_cbar.axis('off') | |
ax_cbar.text(x=0.5,y=1.2,s='Colour Scale - Percentiles',ha='center',fontdict={ 'size': 12},fontname='arial') | |
ax_cbar.text(s='0%',x=0.01,y=0.5,va='center',ha='left') | |
ax_cbar.text(s='100%',x=0.99,y=0.5,va='center',ha='right') | |
# ax_cbar.text(s='50%',x=0.5,y=0.5,va='center',ha='center') | |
# ax_cbar.text(s='50%',x=0.5,y=0.5,va='center',ha='center') | |
# ax_cbar.text(s='50%',x=0.5,y=0.5,va='center',ha='center') | |
ax_cbar.set_xticks([]) | |
ax_cbar.set_yticks([]) | |
ax_cbar.set_xticklabels([]) | |
ax_cbar.set_yticklabels([]) | |
# Display only the outline of the axis | |
for spine in ax_cbar.spines.values(): | |
spine.set_visible(True) # Show only the outline | |
spine.set_color('black') # Set the color to black | |
# fig.set_facecolor('#ffffff') | |
return fig.subplots_adjust(left=0.03, right=0.97, top=0.95, bottom=0.05) | |
return plot_card(sport_id_input=sport_id_input, | |
batter_select=batter_select, | |
df_roll=df_roll, | |
df_summ_player=df_summ_player, | |
df_summ_batter_pitch_pct=df_summ_batter_pitch_pct, | |
) | |
from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui | |
app = 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.row( | |
ui.tags.h5("AAA Batter Cards"), | |
ui.layout_sidebar( | |
ui.panel_sidebar(ui.output_ui('test',"Select Batter"), | |
ui.input_select('stat_1',"Select Rolling Stat 1",stat_roll_dict,selectize=True), | |
ui.input_numeric('window_1',"Select Rolling Window 1",value=100), | |
ui.input_select('stat_2',"Select Rolling Stat 2",stat_roll_dict,selected='k_percent',selectize=True), | |
ui.input_numeric('window_2',"Select Rolling Stat 2",value=100), | |
ui.input_select('stat_3',"Select Rolling Stat 3",stat_roll_dict,selected='bb_percent',selectize=True), | |
ui.input_numeric('window_3',"Select Rolling Stat 3",value=100), | |
ui.input_action_button("go", "Generate",class_="btn-primary"),width=2), | |
ui.page_navbar( | |
ui.nav_panel("Player Cards", | |
ui.output_plot('a_plot',width='1000px',height='1000px')), | |
id="my_tabs", | |
))),)),server) | |
# app = App(app_ui, server) | |