2024_mlb_cards / app.py
nesticot's picture
Update app.py
e1b5a44 verified
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','xwoba_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=['mlb_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)
import datetime
date_string = df_a['game_date'].min()
date_min = datetime.datetime.strptime(date_string, "%Y-%m-%d").date()
sport_id_input = 1
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):
@render.ui
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)
@output
@render.plot(alt="A Plot")
@reactive.event(input.go, ignore_none=False)
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['xwoba_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['xwoba_percent_contact'] = np.nan
if df_summ_batter_pitch['launch_speed'].isna().values[0]:
df_summ_batter_pitch['xwoba_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 = ['xwoba_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("MLB 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)