| import polars as pl
|
| import numpy as np
|
| import pandas as pd
|
| import api_scraper
|
| scrape = api_scraper.MLB_Scrape()
|
| from functions import df_update
|
| from functions import pitch_summary_functions
|
| update = df_update.df_update()
|
| from stuff_model import feature_engineering as fe
|
| from stuff_model import stuff_apply
|
| import requests
|
| import joblib
|
| from matplotlib.gridspec import GridSpec
|
| from shiny import App, reactive, ui, render
|
| from shiny.ui import h2, tags
|
| import matplotlib.pyplot as plt
|
| import matplotlib.gridspec as gridspec
|
| import seaborn as sns
|
| from functions.pitch_summary_functions import *
|
|
|
| colour_palette = ['#FFB000','#648FFF','#785EF0',
|
| '#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']
|
|
|
|
|
| year_list = [2017,2018,2019,2020,2021,2022,2023,2024]
|
|
|
|
|
|
|
| level_dict = {'1':'MLB',
|
| '11':'AAA',
|
| '12':'AA',
|
| '13':'A+',
|
| '14':'A',
|
| '17':'AFL',
|
| '22':'College',
|
| '21':'Prospects',
|
| '51':'International' }
|
|
|
| function_dict={
|
| 'velocity_kdes':'Velocity Distributions',
|
| 'break_plot':'Pitch Movement',
|
| 'tj_stuff_roling':'Rolling tjStuff+ by Pitch',
|
| 'tj_stuff_roling_game':'Rolling tjStuff+ by Game',
|
| 'location_plot_lhb':'Locations vs LHB',
|
| 'location_plot_rhb':'Locations vs RHB',
|
| }
|
|
|
|
|
| split_dict = {'all':'All',
|
| 'left':'LHH',
|
| 'right':'RHH'}
|
|
|
| split_dict_hand = {'all':['L','R'],
|
| 'left':['L'],
|
| 'right':['R']}
|
|
|
| from shiny import App, reactive, ui, render
|
| from shiny.ui import h2, tags
|
|
|
|
|
| app_ui = ui.page_fluid(
|
| ui.layout_sidebar(
|
| ui.panel_sidebar(
|
|
|
| ui.row(
|
| ui.column(6, ui.input_select('year_input', 'Select Season', year_list, selected=2024)),
|
| ui.column(6, ui.input_select('level_input', 'Select Level', level_dict))
|
| ),
|
|
|
| ui.row(ui.input_action_button("player_button", "Get Player List", class_="btn-primary")),
|
|
|
| ui.row(ui.column(12, ui.output_ui('player_select_ui', 'Select Player'))),
|
|
|
| ui.row(ui.column(12, ui.output_ui('date_id', 'Select Date'))),
|
|
|
|
|
| ui.row(
|
| ui.column(4, ui.input_select('plot_id_1', 'Plot Left', function_dict, multiple=False, selected='velocity_kdes')),
|
| ui.column(4, ui.input_select('plot_id_2', 'Plot Middle', function_dict, multiple=False, selected='tj_stuff_roling')),
|
| ui.column(4, ui.input_select('plot_id_3', 'Plot Right', function_dict, multiple=False, selected='break_plot'))
|
| ),
|
| ui.row(
|
| ui.column(6, ui.input_select('split_id', 'Select Split', split_dict, multiple=False)),
|
| ui.column(6, ui.input_numeric('rolling_window', 'Rolling Window (for tjStuff+ Plot)', min=1, value=50))
|
| ),
|
|
|
|
|
| ui.row(ui.input_action_button("generate_plot", "Generate Plot", class_="btn-primary"))
|
| ),
|
|
|
| ui.panel_main(
|
| ui.navset_tab(
|
|
|
| ui.nav("Pitching Summary",
|
| ui.output_text("status"),
|
| ui.output_plot('plot', width='2100px', height='2100px')
|
| ),
|
| )
|
| )
|
| )
|
| )
|
|
|
|
|
| def server(input, output, session):
|
|
|
| @reactive.calc
|
| @reactive.event(input.pitcher_id, input.date_id,input.split_id)
|
| def cached_data():
|
|
|
| year_input = int(input.year_input())
|
| sport_id = int(input.level_input())
|
| player_input = int(input.pitcher_id())
|
| start_date = str(input.date_id()[0])
|
| end_date = str(input.date_id()[1])
|
|
|
| game_list = scrape.get_player_games_list(sport_id = sport_id,
|
| season = year_input,
|
| player_id = player_input,
|
| start_date = start_date,
|
| end_date = end_date)
|
|
|
| data_list = scrape.get_data(game_list_input = game_list[:])
|
| df = (stuff_apply.stuff_apply(fe.feature_engineering(update.update(scrape.get_data_df(data_list = data_list).filter(
|
| (pl.col("pitcher_id") == player_input)&
|
| (pl.col("is_pitch") == True)&
|
| (pl.col('batter_hand').is_in(split_dict_hand[input.split_id()]))
|
|
|
| )))).with_columns(
|
| pl.col('pitch_type').count().over('pitch_type').alias('pitch_count')
|
| ))
|
| return df
|
|
|
| @render.ui
|
| @reactive.event(input.player_button, ignore_none=False)
|
| def player_select_ui():
|
|
|
| df_pitcher_info = scrape.get_players(sport_id=int(input.level_input()), season=int(input.year_input())).filter(
|
| pl.col("position").is_in(['P'])).sort("name")
|
|
|
|
|
| pitcher_dict = dict(zip(df_pitcher_info['player_id'], df_pitcher_info['name']))
|
|
|
|
|
| return ui.input_select("pitcher_id", "Select Pitcher", pitcher_dict, selectize=True)
|
|
|
| @render.ui
|
| @reactive.event(input.player_button, ignore_none=False)
|
| def date_id():
|
|
|
| return ui.input_date_range("date_id", "Select Date Range",
|
| start=f"{int(input.year_input())}-01-01",
|
| end=f"{int(input.year_input())}-12-31",
|
| min=f"{int(input.year_input())}-01-01",
|
| max=f"{int(input.year_input())}-12-31")
|
| @output
|
| @render.text
|
| def status():
|
|
|
| if input.generate == 0:
|
| return ""
|
| return ""
|
|
|
| @output
|
| @render.plot
|
| @reactive.event(input.generate_plot, ignore_none=False)
|
| def plot():
|
|
|
| with ui.Progress(min=0, max=1) as p:
|
| p.set(message="Generating plot", detail="This may take a while...")
|
|
|
|
|
| p.set(0.3, "Gathering data...")
|
| year_input = int(input.year_input())
|
| sport_id = int(input.level_input())
|
| player_input = int(input.pitcher_id())
|
| start_date = str(input.date_id()[0])
|
| end_date = str(input.date_id()[1])
|
|
|
| print(year_input, sport_id, player_input, start_date, end_date)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| df = cached_data()
|
| df = df.clone()
|
|
|
| p.set(0.6, "Creating plot...")
|
|
|
|
|
|
|
| fig = plt.figure(figsize=(26,26))
|
| plt.rcParams.update({'figure.autolayout': True})
|
| fig.set_facecolor('white')
|
| sns.set_theme(style="whitegrid", palette=colour_palette)
|
| print('this is the one plot')
|
|
|
| gs = gridspec.GridSpec(6, 8,
|
| height_ratios=[5,20,12,36,36,7],
|
| width_ratios=[4,18,18,18,18,18,18,4])
|
|
|
|
|
| gs.update(hspace=0.2, wspace=0.5)
|
|
|
|
|
| ax_headshot = fig.add_subplot(gs[1,1:3])
|
| ax_bio = fig.add_subplot(gs[1,3:5])
|
| ax_logo = fig.add_subplot(gs[1,5:7])
|
|
|
| ax_season_table = fig.add_subplot(gs[2,1:7])
|
|
|
| ax_plot_1 = fig.add_subplot(gs[3,1:3])
|
| ax_plot_2 = fig.add_subplot(gs[3,3:5])
|
| ax_plot_3 = fig.add_subplot(gs[3,5:7])
|
|
|
| ax_table = fig.add_subplot(gs[4,1:7])
|
|
|
| ax_footer = fig.add_subplot(gs[-1,1:7])
|
| ax_header = fig.add_subplot(gs[0,1:7])
|
| ax_left = fig.add_subplot(gs[:,0])
|
| ax_right = fig.add_subplot(gs[:,-1])
|
|
|
|
|
| ax_footer.axis('off')
|
| ax_header.axis('off')
|
| ax_left.axis('off')
|
| ax_right.axis('off')
|
|
|
| sns.set_theme(style="whitegrid", palette=colour_palette)
|
| fig.set_facecolor('white')
|
|
|
| df_teams = scrape.get_teams()
|
|
|
| player_headshot(player_input=player_input, ax=ax_headshot,sport_id=sport_id,season=year_input)
|
| player_bio(pitcher_id=player_input, ax=ax_bio,sport_id=sport_id,year_input=year_input)
|
| plot_logo(pitcher_id=player_input, ax=ax_logo, df_team=df_teams,df_players=scrape.get_players(sport_id,year_input))
|
|
|
| stat_summary_table(df=df,
|
| ax=ax_season_table,
|
| player_input=player_input,
|
| split=split_dict[input.split_id()],
|
| sport_id=sport_id)
|
|
|
|
|
| for x,y,z in zip([input.plot_id_1(),input.plot_id_2(),input.plot_id_3()],[ax_plot_1,ax_plot_2,ax_plot_3],[1,3,5]):
|
| if x == 'velocity_kdes':
|
| velocity_kdes(df,
|
| ax=y,
|
| gs=gs,
|
| gs_x=[3,4],
|
| gs_y=[z,z+2],
|
| fig=fig)
|
| if x == 'tj_stuff_roling':
|
| tj_stuff_roling(df=df,
|
| window=int(input.rolling_window()),
|
| ax=y)
|
|
|
| if x == 'tj_stuff_roling_game':
|
| tj_stuff_roling_game(df=df,
|
| window=int(input.rolling_window()),
|
| ax=y)
|
|
|
| if x == 'break_plot':
|
| break_plot(df = df,ax=y)
|
|
|
| if x == 'location_plot_lhb':
|
| location_plot(df = df,ax=y,hand='L')
|
|
|
| if x == 'location_plot_rhb':
|
| location_plot(df = df,ax=y,hand='R')
|
|
|
| summary_table(df=df,
|
| ax=ax_table)
|
|
|
| plot_footer(ax_footer)
|
|
|
| fig.subplots_adjust(left=0.01, right=0.99, top=0.99, bottom=0.01)
|
|
|
|
|
|
|
|
|
|
|
| app = App(app_ui, server) |