| | 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 * |
| | from shiny import App, reactive, ui, render |
| | from shiny.ui import h2, tags |
| |
|
| | 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']} |
| |
|
| |
|
| | type_dict = {'R':'Regular Season', |
| | 'S':'Spring', |
| | 'P':'Playoffs' } |
| |
|
| |
|
| |
|
| | |
| | mlb_teams = [ |
| | {"team": "AZ", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/ari.png&h=500&w=500"}, |
| | {"team": "ATL", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/atl.png&h=500&w=500"}, |
| | {"team": "BAL", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/bal.png&h=500&w=500"}, |
| | {"team": "BOS", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/bos.png&h=500&w=500"}, |
| | {"team": "CHC", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/chc.png&h=500&w=500"}, |
| | {"team": "CWS", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/chw.png&h=500&w=500"}, |
| | {"team": "CIN", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/cin.png&h=500&w=500"}, |
| | {"team": "CLE", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/cle.png&h=500&w=500"}, |
| | {"team": "COL", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/col.png&h=500&w=500"}, |
| | {"team": "DET", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/det.png&h=500&w=500"}, |
| | {"team": "HOU", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/hou.png&h=500&w=500"}, |
| | {"team": "KC", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/kc.png&h=500&w=500"}, |
| | {"team": "LAA", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/laa.png&h=500&w=500"}, |
| | {"team": "LAD", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/lad.png&h=500&w=500"}, |
| | {"team": "MIA", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/mia.png&h=500&w=500"}, |
| | {"team": "MIL", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/mil.png&h=500&w=500"}, |
| | {"team": "MIN", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/min.png&h=500&w=500"}, |
| | {"team": "NYM", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/nym.png&h=500&w=500"}, |
| | {"team": "NYY", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/nyy.png&h=500&w=500"}, |
| | {"team": "OAK", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/oak.png&h=500&w=500"}, |
| | {"team": "PHI", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/phi.png&h=500&w=500"}, |
| | {"team": "PIT", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/pit.png&h=500&w=500"}, |
| | {"team": "SD", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/sd.png&h=500&w=500"}, |
| | {"team": "SF", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/sf.png&h=500&w=500"}, |
| | {"team": "SEA", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/sea.png&h=500&w=500"}, |
| | {"team": "STL", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/stl.png&h=500&w=500"}, |
| | {"team": "TB", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/tb.png&h=500&w=500"}, |
| | {"team": "TEX", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/tex.png&h=500&w=500"}, |
| | {"team": "TOR", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/tor.png&h=500&w=500"}, |
| | {"team": "WSH", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/wsh.png&h=500&w=500"}, |
| | {"team": "ATH", "logo_url": "https://a.espncdn.com/combiner/i?img=/i/teamlogos/mlb/500/scoreboard/oak.png&h=500&w=500"}, |
| | ] |
| |
|
| |
|
| | df_image = pd.DataFrame(mlb_teams) |
| | image_dict = df_image.set_index('team')['logo_url'].to_dict() |
| | image_dict_flip = df_image.set_index('logo_url')['team'].to_dict() |
| |
|
| | 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(4, ui.input_select('year_input', 'Select Season', year_list, selected=2024)), |
| | ui.column(4, ui.input_select('level_input', 'Select Level', level_dict)), |
| | ui.column(4, ui.input_select('type_input', 'Select Type', type_dict,selected='R')) |
| | ), |
| | |
| | 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.column(6, ui.input_switch("switch", "Custom Team?", False)), |
| | ui.column(6, ui.input_select('logo_select', 'Select Custom Logo', image_dict_flip, multiple=False)) |
| | ), |
| | |
| | |
| | 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, |
| | game_type = [input.type_input()]) |
| |
|
| | 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()), game_type = [input.type_input()]).filter( |
| | pl.col("position").is_in(['P','TWP'])).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) |
| |
|
| | if input.switch(): |
| |
|
| | |
| | logo_url = input.logo_select() |
| | |
| | |
| | response = requests.get(logo_url) |
| | |
| | |
| | img = Image.open(BytesIO(response.content)) |
| | |
| | |
| | ax_logo.set_xlim(0, 1.3) |
| | ax_logo.set_ylim(0, 1) |
| | ax_logo.imshow(img, extent=[0.3, 1.3, 0, 1], origin='upper') |
| | |
| | |
| | ax_logo.axis('off') |
| | |
| | else: |
| | 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=input.split_id(), |
| | sport_id=sport_id, |
| | game_type=[input.type_input()]) |
| |
|
| | |
| | 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) |
| |
|
| | |
| |
|
| | app = App(app_ui, server) |