diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -1,513 +1,3068 @@ import pandas as pd -import seaborn as sns -import matplotlib.pyplot as plt -from matplotlib.pyplot import figure -from matplotlib.offsetbox import OffsetImage, AnnotationBbox -from scipy import stats -import pickle -import json -from datetime import timedelta -from urllib.request import urlopen -from datetime import date -from datetime import datetime -import pytz -import json -from matplotlib.ticker import MaxNLocator -import matplotlib.font_manager as font_manager 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 + +column_list = ['woba_percent', + 'woba_percent_contact', + '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'] + +stat_plot_dict = {'woba_percent':{'name':'wOBA','format':'.3f','flip':False}, + 'woba_percent_contact':{'name':'wOBACON','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':'.00f','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'}, + 'woba_percent_contact':{'name':'wOBACON','format':'.3f','flip':False,'y':'woba_contact','div':'bip','y_min':0.2,'y_max':0.6,'x_label':'Balls in Play','form':'3f'}, + 'barrel_percent':{'name':'Barrel%','format':'.1%','flip':False,'y':'barrel','div':'in_play','y_min':0.0,'y_max':0.3,'x_label':'Balls in Play','form':'1%'}, + 'launch_speed':{'name':'Avg EV','format':'.1f','flip':False,'y':'launch_speed','div':'in_play','y_min':0.0,'y_max':0.3,'x_label':'Balls in Play','form':'1f'}, + 'sweet_spot_percent':{'name':'SwSpot%','format':'.1%','flip':False,'y':'sweet_spot','div':'in_play','y_min':0.2,'y_max':0.8,'x_label':'Balls in Play','form':'1%'}, + 'hard_hit_percent':{'name':'HardHit%','format':'.1%','flip':False,'y':'hard_hit','div':'in_play','y_min':0.0,'y_max':0.6,'x_label':'Balls in Play','form':'1%'}, + '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) + +def percentile(n): + def percentile_(x): + return np.nanpercentile(x, n) + percentile_.__name__ = 'percentile_%s' % n + return percentile_ + +def df_update(df=pd.DataFrame()): + hit_codes = ['single', + 'double','home_run', 'triple'] + + ab_codes = ['single', 'strikeout', 'field_out', + 'grounded_into_double_play', 'fielders_choice', 'force_out', + 'double', 'field_error', 'home_run', 'triple', + 'double_play', + 'fielders_choice_out', 'strikeout_double_play', + 'other_out','triple_play'] + + + obp_true_codes = ['single', 'walk', + 'double','home_run', 'triple', + 'hit_by_pitch', 'intent_walk'] + + obp_codes = ['single', 'strikeout', 'walk', 'field_out', + 'grounded_into_double_play', 'fielders_choice', 'force_out', + 'double', 'sac_fly', 'field_error', 'home_run', 'triple', + 'hit_by_pitch', 'double_play', 'intent_walk', + 'fielders_choice_out', 'strikeout_double_play', + 'sac_fly_double_play', + 'other_out','triple_play'] + + + contact_codes = ['In play, no out', + 'Foul', 'In play, out(s)', + 'In play, run(s)', + 'Foul Bunt'] + + + + conditions_hit = [df.event_type.isin(hit_codes)] + choices_hit = [True] + df['hits'] = np.select(conditions_hit, choices_hit, default=False) + + conditions_ab = [df.event_type.isin(ab_codes)] + choices_ab = [True] + df['ab'] = np.select(conditions_ab, choices_ab, default=False) + + conditions_obp_true = [df.event_type.isin(obp_true_codes)] + choices_obp_true = [True] + df['on_base'] = np.select(conditions_obp_true, choices_obp_true, default=False) + + conditions_obp = [df.event_type.isin(obp_codes)] + choices_obp = [True] + df['obp'] = np.select(conditions_obp, choices_obp, default=False) + + df['bip'] = ~df.launch_speed.isna() + conditions = [ + (df['launch_speed'].isna()), + (df['launch_speed']*1.5 - df['launch_angle'] >= 117 ) & (df['launch_speed'] + df['launch_angle'] >= 124) & (df['launch_speed'] > 98) & (df['launch_angle'] >= 8) & (df['launch_angle'] <= 50) + ] + + choices = [False,True] + df['barrel'] = np.select(conditions, choices, default=np.nan) -# team_games_df = pd.read_csv('data/team_games_all.csv',index_col=[0]) -# player_games_df = pd.read_csv('data/player_games_cards.csv',index_col=[0]).sort_values(by='date').reset_index(drop=True) -team_abv_nst = pd.read_csv('data/team_abv_nst.csv') -#player_games_df = player_games_df.loc[:, ~player_games_df.columns.str.contains('^Unnamed')] -#team_abv = pd.read_csv('team_abv.csv') -#team_games_df = team_games_df.merge(right=team_abv,left_on='team',right_on='team_name',how='left').drop(columns='team_name') -team_abv = pd.read_csv('data/team_abv.csv') + conditions_ss = [ + (df['launch_angle'].isna()), + (df['launch_angle'] >= 8 ) * (df['launch_angle'] <= 32 ) + ] -import pickle -from datetime import timedelta + choices_ss = [False,True] + df['sweet_spot'] = np.select(conditions_ss, choices_ss, default=np.nan) -# # Loop over the counter and format the API call -# r = requests.get('https://statsapi.web.nhl.com/api/v1/schedule?startDate=2022-10-01&endDate=2023-06-01') -# schedule = r.json() + conditions_hh = [ + (df['launch_speed'].isna()), + (df['launch_speed'] >= 94.5 ) + ] -# schedule = json.loads(urlopen('https://statsapi.web.nhl.com/api/v1/schedule?startDate=2023-10-07&endDate=2024-04-19').read()) + choices_hh = [False,True] + df['hard_hit'] = np.select(conditions_hh, choices_hh, default=np.nan) -# def flatten(t): -# return [item for sublist in t for item in sublist] -# game_id = flatten([[x['gamePk'] for x in schedule['dates'][y]['games']] for y in range(0,len(schedule['dates']))]) -# game_type = flatten([[x['gameType'] for x in schedule['dates'][y]['games']] for y in range(0,len(schedule['dates']))]) -# game_date = flatten([[(pd.to_datetime(x['gameDate']) - timedelta(hours=8)) for x in schedule['dates'][y]['games']] for y in range(0,len(schedule['dates']))]) -# game_final = flatten([[x['status']['detailedState'] for x in schedule['dates'][y]['games']] for y in range(0,len(schedule['dates']))]) -# game_home = flatten([[x['teams']['home']['team']['name'] for x in schedule['dates'][y]['games']] for y in range(0,len(schedule['dates']))]) -# game_away = flatten([[x['teams']['away']['team']['name'] for x in schedule['dates'][y]['games']] for y in range(0,len(schedule['dates']))]) + conditions_tb = [ + (df['event_type']=='single'), + (df['event_type']=='double'), + (df['event_type']=='triple'), + (df['event_type']=='home_run'), + ] -# schedule_df = pd.DataFrame(data={'game_id': game_id, 'game_type':game_type,'game_date' : game_date, 'game_home' : game_home, 'game_away' : game_away,'status' : game_final}) -# schedule_df = schedule_df[schedule_df.game_type == 'R'].reset_index(drop=True) -# schedule_df = schedule_df[schedule_df.status != 'Postponed'] -# schedule_df = schedule_df.replace('Montréal Canadiens','Montreal Canadiens') -schedule = pd.read_csv('2024_schedule_href.csv') -#schedule = pd.read_html('https://www.hockey-reference.com/leagues/NHL_2024_games.html')[0] - #schedule.to_csv('schedule/schedule_'+str(date.today())+'.csv') -#schedule = pd.read_csv('schedule/schedule_'+str(date.today())+'.csv') -schedule = schedule.replace('St Louis Blues','St. Louis Blues') + choices_tb = [1,2,3,4] -schedule_df = schedule.merge(right=team_abv,left_on='Visitor',right_on='team_name',how='inner',suffixes=['','_away']) -schedule_df = schedule_df.merge(right=team_abv,left_on='Home',right_on='team_name',how='inner',suffixes=['','_home']) + df['tb'] = np.select(conditions_tb, choices_tb, default=np.nan) -schedule_df = schedule_df.rename(columns={'Visitor':'game_away','Home':'game_home','Date':'game_date'}) + conditions_woba = [ + (df['event_type']=='walk'), + (df['event_type']=='hit_by_pitch'), + (df['event_type']=='single'), + (df['event_type']=='double'), + (df['event_type']=='triple'), + (df['event_type']=='home_run'), + ] -schedule_df_merge = schedule_df.merge(right=team_abv,left_on='game_home',right_on='team_name',how='left') -schedule_df_merge = schedule_df_merge.merge(right=team_abv,left_on='game_away',right_on='team_name',how='left') -schedule_df_merge = schedule_df_merge.drop(columns={'team_name_x','team_name_y'}) -schedule_df_merge = schedule_df_merge.rename(columns={'team_abv_x' : 'team_abv_home','team_abv_y' : 'team_abv_away'}) + choices_woba = [0.698, + 0.728, + 0.887, + 1.253, + 1.583, + 2.027] + df['woba'] = np.select(conditions_woba, choices_woba, default=np.nan) -schedule_df_merge = schedule_df_merge.loc[:,~schedule_df_merge.columns.duplicated()].copy() -#schedule_df_merge.game_date = pd.to_datetime(schedule_df_merge['game_date']).dt.tz_convert(tz='US/Eastern').dt.date -# schedule_df_merge = schedule_df_merge.set_index(pd.DatetimeIndex(schedule_df_merge.game_date).strftime('%Y-%m-%d')) -schedule_df_merge.index = pd.to_datetime(schedule_df_merge.game_date) -schedule_df_merge = schedule_df_merge.drop(columns='game_date') -#schedule_df_merge.index = schedule_df_merge.index.tz_convert('US/Pacific') -schedule_df_merge.index = schedule_df_merge.index.date -schedule_df_merge = schedule_df_merge.sort_index() -schedule_df_merge = schedule_df_merge[schedule_df_merge.index <= date(2024,5,1)] + 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'] -schedule_df_merge_final = schedule_df_merge[schedule_df_merge.index= player_games_df.date)].reset_index(drop=True)) - #print('touble',i, player_lookup_list[i],len(player_games_df[(player_games_df.player_id == player_lookup_list[i])])) - team_schedule_url_merge[i].index = team_schedule_url_merge[i].team_game - team_schedule_url_merge[i] = team_schedule_url_merge[i].reindex(np.arange(team_schedule_url_merge[i].team_game.min(), team_schedule_url_merge[i].team_game.max() + 1)).reset_index(drop=True) - #team_schedule_url_merge[0]['team_game'] = team_schedule_url_merge[0]['index'] - #team_schedule_url_merge[0]['player_game'] = - #schedule_ccount_df[schedule_ccount_df['team'].isin(team_schedule_url_merge[0].Team.unique())].merge(right=team_schedule_url_merge[0],left_on=['date','team'],right_on=['date','Team'],how='left') + df_summ['whiff_rate'] = [df_summ.whiffs[x]/df_summ.swings[x] if df_summ.swings[x] != 0 else np.nan for x in range(len(df_summ))] - team_schedule_url_merge[i]['stat'] = team_schedule_url_merge[i][stat].cumsum() + df_summ['swstr_rate'] = [df_summ.whiffs[x]/df_summ.pitches[x] if df_summ.pitches[x] != 0 else np.nan for x in range(len(df_summ))] + df_summ = df_summ.dropna(subset=['bip']) + return df_summ - #team_schedule_url_merge[i]['stat'] = team_schedule_url_merge[i][stat_pick] - team_schedule_url_merge[i] = team_schedule_url_merge[i].append(team_schedule_url_merge[i]).sort_index() - team_schedule_url_merge[i] = team_schedule_url_merge[i].append(team_schedule_url_merge[i].iloc[0]).sort_index().reset_index(drop=True) +def df_summ_filter_out(df_summ=pd.DataFrame(),batter_select = 0): + df_summ_filter = df_summ[df_summ['pa'] >= min(math.floor(df_summ.xs(batter_select,level=0)['pa']/10)*10,500)] + df_summ_filter_pct = df_summ_filter.rank(pct=True,ascending=True) + df_summ_player = df_summ.xs(batter_select,level=0) + df_summ_player_pct = df_summ_filter_pct.xs(batter_select,level=0) + return df_summ_filter,df_summ_filter_pct,df_summ_player,df_summ_player_pct - team_schedule_url_merge[i]['team_game'][0] = 0 - team_schedule_url_merge[i]['player_game'][0] = 0 - team_schedule_url_merge[i]['stat'][0] = 0 +def df_summ_batter_pitch_up(df=pd.DataFrame()): + df_summ_batter_pitch = df.dropna(subset=['pitch_category']).groupby(['batter_id','batter_name','pitch_category']).agg( + pa = ('pa','sum'), + ab = ('ab','sum'), + obp_pa = ('obp','sum'), + hits = ('hits','sum'), + on_base = ('on_base','sum'), + k = ('k','sum'), + bb = ('bb','sum'), + bb_minus_k = ('bb_minus_k','sum'), + csw = ('csw','sum'), + bip = ('bip','sum'), + tb = ('tb','sum'), + woba = ('woba','sum'), + woba_contact = ('woba_contact','sum'), + woba_codes = ('woba_codes','sum'), + hard_hit = ('hard_hit','sum'), + barrel = ('barrel','sum'), + sweet_spot = ('sweet_spot','sum'), + max_launch_speed = ('launch_speed','max'), + launch_speed_90 = ('launch_speed',percentile(90)), + launch_speed = ('launch_speed','mean'), + launch_angle = ('launch_angle','mean'), + pitches = ('is_pitch','sum'), + swings = ('swings','sum'), + in_zone = ('in_zone','sum'), + out_zone = ('out_zone','sum'), + whiffs = ('whiffs','sum'), + zone_swing = ('zone_swing','sum'), + zone_contact = ('zone_contact','sum'), + ozone_swing = ('ozone_swing','sum'), + ozone_contact = ('ozone_contact','sum'), + ).reset_index() + + + df_summ_batter_pitch['avg'] = [df_summ_batter_pitch.hits[x]/df_summ_batter_pitch.ab[x] if df_summ_batter_pitch.ab[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['obp'] = [df_summ_batter_pitch.on_base[x]/df_summ_batter_pitch.obp_pa[x] if df_summ_batter_pitch.obp_pa[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['slg'] = [df_summ_batter_pitch.tb[x]/df_summ_batter_pitch.ab[x] if df_summ_batter_pitch.ab[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] - for j in range(1,len(team_schedule_url_merge[i]),2): - team_schedule_url_merge[i]['player_game'][j] = team_schedule_url_merge[i]['player_game'][j]-1 - team_schedule_url_merge[i]['team_game'][j] = team_schedule_url_merge[i]['team_game'][j]-1 - team_schedule_url_merge[i]['stat'][j] = team_schedule_url_merge[i]['stat'][j] - team_schedule_url_merge[i][stat][j] + df_summ_batter_pitch['ops'] = df_summ_batter_pitch['obp']+df_summ_batter_pitch['slg'] - if len(team_schedule_url_merge[i]) >3: - if pd.isna(team_schedule_url_merge[i].iloc[3]['player_game']) and pd.isna(team_schedule_url_merge[i].iloc[1]['player_game']) == True: - team_schedule_url_merge[i]['player_game'][2] = np.nan - team_schedule_url_merge[i]['stat'][2] = np.nan + df_summ_batter_pitch['k_percent'] = [df_summ_batter_pitch.k[x]/df_summ_batter_pitch.pa[x] if df_summ_batter_pitch.pa[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['bb_percent'] =[df_summ_batter_pitch.bb[x]/df_summ_batter_pitch.pa[x] if df_summ_batter_pitch.pa[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['bb_minus_k_percent'] =[(df_summ_batter_pitch.bb_minus_k[x])/df_summ_batter_pitch.pa[x] if df_summ_batter_pitch.pa[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] - if len(team_schedule_url_merge[i]) >3: - if pd.isna(team_schedule_url_merge[i].iloc[len(team_schedule_url_merge[i])-1]['player_game']) == True: - team_schedule_url_merge[i]['stat'][len(team_schedule_url_merge[i])-1] = np.nanmax(team_schedule_url_merge[i]['stat']) + df_summ_batter_pitch['bb_over_k_percent'] =[df_summ_batter_pitch.bb[x]/df_summ_batter_pitch.k[x] if df_summ_batter_pitch.k[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] - if not (team_schedule_url_merge[i]['team_game'].values[1] == team_schedule_url_merge[i]['player_game'].values[0]): - team_schedule_url_merge[i].loc[0,'team_game'] = np.nan - max_games_player.append(np.around(np.nanmax(team_schedule_url_merge[i]['player_game']))) - max_games_team.append(np.around(np.nanmax(team_schedule_url_merge[i]['team_game']))) - max_stat.append((np.around(np.nanmax(team_schedule_url_merge[i]['stat'])))) + df_summ_batter_pitch['csw_percent'] =[df_summ_batter_pitch.csw[x]/df_summ_batter_pitch.pitches[x] if df_summ_batter_pitch.pitches[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['sweet_spot_percent'] = [df_summ_batter_pitch.sweet_spot[x]/df_summ_batter_pitch.bip[x] if df_summ_batter_pitch.bip[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['woba_percent'] = [df_summ_batter_pitch.woba[x]/df_summ_batter_pitch.woba_codes[x] if df_summ_batter_pitch.woba_codes[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['woba_percent_contact'] = [df_summ_batter_pitch.woba_contact[x]/df_summ_batter_pitch.bip[x] if df_summ_batter_pitch.bip[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + #df_summ_batter_pitch['hard_hit_percent'] = [df_summ_batter_pitch.sweet_spot[x]/df_summ_batter_pitch.bip[x] if df_summ_batter_pitch.bip[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['hard_hit_percent'] = [df_summ_batter_pitch.hard_hit[x]/df_summ_batter_pitch.bip[x] if df_summ_batter_pitch.bip[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] - fig, ax = plt.subplots(figsize=(15,15)) - cgfont = {'fontname':'Century Gothic'} - font = font_manager.FontProperties(family='Century Gothic', - style='normal', size=14) + df_summ_batter_pitch['barrel_percent'] = [df_summ_batter_pitch.barrel[x]/df_summ_batter_pitch.bip[x] if df_summ_batter_pitch.bip[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['zone_contact_percent'] = [df_summ_batter_pitch.zone_contact[x]/df_summ_batter_pitch.zone_swing[x] if df_summ_batter_pitch.zone_swing[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] - ax.axhline(0,color='black',linestyle ="--",linewidth=2,alpha=1.0,label='Missed Games') - ax.axhline(0,color='black',linestyle ="-",linewidth=2,alpha=1.0) + df_summ_batter_pitch['zone_swing_percent'] = [df_summ_batter_pitch.zone_swing[x]/df_summ_batter_pitch.in_zone[x] if df_summ_batter_pitch.pitches[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['zone_percent'] = [df_summ_batter_pitch.in_zone[x]/df_summ_batter_pitch.pitches[x] if df_summ_batter_pitch.pitches[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] - if 'Total' in stat: - stat = stat.replace('Total ',"") + df_summ_batter_pitch['chase_percent'] = [df_summ_batter_pitch.ozone_swing[x]/(df_summ_batter_pitch.pitches[x] - df_summ_batter_pitch.in_zone[x]) if (df_summ_batter_pitch.pitches[x]- df_summ_batter_pitch.in_zone[x]) != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['chase_contact'] = [df_summ_batter_pitch.ozone_contact[x]/df_summ_batter_pitch.ozone_swing[x] if df_summ_batter_pitch.ozone_swing[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] - colour_scheme = ['#648FFF','#785EF0','#DC267F','#FE6100','#FFB000','#FAEF3B','#861318','#2ED3BC','#341BBF','#B37E2C'] + df_summ_batter_pitch['swing_percent'] = [df_summ_batter_pitch.swings[x]/df_summ_batter_pitch.pitches[x] if df_summ_batter_pitch.pitches[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] - for i in range(len(team_schedule_url_merge)): - sns.lineplot(team_schedule_url_merge[i].reset_index()['team_game'],team_schedule_url_merge[i].reset_index()['stat'],linewidth=3-i*.2,color=colour_scheme[i]) - plt.plot(team_schedule_url_merge[i]['team_game'],team_schedule_url_merge[i]['stat'],color=ax.lines[i*2+2].get_color(),label=str(i+1)+'. '+team_schedule_url_merge[i]['Player'][0]+', '+str(int(max_stat[i]))+' '+stat+' in '+str(int(max(team_schedule_url_merge[i]['player_game'])))+' Games',linewidth=6) - ax.lines[i*2+2].set_linestyle("--") + df_summ_batter_pitch['whiff_rate'] = [df_summ_batter_pitch.whiffs[x]/df_summ_batter_pitch.swings[x] if df_summ_batter_pitch.swings[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['swstr_rate'] = [df_summ_batter_pitch.whiffs[x]/df_summ_batter_pitch.pitches[x] if df_summ_batter_pitch.pitches[x] != 0 else np.nan for x in range(len(df_summ_batter_pitch))] + df_summ_batter_pitch['bip'] = df_summ_batter_pitch['bip'].fillna(0) - fig.set_facecolor('#ffffff') - ax.set(xlim=(0,max([team_schedule_url_merge[x].team_game.max() for x in range(len(team_schedule_url_merge))]))) - ax.set(ylim=(0,max([team_schedule_url_merge[x].stat.max() for x in range(len(team_schedule_url_merge))]))) + return df_summ_batter_pitch +print('Reading Data') +print('Reading A') +df_a_update = pd.read_csv('df_a_update.csv',index_col=[0]) +print('Reading A+') +df_ha_update = pd.read_csv('df_ha_update.csv',index_col=[0]) +print('Reading AA') +df_aa_update = pd.read_csv('df_aa_update.csv',index_col=[0]) +print('Reading AAA') +df_aaa_update = pd.read_csv('df_aaa_update.csv',index_col=[0]) +print('Reading MLB') +df_mlb_update = pd.read_csv('df_mlb_update.csv',index_col=[0]) - ax.legend_.remove() +df_a_update['bip'] = df_a_update['bip'].replace({'0':False,'False':False,'True':True}) +df_ha_update['bip'] = df_ha_update['bip'].replace({'0':False,'False':False,'True':True}) +df_aa_update['bip'] = df_aa_update['bip'].replace({'0':False,'False':False,'True':True}) +df_aaa_update['bip'] = df_aaa_update['bip'].replace({'0':False,'False':False,'True':True}) +df_mlb_update['bip'] = df_mlb_update['bip'].replace({'0':False,'False':False,'True':True}) +df_a_update['bip_div'] = ~df_a_update.launch_speed.isna() +df_ha_update['bip_div'] = ~df_ha_update.launch_speed.isna() +df_aa_update['bip_div'] = ~df_aa_update.launch_speed.isna() +df_aaa_update['bip_div'] = ~df_aaa_update.launch_speed.isna() +df_mlb_update['bip_div'] = ~df_mlb_update.launch_speed.isna() +df_summ_a_update = df_summ_changes(df_update_summ(df_a_update)).set_index(['batter_id','batter_name']) +df_summ_ha_update = df_summ_changes(df_update_summ(df_ha_update)).set_index(['batter_id','batter_name']) +df_summ_aa_update = df_summ_changes(df_update_summ(df_aa_update)).set_index(['batter_id','batter_name']) +df_summ_aaa_update = df_summ_changes(df_update_summ(df_aaa_update)).set_index(['batter_id','batter_name']) +df_summ_mlb_update = df_summ_changes(df_update_summ(df_mlb_update)).set_index(['batter_id','batter_name']) - if per_game == False: - fig.suptitle(f'{rookie}{team_select_title}{position_select_title}{stat} Race',y=.98,fontsize=32,color='black',**cgfont) - ax.set_ylabel(stat,fontsize=20,color='black',**cgfont) - # else: - # fig.suptitle(stat+' Per Game, All Situations',y=.99,fontsize=48,color='black',**cgfont) - # ax.set_ylabel(stat+"/GP",fontsize=20,color='black',**cgfont) +df_summ_avg_a_update = df_summ_changes(df_update_summ_avg(df_a_update)).set_index(['average']) +df_summ_avg_ha_update = df_summ_changes(df_update_summ_avg(df_ha_update)).set_index(['average']) +df_summ_avg_aa_update = df_summ_changes(df_update_summ_avg(df_aa_update)).set_index(['average']) +df_summ_avg_aaa_update = df_summ_changes(df_update_summ_avg(df_aaa_update)).set_index(['average']) +df_summ_avg_mlb_update = df_summ_changes(df_update_summ_avg(df_mlb_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])) + +mlb_player_dict = df_mlb_update.drop_duplicates( + 'batter_id')[['batter_id','batter_name']].sort_values(by='batter_name').set_index('batter_id').to_dict()['batter_name'] +aaa_player_dict = df_aaa_update.drop_duplicates( + 'batter_id')[['batter_id','batter_name']].sort_values(by='batter_name').set_index('batter_id').to_dict()['batter_name'] +aa_player_dict = df_aa_update.drop_duplicates( + 'batter_id')[['batter_id','batter_name']].sort_values(by='batter_name').set_index('batter_id').to_dict()['batter_name'] +ha_player_dict = df_ha_update.drop_duplicates( + 'batter_id')[['batter_id','batter_name']].sort_values(by='batter_name').set_index('batter_id').to_dict()['batter_name'] +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'] + +from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui +app_ui = ui.page_fluid( + ui.layout_sidebar( + + ui.panel_sidebar(ui.output_ui('test',"Select Batter"), + ui.input_select('stat_1',"Select Rolling Stat 1",stat_roll_dict), + 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'), + 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'), + ui.input_numeric('window_3',"Select Rolling Stat 3",value=100),width=2), + + ui.page_navbar( + + ui.nav_panel("MLB", + ui.output_plot('mlb_plot',width='1000px',height='1000px')), + ui.nav_panel("AAA", + ui.output_plot('aaa_plot',width='1000px',height='1000px')), + ui.nav_panel("AA", + ui.output_plot('aa_plot',width='1000px',height='1000px')), + ui.nav_panel("High-A", + ui.output_plot('ha_plot',width='1000px',height='1000px')), + ui.nav_panel("A", + ui.output_plot('a_plot',width='1000px',height='1000px')), + id="my_tabs", + ))) + +def server(input: Inputs, output: Outputs, session: Session): + @render.ui + def test(): + # @reactive.Effect + if input.my_tabs() == 'MLB': + print(mlb_player_dict) + return ui.input_select("player_id", "Select Batter",mlb_player_dict) + if input.my_tabs() == 'AAA': + return ui.input_select("player_id", "Select Batter",aaa_player_dict) + if input.my_tabs() == 'AA': + return ui.input_select("player_id", "Select Batter",aa_player_dict) + if input.my_tabs() == 'High-A': + return ui.input_select("player_id", "Select Batter",ha_player_dict) + if input.my_tabs() == 'A': + return ui.input_select("player_id", "Select Batter",a_player_dict) + + + @output + @render.plot(alt="MLB Plot") + def mlb_plot(): + ### Iniput data for the level + + df_update = df_mlb_update.copy() + df_summ_update = df_summ_mlb_update.copy() + df_summ_avg_update = df_summ_avg_mlb_update.copy() + + batter_select = int(input.player_id()) + sport_id_input = 1 + df_roll = df_update[df_update['batter_id']==batter_select] + + df_summ_filter = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[0] + df_summ_filter_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[1] + df_summ_player = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[2] + df_summ_player_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[3] + + df_summ_batter_pitch = df_summ_batter_pitch_up(df= df_update).set_index(['batter_id','batter_name','pitch_category']) - ax.set_title(str(current_season)[0:4]+'-'+str(start_season)[-4:]+' Season',y=1.01,fontsize=18,color='black',**cgfont,x=0,ha='left') - ax.set_xlabel('Team Game',fontsize=20,color='black',**cgfont) - ax.tick_params(axis="x", labelsize=24,colors='black') - ax.set_facecolor('#ffffff') - ax.xaxis.set_major_locator(MaxNLocator(integer=True)) - ax.tick_params(axis="y", labelsize=24,colors='black') - ax.yaxis.set_major_locator(MaxNLocator(integer=True)) - fig.text(x=0.025,y=0.01,s="Created By: @TJStats",color='black', fontsize=20, horizontalalignment='left',**cgfont) - fig.text(x=0.975,y=0.01,s="Data: Natural Stat Trick",color='black', fontsize=20, horizontalalignment='right',**cgfont) - fig.text(x=.975,y=0.92,s='Date: '+input.date().strftime('%B %d, %Y'),color='black', fontsize=18, horizontalalignment='right',**cgfont) + df_summ_batter_pitch_pct = df_summ_batter_pitch.loc[df_summ_filter.index.get_level_values(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) - ax.legend(prop=font,bbox_to_anchor=(0.01, 0.99),loc='upper left',framealpha=1,frameon=True) - plt.tight_layout() - #fig.savefig('gif_race/'+stat+rookie+str(date_range_list[k].date())+'.png', facecolor=fig.get_facecolor(), edgecolor='none',bbox_inches='tight',dpi=100) - #plt.close() - #fig.legend(prop=font,loc='best',framealpha=1,frameon=True) + 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 get_players(sport_id=1): + player_data = requests.get(url=f'https://statsapi.mlb.com/api/v1/sports/{sport_id}/players').json() + + #Select relevant data that will help distinguish players from one another + fullName_list = [x['fullName'] for x in player_data['people']] + id_list = [x['id'] for x in player_data['people']] + position_list = [x['primaryPosition']['abbreviation'] for x in player_data['people']] + team_list = [x['currentTeam']['id']for x in player_data['people']] + age_list = [x['currentAge']for x in player_data['people']] + + player_df = pd.DataFrame(data={'player_id':id_list, + 'name':fullName_list, + 'position':position_list, + 'team':team_list, + 'age':age_list}) + return player_df + + def get_teams(): + teams = requests.get(url='https://statsapi.mlb.com/api/v1/teams/').json() + #Select only teams that are at the MLB level + # mlb_teams_city = [x['franchiseName'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_name = [x['teamName'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_franchise = [x['name'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_id = [x['id'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_abb = [x['abbreviation'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + + mlb_teams_city = [x['franchiseName'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_name = [x['teamName'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_franchise = [x['name'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_id = [x['id'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_abb = [x['abbreviation'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_parent_id = [x['parentOrgId'] if 'parentOrgId' in x else None for x in teams['teams']] + mlb_teams_parent = [x['parentOrgName'] if 'parentOrgName' in x else None for x in teams['teams']] + mlb_teams_league_id = [x['league']['id'] if 'id' in x['league'] else None for x in teams['teams']] + mlb_teams_league_name = [x['league']['name'] if 'name' in x['league'] else None for x in teams['teams']] + + + + #Create a dataframe of all the teams + mlb_teams_df = pd.DataFrame(data={'team_id':mlb_teams_id, + 'city':mlb_teams_franchise, + 'name':mlb_teams_name, + 'franchise':mlb_teams_franchise, + 'abbreviation':mlb_teams_abb, + 'parent_org_id':mlb_teams_parent_id, + 'parent_org':mlb_teams_parent, + 'league_id':mlb_teams_league_id, + 'league_name':mlb_teams_league_name + + }).drop_duplicates().dropna(subset=['team_id']).reset_index(drop=True).sort_values('team_id') + + mlb_teams_df.loc[mlb_teams_df['parent_org_id'].isnull(),'parent_org_id'] = mlb_teams_df.loc[mlb_teams_df['parent_org_id'].isnull(),'team_id'] + mlb_teams_df.loc[mlb_teams_df['parent_org'].isnull(),'parent_org'] = mlb_teams_df.loc[mlb_teams_df['parent_org'].isnull(),'franchise'] + + + mlb_teams_df['parent_org_abbreviation'] = mlb_teams_df['parent_org_id'].map(mlb_teams_df.set_index('team_id')['abbreviation'].to_dict()) + + mlb_teams_df = pd.concat([mlb_teams_df, pd.DataFrame({'team_id': 11, + 'city': 'Major League Baseball', + 'name': 'Major League Baseball', + 'franchise': 'Free Agent', + 'abbreviation': 'MLB', + 'parent_org_id': 11, + 'parent_org': 'Major League Baseball', + 'league_id': 1.0, + 'league_name': 'Major League Baseball', + 'parent_org_abbreviation': 'MLB'},index=[0])]).reset_index(drop=True) + + #mlb_teams_df.loc[mlb_teams_df.franchise.isin(mlb_teams_df.parent_org.unique()),'parent_org'] = mlb_teams_df.loc[mlb_teams_df.franchise.isin(mlb_teams_df.parent_org.unique()),'franchise'] + + return mlb_teams_df + + 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])+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])+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])+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 = get_teams() + team_logos = pd.read_csv('team_logos.csv') + player_bio = requests.get(f'https://statsapi.mlb.com/api/v1/people?personIds={batter_select}&appContext=majorLeague&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, wspace=0) + + # 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 + if df_summ_player['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 + # 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.35) + 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.25) + 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.25) + ab = AnnotationBbox(imagebox, (0.875, 0.8), frameon = False) + ax.add_artist(ab) + + ax.text(s = f'2023 {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)#.dropna() + + 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') + 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.12, 0.0, 0.88, 1],colWidths=[0.03]+[0.03]*(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() + table.add_cell(0, -1, w,h, text='Pitch Type') + min_font_size = 12 + # 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) + + + for n_col in range(0,len(df_plot.columns)): + #print(df_plot.columns[n_col],f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}") + format_col = df_plot[df_plot.columns[n_col]].astype(str) + n_c = 0 + for cell in table.get_celld().values(): + # print([cell.get_text().get_text()],format_col.astype(str).values) + if cell.get_text().get_text() in format_col.astype(str).values: + + + #print(cell.get_text().get_text() in format_col.astype(str).values) + cell.get_text().set_text(f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}".format(float(cell.get_text().get_text()))) + elif cell.get_text().get_text()[:-2] in format_col.astype(str).values: + cell.get_text().set_text(f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}".format(float(cell.get_text().get_text()))) + n_c = n_c + 1 + + 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.tight_layout() + + + + 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, + ) + + @render.plot(alt="AAA Plot") + def aaa_plot(): + ### Iniput data for the level + + df_update = df_aaa_update.copy() + df_summ_update = df_summ_aaa_update.copy() + df_summ_avg_update = df_summ_avg_aaa_update.copy() + + batter_select = int(input.player_id()) + sport_id_input = 11 + df_roll = df_update[df_update['batter_id']==batter_select] + + df_summ_filter = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[0] + df_summ_filter_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[1] + df_summ_player = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[2] + df_summ_player_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[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_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 = df_summ_batter_pitch_pct_rank.dropna() + 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 get_players(sport_id=1): + player_data = requests.get(url=f'https://statsapi.mlb.com/api/v1/sports/{sport_id}/players').json() + + #Select relevant data that will help distinguish players from one another + fullName_list = [x['fullName'] for x in player_data['people']] + id_list = [x['id'] for x in player_data['people']] + position_list = [x['primaryPosition']['abbreviation'] for x in player_data['people']] + team_list = [x['currentTeam']['id']for x in player_data['people']] + age_list = [x['currentAge']for x in player_data['people']] + + player_df = pd.DataFrame(data={'player_id':id_list, + 'name':fullName_list, + 'position':position_list, + 'team':team_list, + 'age':age_list}) + return player_df + + def get_teams(): + teams = requests.get(url='https://statsapi.mlb.com/api/v1/teams/').json() + #Select only teams that are at the MLB level + # mlb_teams_city = [x['franchiseName'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_name = [x['teamName'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_franchise = [x['name'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_id = [x['id'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_abb = [x['abbreviation'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + + mlb_teams_city = [x['franchiseName'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_name = [x['teamName'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_franchise = [x['name'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_id = [x['id'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_abb = [x['abbreviation'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_parent_id = [x['parentOrgId'] if 'parentOrgId' in x else None for x in teams['teams']] + mlb_teams_parent = [x['parentOrgName'] if 'parentOrgName' in x else None for x in teams['teams']] + mlb_teams_league_id = [x['league']['id'] if 'id' in x['league'] else None for x in teams['teams']] + mlb_teams_league_name = [x['league']['name'] if 'name' in x['league'] else None for x in teams['teams']] + + + + #Create a dataframe of all the teams + mlb_teams_df = pd.DataFrame(data={'team_id':mlb_teams_id, + 'city':mlb_teams_franchise, + 'name':mlb_teams_name, + 'franchise':mlb_teams_franchise, + 'abbreviation':mlb_teams_abb, + 'parent_org_id':mlb_teams_parent_id, + 'parent_org':mlb_teams_parent, + 'league_id':mlb_teams_league_id, + 'league_name':mlb_teams_league_name + + }).drop_duplicates().dropna(subset=['team_id']).reset_index(drop=True).sort_values('team_id') + + mlb_teams_df.loc[mlb_teams_df['parent_org_id'].isnull(),'parent_org_id'] = mlb_teams_df.loc[mlb_teams_df['parent_org_id'].isnull(),'team_id'] + mlb_teams_df.loc[mlb_teams_df['parent_org'].isnull(),'parent_org'] = mlb_teams_df.loc[mlb_teams_df['parent_org'].isnull(),'franchise'] + + + mlb_teams_df['parent_org_abbreviation'] = mlb_teams_df['parent_org_id'].map(mlb_teams_df.set_index('team_id')['abbreviation'].to_dict()) + + mlb_teams_df = pd.concat([mlb_teams_df, pd.DataFrame({'team_id': 11, + 'city': 'Major League Baseball', + 'name': 'Major League Baseball', + 'franchise': 'Free Agent', + 'abbreviation': 'MLB', + 'parent_org_id': 11, + 'parent_org': 'Major League Baseball', + 'league_id': 1.0, + 'league_name': 'Major League Baseball', + 'parent_org_abbreviation': 'MLB'},index=[0])]).reset_index(drop=True) + + #mlb_teams_df.loc[mlb_teams_df.franchise.isin(mlb_teams_df.parent_org.unique()),'parent_org'] = mlb_teams_df.loc[mlb_teams_df.franchise.isin(mlb_teams_df.parent_org.unique()),'franchise'] + + return mlb_teams_df + + 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])+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])+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])+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 = get_teams() + team_logos = pd.read_csv('team_logos.csv') + player_bio = requests.get(f'https://statsapi.mlb.com/api/v1/people?personIds={batter_select}&appContext=majorLeague&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, wspace=0) + + # 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 + if df_summ_player['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 + # 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.35) + 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.25) + 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.25) + ab = AnnotationBbox(imagebox, (0.875, 0.8), frameon = False) + ax.add_artist(ab) + + ax.text(s = f'2023 {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)#.dropna() + + 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.12, 0.0, 0.88, 1],colWidths=[0.03]+[0.03]*(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() + table.add_cell(0, -1, w,h, text='Pitch Type') + min_font_size = 12 + # 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) + + + for n_col in range(0,len(df_plot.columns)): + #print(df_plot.columns[n_col],f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}") + format_col = df_plot[df_plot.columns[n_col]].astype(str) + n_c = 0 + for cell in table.get_celld().values(): + # print([cell.get_text().get_text()],format_col.astype(str).values) + if cell.get_text().get_text() in format_col.astype(str).values: + + + #print(cell.get_text().get_text() in format_col.astype(str).values) + cell.get_text().set_text(f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}".format(float(cell.get_text().get_text()))) + elif cell.get_text().get_text()[:-2] in format_col.astype(str).values: + cell.get_text().set_text(f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}".format(float(cell.get_text().get_text()))) + n_c = n_c + 1 + + 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.tight_layout() + + + + 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, + ) + + @render.plot(alt="AA Plot") + def aa_plot(): + ### Iniput data for the level + + df_update = df_aa_update.copy() + df_summ_update = df_summ_aa_update.copy() + df_summ_avg_update = df_summ_avg_aa_update.copy() + + batter_select = int(input.player_id()) + sport_id_input = 12 + df_roll = df_update[df_update['batter_id']==batter_select] + + df_summ_filter = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[0] + df_summ_filter_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[1] + df_summ_player = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[2] + df_summ_player_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[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_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 = df_summ_batter_pitch_pct_rank.dropna() + 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 get_players(sport_id=1): + player_data = requests.get(url=f'https://statsapi.mlb.com/api/v1/sports/{sport_id}/players').json() + + #Select relevant data that will help distinguish players from one another + fullName_list = [x['fullName'] for x in player_data['people']] + id_list = [x['id'] for x in player_data['people']] + position_list = [x['primaryPosition']['abbreviation'] for x in player_data['people']] + team_list = [x['currentTeam']['id']for x in player_data['people']] + age_list = [x['currentAge']for x in player_data['people']] + + player_df = pd.DataFrame(data={'player_id':id_list, + 'name':fullName_list, + 'position':position_list, + 'team':team_list, + 'age':age_list}) + return player_df + + def get_teams(): + teams = requests.get(url='https://statsapi.mlb.com/api/v1/teams/').json() + #Select only teams that are at the MLB level + # mlb_teams_city = [x['franchiseName'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_name = [x['teamName'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_franchise = [x['name'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_id = [x['id'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_abb = [x['abbreviation'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + + mlb_teams_city = [x['franchiseName'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_name = [x['teamName'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_franchise = [x['name'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_id = [x['id'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_abb = [x['abbreviation'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_parent_id = [x['parentOrgId'] if 'parentOrgId' in x else None for x in teams['teams']] + mlb_teams_parent = [x['parentOrgName'] if 'parentOrgName' in x else None for x in teams['teams']] + mlb_teams_league_id = [x['league']['id'] if 'id' in x['league'] else None for x in teams['teams']] + mlb_teams_league_name = [x['league']['name'] if 'name' in x['league'] else None for x in teams['teams']] + + + + #Create a dataframe of all the teams + mlb_teams_df = pd.DataFrame(data={'team_id':mlb_teams_id, + 'city':mlb_teams_franchise, + 'name':mlb_teams_name, + 'franchise':mlb_teams_franchise, + 'abbreviation':mlb_teams_abb, + 'parent_org_id':mlb_teams_parent_id, + 'parent_org':mlb_teams_parent, + 'league_id':mlb_teams_league_id, + 'league_name':mlb_teams_league_name + + }).drop_duplicates().dropna(subset=['team_id']).reset_index(drop=True).sort_values('team_id') + + mlb_teams_df.loc[mlb_teams_df['parent_org_id'].isnull(),'parent_org_id'] = mlb_teams_df.loc[mlb_teams_df['parent_org_id'].isnull(),'team_id'] + mlb_teams_df.loc[mlb_teams_df['parent_org'].isnull(),'parent_org'] = mlb_teams_df.loc[mlb_teams_df['parent_org'].isnull(),'franchise'] + + + mlb_teams_df['parent_org_abbreviation'] = mlb_teams_df['parent_org_id'].map(mlb_teams_df.set_index('team_id')['abbreviation'].to_dict()) + + mlb_teams_df = pd.concat([mlb_teams_df, pd.DataFrame({'team_id': 11, + 'city': 'Major League Baseball', + 'name': 'Major League Baseball', + 'franchise': 'Free Agent', + 'abbreviation': 'MLB', + 'parent_org_id': 11, + 'parent_org': 'Major League Baseball', + 'league_id': 1.0, + 'league_name': 'Major League Baseball', + 'parent_org_abbreviation': 'MLB'},index=[0])]).reset_index(drop=True) + + #mlb_teams_df.loc[mlb_teams_df.franchise.isin(mlb_teams_df.parent_org.unique()),'parent_org'] = mlb_teams_df.loc[mlb_teams_df.franchise.isin(mlb_teams_df.parent_org.unique()),'franchise'] + + return mlb_teams_df + + 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])+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])+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])+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 = get_teams() + team_logos = pd.read_csv('team_logos.csv') + player_bio = requests.get(f'https://statsapi.mlb.com/api/v1/people?personIds={batter_select}&appContext=majorLeague&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, wspace=0) + + # 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 + if df_summ_player['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 + # 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.35) + 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.25) + 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.25) + ab = AnnotationBbox(imagebox, (0.875, 0.8), frameon = False) + ax.add_artist(ab) + + ax.text(s = f'2023 {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)#.dropna() + + 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.12, 0.0, 0.88, 1],colWidths=[0.03]+[0.03]*(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() + table.add_cell(0, -1, w,h, text='Pitch Type') + min_font_size = 12 + # 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) + + + for n_col in range(0,len(df_plot.columns)): + #print(df_plot.columns[n_col],f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}") + format_col = df_plot[df_plot.columns[n_col]].astype(str) + n_c = 0 + for cell in table.get_celld().values(): + # print([cell.get_text().get_text()],format_col.astype(str).values) + if cell.get_text().get_text() in format_col.astype(str).values: + + + #print(cell.get_text().get_text() in format_col.astype(str).values) + cell.get_text().set_text(f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}".format(float(cell.get_text().get_text()))) + elif cell.get_text().get_text()[:-2] in format_col.astype(str).values: + cell.get_text().set_text(f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}".format(float(cell.get_text().get_text()))) + n_c = n_c + 1 + + 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.tight_layout() + + + + 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, + ) + + @render.plot(alt="High-A Plot") + def ha_plot(): + ### Iniput data for the level + + df_update = df_ha_update.copy() + df_summ_update = df_summ_ha_update.copy() + df_summ_avg_update = df_summ_avg_ha_update.copy() + + batter_select = int(input.player_id()) + sport_id_input = 13 + df_roll = df_update[df_update['batter_id']==batter_select] + + df_summ_filter = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[0] + df_summ_filter_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[1] + df_summ_player = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[2] + df_summ_player_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[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_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 = df_summ_batter_pitch_pct_rank.dropna() + 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 get_players(sport_id=1): + player_data = requests.get(url=f'https://statsapi.mlb.com/api/v1/sports/{sport_id}/players').json() + + #Select relevant data that will help distinguish players from one another + fullName_list = [x['fullName'] for x in player_data['people']] + id_list = [x['id'] for x in player_data['people']] + position_list = [x['primaryPosition']['abbreviation'] for x in player_data['people']] + team_list = [x['currentTeam']['id']for x in player_data['people']] + age_list = [x['currentAge']for x in player_data['people']] + + player_df = pd.DataFrame(data={'player_id':id_list, + 'name':fullName_list, + 'position':position_list, + 'team':team_list, + 'age':age_list}) + return player_df + + def get_teams(): + teams = requests.get(url='https://statsapi.mlb.com/api/v1/teams/').json() + #Select only teams that are at the MLB level + # mlb_teams_city = [x['franchiseName'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_name = [x['teamName'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_franchise = [x['name'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_id = [x['id'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_abb = [x['abbreviation'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + + mlb_teams_city = [x['franchiseName'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_name = [x['teamName'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_franchise = [x['name'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_id = [x['id'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_abb = [x['abbreviation'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_parent_id = [x['parentOrgId'] if 'parentOrgId' in x else None for x in teams['teams']] + mlb_teams_parent = [x['parentOrgName'] if 'parentOrgName' in x else None for x in teams['teams']] + mlb_teams_league_id = [x['league']['id'] if 'id' in x['league'] else None for x in teams['teams']] + mlb_teams_league_name = [x['league']['name'] if 'name' in x['league'] else None for x in teams['teams']] + + + + #Create a dataframe of all the teams + mlb_teams_df = pd.DataFrame(data={'team_id':mlb_teams_id, + 'city':mlb_teams_franchise, + 'name':mlb_teams_name, + 'franchise':mlb_teams_franchise, + 'abbreviation':mlb_teams_abb, + 'parent_org_id':mlb_teams_parent_id, + 'parent_org':mlb_teams_parent, + 'league_id':mlb_teams_league_id, + 'league_name':mlb_teams_league_name + + }).drop_duplicates().dropna(subset=['team_id']).reset_index(drop=True).sort_values('team_id') + + mlb_teams_df.loc[mlb_teams_df['parent_org_id'].isnull(),'parent_org_id'] = mlb_teams_df.loc[mlb_teams_df['parent_org_id'].isnull(),'team_id'] + mlb_teams_df.loc[mlb_teams_df['parent_org'].isnull(),'parent_org'] = mlb_teams_df.loc[mlb_teams_df['parent_org'].isnull(),'franchise'] + + + mlb_teams_df['parent_org_abbreviation'] = mlb_teams_df['parent_org_id'].map(mlb_teams_df.set_index('team_id')['abbreviation'].to_dict()) + + mlb_teams_df = pd.concat([mlb_teams_df, pd.DataFrame({'team_id': 11, + 'city': 'Major League Baseball', + 'name': 'Major League Baseball', + 'franchise': 'Free Agent', + 'abbreviation': 'MLB', + 'parent_org_id': 11, + 'parent_org': 'Major League Baseball', + 'league_id': 1.0, + 'league_name': 'Major League Baseball', + 'parent_org_abbreviation': 'MLB'},index=[0])]).reset_index(drop=True) + + #mlb_teams_df.loc[mlb_teams_df.franchise.isin(mlb_teams_df.parent_org.unique()),'parent_org'] = mlb_teams_df.loc[mlb_teams_df.franchise.isin(mlb_teams_df.parent_org.unique()),'franchise'] + + return mlb_teams_df + + 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])+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])+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])+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 = get_teams() + team_logos = pd.read_csv('team_logos.csv') + player_bio = requests.get(f'https://statsapi.mlb.com/api/v1/people?personIds={batter_select}&appContext=majorLeague&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, wspace=0) + + # 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 + if df_summ_player['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 + # 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.35) + 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.25) + 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.25) + ab = AnnotationBbox(imagebox, (0.875, 0.8), frameon = False) + ax.add_artist(ab) + + ax.text(s = f'2023 {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)#.dropna() + + 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.12, 0.0, 0.88, 1],colWidths=[0.03]+[0.03]*(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() + table.add_cell(0, -1, w,h, text='Pitch Type') + min_font_size = 12 + # 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) + + + for n_col in range(0,len(df_plot.columns)): + #print(df_plot.columns[n_col],f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}") + format_col = df_plot[df_plot.columns[n_col]].astype(str) + n_c = 0 + for cell in table.get_celld().values(): + # print([cell.get_text().get_text()],format_col.astype(str).values) + if cell.get_text().get_text() in format_col.astype(str).values: + + + #print(cell.get_text().get_text() in format_col.astype(str).values) + cell.get_text().set_text(f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}".format(float(cell.get_text().get_text()))) + elif cell.get_text().get_text()[:-2] in format_col.astype(str).values: + cell.get_text().set_text(f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}".format(float(cell.get_text().get_text()))) + n_c = n_c + 1 + + 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.tight_layout() + + + + 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, + ) + + @render.plot(alt="A Plot") + def a_plot(): + ### Iniput data for the level + + df_update = df_a_update.copy() + df_summ_update = df_summ_a_update.copy() + df_summ_avg_update = df_summ_avg_a_update.copy() + + batter_select = int(input.player_id()) + sport_id_input = 14 + df_roll = df_update[df_update['batter_id']==batter_select] + + df_summ_filter = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[0] + df_summ_filter_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[1] + df_summ_player = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[2] + df_summ_player_pct = df_summ_filter_out(df_summ=df_summ_update,batter_select = batter_select)[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_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 = df_summ_batter_pitch_pct_rank.dropna() + 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 get_players(sport_id=1): + player_data = requests.get(url=f'https://statsapi.mlb.com/api/v1/sports/{sport_id}/players').json() + + #Select relevant data that will help distinguish players from one another + fullName_list = [x['fullName'] for x in player_data['people']] + id_list = [x['id'] for x in player_data['people']] + position_list = [x['primaryPosition']['abbreviation'] for x in player_data['people']] + team_list = [x['currentTeam']['id']for x in player_data['people']] + age_list = [x['currentAge']for x in player_data['people']] + + player_df = pd.DataFrame(data={'player_id':id_list, + 'name':fullName_list, + 'position':position_list, + 'team':team_list, + 'age':age_list}) + return player_df + + def get_teams(): + teams = requests.get(url='https://statsapi.mlb.com/api/v1/teams/').json() + #Select only teams that are at the MLB level + # mlb_teams_city = [x['franchiseName'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_name = [x['teamName'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_franchise = [x['name'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_id = [x['id'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + # mlb_teams_abb = [x['abbreviation'] for x in teams['teams'] if x['sport']['name'] == 'Major League Baseball'] + + mlb_teams_city = [x['franchiseName'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_name = [x['teamName'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_franchise = [x['name'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_id = [x['id'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_abb = [x['abbreviation'] if 'franchiseName' in x else None for x in teams['teams']] + mlb_teams_parent_id = [x['parentOrgId'] if 'parentOrgId' in x else None for x in teams['teams']] + mlb_teams_parent = [x['parentOrgName'] if 'parentOrgName' in x else None for x in teams['teams']] + mlb_teams_league_id = [x['league']['id'] if 'id' in x['league'] else None for x in teams['teams']] + mlb_teams_league_name = [x['league']['name'] if 'name' in x['league'] else None for x in teams['teams']] + + + + #Create a dataframe of all the teams + mlb_teams_df = pd.DataFrame(data={'team_id':mlb_teams_id, + 'city':mlb_teams_franchise, + 'name':mlb_teams_name, + 'franchise':mlb_teams_franchise, + 'abbreviation':mlb_teams_abb, + 'parent_org_id':mlb_teams_parent_id, + 'parent_org':mlb_teams_parent, + 'league_id':mlb_teams_league_id, + 'league_name':mlb_teams_league_name + + }).drop_duplicates().dropna(subset=['team_id']).reset_index(drop=True).sort_values('team_id') + + mlb_teams_df.loc[mlb_teams_df['parent_org_id'].isnull(),'parent_org_id'] = mlb_teams_df.loc[mlb_teams_df['parent_org_id'].isnull(),'team_id'] + mlb_teams_df.loc[mlb_teams_df['parent_org'].isnull(),'parent_org'] = mlb_teams_df.loc[mlb_teams_df['parent_org'].isnull(),'franchise'] + + + mlb_teams_df['parent_org_abbreviation'] = mlb_teams_df['parent_org_id'].map(mlb_teams_df.set_index('team_id')['abbreviation'].to_dict()) + + mlb_teams_df = pd.concat([mlb_teams_df, pd.DataFrame({'team_id': 11, + 'city': 'Major League Baseball', + 'name': 'Major League Baseball', + 'franchise': 'Free Agent', + 'abbreviation': 'MLB', + 'parent_org_id': 11, + 'parent_org': 'Major League Baseball', + 'league_id': 1.0, + 'league_name': 'Major League Baseball', + 'parent_org_abbreviation': 'MLB'},index=[0])]).reset_index(drop=True) + + #mlb_teams_df.loc[mlb_teams_df.franchise.isin(mlb_teams_df.parent_org.unique()),'parent_org'] = mlb_teams_df.loc[mlb_teams_df.franchise.isin(mlb_teams_df.parent_org.unique()),'franchise'] + + return mlb_teams_df + + 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])+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])+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])+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 = get_teams() + team_logos = pd.read_csv('team_logos.csv') + player_bio = requests.get(f'https://statsapi.mlb.com/api/v1/people?personIds={batter_select}&appContext=majorLeague&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, wspace=0) + + # 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 + if df_summ_player['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 + # 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.35) + 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.25) + 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.25) + ab = AnnotationBbox(imagebox, (0.875, 0.8), frameon = False) + ax.add_artist(ab) + + ax.text(s = f'2023 {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)#.dropna() + + 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.12, 0.0, 0.88, 1],colWidths=[0.03]+[0.03]*(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() + table.add_cell(0, -1, w,h, text='Pitch Type') + min_font_size = 12 + # 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) + + + for n_col in range(0,len(df_plot.columns)): + #print(df_plot.columns[n_col],f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}") + format_col = df_plot[df_plot.columns[n_col]].astype(str) + n_c = 0 + for cell in table.get_celld().values(): + # print([cell.get_text().get_text()],format_col.astype(str).values) + if cell.get_text().get_text() in format_col.astype(str).values: + + + #print(cell.get_text().get_text() in format_col.astype(str).values) + cell.get_text().set_text(f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}".format(float(cell.get_text().get_text()))) + elif cell.get_text().get_text()[:-2] in format_col.astype(str).values: + cell.get_text().set_text(f"{{:{stat_plot_dict[df_plot.columns[n_col]]['format']}}}".format(float(cell.get_text().get_text()))) + n_c = n_c + 1 + + 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.tight_layout() + + + + 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, + ) + -app = App(app_ui, server) \ No newline at end of file +app = App(app_ui, server)