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