Spaces:
Runtime error
Runtime error
Upload 34 files
Browse files- app.py +16 -20
- batter_scatter.py +21 -31
- batter_scatter_2.py +502 -0
- chadwick_df.csv +0 -0
- damage.py +21 -32
- decision_value.py +24 -32
- ev_angle.py +23 -33
- home.py +68 -32
- pitcher_scatter.py +508 -0
- pitcher_summary_graphic.ipynb +0 -0
- pitching_summary_graphic.py +0 -0
- pitching_summary_graphic_new.py +0 -0
- pitching_summary_graphic_new_fg_api.py +0 -0
- rolling_batter.py +732 -0
- rolling_pitcher.py +719 -0
- spray.py +23 -34
- statcast_20152023.csv +0 -0
- statcast_compare.py +701 -0
- statcast_pitch_summary.csv +19 -0
- summary_pitcher.csv +0 -0
- summary_pitcher_level.csv +0 -0
- test_data_mlb.ipynb +238 -0
app.py
CHANGED
|
@@ -9,40 +9,36 @@ import shinyswatch
|
|
| 9 |
|
| 10 |
#Import pages
|
| 11 |
from home import home
|
| 12 |
-
|
| 13 |
-
# from gsax_timeline import gsax_timeline
|
| 14 |
-
# from on_ice_xg_rates import on_ice_xg
|
| 15 |
-
# from gsax_leaderboard import gsax_leaderboard
|
| 16 |
-
# from on_ice_xgfp import on_ice_xgfp
|
| 17 |
-
# from team_xg_rates import team_xg_rates
|
| 18 |
-
# from gsax_comparison import gsax_comparison
|
| 19 |
-
# from game import game
|
| 20 |
from spray import spray
|
| 21 |
from decision_value import decision_value
|
| 22 |
from damage import damage
|
| 23 |
from batter_scatter import batter_scatter
|
| 24 |
from ev_angle import ev_angle
|
| 25 |
-
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
# Create app
|
| 29 |
routes = [
|
| 30 |
Mount('/home', app=home),
|
| 31 |
-
|
| 32 |
-
# Mount('/gsax-timeline', app=gsax_timeline),
|
| 33 |
-
# Mount('/skater-xg-rates', app=on_ice_xg),
|
| 34 |
-
# Mount('/gsax-leaderboard', app=gsax_leaderboard),
|
| 35 |
-
# Mount('/skater-xg-percentages', app=on_ice_xgfp),
|
| 36 |
-
# Mount('/team-xg-rates', app=team_xg_rates),
|
| 37 |
-
# Mount('/gsax-comparison',app=gsax_comparison),
|
| 38 |
Mount('/spray',app=spray),
|
| 39 |
Mount('/decision_value',app=decision_value),
|
| 40 |
Mount('/damage_model',app=damage),
|
| 41 |
Mount('/batter_scatter',app=batter_scatter),
|
| 42 |
Mount('/ev_angle',app=ev_angle),
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
| 46 |
]
|
| 47 |
|
| 48 |
#Run App
|
|
|
|
| 9 |
|
| 10 |
#Import pages
|
| 11 |
from home import home
|
| 12 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
from spray import spray
|
| 14 |
from decision_value import decision_value
|
| 15 |
from damage import damage
|
| 16 |
from batter_scatter import batter_scatter
|
| 17 |
from ev_angle import ev_angle
|
| 18 |
+
from rolling_batter import rolling_batter
|
| 19 |
+
from statcast_compare import statcast_compare
|
| 20 |
+
|
| 21 |
+
from rolling_pitcher import rolling_pitcher
|
| 22 |
+
from pitching_summary_graphic_new_fg_api import pitching_summary_graphic_new
|
| 23 |
+
from pitcher_scatter import pitcher_scatter
|
| 24 |
+
|
| 25 |
+
|
| 26 |
|
| 27 |
# Create app
|
| 28 |
routes = [
|
| 29 |
Mount('/home', app=home),
|
| 30 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
Mount('/spray',app=spray),
|
| 32 |
Mount('/decision_value',app=decision_value),
|
| 33 |
Mount('/damage_model',app=damage),
|
| 34 |
Mount('/batter_scatter',app=batter_scatter),
|
| 35 |
Mount('/ev_angle',app=ev_angle),
|
| 36 |
+
Mount('/rolling_batter',app=rolling_batter),
|
| 37 |
+
Mount('/statcast_compare',app=statcast_compare),
|
| 38 |
+
|
| 39 |
+
Mount('/rolling_pitcher',app=rolling_pitcher),
|
| 40 |
+
Mount('/pitching_summary_graphic_new',app=pitching_summary_graphic_new),
|
| 41 |
+
Mount('/pitcher_scatter',app=pitcher_scatter),
|
| 42 |
]
|
| 43 |
|
| 44 |
#Run App
|
batter_scatter.py
CHANGED
|
@@ -19,6 +19,8 @@ from shinywidgets import output_widget, render_widget
|
|
| 19 |
import pandas as pd
|
| 20 |
from configure import base_url
|
| 21 |
import shinyswatch
|
|
|
|
|
|
|
| 22 |
|
| 23 |
|
| 24 |
exit_velo_df_codes_summ_batter = pd.read_csv('summary_batter.csv',index_col=[0])
|
|
@@ -126,6 +128,7 @@ def server(input,output,session):
|
|
| 126 |
|
| 127 |
@output
|
| 128 |
@render.plot(alt="A histogram")
|
|
|
|
| 129 |
def plot():
|
| 130 |
sns.set_theme(style="whitegrid", palette="pastel")
|
| 131 |
print(input.level_id())
|
|
@@ -387,6 +390,7 @@ batter_scatter = App(ui.page_fluid(
|
|
| 387 |
shinyswatch.theme.simplex(),
|
| 388 |
ui.tags.h4("TJStats"),
|
| 389 |
ui.tags.i("Baseball Analytics and Visualizations"),
|
|
|
|
| 390 |
ui.navset_tab(
|
| 391 |
ui.nav_control(
|
| 392 |
ui.a(
|
|
@@ -397,7 +401,11 @@ batter_scatter = App(ui.page_fluid(
|
|
| 397 |
ui.nav_menu(
|
| 398 |
"Batter Charts",
|
| 399 |
ui.nav_control(
|
| 400 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
"Spray",
|
| 402 |
href="spray/"
|
| 403 |
),
|
|
@@ -416,48 +424,29 @@ batter_scatter = App(ui.page_fluid(
|
|
| 416 |
ui.a(
|
| 417 |
"EV vs LA Plot",
|
| 418 |
href="ev_angle/"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 419 |
)
|
| 420 |
),
|
| 421 |
),
|
| 422 |
ui.nav_menu(
|
| 423 |
-
"
|
| 424 |
ui.nav_control(
|
| 425 |
ui.a(
|
| 426 |
-
"
|
| 427 |
-
href="
|
| 428 |
),
|
| 429 |
ui.a(
|
| 430 |
-
"
|
| 431 |
-
href="
|
| 432 |
),
|
| 433 |
ui.a(
|
| 434 |
-
"
|
| 435 |
-
href="
|
| 436 |
)
|
| 437 |
),
|
| 438 |
-
),ui.nav_menu(
|
| 439 |
-
"Team Charts",
|
| 440 |
-
ui.nav_control(
|
| 441 |
-
ui.a(
|
| 442 |
-
"Team xG Rates",
|
| 443 |
-
href="team-xg-rates/"
|
| 444 |
-
),
|
| 445 |
-
),
|
| 446 |
-
),ui.nav_control(
|
| 447 |
-
ui.a(
|
| 448 |
-
"Games",
|
| 449 |
-
href="games/"
|
| 450 |
-
),
|
| 451 |
-
),ui.nav_control(
|
| 452 |
-
ui.a(
|
| 453 |
-
"About",
|
| 454 |
-
href="about/"
|
| 455 |
-
),
|
| 456 |
-
),ui.nav_control(
|
| 457 |
-
ui.a(
|
| 458 |
-
"Articles",
|
| 459 |
-
href="articles/"
|
| 460 |
-
),
|
| 461 |
)),ui.row(
|
| 462 |
ui.layout_sidebar(
|
| 463 |
|
|
@@ -491,6 +480,7 @@ batter_scatter = App(ui.page_fluid(
|
|
| 491 |
ui.row(
|
| 492 |
ui.input_switch("names", "Toggle Names"),
|
| 493 |
ui.input_switch("group_level", "Group Levels")),
|
|
|
|
| 494 |
),
|
| 495 |
|
| 496 |
ui.panel_main(
|
|
|
|
| 19 |
import pandas as pd
|
| 20 |
from configure import base_url
|
| 21 |
import shinyswatch
|
| 22 |
+
import inflect
|
| 23 |
+
from matplotlib.pyplot import text
|
| 24 |
|
| 25 |
|
| 26 |
exit_velo_df_codes_summ_batter = pd.read_csv('summary_batter.csv',index_col=[0])
|
|
|
|
| 128 |
|
| 129 |
@output
|
| 130 |
@render.plot(alt="A histogram")
|
| 131 |
+
@reactive.event(input.go, ignore_none=False)
|
| 132 |
def plot():
|
| 133 |
sns.set_theme(style="whitegrid", palette="pastel")
|
| 134 |
print(input.level_id())
|
|
|
|
| 390 |
shinyswatch.theme.simplex(),
|
| 391 |
ui.tags.h4("TJStats"),
|
| 392 |
ui.tags.i("Baseball Analytics and Visualizations"),
|
| 393 |
+
ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),
|
| 394 |
ui.navset_tab(
|
| 395 |
ui.nav_control(
|
| 396 |
ui.a(
|
|
|
|
| 401 |
ui.nav_menu(
|
| 402 |
"Batter Charts",
|
| 403 |
ui.nav_control(
|
| 404 |
+
ui.a(
|
| 405 |
+
"Batting Rolling",
|
| 406 |
+
href="rolling_batter/"
|
| 407 |
+
),
|
| 408 |
+
ui.a(
|
| 409 |
"Spray",
|
| 410 |
href="spray/"
|
| 411 |
),
|
|
|
|
| 424 |
ui.a(
|
| 425 |
"EV vs LA Plot",
|
| 426 |
href="ev_angle/"
|
| 427 |
+
),
|
| 428 |
+
ui.a(
|
| 429 |
+
"Statcast Compare",
|
| 430 |
+
href="statcast_compare/"
|
| 431 |
)
|
| 432 |
),
|
| 433 |
),
|
| 434 |
ui.nav_menu(
|
| 435 |
+
"Pitcher Charts",
|
| 436 |
ui.nav_control(
|
| 437 |
ui.a(
|
| 438 |
+
"Pitcher Rolling",
|
| 439 |
+
href="rolling_pitcher/"
|
| 440 |
),
|
| 441 |
ui.a(
|
| 442 |
+
"Pitcher Summary",
|
| 443 |
+
href="pitching_summary_graphic_new/"
|
| 444 |
),
|
| 445 |
ui.a(
|
| 446 |
+
"Pitcher Scatter",
|
| 447 |
+
href="pitcher_scatter/"
|
| 448 |
)
|
| 449 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 450 |
)),ui.row(
|
| 451 |
ui.layout_sidebar(
|
| 452 |
|
|
|
|
| 480 |
ui.row(
|
| 481 |
ui.input_switch("names", "Toggle Names"),
|
| 482 |
ui.input_switch("group_level", "Group Levels")),
|
| 483 |
+
ui.input_action_button("go", "Generate",class_="btn-primary"),
|
| 484 |
),
|
| 485 |
|
| 486 |
ui.panel_main(
|
batter_scatter_2.py
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui
|
| 2 |
+
import datasets
|
| 3 |
+
from datasets import load_dataset
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import numpy as np
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
import seaborn as sns
|
| 8 |
+
import numpy as np
|
| 9 |
+
from scipy.stats import gaussian_kde
|
| 10 |
+
import matplotlib
|
| 11 |
+
from matplotlib.ticker import MaxNLocator
|
| 12 |
+
from matplotlib.gridspec import GridSpec
|
| 13 |
+
from scipy.stats import zscore
|
| 14 |
+
import math
|
| 15 |
+
import matplotlib
|
| 16 |
+
from adjustText import adjust_text
|
| 17 |
+
import matplotlib.ticker as mtick
|
| 18 |
+
from shinywidgets import output_widget, render_widget
|
| 19 |
+
import pandas as pd
|
| 20 |
+
from configure import base_url
|
| 21 |
+
import shinyswatch
|
| 22 |
+
import inflect
|
| 23 |
+
from matplotlib.pyplot import text
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
exit_velo_df_codes_summ_batter = pd.read_csv('summary_batter.csv',index_col=[0])
|
| 27 |
+
#exit_velo_df_codes_summ = pd.read_csv('summary_pitcher.csv',index_col=[0])
|
| 28 |
+
|
| 29 |
+
exit_velo_df_codes_summ_non_level = pd.read_csv('summary_batter_level.csv',index_col=[0]).reset_index(drop=True)
|
| 30 |
+
|
| 31 |
+
exit_velo_df_codes_summ_non_level['levels'] = exit_velo_df_codes_summ_non_level.levels.str.split(', ')
|
| 32 |
+
|
| 33 |
+
exit_velo_df_codes_summ_non_level = exit_velo_df_codes_summ_non_level.rename(columns={'levels':'level'})
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
print(exit_velo_df_codes_summ_batter.bb_minus_k_percent)
|
| 38 |
+
|
| 39 |
+
batter_dict_stat = { 'sweet_spot_percent':{'x_axis':'SweetSpot%','title':'SweetSpot%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 40 |
+
'max_launch_speed':{'x_axis':'Max Exit Velocity','title':'Max Exit Velocity','flip_p':False,'decimal_format':'string_0','percent_adjust':1},
|
| 41 |
+
'launch_speed_90':{'x_axis':'90th Percentile EV','title':'90th Percentile EV','flip_p':False,'decimal_format':'string_0','percent_adjust':1},
|
| 42 |
+
'launch_speed':{'x_axis':'Exit Velocity','title':'Exit Velocity','flip_p':False,'decimal_format':'string_0','percent_adjust':1},
|
| 43 |
+
'launch_angle':{'x_axis':'Launch Angle','title':'Launch Angle','flip_p':False,'decimal_format':'string_0','percent_adjust':100},
|
| 44 |
+
'avg':{'x_axis':'AVG','title':'AVG','flip_p':False,'decimal_format':'string_3','percent_adjust':100},
|
| 45 |
+
'obp':{'x_axis':'OBP','title':'OBP','flip_p':False,'decimal_format':'string_3','percent_adjust':100},
|
| 46 |
+
'slg':{'x_axis':'SLG','title':'SLG','flip_p':False,'decimal_format':'string_3','percent_adjust':100},
|
| 47 |
+
'ops':{'x_axis':'OPS','title':'OPS','flip_p':False,'decimal_format':'string_3','percent_adjust':100},
|
| 48 |
+
'k_percent':{'x_axis':'K%','title':'K%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 49 |
+
'bb_percent':{'x_axis':'BB%','title':'BB%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 50 |
+
'bb_over_k_percent':{'x_axis':'BB/K','title':'BB/K','flip_p':False,'decimal_format':'string_1','percent_adjust':100},
|
| 51 |
+
'bb_minus_k_percent':{'x_axis':'BB%-K%','title':'BB%-K%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 52 |
+
'csw_percent':{'x_axis':'CSW%','title':'CSW%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 53 |
+
'woba_percent':{'x_axis':'wOBA','title':'wOBA','flip_p':False,'decimal_format':'string_3','percent_adjust':100},
|
| 54 |
+
'hard_hit_percent':{'x_axis':'HardHit%','title':'HardHit%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 55 |
+
'barrel_percent':{'x_axis':'Barrel%','title':'Barrel%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 56 |
+
'zone_contact_percent':{'x_axis':'Z-Contact%','title':'Z-Contact%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 57 |
+
'zone_swing_percent':{'x_axis':'Z-Swing%','title':'Z-Swing%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 58 |
+
'zone_percent':{'x_axis':'Zone%','title':'Zone%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 59 |
+
'chase_percent':{'x_axis':'O-Swing%','title':'O-Swing%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 60 |
+
'chase_contact':{'x_axis':'O-Contact%','title':'O-Contact%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 61 |
+
'swing_percent':{'x_axis':'Swing%','title':'Swing%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 62 |
+
'whiff_rate':{'x_axis':'Whiff%','title':'Whiff%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 63 |
+
'swstr_rate':{'x_axis':'SwStr%','title':'SwStr%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
batter_dict_stat_small = { 'sweet_spot_percent':'SweetSpot%',
|
| 67 |
+
'max_launch_speed':'Max Exit Velocity',
|
| 68 |
+
'launch_speed_90':'90th Percentile EV',
|
| 69 |
+
'launch_speed':'Exit Velocity',
|
| 70 |
+
'launch_angle':'Launch Angle',
|
| 71 |
+
'avg':'AVG',
|
| 72 |
+
'obp':'OBP',
|
| 73 |
+
'slg':'SLG',
|
| 74 |
+
'ops':'OPS',
|
| 75 |
+
'k_percent':'K%',
|
| 76 |
+
'bb_percent':'BB%',
|
| 77 |
+
'bb_over_k_percent':'BB/K',
|
| 78 |
+
'bb_minus_k_percent':'BB%-K%',
|
| 79 |
+
'csw_percent':'CSW%',
|
| 80 |
+
'woba_percent':'wOBA',
|
| 81 |
+
'hard_hit_percent':'HardHit%',
|
| 82 |
+
'barrel_percent':'Barrel%',
|
| 83 |
+
'zone_contact_percent':'Z-Contact%',
|
| 84 |
+
'zone_swing_percent':'Z-Swing%',
|
| 85 |
+
'zone_percent':'Zone%',
|
| 86 |
+
'chase_percent':'O-Swing%',
|
| 87 |
+
'chase_contact':'O-Contact%',
|
| 88 |
+
'swing_percent':'Swing%',
|
| 89 |
+
'whiff_rate':'Whiff%',
|
| 90 |
+
'swstr_rate':'SwStr%',
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
colour_palette = ['#FFB000','#648FFF','#785EF0',
|
| 95 |
+
'#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']
|
| 96 |
+
|
| 97 |
+
level_dict = {'MLB':'MLB','AAA':'AAA','AA':'AA','A+':'A+','A':'A','ROK':'ROK'}
|
| 98 |
+
|
| 99 |
+
batter_test_df = exit_velo_df_codes_summ_batter.sort_values(by='batter').drop_duplicates(subset='batter_id').reset_index(drop=True)[['batter_id','batter']]#['pitcher'].to_dict()
|
| 100 |
+
batter_test_df = batter_test_df.set_index('batter_id')
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def decimal_format_assign(x):
|
| 104 |
+
if x['decimal_format'] == 'percent_1':
|
| 105 |
+
return mtick.PercentFormatter(1,decimals=1)
|
| 106 |
+
if x['decimal_format'] == 'string_3':
|
| 107 |
+
return mtick.FormatStrFormatter('%.3f')
|
| 108 |
+
if x['decimal_format'] == 'string_0':
|
| 109 |
+
return mtick.FormatStrFormatter('%.0f')
|
| 110 |
+
if x['decimal_format'] == 'string_1':
|
| 111 |
+
return mtick.FormatStrFormatter('%.1f')
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
#test_df = test_df[test_df.pitcher == 'Chris Bassitt'].append(test_df[test_df.pitcher != 'Chris Bassitt'])
|
| 115 |
+
|
| 116 |
+
batter_dict = batter_test_df['batter'].to_dict()
|
| 117 |
+
|
| 118 |
+
exit_velo_df_codes_summ_batter.position = exit_velo_df_codes_summ_batter.position.replace(['LF','RF','CF','TWP'],['OF','OF','OF','DH'])
|
| 119 |
+
exit_velo_df_codes_summ_non_level.position = exit_velo_df_codes_summ_non_level.position.replace(['LF','RF','CF','TWP'],['OF','OF','OF','DH'])
|
| 120 |
+
|
| 121 |
+
position_list = ['All'] + list(exit_velo_df_codes_summ_batter.position.unique())
|
| 122 |
+
team_list = ['All'] + sorted(list(exit_velo_df_codes_summ_batter.parent_org_abb.unique()))
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def server(input,output,session):
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
@output
|
| 130 |
+
@render.plot(alt="A histogram")
|
| 131 |
+
def plot():
|
| 132 |
+
sns.set_theme(style="whitegrid", palette="pastel")
|
| 133 |
+
print(input.level_id())
|
| 134 |
+
print(input.n())
|
| 135 |
+
print('we made it here',input.team_id(),input.position_id())
|
| 136 |
+
if input.group_level():
|
| 137 |
+
data_df = exit_velo_df_codes_summ_non_level.copy()
|
| 138 |
+
|
| 139 |
+
turth_list = []
|
| 140 |
+
#turth_list_2 = []
|
| 141 |
+
for x in range(0,len(data_df.level)):
|
| 142 |
+
turth_list_2 = []
|
| 143 |
+
for y in range(0,len(data_df.level[x])):
|
| 144 |
+
#print(level_list[x][y])
|
| 145 |
+
turth_list_2.append(data_df.level[x][y] in input.level_id())
|
| 146 |
+
turth_list.append(turth_list_2)
|
| 147 |
+
|
| 148 |
+
final_check_list = [True if True in x else False for x in turth_list]
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
data_df = data_df[(data_df.pa >= input.n())&(data_df.age <= input.n_age())&(final_check_list)]
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
else:
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
data_df = exit_velo_df_codes_summ_batter.copy()
|
| 158 |
+
data_df = data_df[(data_df.pa >= input.n())&(data_df.age <= input.n_age())&(data_df.level.isin(input.level_id()))]
|
| 159 |
+
print(data_df)
|
| 160 |
+
if 'All' in input.team_id():
|
| 161 |
+
print('nice')#data_df = data_df[(data_df.pa >= input.n())&(data_df.age <= input.n_age())].reset_index(drop=True)
|
| 162 |
+
|
| 163 |
+
else:
|
| 164 |
+
data_df = data_df[(data_df.parent_org_abb.isin(input.team_id()))].reset_index(drop=True)
|
| 165 |
+
|
| 166 |
+
if 'All' in input.position_id():
|
| 167 |
+
print('nice')#data_df = data_df[(data_df.level.isin(input.level_id()))&(data_df.pa >= input.n())&(data_df.age <= input.n_age())].reset_index(drop=True)
|
| 168 |
+
|
| 169 |
+
else:
|
| 170 |
+
data_df = data_df[(data_df.position.isin(input.position_id()))].reset_index(drop=True)
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
#print('we made it here')
|
| 174 |
+
print(data_df)
|
| 175 |
+
data_df = data_df.sort_values(by='level').reset_index(drop=True)
|
| 176 |
+
print(batter_dict_stat[input.stat_x()]['flip_p'])
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
x_flip = batter_dict_stat[input.stat_x()]['flip_p']
|
| 181 |
+
y_flip = batter_dict_stat[input.stat_y()]['flip_p']
|
| 182 |
+
cbr_flip = batter_dict_stat[input.stat_z()]['flip_p']
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
data_df[input.stat_x()+'_percent'] = data_df[input.stat_x()].rank(pct=True,ascending=abs(x_flip-1))
|
| 187 |
+
|
| 188 |
+
data_df[input.stat_y()+'_percent'] = data_df[input.stat_y()].rank(pct=True,ascending=abs(y_flip-1))
|
| 189 |
+
|
| 190 |
+
data_df[input.stat_z()+'_percent'] = data_df[input.stat_z()].rank(pct=True,ascending=abs(cbr_flip-1))
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
fig, ax = plt.subplots(1, 1, figsize=(9, 9))
|
| 195 |
+
|
| 196 |
+
#data_df['bb_over_obp'] = data_df['bb']/data_df['k']
|
| 197 |
+
|
| 198 |
+
#data_df[input.stat_z()]= data_df[input.stat_z()].fillna(-100000)
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
if cbr_flip:
|
| 202 |
+
cmap_hue = matplotlib.colors.LinearSegmentedColormap.from_list("", [colour_palette[0],colour_palette[3],colour_palette[1]])
|
| 203 |
+
norm = plt.Normalize(data_df[input.stat_z()].min(), data_df[input.stat_z()].max())
|
| 204 |
+
|
| 205 |
+
else:
|
| 206 |
+
cmap_hue = matplotlib.colors.LinearSegmentedColormap.from_list("", [colour_palette[1],colour_palette[3],colour_palette[0]])
|
| 207 |
+
norm = plt.Normalize(data_df[input.stat_z()].min(), data_df[input.stat_z()].max())
|
| 208 |
+
|
| 209 |
+
sm = plt.cm.ScalarMappable(cmap=cmap_hue, norm=norm)
|
| 210 |
+
print('we made it here')
|
| 211 |
+
|
| 212 |
+
# sns.regplot(x = stat_x, y = stat_y, data=data_df, color = colour_palette[6],ax=ax,scatter=False,
|
| 213 |
+
# line_kws=dict(alpha=0.3,linewidth=2,zorder=1))
|
| 214 |
+
# scatter_plot = sns.scatterplot(x = stat_x, y = stat_y, data=data_df, color = colour_palette[0],ax=ax,hue=stat_z,palette=cmap_hue)
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
# r, p = sp.stats.pearsonr(data_df[input.stat_x()], data_df[input.stat_y()])
|
| 219 |
+
# ax = plt.gca()
|
| 220 |
+
# # ax.text(.25, 0.3, 'r={:.2f}, p={:.2g}'.format(r, p),
|
| 221 |
+
# # transform=ax.transAxes, fontsize=12)
|
| 222 |
+
|
| 223 |
+
# ax.annotate('RΒ²={:.2f}'.format(r, p), ( math.ceil(data_df[input.stat_x()].max()*batter_dict_stat[input.stat_x()]['percent_adjust']/5)*5/batter_dict_stat[input.stat_x()]['percent_adjust']*(1-batter_dict_stat[input.stat_x()]['flip_p']),
|
| 224 |
+
# math.floor(data_df[input.stat_y()].min()*batter_dict_stat[input.stat_y()]['percent_adjust']/5)*5/batter_dict_stat[input.stat_y()]['percent_adjust']*(1-batter_dict_stat[input.stat_y()]['flip_p'])),
|
| 225 |
+
# fontsize=18,fontname='Century Gothic',ha='right')
|
| 226 |
+
|
| 227 |
+
if input.group_level():
|
| 228 |
+
scatter = sns.scatterplot(x = input.stat_x(), y = input.stat_y(), data=data_df, color = '#b3b3b3')
|
| 229 |
+
#ax.get_legend().remove()
|
| 230 |
+
scatter = sns.scatterplot(x = input.stat_x(), y = input.stat_y(), data=data_df, color = colour_palette[0],ax=ax,hue=input.stat_z(),palette=cmap_hue)
|
| 231 |
+
else:
|
| 232 |
+
scatter = sns.scatterplot(x = input.stat_x(), y = input.stat_y(), data=data_df, color = '#b3b3b3',style='level')
|
| 233 |
+
#ax.get_legend().remove()
|
| 234 |
+
scatter = sns.scatterplot(x = input.stat_x(), y = input.stat_y(), data=data_df, color = colour_palette[0],ax=ax,hue=input.stat_z(),palette=cmap_hue,style='level')
|
| 235 |
+
sns.set_theme(style="whitegrid", palette="pastel")
|
| 236 |
+
|
| 237 |
+
fig.set_facecolor('#F0F0F0')
|
| 238 |
+
ax.set_facecolor('white')
|
| 239 |
+
|
| 240 |
+
print('we made it here')
|
| 241 |
+
# for i in range(0,len(pitch_group_unique)):
|
| 242 |
+
# data_df = elly_zone_df[elly_zone_df.pitch_group==pitch_group_unique[i]]
|
| 243 |
+
# len_df.append(len(data_df))
|
| 244 |
+
# sns.lineplot(x=range(1,len(data_df)+1),y=data_df.swings.rolling(window=rolling_window_input).sum()/data_df.pitches.rolling(window=rolling_window_input).sum(),color=colour_palette[i],linewidth=3,ax=ax,
|
| 245 |
+
# label=f'{pitch_group_unique[i]} (Season Average {float(data_df.swings.sum()/data_df.pitches.sum()):.1%})',zorder=i+10)
|
| 246 |
+
# ax.hlines(xmin=0,xmax=len(elly_zone_df),y=data_df.swings.sum()/data_df.pitches.sum(),color=colour_palette[i],linewidth=3,linestyle='-.',alpha=0.4,zorder=i)
|
| 247 |
+
|
| 248 |
+
ts=[]
|
| 249 |
+
print(input.player_id())
|
| 250 |
+
|
| 251 |
+
print(len(data_df))
|
| 252 |
+
if input.names():
|
| 253 |
+
for i in range(len(data_df)):
|
| 254 |
+
if (data_df[input.stat_x()+'_percent'].values[i] < input.n_percent_bot_x() or data_df[input.stat_x()+'_percent'].values[i] > 1 - input.n_percent_top_x() ) \
|
| 255 |
+
or (data_df[input.stat_y()+'_percent'].values[i] < input.n_percent_bot_y() or data_df[input.stat_y()+'_percent'].values[i] > 1 -input.n_percent_top_y()) \
|
| 256 |
+
or (data_df[input.stat_z()+'_percent'].values[i] < input.n_percent_bot_z() or data_df[input.stat_z()+'_percent'].values[i] > 1 -input.n_percent_top_z() )\
|
| 257 |
+
or (str(data_df.batter_id[i]) in (input.player_id())):
|
| 258 |
+
# print(data_df.batter[i])
|
| 259 |
+
# ax.annotate(data_df.batter[i], xy=((data_df[input.stat_x()][i])+0.025/batter_dict_stat[input.stat_x()]['percent_adjust'], data_df[input.stat_y()][i]+0.01/batter_dict_stat[input.stat_x()]['percent_adjust']), xytext=(-20,20),
|
| 260 |
+
# textcoords='offset points', ha='center', va='bottom',fontsize=7,
|
| 261 |
+
# bbox=dict(boxstyle='round,pad=0', fc=colour_palette[6], alpha=0.0),
|
| 262 |
+
# arrowprops=dict(arrowstyle='->', connectionstyle="angle,angleA=-90,angleB=-10,rad=2",
|
| 263 |
+
# color=colour_palette[8]))
|
| 264 |
+
|
| 265 |
+
#if data_df['batter'][i] != 'Jo Adell':
|
| 266 |
+
# ax.annotate(data_df.batter[i], (data_df[input.stat_x()][i]-len(data_df.batter[i])*0.00025, data_df[input.stat_y()][i]+0.001),fontsize=8)
|
| 267 |
+
ts.append(ax.text(data_df[input.stat_x()][i], data_df[input.stat_y()][i], data_df.batter[i],fontsize=8))
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
ax.hlines(xmin=(math.floor((data_df[input.stat_x()].min()*batter_dict_stat[input.stat_x()]['percent_adjust']-0.01)/5))*5/batter_dict_stat[input.stat_x()]['percent_adjust'],
|
| 272 |
+
xmax= (math.ceil((data_df[input.stat_x()].max()*batter_dict_stat[input.stat_x()]['percent_adjust']+0.01)/5))*5/batter_dict_stat[input.stat_x()]['percent_adjust'],
|
| 273 |
+
y=data_df[input.stat_y()].mean(),color='gray',linewidth=3,linestyle='dotted',alpha=0.4)
|
| 274 |
+
|
| 275 |
+
print('we made it here')
|
| 276 |
+
|
| 277 |
+
ax.vlines(ymin=(math.floor((data_df[input.stat_y()].min()*batter_dict_stat[input.stat_y()]['percent_adjust']-0.01)/5))*5/batter_dict_stat[input.stat_y()]['percent_adjust'],
|
| 278 |
+
ymax= (math.ceil((data_df[input.stat_y()].max()*batter_dict_stat[input.stat_y()]['percent_adjust']+0.01)/5))*5/batter_dict_stat[input.stat_y()]['percent_adjust'],
|
| 279 |
+
x=data_df[input.stat_x()].mean(),color='gray',linewidth=3,linestyle='dotted',alpha=0.4)
|
| 280 |
+
|
| 281 |
+
print(data_df[input.stat_x()].min())
|
| 282 |
+
print(batter_dict_stat[input.stat_x()]['percent_adjust'])
|
| 283 |
+
print((math.floor((data_df[input.stat_x()].min()*batter_dict_stat[input.stat_x()]['percent_adjust']-0.01)/5))*5/batter_dict_stat[input.stat_x()]['percent_adjust'])
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
ax.set_xlim((math.floor((data_df[input.stat_x()].min()*batter_dict_stat[input.stat_x()]['percent_adjust'])/5))*5/batter_dict_stat[input.stat_x()]['percent_adjust'],
|
| 287 |
+
(math.ceil((data_df[input.stat_x()].max()*batter_dict_stat[input.stat_x()]['percent_adjust'])/5))*5/batter_dict_stat[input.stat_x()]['percent_adjust'])
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
ax.set_ylim((math.floor((data_df[input.stat_y()].min()*batter_dict_stat[input.stat_y()]['percent_adjust'])/5))*5/batter_dict_stat[input.stat_y()]['percent_adjust'],
|
| 291 |
+
(math.ceil((data_df[input.stat_y()].max()*batter_dict_stat[input.stat_y()]['percent_adjust'])/5))*5/batter_dict_stat[input.stat_y()]['percent_adjust'])
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
|
| 295 |
+
title_level = str([x .strip("\'")for x in input.level_id()]).strip('[').strip(']').replace("'",'')
|
| 296 |
+
|
| 297 |
+
if title_level == 'AAA, AA, A+, A':
|
| 298 |
+
title_level='MiLB'
|
| 299 |
+
#title_level = input.level_id()[0]
|
| 300 |
+
if input.n_age() >= 50:
|
| 301 |
+
title_spot = f'{title_level} Batter {batter_dict_stat[input.stat_y()]["title"]} vs {batter_dict_stat[input.stat_x()]["title"]} (min. {input.n()} PA)'
|
| 302 |
+
|
| 303 |
+
else:
|
| 304 |
+
title_spot = f'{title_level} Batter {batter_dict_stat[input.stat_y()]["title"]} vs {batter_dict_stat[input.stat_x()]["title"]} (min. {input.n()} PA, Max Age {input.n_age()})'
|
| 305 |
+
|
| 306 |
+
ax.set_title(title_spot, fontsize=24/(len(title_spot)*0.03),fontname='Century Gothic')
|
| 307 |
+
# #vals = ax.get_yticks()
|
| 308 |
+
ax.set_xlabel(batter_dict_stat[input.stat_x()]['x_axis'], fontsize=16,fontname='Century Gothic')
|
| 309 |
+
ax.set_ylabel(batter_dict_stat[input.stat_y()]['x_axis'], fontsize=16,fontname='Century Gothic')
|
| 310 |
+
|
| 311 |
+
|
| 312 |
+
if input.group_level():
|
| 313 |
+
ax.get_legend().remove()
|
| 314 |
+
|
| 315 |
+
if not input.group_level():
|
| 316 |
+
if len(input.level_id()) > 1:
|
| 317 |
+
h,l = scatter.get_legend_handles_labels()
|
| 318 |
+
l[-(len(input.level_id())+1)] = 'Level'
|
| 319 |
+
ax.legend(h[-(len(input.level_id())+1):],l[-(len(input.level_id())+1):], borderaxespad=0.1,loc=0)
|
| 320 |
+
|
| 321 |
+
else:
|
| 322 |
+
ax.get_legend().remove()
|
| 323 |
+
|
| 324 |
+
#plt.show(g)
|
| 325 |
+
# ax.figure.colorbar(sm, ax=ax)
|
| 326 |
+
|
| 327 |
+
cbar = ax.figure.colorbar(sm, ax=ax,format=decimal_format_assign(x=batter_dict_stat[input.stat_z()]),orientation='vertical',aspect=30)
|
| 328 |
+
cbar.set_label(batter_dict_stat[input.stat_z()]['x_axis'])
|
| 329 |
+
#fig.axes[0].invert_yaxis()
|
| 330 |
+
print('we made it here5')
|
| 331 |
+
fig.subplots_adjust(wspace=.02, hspace=.02)
|
| 332 |
+
# ax.xaxis.set_major_formatter(FuncFormatter(lambda x, _: int(x)))
|
| 333 |
+
#ax.set_yticks([0,0.1,0.2,0.3,0.4,0.5])
|
| 334 |
+
# fig.colorbar(plot_dist, ax=ax)
|
| 335 |
+
# fig.colorbar(plot_dist)
|
| 336 |
+
|
| 337 |
+
if batter_dict_stat[input.stat_x()]['flip_p']:
|
| 338 |
+
fig.axes[0].invert_xaxis()
|
| 339 |
+
|
| 340 |
+
if batter_dict_stat[input.stat_y()]['flip_p']:
|
| 341 |
+
fig.axes[0].invert_yaxis()
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
# ax.xaxis.set_major_formatter(mtick.PercentFormatter(1,decimals=0))
|
| 345 |
+
# ax.yaxis.set_major_formatter(mtick.PercentFormatter(1))
|
| 346 |
+
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
|
| 351 |
+
print('we made it here6')
|
| 352 |
+
|
| 353 |
+
ax.xaxis.set_major_formatter(decimal_format_assign(x=batter_dict_stat[input.stat_x()]))
|
| 354 |
+
ax.yaxis.set_major_formatter(decimal_format_assign(x=batter_dict_stat[input.stat_y()]))
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
print('we made it here7')
|
| 358 |
+
# ax.text(0.5, 0.5, '/u/tomstoms', transform=ax.transAxes,
|
| 359 |
+
# fontsize=60, color='gray', alpha=0.075,
|
| 360 |
+
# ha='center', va='center', rotation=45)
|
| 361 |
+
|
| 362 |
+
print(ts)
|
| 363 |
+
if len(ts) > 0:
|
| 364 |
+
adjust_text(ts,
|
| 365 |
+
arrowprops=dict(arrowstyle="-", color=colour_palette[4], lw=1),ax=ax)
|
| 366 |
+
|
| 367 |
+
#ax.legend(fontsize='16')
|
| 368 |
+
fig.text(x=0.03,y=0.02,s='By: @TJStats',fontname='Century Gothic')
|
| 369 |
+
fig.text(x=1-0.03,y=0.02,s='Data: MLB',ha='right',fontname='Century Gothic')
|
| 370 |
+
fig.tight_layout()
|
| 371 |
+
#matplotlib.rcParams["figure.dpi"] = 600
|
| 372 |
+
#plt.show()
|
| 373 |
+
|
| 374 |
+
|
| 375 |
+
rolling_pitcher = App(ui.page_fluid(
|
| 376 |
+
ui.tags.base(href=base_url),
|
| 377 |
+
ui.tags.div(
|
| 378 |
+
{"style": "width:90%;margin: 0 auto;max-width: 1600px;"},
|
| 379 |
+
ui.tags.style(
|
| 380 |
+
"""
|
| 381 |
+
h4 {
|
| 382 |
+
margin-top: 1em;font-size:35px;
|
| 383 |
+
}
|
| 384 |
+
h2{
|
| 385 |
+
font-size:25px;
|
| 386 |
+
}
|
| 387 |
+
"""
|
| 388 |
+
),
|
| 389 |
+
shinyswatch.theme.simplex(),
|
| 390 |
+
ui.tags.h4("TJStats"),
|
| 391 |
+
ui.tags.i("Baseball Analytics and Visualizations"),
|
| 392 |
+
ui.navset_tab(
|
| 393 |
+
ui.nav_control(
|
| 394 |
+
ui.a(
|
| 395 |
+
"Home",
|
| 396 |
+
href="home/"
|
| 397 |
+
),
|
| 398 |
+
),
|
| 399 |
+
ui.nav_menu(
|
| 400 |
+
"Batter Charts",
|
| 401 |
+
ui.nav_control(
|
| 402 |
+
ui.a(
|
| 403 |
+
"Spray",
|
| 404 |
+
href="spray/"
|
| 405 |
+
),
|
| 406 |
+
ui.a(
|
| 407 |
+
"Decision Value",
|
| 408 |
+
href="decision_value/"
|
| 409 |
+
),
|
| 410 |
+
ui.a(
|
| 411 |
+
"Damage Model",
|
| 412 |
+
href="damage_model/"
|
| 413 |
+
),
|
| 414 |
+
ui.a(
|
| 415 |
+
"Batter Scatter",
|
| 416 |
+
href="batter_scatter/"
|
| 417 |
+
),
|
| 418 |
+
ui.a(
|
| 419 |
+
"EV vs LA Plot",
|
| 420 |
+
href="ev_angle/"
|
| 421 |
+
)
|
| 422 |
+
),
|
| 423 |
+
),
|
| 424 |
+
ui.nav_menu(
|
| 425 |
+
"Goalie Charts",
|
| 426 |
+
ui.nav_control(
|
| 427 |
+
ui.a(
|
| 428 |
+
"GSAx Timeline",
|
| 429 |
+
href="gsax-timeline/"
|
| 430 |
+
),
|
| 431 |
+
ui.a(
|
| 432 |
+
"GSAx Leaderboard",
|
| 433 |
+
href="gsax-leaderboard/"
|
| 434 |
+
),
|
| 435 |
+
ui.a(
|
| 436 |
+
"GSAx Comparison",
|
| 437 |
+
href="gsax-comparison/"
|
| 438 |
+
)
|
| 439 |
+
),
|
| 440 |
+
),ui.nav_menu(
|
| 441 |
+
"Team Charts",
|
| 442 |
+
ui.nav_control(
|
| 443 |
+
ui.a(
|
| 444 |
+
"Team xG Rates",
|
| 445 |
+
href="team-xg-rates/"
|
| 446 |
+
),
|
| 447 |
+
),
|
| 448 |
+
),ui.nav_control(
|
| 449 |
+
ui.a(
|
| 450 |
+
"Games",
|
| 451 |
+
href="games/"
|
| 452 |
+
),
|
| 453 |
+
),ui.nav_control(
|
| 454 |
+
ui.a(
|
| 455 |
+
"About",
|
| 456 |
+
href="about/"
|
| 457 |
+
),
|
| 458 |
+
),ui.nav_control(
|
| 459 |
+
ui.a(
|
| 460 |
+
"Articles",
|
| 461 |
+
href="articles/"
|
| 462 |
+
),
|
| 463 |
+
)),ui.row(
|
| 464 |
+
ui.layout_sidebar(
|
| 465 |
+
|
| 466 |
+
|
| 467 |
+
|
| 468 |
+
ui.panel_sidebar(
|
| 469 |
+
#ui.input_select("id", "Select Batter",batter_dict,selected=675911,width=1,size=1),
|
| 470 |
+
ui.row(
|
| 471 |
+
ui.column(4,ui.input_select("level_id", "Select Level",level_dict,width=1,size=1,multiple=True,selected='MLB',selectize=True),),
|
| 472 |
+
ui.column(4,ui.input_select("team_id", "Select Team",team_list,width=1,size=1,multiple=True,selected='All',selectize=True),),
|
| 473 |
+
ui.column(4,ui.input_select("position_id", "Select Position",position_list,width=1,size=1,selected='All',multiple=True,selectize=True))),
|
| 474 |
+
ui.row(
|
| 475 |
+
ui.column(6,ui.input_numeric("n", "Minimum PA", value=100)),
|
| 476 |
+
ui.column(6,ui.input_numeric("n_age", "Maximum Age", value=50))),
|
| 477 |
+
ui.row(
|
| 478 |
+
ui.column(4,ui.input_select("stat_x", "X-Axis",batter_dict_stat_small,selected='k_percent',width=1,size=1)),
|
| 479 |
+
ui.column(4,ui.input_select("stat_y", "Y-Axis",batter_dict_stat_small,selected='bb_percent',width=1,size=1)),
|
| 480 |
+
ui.column(4,ui.input_select("stat_z", "Colour-Bar Axis",batter_dict_stat_small,selected='bb_over_k_percent',width=1,size=1))),
|
| 481 |
+
|
| 482 |
+
ui.row(
|
| 483 |
+
ui.column(6,ui.input_numeric("n_percent_top_x", "Top 'n' Percentile X-Labels", value=0.01)),
|
| 484 |
+
ui.column(6,ui.input_numeric("n_percent_bot_x", "Bottom 'n' Percentile X-Labels", value=0.01))),
|
| 485 |
+
ui.row(
|
| 486 |
+
ui.column(6,ui.input_numeric("n_percent_top_y", "Top 'n' Percentile Y-Labels", value=0.01)),
|
| 487 |
+
ui.column(6,ui.input_numeric("n_percent_bot_y", "Bottom 'n' Percentile Y-Labels", value=0.01))),
|
| 488 |
+
ui.row(
|
| 489 |
+
ui.column(6,ui.input_numeric("n_percent_top_z", "Top 'n' Percentile Z-Labels", value=0.01)),
|
| 490 |
+
ui.column(6,ui.input_numeric("n_percent_bot_z", "Bottom 'n' Percentile Z-Labels", value=0.01))),
|
| 491 |
+
|
| 492 |
+
ui.input_select("player_id", "Label Player",batter_dict,width=1,size=1,multiple=True,selectize=True),
|
| 493 |
+
ui.row(
|
| 494 |
+
ui.input_switch("names", "Toggle Names"),
|
| 495 |
+
ui.input_switch("group_level", "Group Levels")),
|
| 496 |
+
),
|
| 497 |
+
|
| 498 |
+
ui.panel_main(
|
| 499 |
+
ui.output_plot("plot",height = "1000px",width="1000px")
|
| 500 |
+
,
|
| 501 |
+
),
|
| 502 |
+
)),)),server)
|
chadwick_df.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
damage.py
CHANGED
|
@@ -165,6 +165,7 @@ def server(input,output,session):
|
|
| 165 |
|
| 166 |
@output
|
| 167 |
@render.plot(alt="hex_plot")
|
|
|
|
| 168 |
def hex_plot():
|
| 169 |
|
| 170 |
if input.batter_id() is "":
|
|
@@ -391,6 +392,7 @@ def server(input,output,session):
|
|
| 391 |
|
| 392 |
@output
|
| 393 |
@render.plot(alt="roll_plot")
|
|
|
|
| 394 |
def roll_plot():
|
| 395 |
# player_select = 'Nolan Gorman'
|
| 396 |
# player_select_full =player_select
|
|
@@ -505,6 +507,7 @@ damage = App(ui.page_fluid(
|
|
| 505 |
shinyswatch.theme.simplex(),
|
| 506 |
ui.tags.h4("TJStats"),
|
| 507 |
ui.tags.i("Baseball Analytics and Visualizations"),
|
|
|
|
| 508 |
ui.navset_tab(
|
| 509 |
ui.nav_control(
|
| 510 |
ui.a(
|
|
@@ -515,7 +518,11 @@ damage = App(ui.page_fluid(
|
|
| 515 |
ui.nav_menu(
|
| 516 |
"Batter Charts",
|
| 517 |
ui.nav_control(
|
| 518 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 519 |
"Spray",
|
| 520 |
href="spray/"
|
| 521 |
),
|
|
@@ -534,48 +541,29 @@ damage = App(ui.page_fluid(
|
|
| 534 |
ui.a(
|
| 535 |
"EV vs LA Plot",
|
| 536 |
href="ev_angle/"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 537 |
)
|
| 538 |
),
|
| 539 |
),
|
| 540 |
ui.nav_menu(
|
| 541 |
-
"
|
| 542 |
ui.nav_control(
|
| 543 |
ui.a(
|
| 544 |
-
"
|
| 545 |
-
href="
|
| 546 |
),
|
| 547 |
ui.a(
|
| 548 |
-
"
|
| 549 |
-
href="
|
| 550 |
),
|
| 551 |
ui.a(
|
| 552 |
-
"
|
| 553 |
-
href="
|
| 554 |
)
|
| 555 |
),
|
| 556 |
-
),ui.nav_menu(
|
| 557 |
-
"Team Charts",
|
| 558 |
-
ui.nav_control(
|
| 559 |
-
ui.a(
|
| 560 |
-
"Team xG Rates",
|
| 561 |
-
href="team-xg-rates/"
|
| 562 |
-
),
|
| 563 |
-
),
|
| 564 |
-
),ui.nav_control(
|
| 565 |
-
ui.a(
|
| 566 |
-
"Games",
|
| 567 |
-
href="games/"
|
| 568 |
-
),
|
| 569 |
-
),ui.nav_control(
|
| 570 |
-
ui.a(
|
| 571 |
-
"About",
|
| 572 |
-
href="about/"
|
| 573 |
-
),
|
| 574 |
-
),ui.nav_control(
|
| 575 |
-
ui.a(
|
| 576 |
-
"Articles",
|
| 577 |
-
href="articles/"
|
| 578 |
-
),
|
| 579 |
)),ui.row(
|
| 580 |
ui.layout_sidebar(
|
| 581 |
|
|
@@ -593,7 +581,8 @@ damage = App(ui.page_fluid(
|
|
| 593 |
ui.input_numeric("rolling_window",
|
| 594 |
"Select Rolling Window",
|
| 595 |
value=50,
|
| 596 |
-
min=1)
|
|
|
|
| 597 |
|
| 598 |
ui.panel_main(
|
| 599 |
ui.navset_tab(
|
|
|
|
| 165 |
|
| 166 |
@output
|
| 167 |
@render.plot(alt="hex_plot")
|
| 168 |
+
@reactive.event(input.go, ignore_none=False)
|
| 169 |
def hex_plot():
|
| 170 |
|
| 171 |
if input.batter_id() is "":
|
|
|
|
| 392 |
|
| 393 |
@output
|
| 394 |
@render.plot(alt="roll_plot")
|
| 395 |
+
@reactive.event(input.go, ignore_none=False)
|
| 396 |
def roll_plot():
|
| 397 |
# player_select = 'Nolan Gorman'
|
| 398 |
# player_select_full =player_select
|
|
|
|
| 507 |
shinyswatch.theme.simplex(),
|
| 508 |
ui.tags.h4("TJStats"),
|
| 509 |
ui.tags.i("Baseball Analytics and Visualizations"),
|
| 510 |
+
ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),
|
| 511 |
ui.navset_tab(
|
| 512 |
ui.nav_control(
|
| 513 |
ui.a(
|
|
|
|
| 518 |
ui.nav_menu(
|
| 519 |
"Batter Charts",
|
| 520 |
ui.nav_control(
|
| 521 |
+
ui.a(
|
| 522 |
+
"Batting Rolling",
|
| 523 |
+
href="rolling_batter/"
|
| 524 |
+
),
|
| 525 |
+
ui.a(
|
| 526 |
"Spray",
|
| 527 |
href="spray/"
|
| 528 |
),
|
|
|
|
| 541 |
ui.a(
|
| 542 |
"EV vs LA Plot",
|
| 543 |
href="ev_angle/"
|
| 544 |
+
),
|
| 545 |
+
ui.a(
|
| 546 |
+
"Statcast Compare",
|
| 547 |
+
href="statcast_compare/"
|
| 548 |
)
|
| 549 |
),
|
| 550 |
),
|
| 551 |
ui.nav_menu(
|
| 552 |
+
"Pitcher Charts",
|
| 553 |
ui.nav_control(
|
| 554 |
ui.a(
|
| 555 |
+
"Pitcher Rolling",
|
| 556 |
+
href="rolling_pitcher/"
|
| 557 |
),
|
| 558 |
ui.a(
|
| 559 |
+
"Pitcher Summary",
|
| 560 |
+
href="pitching_summary_graphic_new/"
|
| 561 |
),
|
| 562 |
ui.a(
|
| 563 |
+
"Pitcher Scatter",
|
| 564 |
+
href="pitcher_scatter/"
|
| 565 |
)
|
| 566 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 567 |
)),ui.row(
|
| 568 |
ui.layout_sidebar(
|
| 569 |
|
|
|
|
| 581 |
ui.input_numeric("rolling_window",
|
| 582 |
"Select Rolling Window",
|
| 583 |
value=50,
|
| 584 |
+
min=1),
|
| 585 |
+
ui.input_action_button("go", "Generate",class_="btn-primary")),
|
| 586 |
|
| 587 |
ui.panel_main(
|
| 588 |
ui.navset_tab(
|
decision_value.py
CHANGED
|
@@ -146,6 +146,7 @@ def server(input,output,session):
|
|
| 146 |
|
| 147 |
@output
|
| 148 |
@render.plot(alt="hex_plot")
|
|
|
|
| 149 |
def scatter_plot():
|
| 150 |
|
| 151 |
if input.batter_id() is "":
|
|
@@ -264,6 +265,7 @@ def server(input,output,session):
|
|
| 264 |
|
| 265 |
@output
|
| 266 |
@render.plot(alt="hex_plot")
|
|
|
|
| 267 |
def dv_plot():
|
| 268 |
|
| 269 |
if input.batter_id() is "":
|
|
@@ -357,6 +359,7 @@ def server(input,output,session):
|
|
| 357 |
|
| 358 |
@output
|
| 359 |
@render.plot(alt="hex_plot")
|
|
|
|
| 360 |
def iz_plot():
|
| 361 |
|
| 362 |
if input.batter_id() is "":
|
|
@@ -450,6 +453,7 @@ def server(input,output,session):
|
|
| 450 |
|
| 451 |
@output
|
| 452 |
@render.plot(alt="hex_plot")
|
|
|
|
| 453 |
def oz_plot():
|
| 454 |
if input.batter_id() is "":
|
| 455 |
fig = plt.figure(figsize=(12, 12))
|
|
@@ -557,6 +561,7 @@ decision_value = App(ui.page_fluid(
|
|
| 557 |
shinyswatch.theme.simplex(),
|
| 558 |
ui.tags.h4("TJStats"),
|
| 559 |
ui.tags.i("Baseball Analytics and Visualizations"),
|
|
|
|
| 560 |
ui.navset_tab(
|
| 561 |
ui.nav_control(
|
| 562 |
ui.a(
|
|
@@ -567,7 +572,11 @@ decision_value = App(ui.page_fluid(
|
|
| 567 |
ui.nav_menu(
|
| 568 |
"Batter Charts",
|
| 569 |
ui.nav_control(
|
| 570 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 571 |
"Spray",
|
| 572 |
href="spray/"
|
| 573 |
),
|
|
@@ -586,48 +595,29 @@ decision_value = App(ui.page_fluid(
|
|
| 586 |
ui.a(
|
| 587 |
"EV vs LA Plot",
|
| 588 |
href="ev_angle/"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 589 |
)
|
| 590 |
),
|
| 591 |
),
|
| 592 |
ui.nav_menu(
|
| 593 |
-
"
|
| 594 |
ui.nav_control(
|
| 595 |
ui.a(
|
| 596 |
-
"
|
| 597 |
-
href="
|
| 598 |
),
|
| 599 |
ui.a(
|
| 600 |
-
"
|
| 601 |
-
href="
|
| 602 |
),
|
| 603 |
ui.a(
|
| 604 |
-
"
|
| 605 |
-
href="
|
| 606 |
)
|
| 607 |
),
|
| 608 |
-
),ui.nav_menu(
|
| 609 |
-
"Team Charts",
|
| 610 |
-
ui.nav_control(
|
| 611 |
-
ui.a(
|
| 612 |
-
"Team xG Rates",
|
| 613 |
-
href="team-xg-rates/"
|
| 614 |
-
),
|
| 615 |
-
),
|
| 616 |
-
),ui.nav_control(
|
| 617 |
-
ui.a(
|
| 618 |
-
"Games",
|
| 619 |
-
href="games/"
|
| 620 |
-
),
|
| 621 |
-
),ui.nav_control(
|
| 622 |
-
ui.a(
|
| 623 |
-
"About",
|
| 624 |
-
href="about/"
|
| 625 |
-
),
|
| 626 |
-
),ui.nav_control(
|
| 627 |
-
ui.a(
|
| 628 |
-
"Articles",
|
| 629 |
-
href="articles/"
|
| 630 |
-
),
|
| 631 |
)),ui.row(
|
| 632 |
ui.layout_sidebar(
|
| 633 |
|
|
@@ -658,7 +648,9 @@ decision_value = App(ui.page_fluid(
|
|
| 658 |
ui.input_select("level_list",
|
| 659 |
"Select Level",
|
| 660 |
['MLB','AAA'],
|
| 661 |
-
selected='MLB')
|
|
|
|
|
|
|
| 662 |
|
| 663 |
ui.panel_main(
|
| 664 |
ui.navset_tab(
|
|
|
|
| 146 |
|
| 147 |
@output
|
| 148 |
@render.plot(alt="hex_plot")
|
| 149 |
+
@reactive.event(input.go, ignore_none=False)
|
| 150 |
def scatter_plot():
|
| 151 |
|
| 152 |
if input.batter_id() is "":
|
|
|
|
| 265 |
|
| 266 |
@output
|
| 267 |
@render.plot(alt="hex_plot")
|
| 268 |
+
@reactive.event(input.go, ignore_none=False)
|
| 269 |
def dv_plot():
|
| 270 |
|
| 271 |
if input.batter_id() is "":
|
|
|
|
| 359 |
|
| 360 |
@output
|
| 361 |
@render.plot(alt="hex_plot")
|
| 362 |
+
@reactive.event(input.go, ignore_none=False)
|
| 363 |
def iz_plot():
|
| 364 |
|
| 365 |
if input.batter_id() is "":
|
|
|
|
| 453 |
|
| 454 |
@output
|
| 455 |
@render.plot(alt="hex_plot")
|
| 456 |
+
@reactive.event(input.go, ignore_none=False)
|
| 457 |
def oz_plot():
|
| 458 |
if input.batter_id() is "":
|
| 459 |
fig = plt.figure(figsize=(12, 12))
|
|
|
|
| 561 |
shinyswatch.theme.simplex(),
|
| 562 |
ui.tags.h4("TJStats"),
|
| 563 |
ui.tags.i("Baseball Analytics and Visualizations"),
|
| 564 |
+
ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),
|
| 565 |
ui.navset_tab(
|
| 566 |
ui.nav_control(
|
| 567 |
ui.a(
|
|
|
|
| 572 |
ui.nav_menu(
|
| 573 |
"Batter Charts",
|
| 574 |
ui.nav_control(
|
| 575 |
+
ui.a(
|
| 576 |
+
"Batting Rolling",
|
| 577 |
+
href="rolling_batter/"
|
| 578 |
+
),
|
| 579 |
+
ui.a(
|
| 580 |
"Spray",
|
| 581 |
href="spray/"
|
| 582 |
),
|
|
|
|
| 595 |
ui.a(
|
| 596 |
"EV vs LA Plot",
|
| 597 |
href="ev_angle/"
|
| 598 |
+
),
|
| 599 |
+
ui.a(
|
| 600 |
+
"Statcast Compare",
|
| 601 |
+
href="statcast_compare/"
|
| 602 |
)
|
| 603 |
),
|
| 604 |
),
|
| 605 |
ui.nav_menu(
|
| 606 |
+
"Pitcher Charts",
|
| 607 |
ui.nav_control(
|
| 608 |
ui.a(
|
| 609 |
+
"Pitcher Rolling",
|
| 610 |
+
href="rolling_pitcher/"
|
| 611 |
),
|
| 612 |
ui.a(
|
| 613 |
+
"Pitcher Summary",
|
| 614 |
+
href="pitching_summary_graphic_new/"
|
| 615 |
),
|
| 616 |
ui.a(
|
| 617 |
+
"Pitcher Scatter",
|
| 618 |
+
href="pitcher_scatter/"
|
| 619 |
)
|
| 620 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 621 |
)),ui.row(
|
| 622 |
ui.layout_sidebar(
|
| 623 |
|
|
|
|
| 648 |
ui.input_select("level_list",
|
| 649 |
"Select Level",
|
| 650 |
['MLB','AAA'],
|
| 651 |
+
selected='MLB'),
|
| 652 |
+
ui.input_action_button("go", "Generate",class_="btn-primary"),
|
| 653 |
+
),
|
| 654 |
|
| 655 |
ui.panel_main(
|
| 656 |
ui.navset_tab(
|
ev_angle.py
CHANGED
|
@@ -60,6 +60,7 @@ angle_ranges = list(range(8,51))
|
|
| 60 |
def server(input,output,session):
|
| 61 |
@output
|
| 62 |
@render.plot(alt="A histogram")
|
|
|
|
| 63 |
def plot():
|
| 64 |
data_df = exit_velo_df[exit_velo_df.batter_id==int(input.id())]
|
| 65 |
#pitch_list = exit_velo_df_small.pitch_type.unique()
|
|
@@ -182,6 +183,7 @@ ev_angle = App(ui.page_fluid(
|
|
| 182 |
shinyswatch.theme.simplex(),
|
| 183 |
ui.tags.h4("TJStats"),
|
| 184 |
ui.tags.i("Baseball Analytics and Visualizations"),
|
|
|
|
| 185 |
ui.navset_tab(
|
| 186 |
ui.nav_control(
|
| 187 |
ui.a(
|
|
@@ -192,7 +194,11 @@ ev_angle = App(ui.page_fluid(
|
|
| 192 |
ui.nav_menu(
|
| 193 |
"Batter Charts",
|
| 194 |
ui.nav_control(
|
| 195 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 196 |
"Spray",
|
| 197 |
href="spray/"
|
| 198 |
),
|
|
@@ -211,48 +217,29 @@ ev_angle = App(ui.page_fluid(
|
|
| 211 |
ui.a(
|
| 212 |
"EV vs LA Plot",
|
| 213 |
href="ev_angle/"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
)
|
| 215 |
),
|
| 216 |
),
|
| 217 |
ui.nav_menu(
|
| 218 |
-
"
|
| 219 |
ui.nav_control(
|
| 220 |
ui.a(
|
| 221 |
-
"
|
| 222 |
-
href="
|
| 223 |
),
|
| 224 |
ui.a(
|
| 225 |
-
"
|
| 226 |
-
href="
|
| 227 |
),
|
| 228 |
ui.a(
|
| 229 |
-
"
|
| 230 |
-
href="
|
| 231 |
)
|
| 232 |
),
|
| 233 |
-
),ui.nav_menu(
|
| 234 |
-
"Team Charts",
|
| 235 |
-
ui.nav_control(
|
| 236 |
-
ui.a(
|
| 237 |
-
"Team xG Rates",
|
| 238 |
-
href="team-xg-rates/"
|
| 239 |
-
),
|
| 240 |
-
),
|
| 241 |
-
),ui.nav_control(
|
| 242 |
-
ui.a(
|
| 243 |
-
"Games",
|
| 244 |
-
href="games/"
|
| 245 |
-
),
|
| 246 |
-
),ui.nav_control(
|
| 247 |
-
ui.a(
|
| 248 |
-
"About",
|
| 249 |
-
href="about/"
|
| 250 |
-
),
|
| 251 |
-
),ui.nav_control(
|
| 252 |
-
ui.a(
|
| 253 |
-
"Articles",
|
| 254 |
-
href="articles/"
|
| 255 |
-
),
|
| 256 |
)),ui.row(
|
| 257 |
ui.layout_sidebar(
|
| 258 |
|
|
@@ -261,8 +248,11 @@ ev_angle = App(ui.page_fluid(
|
|
| 261 |
|
| 262 |
ui.panel_sidebar(
|
| 263 |
ui.input_select("id", "Select Batter",batter_dict,width=1),
|
| 264 |
-
ui.input_select("plot_id", "Select Plot",{'scatter':'Scatter Plot','dist':'Distribution Plot'},width=1)
|
| 265 |
-
|
|
|
|
|
|
|
|
|
|
| 266 |
|
| 267 |
ui.panel_main(
|
| 268 |
ui.output_plot("plot",height = "1000px",width="1000px")
|
|
|
|
| 60 |
def server(input,output,session):
|
| 61 |
@output
|
| 62 |
@render.plot(alt="A histogram")
|
| 63 |
+
@reactive.event(input.go, ignore_none=False)
|
| 64 |
def plot():
|
| 65 |
data_df = exit_velo_df[exit_velo_df.batter_id==int(input.id())]
|
| 66 |
#pitch_list = exit_velo_df_small.pitch_type.unique()
|
|
|
|
| 183 |
shinyswatch.theme.simplex(),
|
| 184 |
ui.tags.h4("TJStats"),
|
| 185 |
ui.tags.i("Baseball Analytics and Visualizations"),
|
| 186 |
+
ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),
|
| 187 |
ui.navset_tab(
|
| 188 |
ui.nav_control(
|
| 189 |
ui.a(
|
|
|
|
| 194 |
ui.nav_menu(
|
| 195 |
"Batter Charts",
|
| 196 |
ui.nav_control(
|
| 197 |
+
ui.a(
|
| 198 |
+
"Batting Rolling",
|
| 199 |
+
href="rolling_batter/"
|
| 200 |
+
),
|
| 201 |
+
ui.a(
|
| 202 |
"Spray",
|
| 203 |
href="spray/"
|
| 204 |
),
|
|
|
|
| 217 |
ui.a(
|
| 218 |
"EV vs LA Plot",
|
| 219 |
href="ev_angle/"
|
| 220 |
+
),
|
| 221 |
+
ui.a(
|
| 222 |
+
"Statcast Compare",
|
| 223 |
+
href="statcast_compare/"
|
| 224 |
)
|
| 225 |
),
|
| 226 |
),
|
| 227 |
ui.nav_menu(
|
| 228 |
+
"Pitcher Charts",
|
| 229 |
ui.nav_control(
|
| 230 |
ui.a(
|
| 231 |
+
"Pitcher Rolling",
|
| 232 |
+
href="rolling_pitcher/"
|
| 233 |
),
|
| 234 |
ui.a(
|
| 235 |
+
"Pitcher Summary",
|
| 236 |
+
href="pitching_summary_graphic_new/"
|
| 237 |
),
|
| 238 |
ui.a(
|
| 239 |
+
"Pitcher Scatter",
|
| 240 |
+
href="pitcher_scatter/"
|
| 241 |
)
|
| 242 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 243 |
)),ui.row(
|
| 244 |
ui.layout_sidebar(
|
| 245 |
|
|
|
|
| 248 |
|
| 249 |
ui.panel_sidebar(
|
| 250 |
ui.input_select("id", "Select Batter",batter_dict,width=1),
|
| 251 |
+
ui.input_select("plot_id", "Select Plot",{'scatter':'Scatter Plot','dist':'Distribution Plot'},width=1),
|
| 252 |
+
|
| 253 |
+
ui.input_action_button("go", "Generate",class_="btn-primary",
|
| 254 |
+
)),
|
| 255 |
+
|
| 256 |
|
| 257 |
ui.panel_main(
|
| 258 |
ui.output_plot("plot",height = "1000px",width="1000px")
|
home.py
CHANGED
|
@@ -28,6 +28,7 @@ home = App(ui.page_fluid(
|
|
| 28 |
shinyswatch.theme.simplex(),
|
| 29 |
ui.tags.h4("TJStats"),
|
| 30 |
ui.tags.i("Baseball Analytics and Visualizations"),
|
|
|
|
| 31 |
ui.navset_tab(
|
| 32 |
ui.nav_control(
|
| 33 |
ui.a(
|
|
@@ -38,7 +39,11 @@ home = App(ui.page_fluid(
|
|
| 38 |
ui.nav_menu(
|
| 39 |
"Batter Charts",
|
| 40 |
ui.nav_control(
|
| 41 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
"Spray",
|
| 43 |
href="spray/"
|
| 44 |
),
|
|
@@ -57,46 +62,77 @@ home = App(ui.page_fluid(
|
|
| 57 |
ui.a(
|
| 58 |
"EV vs LA Plot",
|
| 59 |
href="ev_angle/"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
)
|
| 61 |
),
|
| 62 |
),
|
| 63 |
ui.nav_menu(
|
| 64 |
-
"
|
| 65 |
ui.nav_control(
|
| 66 |
ui.a(
|
| 67 |
-
"
|
| 68 |
-
href="
|
| 69 |
),
|
| 70 |
ui.a(
|
| 71 |
-
"
|
| 72 |
-
href="
|
| 73 |
),
|
| 74 |
ui.a(
|
| 75 |
-
"
|
| 76 |
-
href="
|
| 77 |
)
|
| 78 |
),
|
| 79 |
-
),ui.
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
shinyswatch.theme.simplex(),
|
| 29 |
ui.tags.h4("TJStats"),
|
| 30 |
ui.tags.i("Baseball Analytics and Visualizations"),
|
| 31 |
+
ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),
|
| 32 |
ui.navset_tab(
|
| 33 |
ui.nav_control(
|
| 34 |
ui.a(
|
|
|
|
| 39 |
ui.nav_menu(
|
| 40 |
"Batter Charts",
|
| 41 |
ui.nav_control(
|
| 42 |
+
ui.a(
|
| 43 |
+
"Batting Rolling",
|
| 44 |
+
href="rolling_batter/"
|
| 45 |
+
),
|
| 46 |
+
ui.a(
|
| 47 |
"Spray",
|
| 48 |
href="spray/"
|
| 49 |
),
|
|
|
|
| 62 |
ui.a(
|
| 63 |
"EV vs LA Plot",
|
| 64 |
href="ev_angle/"
|
| 65 |
+
),
|
| 66 |
+
ui.a(
|
| 67 |
+
"Statcast Compare",
|
| 68 |
+
href="statcast_compare/"
|
| 69 |
)
|
| 70 |
),
|
| 71 |
),
|
| 72 |
ui.nav_menu(
|
| 73 |
+
"Pitcher Charts",
|
| 74 |
ui.nav_control(
|
| 75 |
ui.a(
|
| 76 |
+
"Pitcher Rolling",
|
| 77 |
+
href="rolling_pitcher/"
|
| 78 |
),
|
| 79 |
ui.a(
|
| 80 |
+
"Pitcher Summary",
|
| 81 |
+
href="pitching_summary_graphic_new/"
|
| 82 |
),
|
| 83 |
ui.a(
|
| 84 |
+
"Pitcher Scatter",
|
| 85 |
+
href="pitcher_scatter/"
|
| 86 |
)
|
| 87 |
),
|
| 88 |
+
)),ui.tags.h6(""),
|
| 89 |
+
ui.markdown(
|
| 90 |
+
"""
|
| 91 |
+
# Welcome to TJStats!
|
| 92 |
+
---
|
| 93 |
+
## Intro
|
| 94 |
+
Hello, my name is Thomas Nestico, and welcome to my Baseball Analytics and Visualizations Website, TJStats!
|
| 95 |
+
I am a Civil Engineering by trade, but I have a passion for data analytics and machine learning, specifically
|
| 96 |
+
with respect to sports. This site and all of my visualizations were created using Python and its vast array of packages.
|
| 97 |
+
The framework for this site and all of the apps is <a href='https://shiny.posit.co/py/'>Shiny Python</a><sup>1</sup>.
|
| 98 |
+
All sources of data are referenced on each visualization.
|
| 99 |
+
|
| 100 |
+
## Apps
|
| 101 |
+
This site hosts my Baseball Data Viz Apps from the 2023 season. Here is a summary of what is accessible:
|
| 102 |
+
|
| 103 |
+
* Batter Charts
|
| 104 |
+
- **Batter Rolling**: Rolling Stats Charts for MLB and MiLB batters
|
| 105 |
+
- **Spray**: Batted Ball Trajectory Distributions of MLB batters vs rest of league
|
| 106 |
+
- **Decision Value**: Scatter plot and Rolling Stats Chart for my <a href='https://medium.com/@thomasjamesnestico/modelling-batter-decision-value-dac74c55e20a'>Decision Value Model</a><sup>1</sup>
|
| 107 |
+
- **Damage Model**: Hexbin and Rolling Stat Chart for my Damage Model (Article TBD)
|
| 108 |
+
- **Batter Scatter**: Scatter Plots for MLB and MiLB batters
|
| 109 |
+
- **EV vs LA Plot**: Scatter plot visualizing a MLB Batter's Exit Velocities and Launch Angles
|
| 110 |
+
- **Statcast Compare**: Table which compares MLB Batter's Statcast metrics
|
| 111 |
+
<br>
|
| 112 |
+
* Pitcher Charts
|
| 113 |
+
- **Pitcher Rolling**: Rolling Stats Charts for MLB and MiLB pitchers
|
| 114 |
+
- **Pitcher Summary**: Pitching Summary for MLB pitchers for any date range during 2023
|
| 115 |
+
- **Pitcher Scatter**: Scatter Plots for MLB and MiLB pitchers
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
## Articles
|
| 119 |
+
I have published several articles on Medium covering different Baseball Analytics topics. In these articles I go through my methodology
|
| 120 |
+
when applying machine learning algorithms to create machine learning models to help better understand baseball concepts. Here is a list of
|
| 121 |
+
articles which I have published:
|
| 122 |
+
* <a href='https://medium.com/@thomasjamesnestico/modelling-tjstuff-d9a451765484'>Modelling tjStuff+</a><sup>1</sup>
|
| 123 |
+
* <a href='https://medium.com/@thomasjamesnestico/modelling-batter-decision-value-dac74c55e20a'>Modelling Batter Decision Value</a><sup>1</sup>
|
| 124 |
+
* <a href='https://medium.com/@thomasjamesnestico/classifying-mlb-pitch-zones-and-predicting-milb-zones-7e95cf308254'>Classifying MLB Pitch Zones and Predicting MiLB Zones</a><sup>1</sup>
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
## 2024 Apps
|
| 128 |
+
Thank you for checking out the site. If you would like to further support my endeavours in the Baseball Analytics sphere,
|
| 129 |
+
here are few places where you can support me and get up to date baseball coverage and Apps for the 2024 season.
|
| 130 |
+
|
| 131 |
+
* Patreon: https://www.patreon.com/tj_stats
|
| 132 |
+
* Twitter: https://twitter.com/TJStats
|
| 133 |
+
|
| 134 |
+
## About Me
|
| 135 |
+
Here are a few other sites to learn more about me:
|
| 136 |
+
* LinkedIn: https://www.linkedin.com/in/thomas-nestico-b66013173/
|
| 137 |
+
* GitHub: https://github.com/tnestico
|
| 138 |
+
"""))), None)
|
pitcher_scatter.py
ADDED
|
@@ -0,0 +1,508 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui
|
| 2 |
+
import datasets
|
| 3 |
+
from datasets import load_dataset
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import numpy as np
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
import seaborn as sns
|
| 8 |
+
import numpy as np
|
| 9 |
+
from scipy.stats import gaussian_kde
|
| 10 |
+
import matplotlib
|
| 11 |
+
from matplotlib.ticker import MaxNLocator
|
| 12 |
+
from matplotlib.gridspec import GridSpec
|
| 13 |
+
from scipy.stats import zscore
|
| 14 |
+
import math
|
| 15 |
+
import matplotlib
|
| 16 |
+
from adjustText import adjust_text
|
| 17 |
+
import matplotlib.ticker as mtick
|
| 18 |
+
from shinywidgets import output_widget, render_widget
|
| 19 |
+
import pandas as pd
|
| 20 |
+
from configure import base_url
|
| 21 |
+
import shinyswatch
|
| 22 |
+
import inflect
|
| 23 |
+
from matplotlib.pyplot import text
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
exit_velo_df_codes_summ = pd.read_csv('summary_pitcher.csv',index_col=[0]).reset_index(drop=True)
|
| 27 |
+
exit_velo_df_codes_summ_non_level = pd.read_csv('summary_pitcher_level.csv',index_col=[0]).reset_index(drop=True)
|
| 28 |
+
|
| 29 |
+
exit_velo_df_codes_summ_non_level['levels'] = exit_velo_df_codes_summ_non_level.levels.str.split(', ')
|
| 30 |
+
|
| 31 |
+
exit_velo_df_codes_summ_non_level = exit_velo_df_codes_summ_non_level.rename(columns={'levels':'level'})
|
| 32 |
+
|
| 33 |
+
print(exit_velo_df_codes_summ.bb_minus_k_percent)
|
| 34 |
+
exit_velo_df_codes_summ.bb_minus_k_percent = -1*exit_velo_df_codes_summ.bb_minus_k_percent
|
| 35 |
+
exit_velo_df_codes_summ.bb_over_k_percent = exit_velo_df_codes_summ.k_percent/exit_velo_df_codes_summ.bb_percent
|
| 36 |
+
|
| 37 |
+
exit_velo_df_codes_summ_non_level.bb_minus_k_percent = -1*exit_velo_df_codes_summ_non_level.bb_minus_k_percent
|
| 38 |
+
exit_velo_df_codes_summ_non_level.bb_over_k_percent = exit_velo_df_codes_summ_non_level.k_percent/exit_velo_df_codes_summ_non_level.bb_percent
|
| 39 |
+
|
| 40 |
+
pitcher_dict_stat = { 'sweet_spot_percent':{'x_axis':'SweetSpot%','title':'SweetSpot%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 41 |
+
'max_launch_speed':{'x_axis':'Max Exit Velocity','title':'Max Exit Velocity','flip_p':False,'decimal_format':'string_0','percent_adjust':1},
|
| 42 |
+
'launch_speed_90':{'x_axis':'90th Percentile EV','title':'90th Percentile EV','flip_p':False,'decimal_format':'string_0','percent_adjust':1},
|
| 43 |
+
'launch_speed':{'x_axis':'Exit Velocity','title':'Exit Velocity','flip_p':False,'decimal_format':'string_0','percent_adjust':1},
|
| 44 |
+
'launch_angle':{'x_axis':'Launch Angle','title':'Launch Angle','flip_p':False,'decimal_format':'string_0','percent_adjust':100},
|
| 45 |
+
'avg':{'x_axis':'AVG','title':'AVG','flip_p':False,'decimal_format':'string_3','percent_adjust':100},
|
| 46 |
+
'obp':{'x_axis':'OBP','title':'OBP','flip_p':False,'decimal_format':'string_3','percent_adjust':100},
|
| 47 |
+
'slg':{'x_axis':'SLG','title':'SLG','flip_p':False,'decimal_format':'string_3','percent_adjust':100},
|
| 48 |
+
'ops':{'x_axis':'OPS','title':'OPS','flip_p':False,'decimal_format':'string_3','percent_adjust':100},
|
| 49 |
+
'k_percent':{'x_axis':'K%','title':'K%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 50 |
+
'bb_percent':{'x_axis':'BB%','title':'BB%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 51 |
+
'bb_over_k_percent':{'x_axis':'K/BB','title':'K/BB','flip_p':True,'decimal_format':'string_1','percent_adjust':100},
|
| 52 |
+
'bb_minus_k_percent':{'x_axis':'K%-BB%','title':'K%-BB%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 53 |
+
'csw_percent':{'x_axis':'CSW%','title':'CSW%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 54 |
+
'woba_percent':{'x_axis':'wOBA','title':'wOBA','flip_p':False,'decimal_format':'string_3','percent_adjust':100},
|
| 55 |
+
'hard_hit_percent':{'x_axis':'HardHit%','title':'HardHit%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 56 |
+
'barrel_percent':{'x_axis':'Barrel%','title':'Barrel%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 57 |
+
'zone_contact_percent':{'x_axis':'Z-Contact%','title':'Z-Contact%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 58 |
+
'zone_swing_percent':{'x_axis':'Z-Swing%','title':'Z-Swing%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 59 |
+
'zone_percent':{'x_axis':'Zone%','title':'Zone%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 60 |
+
'chase_percent':{'x_axis':'O-Swing%','title':'O-Swing%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 61 |
+
'chase_contact':{'x_axis':'O-Contact%','title':'O-Contact%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 62 |
+
'swing_percent':{'x_axis':'Swing%','title':'Swing%','flip_p':False,'decimal_format':'percent_1','percent_adjust':100},
|
| 63 |
+
'whiff_rate':{'x_axis':'Whiff%','title':'Whiff%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 64 |
+
'swstr_rate':{'x_axis':'SwStr%','title':'SwStr%','flip_p':True,'decimal_format':'percent_1','percent_adjust':100},
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
pitcher_dict_stat_small = { 'sweet_spot_percent':'SweetSpot%',
|
| 68 |
+
'max_launch_speed':'Max Exit Velocity',
|
| 69 |
+
'launch_speed_90':'90th Percentile EV',
|
| 70 |
+
'launch_speed':'Exit Velocity',
|
| 71 |
+
'launch_angle':'Launch Angle',
|
| 72 |
+
'avg':'AVG',
|
| 73 |
+
'obp':'OBP',
|
| 74 |
+
'slg':'SLG',
|
| 75 |
+
'ops':'OPS',
|
| 76 |
+
'k_percent':'K%',
|
| 77 |
+
'bb_percent':'BB%',
|
| 78 |
+
'bb_over_k_percent':'K/BB',
|
| 79 |
+
'bb_minus_k_percent':'K%-BB%',
|
| 80 |
+
'csw_percent':'CSW%',
|
| 81 |
+
'woba_percent':'wOBA',
|
| 82 |
+
'hard_hit_percent':'HardHit%',
|
| 83 |
+
'barrel_percent':'Barrel%',
|
| 84 |
+
'zone_contact_percent':'Z-Contact%',
|
| 85 |
+
'zone_swing_percent':'Z-Swing%',
|
| 86 |
+
'zone_percent':'Zone%',
|
| 87 |
+
'chase_percent':'O-Swing%',
|
| 88 |
+
'chase_contact':'O-Contact%',
|
| 89 |
+
'swing_percent':'Swing%',
|
| 90 |
+
'whiff_rate':'Whiff%',
|
| 91 |
+
'swstr_rate':'SwStr%',
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
colour_palette = ['#FFB000','#648FFF','#785EF0',
|
| 96 |
+
'#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']
|
| 97 |
+
|
| 98 |
+
level_dict = {'MLB':'MLB','AAA':'AAA','AA':'AA','A+':'A+','A':'A','ROK':'ROK'}
|
| 99 |
+
|
| 100 |
+
batter_test_df = exit_velo_df_codes_summ.sort_values(by='pitcher').drop_duplicates(subset='pitcher_id').reset_index(drop=True)[['pitcher_id','pitcher']]#['pitcher'].to_dict()
|
| 101 |
+
batter_test_df = batter_test_df.set_index('pitcher_id')
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def decimal_format_assign(x):
|
| 105 |
+
if x['decimal_format'] == 'percent_1':
|
| 106 |
+
return mtick.PercentFormatter(1,decimals=1)
|
| 107 |
+
if x['decimal_format'] == 'string_3':
|
| 108 |
+
return mtick.FormatStrFormatter('%.3f')
|
| 109 |
+
if x['decimal_format'] == 'string_0':
|
| 110 |
+
return mtick.FormatStrFormatter('%.0f')
|
| 111 |
+
if x['decimal_format'] == 'string_1':
|
| 112 |
+
return mtick.FormatStrFormatter('%.1f')
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
#test_df = test_df[test_df.pitcher == 'Chris Bassitt'].append(test_df[test_df.pitcher != 'Chris Bassitt'])
|
| 116 |
+
|
| 117 |
+
pitcher_dict = batter_test_df['pitcher'].to_dict()
|
| 118 |
+
|
| 119 |
+
exit_velo_df_codes_summ.position = exit_velo_df_codes_summ.position.replace(['TWP'],['P'])
|
| 120 |
+
|
| 121 |
+
exit_velo_df_codes_summ_non_level.position = exit_velo_df_codes_summ_non_level.position.replace(['TWP'],['P'])
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
position_list = ['All'] + list(exit_velo_df_codes_summ.position.unique())
|
| 125 |
+
team_list = ['All'] + sorted(list(exit_velo_df_codes_summ.parent_org_abb.unique()))
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def server(input,output,session):
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
@output
|
| 133 |
+
@render.plot(alt="A histogram")
|
| 134 |
+
@reactive.event(input.go, ignore_none=False)
|
| 135 |
+
def plot():
|
| 136 |
+
sns.set_theme(style="whitegrid", palette="pastel")
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
print(input.level_id())
|
| 140 |
+
print(input.n())
|
| 141 |
+
print('we made it here')
|
| 142 |
+
|
| 143 |
+
#data_df = exit_velo_df_codes_summ.copy()
|
| 144 |
+
|
| 145 |
+
|
| 146 |
+
if input.group_level():
|
| 147 |
+
data_df = exit_velo_df_codes_summ_non_level.copy()
|
| 148 |
+
|
| 149 |
+
turth_list = []
|
| 150 |
+
#turth_list_2 = []
|
| 151 |
+
for x in range(0,len(data_df.level)):
|
| 152 |
+
turth_list_2 = []
|
| 153 |
+
for y in range(0,len(data_df.level[x])):
|
| 154 |
+
#print(level_list[x][y])
|
| 155 |
+
turth_list_2.append(data_df.level[x][y] in input.level_id())
|
| 156 |
+
turth_list.append(turth_list_2)
|
| 157 |
+
|
| 158 |
+
final_check_list = [True if True in x else False for x in turth_list]
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
data_df = data_df[(data_df.pa >= input.n())&(data_df.age <= input.n_age())&(final_check_list)]
|
| 162 |
+
|
| 163 |
+
else:
|
| 164 |
+
data_df = exit_velo_df_codes_summ.copy()
|
| 165 |
+
|
| 166 |
+
data_df = data_df[(data_df.pa >= input.n())&(data_df.age <= input.n_age())&(data_df.level.isin(input.level_id()))]
|
| 167 |
+
print(data_df)
|
| 168 |
+
if 'All' in input.team_id():
|
| 169 |
+
print('nice')#data_df = data_df[(data_df.pa >= input.n())&(data_df.age <= input.n_age())].reset_index(drop=True)
|
| 170 |
+
|
| 171 |
+
else:
|
| 172 |
+
data_df = data_df[(data_df.parent_org_abb.isin(input.team_id()))].reset_index(drop=True)
|
| 173 |
+
|
| 174 |
+
if 'All' in input.position_id():
|
| 175 |
+
print('nice')#data_df = data_df[(data_df.level.isin(input.level_id()))&(data_df.pa >= input.n())&(data_df.age <= input.n_age())].reset_index(drop=True)
|
| 176 |
+
|
| 177 |
+
else:
|
| 178 |
+
data_df = data_df[(data_df.position.isin(input.position_id()))].reset_index(drop=True)
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
data_df = data_df.sort_values(by='level').reset_index(drop=True)
|
| 186 |
+
print(pitcher_dict_stat[input.stat_x()]['flip_p'])
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
x_flip = pitcher_dict_stat[input.stat_x()]['flip_p']
|
| 191 |
+
y_flip = pitcher_dict_stat[input.stat_y()]['flip_p']
|
| 192 |
+
cbr_flip = pitcher_dict_stat[input.stat_z()]['flip_p']
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
data_df[input.stat_x()+'_percent'] = data_df[input.stat_x()].rank(pct=True,ascending=abs(x_flip-1))
|
| 197 |
+
|
| 198 |
+
data_df[input.stat_y()+'_percent'] = data_df[input.stat_y()].rank(pct=True,ascending=abs(y_flip-1))
|
| 199 |
+
|
| 200 |
+
data_df[input.stat_z()+'_percent'] = data_df[input.stat_z()].rank(pct=True,ascending=abs(cbr_flip-1))
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
fig, ax = plt.subplots(1, 1, figsize=(9, 9))
|
| 205 |
+
|
| 206 |
+
#data_df['bb_over_obp'] = data_df['bb']/data_df['k']
|
| 207 |
+
|
| 208 |
+
#data_df[input.stat_z()]= data_df[input.stat_z()].fillna(-100000)
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
if not cbr_flip:
|
| 212 |
+
cmap_hue = matplotlib.colors.LinearSegmentedColormap.from_list("", [colour_palette[0],colour_palette[3],colour_palette[1]])
|
| 213 |
+
norm = plt.Normalize(data_df[input.stat_z()].min(), data_df[input.stat_z()].max())
|
| 214 |
+
|
| 215 |
+
else:
|
| 216 |
+
cmap_hue = matplotlib.colors.LinearSegmentedColormap.from_list("", [colour_palette[1],colour_palette[3],colour_palette[0]])
|
| 217 |
+
norm = plt.Normalize(data_df[input.stat_z()].min(), data_df[input.stat_z()].max())
|
| 218 |
+
|
| 219 |
+
sm = plt.cm.ScalarMappable(cmap=cmap_hue, norm=norm)
|
| 220 |
+
print('we made it here')
|
| 221 |
+
|
| 222 |
+
# sns.regplot(x = stat_x, y = stat_y, data=data_df, color = colour_palette[6],ax=ax,scatter=False,
|
| 223 |
+
# line_kws=dict(alpha=0.3,linewidth=2,zorder=1))
|
| 224 |
+
# scatter_plot = sns.scatterplot(x = stat_x, y = stat_y, data=data_df, color = colour_palette[0],ax=ax,hue=stat_z,palette=cmap_hue)
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
# r, p = sp.stats.pearsonr(data_df[input.stat_x()], data_df[input.stat_y()])
|
| 229 |
+
# ax = plt.gca()
|
| 230 |
+
# # ax.text(.25, 0.3, 'r={:.2f}, p={:.2g}'.format(r, p),
|
| 231 |
+
# # transform=ax.transAxes, fontsize=12)
|
| 232 |
+
|
| 233 |
+
# ax.annotate('RΒ²={:.2f}'.format(r, p), ( math.ceil(data_df[input.stat_x()].max()*pitcher_dict_stat[input.stat_x()]['percent_adjust']/5)*5/pitcher_dict_stat[input.stat_x()]['percent_adjust']*(1-pitcher_dict_stat[input.stat_x()]['flip_p']),
|
| 234 |
+
# math.floor(data_df[input.stat_y()].min()*pitcher_dict_stat[input.stat_y()]['percent_adjust']/5)*5/pitcher_dict_stat[input.stat_y()]['percent_adjust']*(1-pitcher_dict_stat[input.stat_y()]['flip_p'])),
|
| 235 |
+
# fontsize=18,fontname='Century Gothic',ha='right')
|
| 236 |
+
|
| 237 |
+
if input.group_level():
|
| 238 |
+
scatter = sns.scatterplot(x = input.stat_x(), y = input.stat_y(), data=data_df, color = '#b3b3b3')
|
| 239 |
+
#ax.get_legend().remove()
|
| 240 |
+
scatter = sns.scatterplot(x = input.stat_x(), y = input.stat_y(), data=data_df, color = colour_palette[0],ax=ax,hue=input.stat_z(),palette=cmap_hue)
|
| 241 |
+
else:
|
| 242 |
+
scatter = sns.scatterplot(x = input.stat_x(), y = input.stat_y(), data=data_df, color = '#b3b3b3',style='level')
|
| 243 |
+
#ax.get_legend().remove()
|
| 244 |
+
scatter = sns.scatterplot(x = input.stat_x(), y = input.stat_y(), data=data_df, color = colour_palette[0],ax=ax,hue=input.stat_z(),palette=cmap_hue,style='level')
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
sns.set_theme(style="whitegrid", palette="pastel")
|
| 248 |
+
|
| 249 |
+
fig.set_facecolor('#F0F0F0')
|
| 250 |
+
ax.set_facecolor('white')
|
| 251 |
+
|
| 252 |
+
print('we made it here!')
|
| 253 |
+
# for i in range(0,len(pitch_group_unique)):
|
| 254 |
+
# data_df = elly_zone_df[elly_zone_df.pitch_group==pitch_group_unique[i]]
|
| 255 |
+
# len_df.append(len(data_df))
|
| 256 |
+
# sns.lineplot(x=range(1,len(data_df)+1),y=data_df.swings.rolling(window=rolling_window_input).sum()/data_df.pitches.rolling(window=rolling_window_input).sum(),color=colour_palette[i],linewidth=3,ax=ax,
|
| 257 |
+
# label=f'{pitch_group_unique[i]} (Season Average {float(data_df.swings.sum()/data_df.pitches.sum()):.1%})',zorder=i+10)
|
| 258 |
+
# ax.hlines(xmin=0,xmax=len(elly_zone_df),y=data_df.swings.sum()/data_df.pitches.sum(),color=colour_palette[i],linewidth=3,linestyle='-.',alpha=0.4,zorder=i)
|
| 259 |
+
|
| 260 |
+
ts=[]
|
| 261 |
+
xs=[]
|
| 262 |
+
ys=[]
|
| 263 |
+
labels=[]
|
| 264 |
+
|
| 265 |
+
print(input.player_id())
|
| 266 |
+
if input.names():
|
| 267 |
+
for i in range(len(data_df)):
|
| 268 |
+
if (data_df[input.stat_x()+'_percent'][i] < input.n_percent_bot_x() or data_df[input.stat_x()+'_percent'][i] > 1 - input.n_percent_top_x() ) \
|
| 269 |
+
or (data_df[input.stat_y()+'_percent'][i] < input.n_percent_bot_y() or data_df[input.stat_y()+'_percent'][i] > 1 -input.n_percent_top_y()) \
|
| 270 |
+
or (data_df[input.stat_z()+'_percent'][i] < input.n_percent_bot_z() or data_df[input.stat_z()+'_percent'][i] > 1 -input.n_percent_top_z() )\
|
| 271 |
+
or (str(data_df.pitcher_id[i]) in (input.player_id())):
|
| 272 |
+
ts.append(ax.text(data_df[input.stat_x()][i], data_df[input.stat_y()][i], data_df.pitcher[i],fontsize=8))
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
# ax.annotate(data_df.pitcher[i], xy=((data_df[input.stat_x()][i])+0.025/pitcher_dict_stat[input.stat_x()]['percent_adjust'], data_df[input.stat_y()][i]+0.01/pitcher_dict_stat[input.stat_x()]['percent_adjust']), xytext=(-20,20),
|
| 276 |
+
# textcoords='offset points', ha='center', va='bottom',fontsize=7,
|
| 277 |
+
# bbox=dict(boxstyle='round,pad=0', fc=colour_palette[6], alpha=0.0),
|
| 278 |
+
# arrowprops=dict(arrowstyle='->', connectionstyle="angle,angleA=-90,angleB=-10,rad=5",
|
| 279 |
+
# color=colour_palette[4]))
|
| 280 |
+
|
| 281 |
+
# print('CONDITION CHEK')
|
| 282 |
+
# print(data_df[input.stat_x()][i])
|
| 283 |
+
# #if data_df['pitcher'][i] != 'Jo Adell':
|
| 284 |
+
# #ax.annotate(data_df.pitcher[i], ((data_df[input.stat_x()][i])+0.025/pitcher_dict_stat[input.stat_x()]['percent_adjust'], data_df[input.stat_y()][i]+0.01/pitcher_dict_stat[input.stat_x()]['percent_adjust']),fontsize=8)
|
| 285 |
+
# #ts.append(data_df[input.stat_x()][i], data_df[input.stat_y()][i], data_df.pitcher[i],fontsize=8))
|
| 286 |
+
# xs.append(data_df[input.stat_x()][i])
|
| 287 |
+
# ys.append(data_df[input.stat_y()][i])
|
| 288 |
+
# labels.append(data_df.pitcher[i])
|
| 289 |
+
|
| 290 |
+
# # adjust_text(ts, force_points=0.2, force_text=0.2,
|
| 291 |
+
# # expand_points=(1, 1), expand_text=(1, 1),
|
| 292 |
+
# # arrowprops=dict(arrowstyle="-", color='black', lw=0))
|
| 293 |
+
# #print(xs)
|
| 294 |
+
# if len(xs)>0:
|
| 295 |
+
# repel_labels(ax=ax, x = xs,y = ys, labels = labels, k=0.0025)
|
| 296 |
+
|
| 297 |
+
ax.hlines(xmin=math.floor((data_df[input.stat_x()].min()*pitcher_dict_stat[input.stat_x()]['percent_adjust']-0.01)/5)*5/pitcher_dict_stat[input.stat_x()]['percent_adjust'],
|
| 298 |
+
xmax= math.ceil((data_df[input.stat_x()].max()*pitcher_dict_stat[input.stat_x()]['percent_adjust']+0.01)/5)*5/pitcher_dict_stat[input.stat_x()]['percent_adjust'],
|
| 299 |
+
y=data_df[input.stat_y()].mean(),color='gray',linewidth=3,linestyle='dotted',alpha=0.4)
|
| 300 |
+
|
| 301 |
+
print('we made it here')
|
| 302 |
+
ax.vlines(ymin=(math.floor(data_df[input.stat_y()].min()*pitcher_dict_stat[input.stat_y()]['percent_adjust']-0.01)/5)*5/pitcher_dict_stat[input.stat_y()]['percent_adjust'],
|
| 303 |
+
ymax= (math.ceil(data_df[input.stat_y()].max()*pitcher_dict_stat[input.stat_y()]['percent_adjust']+0.01)/5)*5/pitcher_dict_stat[input.stat_y()]['percent_adjust'],
|
| 304 |
+
x=data_df[input.stat_x()].mean(),color='gray',linewidth=3,linestyle='dotted',alpha=0.4)
|
| 305 |
+
|
| 306 |
+
ax.set_xlim((math.floor(data_df[input.stat_x()].min()*pitcher_dict_stat[input.stat_x()]['percent_adjust']-0.01)/5)*5/pitcher_dict_stat[input.stat_x()]['percent_adjust'],
|
| 307 |
+
(math.ceil(data_df[input.stat_x()].max()*pitcher_dict_stat[input.stat_x()]['percent_adjust']+0.01)/5)*5/pitcher_dict_stat[input.stat_x()]['percent_adjust'])
|
| 308 |
+
|
| 309 |
+
ax.set_ylim((math.floor(data_df[input.stat_y()].min()*pitcher_dict_stat[input.stat_y()]['percent_adjust']-0.01)/5)*5/pitcher_dict_stat[input.stat_y()]['percent_adjust'],
|
| 310 |
+
(math.ceil(data_df[input.stat_y()].max()*pitcher_dict_stat[input.stat_y()]['percent_adjust']+0.01)/5)*5/pitcher_dict_stat[input.stat_y()]['percent_adjust'])
|
| 311 |
+
|
| 312 |
+
title_level = str([x .strip("\'")for x in input.level_id()]).strip('[').strip(']').replace("'",'')
|
| 313 |
+
|
| 314 |
+
if title_level == 'AAA, AA, A+, A':
|
| 315 |
+
title_level='MiLB'
|
| 316 |
+
|
| 317 |
+
|
| 318 |
+
if input.n_age() >= 50:
|
| 319 |
+
title_spot = f'{title_level} Pitcher {pitcher_dict_stat[input.stat_y()]["title"]} vs {pitcher_dict_stat[input.stat_x()]["title"]} (min. {input.n()} PA)'
|
| 320 |
+
|
| 321 |
+
else:
|
| 322 |
+
title_spot = f'{title_level} Pitcher {pitcher_dict_stat[input.stat_y()]["title"]} vs {pitcher_dict_stat[input.stat_x()]["title"]} (min. {input.n()} PA, Max Age {input.n_age()})'
|
| 323 |
+
|
| 324 |
+
#title_level = input.level_id()[0]
|
| 325 |
+
#title_spot = f'{title_level} pitcher {pitcher_dict_stat[input.stat_y()]["title"]} vs {pitcher_dict_stat[input.stat_x()]["title"]} (min. {input.n()} PA)'
|
| 326 |
+
|
| 327 |
+
ax.set_title(title_spot, fontsize=24/(len(title_spot)*0.03),fontname='Century Gothic')
|
| 328 |
+
# #vals = ax.get_yticks()
|
| 329 |
+
ax.set_xlabel(pitcher_dict_stat[input.stat_x()]['x_axis'], fontsize=16,fontname='Century Gothic')
|
| 330 |
+
ax.set_ylabel(pitcher_dict_stat[input.stat_y()]['x_axis'], fontsize=16,fontname='Century Gothic')
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
if input.group_level():
|
| 334 |
+
ax.get_legend().remove()
|
| 335 |
+
|
| 336 |
+
if not input.group_level():
|
| 337 |
+
if len(input.level_id()) > 1:
|
| 338 |
+
h,l = scatter.get_legend_handles_labels()
|
| 339 |
+
l[-(len(input.level_id())+1)] = 'Level'
|
| 340 |
+
ax.legend(h[-(len(input.level_id())+1):],l[-(len(input.level_id())+1):], borderaxespad=0.1,loc=0)
|
| 341 |
+
|
| 342 |
+
else:
|
| 343 |
+
ax.get_legend().remove()
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
|
| 347 |
+
#plt.show(g)
|
| 348 |
+
# ax.figure.colorbar(sm, ax=ax)
|
| 349 |
+
|
| 350 |
+
cbar = ax.figure.colorbar(sm, ax=ax,format=decimal_format_assign(x=pitcher_dict_stat[input.stat_z()]),orientation='vertical',aspect=30)
|
| 351 |
+
cbar.set_label(pitcher_dict_stat[input.stat_z()]['x_axis'])
|
| 352 |
+
#fig.axes[0].invert_yaxis()
|
| 353 |
+
print('we made it here5')
|
| 354 |
+
fig.subplots_adjust(wspace=.02, hspace=.02)
|
| 355 |
+
# ax.xaxis.set_major_formatter(FuncFormatter(lambda x, _: int(x)))
|
| 356 |
+
#ax.set_yticks([0,0.1,0.2,0.3,0.4,0.5])
|
| 357 |
+
# fig.colorbar(plot_dist, ax=ax)
|
| 358 |
+
# fig.colorbar(plot_dist)
|
| 359 |
+
|
| 360 |
+
if not pitcher_dict_stat[input.stat_x()]['flip_p']:
|
| 361 |
+
fig.axes[0].invert_xaxis()
|
| 362 |
+
|
| 363 |
+
if not pitcher_dict_stat[input.stat_y()]['flip_p']:
|
| 364 |
+
fig.axes[0].invert_yaxis()
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
# ax.xaxis.set_major_formatter(mtick.PercentFormatter(1,decimals=0))
|
| 368 |
+
# ax.yaxis.set_major_formatter(mtick.PercentFormatter(1))
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
print('we made it here6')
|
| 375 |
+
|
| 376 |
+
ax.xaxis.set_major_formatter(decimal_format_assign(x=pitcher_dict_stat[input.stat_x()]))
|
| 377 |
+
ax.yaxis.set_major_formatter(decimal_format_assign(x=pitcher_dict_stat[input.stat_y()]))
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
print('we made it here7')
|
| 381 |
+
# ax.text(0.5, 0.5, '/u/tomstoms', transform=ax.transAxes,
|
| 382 |
+
# fontsize=60, color='gray', alpha=0.075,
|
| 383 |
+
# ha='center', va='center', rotation=45)
|
| 384 |
+
adjust_text(ts,
|
| 385 |
+
arrowprops=dict(arrowstyle="-", color=colour_palette[4], lw=1),ax=ax)
|
| 386 |
+
|
| 387 |
+
#ax.legend(fontsize='16')
|
| 388 |
+
fig.text(x=0.03,y=0.02,s='By: @TJStats',fontname='Century Gothic')
|
| 389 |
+
fig.text(x=1-0.03,y=0.02,s='Data: MLB',ha='right',fontname='Century Gothic')
|
| 390 |
+
fig.tight_layout()
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
pitcher_scatter = App(ui.page_fluid(
|
| 394 |
+
ui.tags.base(href=base_url),
|
| 395 |
+
ui.tags.div(
|
| 396 |
+
{"style": "width:90%;margin: 0 auto;max-width: 1600px;"},
|
| 397 |
+
ui.tags.style(
|
| 398 |
+
"""
|
| 399 |
+
h4 {
|
| 400 |
+
margin-top: 1em;font-size:35px;
|
| 401 |
+
}
|
| 402 |
+
h2{
|
| 403 |
+
font-size:25px;
|
| 404 |
+
}
|
| 405 |
+
"""
|
| 406 |
+
),
|
| 407 |
+
shinyswatch.theme.simplex(),
|
| 408 |
+
ui.tags.h4("TJStats"),
|
| 409 |
+
ui.tags.i("Baseball Analytics and Visualizations"),
|
| 410 |
+
ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),
|
| 411 |
+
ui.navset_tab(
|
| 412 |
+
ui.nav_control(
|
| 413 |
+
ui.a(
|
| 414 |
+
"Home",
|
| 415 |
+
href="home/"
|
| 416 |
+
),
|
| 417 |
+
),
|
| 418 |
+
ui.nav_menu(
|
| 419 |
+
"Batter Charts",
|
| 420 |
+
ui.nav_control(
|
| 421 |
+
ui.a(
|
| 422 |
+
"Batting Rolling",
|
| 423 |
+
href="rolling_batter/"
|
| 424 |
+
),
|
| 425 |
+
ui.a(
|
| 426 |
+
"Spray",
|
| 427 |
+
href="spray/"
|
| 428 |
+
),
|
| 429 |
+
ui.a(
|
| 430 |
+
"Decision Value",
|
| 431 |
+
href="decision_value/"
|
| 432 |
+
),
|
| 433 |
+
ui.a(
|
| 434 |
+
"Damage Model",
|
| 435 |
+
href="damage_model/"
|
| 436 |
+
),
|
| 437 |
+
ui.a(
|
| 438 |
+
"Batter Scatter",
|
| 439 |
+
href="batter_scatter/"
|
| 440 |
+
),
|
| 441 |
+
ui.a(
|
| 442 |
+
"EV vs LA Plot",
|
| 443 |
+
href="ev_angle/"
|
| 444 |
+
),
|
| 445 |
+
ui.a(
|
| 446 |
+
"Statcast Compare",
|
| 447 |
+
href="statcast_compare/"
|
| 448 |
+
)
|
| 449 |
+
),
|
| 450 |
+
),
|
| 451 |
+
ui.nav_menu(
|
| 452 |
+
"Pitcher Charts",
|
| 453 |
+
ui.nav_control(
|
| 454 |
+
ui.a(
|
| 455 |
+
"Pitcher Rolling",
|
| 456 |
+
href="rolling_pitcher/"
|
| 457 |
+
),
|
| 458 |
+
ui.a(
|
| 459 |
+
"Pitcher Summary",
|
| 460 |
+
href="pitching_summary_graphic_new/"
|
| 461 |
+
),
|
| 462 |
+
ui.a(
|
| 463 |
+
"Pitcher Scatter",
|
| 464 |
+
href="pitcher_scatter/"
|
| 465 |
+
)
|
| 466 |
+
),
|
| 467 |
+
)),ui.row(
|
| 468 |
+
ui.layout_sidebar(
|
| 469 |
+
|
| 470 |
+
|
| 471 |
+
|
| 472 |
+
ui.panel_sidebar(
|
| 473 |
+
#ui.input_select("id", "Select Batter",batter_dict,selected=675911,width=1,size=1),
|
| 474 |
+
ui.row(
|
| 475 |
+
ui.column(4,ui.input_select("level_id", "Select Level",level_dict,width=1,size=1,multiple=True,selected='MLB',selectize=True),),
|
| 476 |
+
ui.column(4,ui.input_select("team_id", "Select Team",team_list,width=1,size=1,multiple=True,selected='All',selectize=True),),
|
| 477 |
+
ui.column(4,ui.input_select("position_id", "Select Position",position_list,width=1,size=1,selected='All',multiple=True,selectize=True))),
|
| 478 |
+
ui.row(
|
| 479 |
+
ui.column(6,ui.input_numeric("n", "Minimum PA", value=100)),
|
| 480 |
+
ui.column(6,ui.input_numeric("n_age", "Maximum Age", value=50))),
|
| 481 |
+
ui.row(
|
| 482 |
+
ui.column(4,ui.input_select("stat_x", "X-Axis",pitcher_dict_stat_small,selected='k_percent',width=1,size=1)),
|
| 483 |
+
ui.column(4,ui.input_select("stat_y", "Y-Axis",pitcher_dict_stat_small,selected='bb_percent',width=1,size=1)),
|
| 484 |
+
ui.column(4,ui.input_select("stat_z", "Colour-Bar Axis",pitcher_dict_stat_small,selected='bb_minus_k_percent',width=1,size=1))),
|
| 485 |
+
|
| 486 |
+
ui.row(
|
| 487 |
+
ui.column(6,ui.input_numeric("n_percent_top_x", "Top 'n' Percentile X-Labels", value=0.01)),
|
| 488 |
+
ui.column(6,ui.input_numeric("n_percent_bot_x", "Bottom 'n' Percentile X-Labels", value=0.01))),
|
| 489 |
+
ui.row(
|
| 490 |
+
ui.column(6,ui.input_numeric("n_percent_top_y", "Top 'n' Percentile Y-Labels", value=0.01)),
|
| 491 |
+
ui.column(6,ui.input_numeric("n_percent_bot_y", "Bottom 'n' Percentile Y-Labels", value=0.01))),
|
| 492 |
+
ui.row(
|
| 493 |
+
ui.column(6,ui.input_numeric("n_percent_top_z", "Top 'n' Percentile Z-Labels", value=0.01)),
|
| 494 |
+
ui.column(6,ui.input_numeric("n_percent_bot_z", "Bottom 'n' Percentile Z-Labels", value=0.01))),
|
| 495 |
+
|
| 496 |
+
ui.input_select("player_id", "Label Player",pitcher_dict,width=1,size=1,multiple=True,selectize=True),
|
| 497 |
+
ui.row(
|
| 498 |
+
ui.input_switch("names", "Toggle Names"),
|
| 499 |
+
ui.input_switch("group_level", "Group Levels")),
|
| 500 |
+
ui.input_action_button("go", "Generate",class_="btn-primary"),
|
| 501 |
+
),
|
| 502 |
+
|
| 503 |
+
|
| 504 |
+
ui.panel_main(
|
| 505 |
+
ui.output_plot("plot",height = "1000px",width="1000px")
|
| 506 |
+
,
|
| 507 |
+
),
|
| 508 |
+
)),)),server)
|
pitcher_summary_graphic.ipynb
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
pitching_summary_graphic.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
pitching_summary_graphic_new.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
pitching_summary_graphic_new_fg_api.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
rolling_batter.py
ADDED
|
@@ -0,0 +1,732 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui
|
| 2 |
+
import datasets
|
| 3 |
+
from datasets import load_dataset
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import numpy as np
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
import seaborn as sns
|
| 8 |
+
import numpy as np
|
| 9 |
+
from scipy.stats import gaussian_kde
|
| 10 |
+
import matplotlib
|
| 11 |
+
from matplotlib.ticker import MaxNLocator
|
| 12 |
+
from matplotlib.gridspec import GridSpec
|
| 13 |
+
from scipy.stats import zscore
|
| 14 |
+
import math
|
| 15 |
+
import matplotlib
|
| 16 |
+
from adjustText import adjust_text
|
| 17 |
+
import matplotlib.ticker as mtick
|
| 18 |
+
from shinywidgets import output_widget, render_widget
|
| 19 |
+
import pandas as pd
|
| 20 |
+
from configure import base_url
|
| 21 |
+
import shinyswatch
|
| 22 |
+
import inflect
|
| 23 |
+
from matplotlib.pyplot import text
|
| 24 |
+
|
| 25 |
+
def percentile(n):
|
| 26 |
+
def percentile_(x):
|
| 27 |
+
return np.nanpercentile(x, n)
|
| 28 |
+
percentile_.__name__ = 'percentile_%s' % n
|
| 29 |
+
return percentile_
|
| 30 |
+
|
| 31 |
+
colour_palette = ['#FFB000','#648FFF','#785EF0',
|
| 32 |
+
'#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
print('Starting Everything:')
|
| 37 |
+
# exit_velo_df = milb_a_ev_df.append([triple_a_ev_df,double_a_ev_df,a_high_a_ev_df,single_a_ev_df]).reset_index(drop=True)
|
| 38 |
+
# player_df_all = mlb_a_player_df.append([triple_a_player_df,double_a_player_df,a_high_a_player_df,single_a_player_df]).reset_index(drop=True)
|
| 39 |
+
# exit_velo_df = pd.read_csv('exit_velo_df_all.csv',index_col=[0])
|
| 40 |
+
# player_df_all = pd.read_csv('player_df_all.csv',index_col=[0])
|
| 41 |
+
|
| 42 |
+
# pa_df = pd.read_csv('pa_df_all.csv',index_col=[0])
|
| 43 |
+
# pa_df_full_na = pa_df.dropna()
|
| 44 |
+
|
| 45 |
+
### Import Datasets
|
| 46 |
+
dataset = load_dataset('nesticot/mlb_data', data_files=['mlb_pitch_data_2023.csv',
|
| 47 |
+
])
|
| 48 |
+
dataset_train = dataset['train']
|
| 49 |
+
exit_velo_df_mlb = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)
|
| 50 |
+
#print(df_2023)
|
| 51 |
+
exit_velo_df_mlb['level'] = 'MLB'
|
| 52 |
+
|
| 53 |
+
# ### Import Datasets
|
| 54 |
+
# dataset = load_dataset('nesticot/mlb_data', data_files=['aaa_pitch_data_2023.csv',
|
| 55 |
+
# ])
|
| 56 |
+
# dataset_train = dataset['train']
|
| 57 |
+
# exit_velo_df_aaa = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)
|
| 58 |
+
# #print(df_2023)
|
| 59 |
+
# exit_velo_df_aaa['level'] = 'AAA'
|
| 60 |
+
|
| 61 |
+
# ### Import Datasets
|
| 62 |
+
# dataset = load_dataset('nesticot/mlb_data', data_files=['aa_pitch_data_2023.csv',
|
| 63 |
+
# ])
|
| 64 |
+
# dataset_train = dataset['train']
|
| 65 |
+
# exit_velo_df_aa = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)
|
| 66 |
+
# #print(df_2023)
|
| 67 |
+
# exit_velo_df_aa['level'] = 'AA'
|
| 68 |
+
|
| 69 |
+
# ### Import Datasets
|
| 70 |
+
# dataset = load_dataset('nesticot/mlb_data', data_files=['high_a_pitch_data_2023.csv',
|
| 71 |
+
# ])
|
| 72 |
+
# dataset_train = dataset['train']
|
| 73 |
+
# exit_velo_df_ha = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)
|
| 74 |
+
# #print(df_2023)
|
| 75 |
+
# exit_velo_df_ha['level'] = 'A+'
|
| 76 |
+
|
| 77 |
+
# ### Import Datasets
|
| 78 |
+
# dataset = load_dataset('nesticot/mlb_data', data_files=['a_pitch_data_2023.csv',
|
| 79 |
+
# ])
|
| 80 |
+
# dataset_train = dataset['train']
|
| 81 |
+
# exit_velo_df_a = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)
|
| 82 |
+
# #print(df_2023)
|
| 83 |
+
# exit_velo_df_a['level'] = 'A'
|
| 84 |
+
|
| 85 |
+
# exit_velo_df = pd.concat([exit_velo_df_mlb,exit_velo_df_aaa,exit_velo_df_aa,exit_velo_df_ha,exit_velo_df_a])
|
| 86 |
+
exit_velo_df = pd.concat([exit_velo_df_mlb])
|
| 87 |
+
# exit_velo_df_copy = exit_velo_df.copy()
|
| 88 |
+
|
| 89 |
+
# exit_velo_df = exit_velo_df_copy.copy()
|
| 90 |
+
|
| 91 |
+
end_codes = ['strikeout', 'field_out', 'single', 'walk', 'hit_by_pitch',
|
| 92 |
+
'double', 'sac_fly', 'force_out', 'home_run',
|
| 93 |
+
'grounded_into_double_play', 'fielders_choice', 'field_error',
|
| 94 |
+
'triple', 'sac_bunt', 'double_play', 'intent_walk',
|
| 95 |
+
'fielders_choice_out', 'strikeout_double_play',
|
| 96 |
+
'sac_fly_double_play', 'catcher_interf', 'other_out']
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
exit_velo_df['pa'] = exit_velo_df.event_type.isin(end_codes)
|
| 101 |
+
#exit_velo_df['pa'] = 1
|
| 102 |
+
exit_velo_df['k'] = exit_velo_df.event_type.isin(list(filter(None, [x if 'strikeout' in x else '' for x in exit_velo_df.event_type.fillna('None').unique()])))
|
| 103 |
+
exit_velo_df['bb'] = exit_velo_df.event_type.isin(list(filter(None, [x if 'walk' in x else '' for x in exit_velo_df.event_type.fillna('None').unique()])))
|
| 104 |
+
|
| 105 |
+
#exit_velo_df['k_minus_bb'] = exit_velo_df['k'].astype(np.float32)-exit_velo_df['bb'].astype(np.float32)
|
| 106 |
+
exit_velo_df['bb_minus_k'] = exit_velo_df['bb'].astype(np.float32)-exit_velo_df['k'].astype(np.float32)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
exit_velo_df = exit_velo_df.drop_duplicates(subset=['play_id'])
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
swing_codes = ['Swinging Strike', 'In play, no out',
|
| 115 |
+
'Foul', 'In play, out(s)',
|
| 116 |
+
'In play, run(s)', 'Swinging Strike (Blocked)',
|
| 117 |
+
'Foul Bunt','Foul Tip', 'Missed Bunt','Foul Pitchout','Swinging Pitchout']
|
| 118 |
+
|
| 119 |
+
swings_in = ['Swinging Strike', 'In play, no out',
|
| 120 |
+
'Foul', 'In play, out(s)',
|
| 121 |
+
'In play, run(s)', 'Swinging Strike (Blocked)',
|
| 122 |
+
'Foul Bunt','Foul Tip', 'Missed Bunt','Foul Pitchout','Swinging Pitchout']
|
| 123 |
+
|
| 124 |
+
swing_strike_codes = ['Swinging Strike',
|
| 125 |
+
'Swinging Strike (Blocked)','Missed Bunt','Foul Tip','Swinging Pitchout']
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
contact_codes = ['In play, no out',
|
| 129 |
+
'Foul', 'In play, out(s)',
|
| 130 |
+
'In play, run(s)',
|
| 131 |
+
'Foul Bunt']
|
| 132 |
+
|
| 133 |
+
codes_in = ['In play, out(s)',
|
| 134 |
+
'Swinging Strike',
|
| 135 |
+
'Ball',
|
| 136 |
+
'Foul',
|
| 137 |
+
'In play, no out',
|
| 138 |
+
'Called Strike',
|
| 139 |
+
'Foul Tip',
|
| 140 |
+
'In play, run(s)',
|
| 141 |
+
'Hit By Pitch',
|
| 142 |
+
'Ball In Dirt',
|
| 143 |
+
'Pitchout',
|
| 144 |
+
'Swinging Strike (Blocked)',
|
| 145 |
+
'Foul Bunt',
|
| 146 |
+
'Missed Bunt',
|
| 147 |
+
'Foul Pitchout',
|
| 148 |
+
'Intent Ball',
|
| 149 |
+
'Swinging Pitchout']
|
| 150 |
+
|
| 151 |
+
exit_velo_df['in_zone'] = exit_velo_df['zone'] < 10
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
exit_velo_df = exit_velo_df.drop_duplicates(subset=['play_id'])
|
| 155 |
+
|
| 156 |
+
exit_velo_df_codes = exit_velo_df[exit_velo_df.play_description.isin(codes_in)].dropna(subset=['in_zone'])
|
| 157 |
+
|
| 158 |
+
exit_velo_df_codes['bip'] = ~exit_velo_df_codes.launch_speed.isna()
|
| 159 |
+
conditions = [
|
| 160 |
+
(exit_velo_df_codes['launch_speed'].isna()),
|
| 161 |
+
(exit_velo_df_codes['launch_speed']*1.5 - exit_velo_df_codes['launch_angle'] >= 117 ) & (exit_velo_df_codes['launch_speed'] + exit_velo_df_codes['launch_angle'] >= 124) & (exit_velo_df_codes['launch_speed'] > 98) & (exit_velo_df_codes['launch_angle'] >= 8) & (exit_velo_df_codes['launch_angle'] <= 50)
|
| 162 |
+
]
|
| 163 |
+
|
| 164 |
+
choices = [False,True]
|
| 165 |
+
exit_velo_df_codes['barrel'] = np.select(conditions, choices, default=np.nan)
|
| 166 |
+
|
| 167 |
+
conditions_ss = [
|
| 168 |
+
(exit_velo_df_codes['launch_angle'].isna()),
|
| 169 |
+
(exit_velo_df_codes['launch_angle'] >= 8 ) * (exit_velo_df_codes['launch_angle'] <= 32 )
|
| 170 |
+
]
|
| 171 |
+
|
| 172 |
+
choices_ss = [False,True]
|
| 173 |
+
exit_velo_df_codes['sweet_spot'] = np.select(conditions_ss, choices_ss, default=np.nan)
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
conditions_hh = [
|
| 177 |
+
(exit_velo_df_codes['launch_speed'].isna()),
|
| 178 |
+
(exit_velo_df_codes['launch_speed'] >= 94.5 )
|
| 179 |
+
]
|
| 180 |
+
|
| 181 |
+
choices_hh = [False,True]
|
| 182 |
+
exit_velo_df_codes['hard_hit'] = np.select(conditions_hh, choices_hh, default=np.nan)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
conditions_tb = [
|
| 186 |
+
(exit_velo_df_codes['event_type']=='single'),
|
| 187 |
+
(exit_velo_df_codes['event_type']=='double'),
|
| 188 |
+
(exit_velo_df_codes['event_type']=='triple'),
|
| 189 |
+
(exit_velo_df_codes['event_type']=='home_run'),
|
| 190 |
+
]
|
| 191 |
+
|
| 192 |
+
choices_tb = [1,2,3,4]
|
| 193 |
+
|
| 194 |
+
exit_velo_df_codes['tb'] = np.select(conditions_tb, choices_tb, default=np.nan)
|
| 195 |
+
|
| 196 |
+
conditions_woba = [
|
| 197 |
+
(exit_velo_df_codes['event_type']=='walk'),
|
| 198 |
+
(exit_velo_df_codes['event_type']=='hit_by_pitch'),
|
| 199 |
+
(exit_velo_df_codes['event_type']=='single'),
|
| 200 |
+
(exit_velo_df_codes['event_type']=='double'),
|
| 201 |
+
(exit_velo_df_codes['event_type']=='triple'),
|
| 202 |
+
(exit_velo_df_codes['event_type']=='home_run'),
|
| 203 |
+
]
|
| 204 |
+
|
| 205 |
+
choices_woba = [0.705,
|
| 206 |
+
0.688,
|
| 207 |
+
0.897,
|
| 208 |
+
1.233,
|
| 209 |
+
1.612,
|
| 210 |
+
2.013]
|
| 211 |
+
|
| 212 |
+
exit_velo_df_codes['woba'] = np.select(conditions_woba, choices_woba, default=np.nan)
|
| 213 |
+
|
| 214 |
+
|
| 215 |
+
woba_codes = ['strikeout', 'field_out', 'single', 'walk', 'hit_by_pitch',
|
| 216 |
+
'double', 'sac_fly', 'force_out', 'home_run',
|
| 217 |
+
'grounded_into_double_play', 'fielders_choice', 'field_error',
|
| 218 |
+
'triple', 'sac_bunt', 'double_play',
|
| 219 |
+
'fielders_choice_out', 'strikeout_double_play',
|
| 220 |
+
'sac_fly_double_play', 'other_out']
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
conditions_woba_code = [
|
| 227 |
+
(exit_velo_df_codes['event_type'].isin(woba_codes))
|
| 228 |
+
]
|
| 229 |
+
|
| 230 |
+
choices_woba_code = [1]
|
| 231 |
+
|
| 232 |
+
exit_velo_df_codes['woba_codes'] = np.select(conditions_woba_code, choices_woba_code, default=np.nan)
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
#exit_velo_df_codes['barrel'] = (exit_velo_df_codes.launch_speed >= 98) & (exit_velo_df_codes.launch_angle >= (26 - (-98 + exit_velo_df_codes.launch_speed))) & (exit_velo_df_codes.launch_angle <= 30 + (-98 + exit_velo_df_codes.launch_speed)) & (exit_velo_df_codes.launch_angle >= 8) & (exit_velo_df_codes.launch_angle <= 50)
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
#exit_velo_df_codes['barrel'] = (exit_velo_df_codes.launch_speed >= 98) & (exit_velo_df_codes.launch_angle >= (26 - (-98 + exit_velo_df_codes.launch_speed))) & (exit_velo_df_codes.launch_angle <= 30 + (-98 + exit_velo_df_codes.launch_speed)) & (exit_velo_df_codes.launch_angle >= 8) & (exit_velo_df_codes.launch_angle <= 50)
|
| 242 |
+
exit_velo_df_codes['pitches'] = 1
|
| 243 |
+
exit_velo_df_codes['whiffs'] = [1 if ((x == 'S')|(x == 'W')|(x =='T')) else 0 for x in exit_velo_df_codes.play_code]
|
| 244 |
+
exit_velo_df_codes['csw'] = [1 if ((x == 'S')|(x == 'W')|(x =='T')|(x == 'C')) else 0 for x in exit_velo_df_codes.play_code]
|
| 245 |
+
exit_velo_df_codes['swings'] = [1 if x in swings_in else 0 for x in exit_velo_df_codes.play_description]
|
| 246 |
+
|
| 247 |
+
exit_velo_df_codes['out_zone'] = exit_velo_df_codes.in_zone == False
|
| 248 |
+
exit_velo_df_codes['zone_swing'] = (exit_velo_df_codes.in_zone == True)&(exit_velo_df_codes.swings == 1)
|
| 249 |
+
exit_velo_df_codes['zone_contact'] = (exit_velo_df_codes.in_zone == True)&(exit_velo_df_codes.swings == 1)&(exit_velo_df_codes.whiffs == 0)
|
| 250 |
+
exit_velo_df_codes['ozone_swing'] = (exit_velo_df_codes.in_zone==False)&(exit_velo_df_codes.swings == 1)
|
| 251 |
+
exit_velo_df_codes['ozone_contact'] = (exit_velo_df_codes.in_zone==False)&(exit_velo_df_codes.swings == 1)&(exit_velo_df_codes.whiffs == 0)
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
exit_velo_df_codes_summ = exit_velo_df_codes.groupby(['batter_id','batter_name','level']).agg(
|
| 256 |
+
pa = ('pa','sum'),
|
| 257 |
+
k = ('k','sum'),
|
| 258 |
+
bb = ('bb','sum'),
|
| 259 |
+
bb_minus_k = ('bb_minus_k','sum'),
|
| 260 |
+
csw = ('csw','sum'),
|
| 261 |
+
bip = ('bip','sum'),
|
| 262 |
+
tb = ('tb','sum'),
|
| 263 |
+
woba = ('woba','sum'),
|
| 264 |
+
woba_codes = ('woba_codes','sum'),
|
| 265 |
+
hard_hit = ('hard_hit','sum'),
|
| 266 |
+
barrel = ('barrel','sum'),
|
| 267 |
+
sweet_spot = ('sweet_spot','sum'),
|
| 268 |
+
max_launch_speed = ('launch_speed','max'),
|
| 269 |
+
launch_speed_90 = ('launch_speed',percentile(90)),
|
| 270 |
+
launch_speed = ('launch_speed','mean'),
|
| 271 |
+
launch_angle = ('launch_angle','mean'),
|
| 272 |
+
pitches = ('pitches','sum'),
|
| 273 |
+
swings = ('swings','sum'),
|
| 274 |
+
in_zone = ('in_zone','sum'),
|
| 275 |
+
out_zone = ('out_zone','sum'),
|
| 276 |
+
whiffs = ('whiffs','sum'),
|
| 277 |
+
zone_swing = ('zone_swing','sum'),
|
| 278 |
+
zone_contact = ('zone_contact','sum'),
|
| 279 |
+
ozone_swing = ('ozone_swing','sum'),
|
| 280 |
+
ozone_contact = ('ozone_contact','sum'),
|
| 281 |
+
).reset_index()
|
| 282 |
+
|
| 283 |
+
#exit_velo_df_codes_summ['out_zone'] = ~exit_velo_df_codes_summ.in_zone
|
| 284 |
+
#bip_min_input = int(input())
|
| 285 |
+
#bip_min = min(bip_min_input,50)
|
| 286 |
+
#exit_velo_df_codes_summ = exit_velo_df_codes_summ[exit_velo_df_codes_summ.balls_in_play>=bip_min]
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
exit_velo_df_codes_summ['k_percent'] = [exit_velo_df_codes_summ.k[x]/exit_velo_df_codes_summ.pa[x] if exit_velo_df_codes_summ.pa[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 290 |
+
exit_velo_df_codes_summ['bb_percent'] =[exit_velo_df_codes_summ.bb[x]/exit_velo_df_codes_summ.pa[x] if exit_velo_df_codes_summ.pa[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 291 |
+
exit_velo_df_codes_summ['bb_minus_k_percent'] =[exit_velo_df_codes_summ.bb_minus_k[x]/exit_velo_df_codes_summ.pa[x] if exit_velo_df_codes_summ.pa[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 292 |
+
|
| 293 |
+
exit_velo_df_codes_summ['csw_percent'] =[exit_velo_df_codes_summ.csw[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
exit_velo_df_codes_summ['sweet_spot_percent'] = [exit_velo_df_codes_summ.sweet_spot[x]/exit_velo_df_codes_summ.bip[x] if exit_velo_df_codes_summ.bip[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 297 |
+
|
| 298 |
+
exit_velo_df_codes_summ['woba_percent'] = [exit_velo_df_codes_summ.woba[x]/exit_velo_df_codes_summ.woba_codes[x] if exit_velo_df_codes_summ.woba_codes[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 299 |
+
#exit_velo_df_codes_summ['hard_hit_percent'] = [exit_velo_df_codes_summ.sweet_spot[x]/exit_velo_df_codes_summ.bip[x] if exit_velo_df_codes_summ.bip[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 300 |
+
exit_velo_df_codes_summ['hard_hit_percent'] = [exit_velo_df_codes_summ.hard_hit[x]/exit_velo_df_codes_summ.bip[x] if exit_velo_df_codes_summ.bip[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
exit_velo_df_codes_summ['barrel_percent'] = [exit_velo_df_codes_summ.barrel[x]/exit_velo_df_codes_summ.bip[x] if exit_velo_df_codes_summ.bip[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 304 |
+
|
| 305 |
+
exit_velo_df_codes_summ['zone_contact_percent'] = [exit_velo_df_codes_summ.zone_contact[x]/exit_velo_df_codes_summ.zone_swing[x] if exit_velo_df_codes_summ.zone_swing[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 306 |
+
|
| 307 |
+
exit_velo_df_codes_summ['zone_swing_percent'] = [exit_velo_df_codes_summ.zone_swing[x]/exit_velo_df_codes_summ.in_zone[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 308 |
+
|
| 309 |
+
exit_velo_df_codes_summ['zone_percent'] = [exit_velo_df_codes_summ.in_zone[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 310 |
+
|
| 311 |
+
exit_velo_df_codes_summ['chase_percent'] = [exit_velo_df_codes_summ.ozone_swing[x]/(exit_velo_df_codes_summ.pitches[x] - exit_velo_df_codes_summ.in_zone[x]) if (exit_velo_df_codes_summ.pitches[x]- exit_velo_df_codes_summ.in_zone[x]) != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 312 |
+
|
| 313 |
+
exit_velo_df_codes_summ['chase_contact'] = [exit_velo_df_codes_summ.ozone_contact[x]/exit_velo_df_codes_summ.ozone_swing[x] if exit_velo_df_codes_summ.ozone_swing[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 314 |
+
|
| 315 |
+
exit_velo_df_codes_summ['swing_percent'] = [exit_velo_df_codes_summ.swings[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 316 |
+
|
| 317 |
+
exit_velo_df_codes_summ['whiff_rate'] = [exit_velo_df_codes_summ.whiffs[x]/exit_velo_df_codes_summ.swings[x] if exit_velo_df_codes_summ.swings[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 318 |
+
|
| 319 |
+
exit_velo_df_codes_summ['swstr_rate'] = [exit_velo_df_codes_summ.whiffs[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 320 |
+
|
| 321 |
+
exit_velo_df_codes_summ = exit_velo_df_codes_summ.dropna(subset=['bip'])
|
| 322 |
+
|
| 323 |
+
woba_list = ['woba']
|
| 324 |
+
pa_list = ['k','bb','bb_minus_k']
|
| 325 |
+
balls_in_play_list = ['hard_hit','launch_speed','launch_speed_90','launch_angle','barrel','sweet_spot']
|
| 326 |
+
pitches_list = ['zone_percent','swing_percent','sw_str','csw']
|
| 327 |
+
swings_list = ['whiff_percent']
|
| 328 |
+
in_zone_pitches_list = ['zone_swing']
|
| 329 |
+
in_zone_swings_list = ['zone_contact']
|
| 330 |
+
out_zone_pitches_list = ['chase_percent']
|
| 331 |
+
out_zone_swings_list = ['chase_contact']
|
| 332 |
+
|
| 333 |
+
plot_dict = {
|
| 334 |
+
'k':{'x_axis':'Plate Appearances','y_axis':'K%','title':'K%','x_value':'k','x_range':[0.0,0.1,0.2,0.3,0.4],'percent':True,'percentile_label':'k_percent','flip_p':True,'percentile':False,'avg_adjust':False},
|
| 335 |
+
'bb':{'x_axis':'Plate Appearances','y_axis':'BB%','title':'BB%','x_value':'bb','x_range':[0.0,0.1,0.2,0.3],'percent':True,'percentile_label':'bb_percent','flip_p':False,'percentile':False,'avg_adjust':False},
|
| 336 |
+
'bb_minus_k':{'x_axis':'Plate Appearances','y_axis':'BB-K%','title':'BB-K%','x_value':'bb_minus_k','x_range':[-0.3,-0.2,-0.1,0,0.1,0.2],'percent':True,'percentile_label':'bb_minus_k_percent','flip_p':False,'percentile':False,'avg_adjust':False},
|
| 337 |
+
'csw':{'x_axis':'Pitches','y_axis':'CSW%','title':'CSW%','x_value':'csw','x_range':[.2,.25,.3,.35,.4],'percent':True,'percentile_label':'csw_percent','flip_p':True,'percentile':False,'avg_adjust':False},
|
| 338 |
+
'woba':{'x_axis':'wOBA PA','y_axis':'wOBA','title':'wOBA','x_value':'woba','x_range':[.20,.30,.40,.50],'percent':False,'percentile_label':'woba_percent','flip_p':False,'percentile':False,'avg_adjust':True},
|
| 339 |
+
'launch_speed':{'x_axis':'Balls In Play','y_axis':'Exit Velocity','title':'Exit Velocity','x_value':'launch_speed','x_range':[85,90,95,100],'percent':False,'percentile_label':'launch_speed','flip_p':False,'percentile':False,'avg_adjust':False},
|
| 340 |
+
'launch_speed_90':{'x_axis':'Balls In Play','y_axis':'90th Percentile Exit Velocity','title':'90th Percentile Exit Velocity','x_value':'launch_speed','x_range':[95,100,105,110,115],'percent':False,'percentile_label':'launch_speed_90','flip_p':False,'percentile':True,'avg_adjust':False},
|
| 341 |
+
'hard_hit':{'x_axis':'Balls In Play','y_axis':'HardHit%','title':'HardHit%','x_value':'hard_hit','x_range':[0.2,0.3,0.4,0.5,0.6,0.7],'percent':True,'percentile_label':'hard_hit_percent','flip_p':False,'percentile':False,'avg_adjust':False},
|
| 342 |
+
'sweet_spot':{'x_axis':'Balls In Play','y_axis':'SweetSpot%','title':'SweetSpot%','x_value':'sweet_spot','x_range':[0.2,0.3,0.4,0.5],'percent':True,'percentile_label':'sweet_spot_percent','flip_p':False,'percentile':False,'avg_adjust':False},
|
| 343 |
+
'launch_angle':{'x_axis':'Balls In Play','y_axis':'Launch Angle','title':'Launch Angle','x_value':'launch_angle','x_range':[-20,-10,0,10,20],'percent':False,'percentile_label':'launch_angle','flip_p':False,'percentile':False,'avg_adjust':False},
|
| 344 |
+
'barrel':{'x_axis':'Balls In Play','y_axis':'Barrel%','title':'Barrel%','x_value':'barrel','x_range':[0,0.05,0.10,.15,.20,.25,.30],'percent':True,'percentile_label':'barrel_percent','flip_p':False,'percentile':False,'avg_adjust':False},
|
| 345 |
+
'zone_percent':{'x_axis':'Pitches','y_axis':'Zone%','title':'Zone%','x_value':'in_zone','x_range':[0.3,0.4,0.5,0.6,0.7],'percent':True,'percentile_label':'zone_percent','flip_p':False,'percentile':False,'avg_adjust':False},
|
| 346 |
+
'swing_percent':{'x_axis':'Pitches','y_axis':'Swing%','title':'Swing%','x_value':'swings','x_range':[0.2,0.3,0.4,0.5,0.6,0.7,0.8],'percent':True,'percentile_label':'swing_percent','flip_p':False,'percentile':False,'avg_adjust':False},
|
| 347 |
+
'whiff_percent':{'x_axis':'Swings','y_axis':'Whiff%','title':'Whiff%','x_value':'whiffs','x_range':[0.0,0.1,0.2,0.3,0.4,0.5],'percent':True,'percentile_label':'whiff_rate','flip_p':True,'percentile':False,'avg_adjust':False},
|
| 348 |
+
'sw_str':{'x_axis':'Pitches','y_axis':'SwStr%','title':'SwStr%','x_value':'whiffs','x_range':[0.0,0.05,0.1,0.15,0.2,0.25],'percent':True,'percentile_label':'swstr_rate','flip_p':True,'percentile':False,'avg_adjust':False},
|
| 349 |
+
'zone_swing':{'x_axis':'In-Zone Pitches','y_axis':'Z-Swing%','title':'Z-Swing%','x_value':'zone_swing','x_range':[0.3,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1],'percent':True,'percentile_label':'zone_swing_percent','flip_p':False,'percentile':False,'avg_adjust':False},
|
| 350 |
+
'zone_contact':{'x_axis':'In-Zone Swings','y_axis':'Z-Contact%','title':'Z-Contact%','x_value':'zone_contact','x_range':[0.5,0.6,0.7,0.8,0.9,1],'percent':True,'percentile_label':'zone_contact_percent','flip_p':False,'percentile':False,'avg_adjust':False},
|
| 351 |
+
'chase_percent':{'x_axis':'Out-of-Zone Pitches','y_axis':'O-Swing%','title':'O-Swing%','x_value':'ozone_swing','x_range':[0.0,0.1,0.2,0.3,0.4,0.5],'percent':True,'percentile_label':'chase_percent','flip_p':True,'percentile':False,'avg_adjust':False},
|
| 352 |
+
'chase_contact':{'x_axis':'Out-of-Zone Swings','y_axis':'O-Contact%','title':'O-Contact%','x_value':'ozone_contact','x_range':[0.2,0.3,0.4,0.5,0.6,0.7,0.8],'percent':True,'percentile_label':'chase_contact','flip_p':False,'percentile':False,'avg_adjust':False},}
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
|
| 357 |
+
test_df = exit_velo_df.sort_values(by='batter_name').drop_duplicates(subset='batter_id').reset_index(drop=True)[['batter_id','batter_name']]#['pitcher'].to_dict()
|
| 358 |
+
test_df = test_df.dropna()
|
| 359 |
+
test_df['batter_id'] = test_df['batter_id'].astype(int)
|
| 360 |
+
test_df = test_df.set_index('batter_id')
|
| 361 |
+
#test_df = test_df[test_df.pitcher == 'Chris Bassitt'].append(test_df[test_df.pitcher != 'Chris Bassitt'])
|
| 362 |
+
|
| 363 |
+
batter_dict = test_df['batter_name'].to_dict()
|
| 364 |
+
|
| 365 |
+
level_dict = {'MLB':'MLB','AAA':'AAA','AA':'AA','A+':'A+','A':'A'}
|
| 366 |
+
|
| 367 |
+
plot_dict_small = {
|
| 368 |
+
'k':'K%',
|
| 369 |
+
'bb':'BB%',
|
| 370 |
+
'csw':'CSW%',
|
| 371 |
+
'launch_speed':'Exit Velocity',
|
| 372 |
+
'launch_speed_90':'90th Percentile Exit Velocity',
|
| 373 |
+
'sweet_spot':'SweetSpot%',
|
| 374 |
+
'launch_angle':'Launch Angle',
|
| 375 |
+
'zone_percent':'Zone%',
|
| 376 |
+
'barrel':'Barrel%',
|
| 377 |
+
'swing_percent':'Swing%',
|
| 378 |
+
'whiff_percent':'Whiff%',
|
| 379 |
+
'sw_str':'SwStr%',
|
| 380 |
+
'zone_swing':'Z-Swing%',
|
| 381 |
+
'zone_contact':'Z-Contact%',
|
| 382 |
+
'chase_percent':'O-Swing%',
|
| 383 |
+
'chase_contact':'O-Contact%',}
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
def server(input,output,session):
|
| 387 |
+
|
| 388 |
+
@output
|
| 389 |
+
@render.plot(alt="A histogram")
|
| 390 |
+
@reactive.event(input.go, ignore_none=False)
|
| 391 |
+
def plot():
|
| 392 |
+
# np.random.seed(19680801)
|
| 393 |
+
# x = 100 + 15 * np.random.randn(437)
|
| 394 |
+
# fig, ax = plt.subplots()
|
| 395 |
+
# ax.hist(x, input.n(), density=True)
|
| 396 |
+
# return fig
|
| 397 |
+
sns.set_theme(style="whitegrid", palette="pastel")
|
| 398 |
+
if input.id() is "":
|
| 399 |
+
fig = plt.figure(figsize=(12, 12))
|
| 400 |
+
fig.text(s='Please Select a Pitcher',x=0.5,y=0.5)
|
| 401 |
+
return
|
| 402 |
+
|
| 403 |
+
swing_min = int(input.n())
|
| 404 |
+
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
|
| 405 |
+
|
| 406 |
+
fig.set_facecolor('white')
|
| 407 |
+
#ax.set_facecolor('white')
|
| 408 |
+
#fig.patch.set_facecolor('lightblue')
|
| 409 |
+
|
| 410 |
+
print(input.stat_id())
|
| 411 |
+
|
| 412 |
+
if input.stat_id() in pa_list:
|
| 413 |
+
print('we hAVE MADE IT TO THIS PART OF THE CODE')
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
if input.stat_id() in pa_list:
|
| 417 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.pa==1)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 418 |
+
divisor_x = 'pa'
|
| 419 |
+
print('this is short')
|
| 420 |
+
print(elly_zone_df)
|
| 421 |
+
|
| 422 |
+
|
| 423 |
+
if input.stat_id() in balls_in_play_list:
|
| 424 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.bip)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 425 |
+
divisor_x = 'bip'
|
| 426 |
+
#print('this is short')
|
| 427 |
+
|
| 428 |
+
if input.stat_id() in balls_in_play_list:
|
| 429 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.bip)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 430 |
+
divisor_x = 'bip'
|
| 431 |
+
print('this is short')
|
| 432 |
+
|
| 433 |
+
if input.stat_id() in pitches_list:
|
| 434 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.pitches == 1)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 435 |
+
divisor_x = 'pitches'
|
| 436 |
+
|
| 437 |
+
if input.stat_id() in swings_list:
|
| 438 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.swings == 1)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 439 |
+
divisor_x = 'swings'
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
if input.stat_id() in in_zone_pitches_list:
|
| 443 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.in_zone)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 444 |
+
divisor_x = 'in_zone'
|
| 445 |
+
|
| 446 |
+
|
| 447 |
+
if input.stat_id() in in_zone_swings_list:
|
| 448 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.zone_swing)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 449 |
+
divisor_x = 'zone_swing'
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
if input.stat_id() in out_zone_pitches_list:
|
| 453 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.in_zone == False)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 454 |
+
divisor_x = 'out_zone'
|
| 455 |
+
|
| 456 |
+
|
| 457 |
+
if input.stat_id() in out_zone_swings_list:
|
| 458 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.ozone_swing)&(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 459 |
+
divisor_x = 'ozone_swing'
|
| 460 |
+
|
| 461 |
+
# penguins = sns.load_dataset("penguins")
|
| 462 |
+
# sns.histplot(data=penguins, x="flipper_length_mm")
|
| 463 |
+
# print('we made it here:')
|
| 464 |
+
# print(int(input.id()))
|
| 465 |
+
# print(input.stat_id())
|
| 466 |
+
# print(input.level_id())
|
| 467 |
+
# print(exit_velo_df_codes[(exit_velo_df_codes.batter_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())])
|
| 468 |
+
# print(exit_velo_df_codes.columns)
|
| 469 |
+
# print(elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum())
|
| 470 |
+
|
| 471 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ.copy()
|
| 472 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.set_index('batter_id','batter_name','level')
|
| 473 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new[divisor_x] >= int(input.n())]
|
| 474 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.level==input.level_id()]
|
| 475 |
+
|
| 476 |
+
exit_velo_df_codes_summ_rank = exit_velo_df_codes_summ_new.rank(method='max',ascending=False)
|
| 477 |
+
exit_velo_df_codes_summ_rank.columns = exit_velo_df_codes_summ_rank.columns+['_rank']
|
| 478 |
+
|
| 479 |
+
exit_velo_df_codes_summ_rank_percent = exit_velo_df_codes_summ_new.rank(pct=True)
|
| 480 |
+
exit_velo_df_codes_summ_rank_percent.columns = exit_velo_df_codes_summ_rank_percent.columns+['_percent']
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.reset_index()
|
| 484 |
+
exit_velo_df_codes_summ_rank = exit_velo_df_codes_summ_rank.reset_index()
|
| 485 |
+
exit_velo_df_codes_summ_rank_percent = exit_velo_df_codes_summ_rank_percent.reset_index()
|
| 486 |
+
print('Table columns:')
|
| 487 |
+
|
| 488 |
+
exit_velo_df_codes_summ_new.batter_id = exit_velo_df_codes_summ_new.batter_id.astype(int)
|
| 489 |
+
exit_velo_df_codes_summ_rank.batter_id = exit_velo_df_codes_summ_rank.batter_id.astype(int)
|
| 490 |
+
exit_velo_df_codes_summ_rank_percent.batter_id = exit_velo_df_codes_summ_rank_percent.batter_id.astype(int)
|
| 491 |
+
|
| 492 |
+
print('Table columns2:')
|
| 493 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.merge(exit_velo_df_codes_summ_rank,left_on=['batter_id'],right_on=['batter_id'],how='left',suffixes=['','_rank'])
|
| 494 |
+
|
| 495 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.merge(exit_velo_df_codes_summ_rank_percent,left_on=['batter_id'],right_on=['batter_id'],how='left',suffixes=['','_percent'])
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
print(exit_velo_df_codes_summ_new)
|
| 499 |
+
print(exit_velo_df_codes_summ_rank)
|
| 500 |
+
print(exit_velo_df_codes_summ_rank_percent)
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
|
| 504 |
+
|
| 505 |
+
#sns.scatterplot(x=data_df.launch_speed_90,y=data_df.zone_contact,color=colour_palette[0],s=75,label=int(input.id()))
|
| 506 |
+
|
| 507 |
+
exit_velo_df_codes_summ_new_select = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.batter_id == int(input.id())].reset_index(drop=True)
|
| 508 |
+
print('whiffing')
|
| 509 |
+
print(exit_velo_df_codes)
|
| 510 |
+
print('Player _df:')
|
| 511 |
+
print(exit_velo_df_codes_summ_new_select)
|
| 512 |
+
|
| 513 |
+
if len(exit_velo_df_codes_summ_new_select) < 1:
|
| 514 |
+
ax.text(x=0.5,y=0.5,s='Please Select Different Parameters to Produce a plot',fontsize=18,ha='center')
|
| 515 |
+
return
|
| 516 |
+
|
| 517 |
+
p = inflect.engine()
|
| 518 |
+
|
| 519 |
+
exit_velo_df_codes_summ_new_select = exit_velo_df_codes_summ_new_select.loc[:,~exit_velo_df_codes_summ_new_select.columns.duplicated(keep='last')].copy()
|
| 520 |
+
print('Table for the player:')
|
| 521 |
+
print(list(exit_velo_df_codes_summ_new_select.columns))
|
| 522 |
+
print(plot_dict[input.stat_id()]["percentile_label"])
|
| 523 |
+
print(plot_dict[input.stat_id()]["percentile_label"]+'_percent')
|
| 524 |
+
print(exit_velo_df_codes_summ_new_select)
|
| 525 |
+
print(1*plot_dict[input.stat_id()]["flip_p"])
|
| 526 |
+
print(round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))
|
| 527 |
+
print((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)
|
| 528 |
+
|
| 529 |
+
# print(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+'_percent'])
|
| 530 |
+
|
| 531 |
+
if plot_dict[input.stat_id()]['percent']:
|
| 532 |
+
label_1=f'{input.level_id()} Average {exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].sum()/exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][divisor_x].sum():.1%}'
|
| 533 |
+
label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1%} ({p.ordinal(abs(int((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)))} Percentile)'
|
| 534 |
+
#label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1%}'
|
| 535 |
+
ax.yaxis.set_major_formatter(mtick.PercentFormatter(1))
|
| 536 |
+
|
| 537 |
+
else:
|
| 538 |
+
label_1=f'{input.level_id()} Average {exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].sum()/exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][divisor_x].sum():.1f}'
|
| 539 |
+
label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1f} ({p.ordinal(abs(int((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)))} Percentile)'
|
| 540 |
+
#label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1f}'
|
| 541 |
+
#ax.yaxis.set_major_formatter(mtick.int)
|
| 542 |
+
|
| 543 |
+
|
| 544 |
+
if plot_dict[input.stat_id()]['percentile']:
|
| 545 |
+
label_1=f'{input.level_id()} Average {exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].quantile(0.9):.1f}'
|
| 546 |
+
label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].quantile(0.9):.1f} ({p.ordinal(abs(int((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)))} Percentile)'
|
| 547 |
+
#label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1%}'
|
| 548 |
+
#ax.yaxis.set_major_formatter(mtick.int)
|
| 549 |
+
|
| 550 |
+
|
| 551 |
+
print(plot_dict[input.stat_id()]["x_value"])
|
| 552 |
+
print(divisor_x)
|
| 553 |
+
|
| 554 |
+
# exit_velo_df_codes_summ_new = exit_velo_df_codes_summ.copy()
|
| 555 |
+
# exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.balls_in_play >= int(input.n())]
|
| 556 |
+
# exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.level==input.level_id()]
|
| 557 |
+
|
| 558 |
+
|
| 559 |
+
print('this is here:')
|
| 560 |
+
print(exit_velo_df_codes_summ_new.head())
|
| 561 |
+
print(exit_velo_df_codes_summ_new.columns)
|
| 562 |
+
|
| 563 |
+
|
| 564 |
+
if plot_dict[input.stat_id()]["flip_p"] == False:
|
| 565 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[2],linestyle='dotted',alpha=0.5)
|
| 566 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[3],linestyle='dotted',alpha=0.5)
|
| 567 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[4],linestyle='dotted',alpha=0.5)
|
| 568 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[5],linestyle='dotted',alpha=0.5)
|
| 569 |
+
|
| 570 |
+
|
| 571 |
+
hard_hit_dates = [(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),
|
| 572 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),
|
| 573 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),
|
| 574 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1)]
|
| 575 |
+
hard_hit_text = ['90th %','75th %','25th %','10th %']
|
| 576 |
+
for i, x in enumerate(hard_hit_dates):
|
| 577 |
+
text(min(input.n()+input.n()/100,+input.n()+1), x ,hard_hit_text[i], rotation=0, ha='left',
|
| 578 |
+
bbox=dict(facecolor='white',alpha=0.5, edgecolor=colour_palette[2+i], pad=2))
|
| 579 |
+
|
| 580 |
+
|
| 581 |
+
|
| 582 |
+
if plot_dict[input.stat_id()]["flip_p"] == True:
|
| 583 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[2],linestyle='dotted',alpha=0.5)
|
| 584 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[3],linestyle='dotted',alpha=0.5)
|
| 585 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[4],linestyle='dotted',alpha=0.5)
|
| 586 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[5],linestyle='dotted',alpha=0.5)
|
| 587 |
+
|
| 588 |
+
hard_hit_dates = [(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),
|
| 589 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),
|
| 590 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),
|
| 591 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1)]
|
| 592 |
+
hard_hit_text = ['10th %','25th %','75th %','90th %']
|
| 593 |
+
for i, x in enumerate(hard_hit_dates):
|
| 594 |
+
text(min(input.n()+input.n()/100,input.n()+input.n()+3), x ,hard_hit_text[i], rotation=0, ha='left',
|
| 595 |
+
bbox=dict(facecolor='white',alpha=0.5, edgecolor=colour_palette[2+i], pad=2))
|
| 596 |
+
|
| 597 |
+
|
| 598 |
+
|
| 599 |
+
|
| 600 |
+
|
| 601 |
+
|
| 602 |
+
if plot_dict[input.stat_id()]["percentile"] == False:
|
| 603 |
+
ax.hlines(y=exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].sum()/exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][divisor_x].sum(),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[1],linestyle='-.',label=label_1)
|
| 604 |
+
|
| 605 |
+
ax.hlines(y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum(),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[0],linestyle='--',label=label_2)
|
| 606 |
+
|
| 607 |
+
sns.lineplot(x=range(1,len(elly_zone_df)+1),y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].fillna(0).rolling(window=swing_min).sum()/swing_min,color=colour_palette[0],linewidth=3,ax=ax)
|
| 608 |
+
|
| 609 |
+
|
| 610 |
+
|
| 611 |
+
if plot_dict[input.stat_id()]["percentile"] == True:
|
| 612 |
+
|
| 613 |
+
ax.hlines(y=exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[1],linestyle='-.',label=label_1)
|
| 614 |
+
|
| 615 |
+
ax.hlines(y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].fillna(0).quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[0],linestyle='--',label=label_2)
|
| 616 |
+
|
| 617 |
+
sns.lineplot(x=range(1,len(elly_zone_df)+1),y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].fillna(0).rolling(window=swing_min).quantile(0.9),color=colour_palette[0],linewidth=3,ax=ax)
|
| 618 |
+
|
| 619 |
+
|
| 620 |
+
#ax.set_xlim(input.n(),exit_velo_df_small.pitch.max())
|
| 621 |
+
#plt.yticks([0,0.2,0.4,0.6,0.8,1])
|
| 622 |
+
#ax.set_ylim(math.floor((min(exit_velo_df_codes_summ.zone_contact)/5)*100)*5/100,1)
|
| 623 |
+
ax.set_xlim(math.floor(swing_min),len(elly_zone_df))
|
| 624 |
+
ax.set_title(f'{batter_dict[int(input.id())]} - {input.level_id()} - {swing_min} {plot_dict[input.stat_id()]["x_axis"]} Rolling {plot_dict[input.stat_id()]["title"]}', fontsize=16,fontname='Century Gothic',)
|
| 625 |
+
#vals = ax.get_yticks()
|
| 626 |
+
ax.set_xlabel(plot_dict[input.stat_id()]['x_axis'], fontsize=16,fontname='Century Gothic')
|
| 627 |
+
ax.set_ylabel(plot_dict[input.stat_id()]['y_axis'], fontsize=16,fontname='Century Gothic')
|
| 628 |
+
|
| 629 |
+
#fig.axes[0].invert_yaxis()
|
| 630 |
+
|
| 631 |
+
#fig.subplots_adjust(wspace=.02, hspace=.02)
|
| 632 |
+
#ax.xaxis.set_major_formatter(FuncFormatter(lambda x, _: int(x)))
|
| 633 |
+
ax.set_yticks(plot_dict[input.stat_id()]["x_range"])
|
| 634 |
+
#fig.colorbar(plot_dist, ax=ax)
|
| 635 |
+
#fig.colorbar(plot_dist)
|
| 636 |
+
#fig.axes[0].invert_yaxis()
|
| 637 |
+
ax.legend(fontsize='16')
|
| 638 |
+
fig.text(x=0.03,y=0.02,s='By: @TJStats',fontname='Century Gothic')
|
| 639 |
+
fig.text(x=1-0.03,y=0.02,s='Data: MLB',ha='right',fontname='Century Gothic')
|
| 640 |
+
fig.tight_layout()
|
| 641 |
+
|
| 642 |
+
|
| 643 |
+
rolling_batter = App(ui.page_fluid(
|
| 644 |
+
ui.tags.base(href=base_url),
|
| 645 |
+
ui.tags.div(
|
| 646 |
+
{"style": "width:90%;margin: 0 auto;max-width: 1600px;"},
|
| 647 |
+
ui.tags.style(
|
| 648 |
+
"""
|
| 649 |
+
h4 {
|
| 650 |
+
margin-top: 1em;font-size:35px;
|
| 651 |
+
}
|
| 652 |
+
h2{
|
| 653 |
+
font-size:25px;
|
| 654 |
+
}
|
| 655 |
+
"""
|
| 656 |
+
),
|
| 657 |
+
shinyswatch.theme.simplex(),
|
| 658 |
+
ui.tags.h4("TJStats"),
|
| 659 |
+
ui.tags.i("Baseball Analytics and Visualizations"),
|
| 660 |
+
ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),
|
| 661 |
+
ui.navset_tab(
|
| 662 |
+
ui.nav_control(
|
| 663 |
+
ui.a(
|
| 664 |
+
"Home",
|
| 665 |
+
href="home/"
|
| 666 |
+
),
|
| 667 |
+
),
|
| 668 |
+
ui.nav_menu(
|
| 669 |
+
"Batter Charts",
|
| 670 |
+
ui.nav_control(
|
| 671 |
+
ui.a(
|
| 672 |
+
"Batting Rolling",
|
| 673 |
+
href="rolling_batter/"
|
| 674 |
+
),
|
| 675 |
+
ui.a(
|
| 676 |
+
"Spray",
|
| 677 |
+
href="spray/"
|
| 678 |
+
),
|
| 679 |
+
ui.a(
|
| 680 |
+
"Decision Value",
|
| 681 |
+
href="decision_value/"
|
| 682 |
+
),
|
| 683 |
+
ui.a(
|
| 684 |
+
"Damage Model",
|
| 685 |
+
href="damage_model/"
|
| 686 |
+
),
|
| 687 |
+
ui.a(
|
| 688 |
+
"Batter Scatter",
|
| 689 |
+
href="batter_scatter/"
|
| 690 |
+
),
|
| 691 |
+
ui.a(
|
| 692 |
+
"EV vs LA Plot",
|
| 693 |
+
href="ev_angle/"
|
| 694 |
+
),
|
| 695 |
+
ui.a(
|
| 696 |
+
"Statcast Compare",
|
| 697 |
+
href="statcast_compare/"
|
| 698 |
+
)
|
| 699 |
+
),
|
| 700 |
+
),
|
| 701 |
+
ui.nav_menu(
|
| 702 |
+
"Pitcher Charts",
|
| 703 |
+
ui.nav_control(
|
| 704 |
+
ui.a(
|
| 705 |
+
"Pitcher Rolling",
|
| 706 |
+
href="rolling_pitcher/"
|
| 707 |
+
),
|
| 708 |
+
ui.a(
|
| 709 |
+
"Pitcher Summary",
|
| 710 |
+
href="pitching_summary_graphic_new/"
|
| 711 |
+
),
|
| 712 |
+
ui.a(
|
| 713 |
+
"Pitcher Scatter",
|
| 714 |
+
href="pitcher_scatter/"
|
| 715 |
+
)
|
| 716 |
+
),
|
| 717 |
+
)),ui.row(
|
| 718 |
+
ui.layout_sidebar(
|
| 719 |
+
|
| 720 |
+
ui.panel_sidebar(
|
| 721 |
+
ui.input_select("id", "Select Pitcher",batter_dict,selected=675911,width=1,size=1,selectize=True),
|
| 722 |
+
ui.input_select("level_id", "Select Level",level_dict,width=1,size=1),
|
| 723 |
+
ui.input_select("stat_id", "Select Stat",plot_dict_small,width=1,size=1),
|
| 724 |
+
ui.input_numeric("n", "Rolling Window Size", value=50),
|
| 725 |
+
ui.input_action_button("go", "Generate",class_="btn-primary"),
|
| 726 |
+
ui.output_table("result")
|
| 727 |
+
),
|
| 728 |
+
|
| 729 |
+
ui.panel_main(
|
| 730 |
+
ui.output_plot("plot",height = "1000px",width="1000px")
|
| 731 |
+
),
|
| 732 |
+
)),)),server)
|
rolling_pitcher.py
ADDED
|
@@ -0,0 +1,719 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui
|
| 2 |
+
import datasets
|
| 3 |
+
from datasets import load_dataset
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import numpy as np
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
import seaborn as sns
|
| 8 |
+
import numpy as np
|
| 9 |
+
from scipy.stats import gaussian_kde
|
| 10 |
+
import matplotlib
|
| 11 |
+
from matplotlib.ticker import MaxNLocator
|
| 12 |
+
from matplotlib.gridspec import GridSpec
|
| 13 |
+
from scipy.stats import zscore
|
| 14 |
+
import math
|
| 15 |
+
import matplotlib
|
| 16 |
+
from adjustText import adjust_text
|
| 17 |
+
import matplotlib.ticker as mtick
|
| 18 |
+
from shinywidgets import output_widget, render_widget
|
| 19 |
+
import pandas as pd
|
| 20 |
+
from configure import base_url
|
| 21 |
+
import shinyswatch
|
| 22 |
+
import inflect
|
| 23 |
+
from matplotlib.pyplot import text
|
| 24 |
+
|
| 25 |
+
def percentile(n):
|
| 26 |
+
def percentile_(x):
|
| 27 |
+
return np.nanpercentile(x, n)
|
| 28 |
+
percentile_.__name__ = 'percentile_%s' % n
|
| 29 |
+
return percentile_
|
| 30 |
+
|
| 31 |
+
colour_palette = ['#FFB000','#648FFF','#785EF0',
|
| 32 |
+
'#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
print('Starting Everything:')
|
| 37 |
+
# exit_velo_df = milb_a_ev_df.append([triple_a_ev_df,double_a_ev_df,a_high_a_ev_df,single_a_ev_df]).reset_index(drop=True)
|
| 38 |
+
# player_df_all = mlb_a_player_df.append([triple_a_player_df,double_a_player_df,a_high_a_player_df,single_a_player_df]).reset_index(drop=True)
|
| 39 |
+
# exit_velo_df = pd.read_csv('exit_velo_df_all.csv',index_col=[0])
|
| 40 |
+
# player_df_all = pd.read_csv('player_df_all.csv',index_col=[0])
|
| 41 |
+
|
| 42 |
+
# pa_df = pd.read_csv('pa_df_all.csv',index_col=[0])
|
| 43 |
+
# pa_df_full_na = pa_df.dropna()
|
| 44 |
+
|
| 45 |
+
### Import Datasets
|
| 46 |
+
dataset = load_dataset('nesticot/mlb_data', data_files=['mlb_pitch_data_2023.csv',
|
| 47 |
+
])
|
| 48 |
+
dataset_train = dataset['train']
|
| 49 |
+
exit_velo_df_mlb = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)
|
| 50 |
+
#print(df_2023)
|
| 51 |
+
exit_velo_df_mlb['level'] = 'MLB'
|
| 52 |
+
|
| 53 |
+
### Import Datasets
|
| 54 |
+
dataset = load_dataset('nesticot/mlb_data', data_files=['aaa_pitch_data_2023.csv',
|
| 55 |
+
])
|
| 56 |
+
dataset_train = dataset['train']
|
| 57 |
+
exit_velo_df_aaa = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)
|
| 58 |
+
#print(df_2023)
|
| 59 |
+
exit_velo_df_aaa['level'] = 'AAA'
|
| 60 |
+
|
| 61 |
+
### Import Datasets
|
| 62 |
+
dataset = load_dataset('nesticot/mlb_data', data_files=['aa_pitch_data_2023.csv',
|
| 63 |
+
])
|
| 64 |
+
dataset_train = dataset['train']
|
| 65 |
+
exit_velo_df_aa = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)
|
| 66 |
+
#print(df_2023)
|
| 67 |
+
exit_velo_df_aa['level'] = 'AA'
|
| 68 |
+
|
| 69 |
+
### Import Datasets
|
| 70 |
+
dataset = load_dataset('nesticot/mlb_data', data_files=['high_a_pitch_data_2023.csv',
|
| 71 |
+
])
|
| 72 |
+
dataset_train = dataset['train']
|
| 73 |
+
exit_velo_df_ha = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)
|
| 74 |
+
#print(df_2023)
|
| 75 |
+
exit_velo_df_ha['level'] = 'A+'
|
| 76 |
+
|
| 77 |
+
### Import Datasets
|
| 78 |
+
dataset = load_dataset('nesticot/mlb_data', data_files=['a_pitch_data_2023.csv',
|
| 79 |
+
])
|
| 80 |
+
dataset_train = dataset['train']
|
| 81 |
+
exit_velo_df_a = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)
|
| 82 |
+
#print(df_2023)
|
| 83 |
+
exit_velo_df_a['level'] = 'A'
|
| 84 |
+
|
| 85 |
+
exit_velo_df = pd.concat([exit_velo_df_mlb,exit_velo_df_aaa,exit_velo_df_aa,exit_velo_df_ha,exit_velo_df_a])
|
| 86 |
+
# exit_velo_df_copy = exit_velo_df.copy()
|
| 87 |
+
|
| 88 |
+
# exit_velo_df = exit_velo_df_copy.copy()
|
| 89 |
+
|
| 90 |
+
end_codes = ['strikeout', 'field_out', 'single', 'walk', 'hit_by_pitch',
|
| 91 |
+
'double', 'sac_fly', 'force_out', 'home_run',
|
| 92 |
+
'grounded_into_double_play', 'fielders_choice', 'field_error',
|
| 93 |
+
'triple', 'sac_bunt', 'double_play', 'intent_walk',
|
| 94 |
+
'fielders_choice_out', 'strikeout_double_play',
|
| 95 |
+
'sac_fly_double_play', 'catcher_interf', 'other_out']
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
exit_velo_df['pa'] = exit_velo_df.event_type.isin(end_codes)
|
| 100 |
+
#exit_velo_df['pa'] = 1
|
| 101 |
+
exit_velo_df['k'] = exit_velo_df.event_type.isin(list(filter(None, [x if 'strikeout' in x else '' for x in exit_velo_df.event_type.fillna('None').unique()])))
|
| 102 |
+
exit_velo_df['bb'] = exit_velo_df.event_type.isin(list(filter(None, [x if 'walk' in x else '' for x in exit_velo_df.event_type.fillna('None').unique()])))
|
| 103 |
+
exit_velo_df['k_minus_bb'] = exit_velo_df['k'].astype(np.float32)-exit_velo_df['bb'].astype(np.float32)
|
| 104 |
+
|
| 105 |
+
exit_velo_df = exit_velo_df.drop_duplicates(subset=['play_id'])
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
swing_codes = ['Swinging Strike', 'In play, no out',
|
| 110 |
+
'Foul', 'In play, out(s)',
|
| 111 |
+
'In play, run(s)', 'Swinging Strike (Blocked)',
|
| 112 |
+
'Foul Bunt','Foul Tip', 'Missed Bunt','Foul Pitchout','Swinging Pitchout']
|
| 113 |
+
|
| 114 |
+
swings_in = ['Swinging Strike', 'In play, no out',
|
| 115 |
+
'Foul', 'In play, out(s)',
|
| 116 |
+
'In play, run(s)', 'Swinging Strike (Blocked)',
|
| 117 |
+
'Foul Bunt','Foul Tip', 'Missed Bunt','Foul Pitchout','Swinging Pitchout']
|
| 118 |
+
|
| 119 |
+
swing_strike_codes = ['Swinging Strike',
|
| 120 |
+
'Swinging Strike (Blocked)','Missed Bunt','Foul Tip','Swinging Pitchout']
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
contact_codes = ['In play, no out',
|
| 124 |
+
'Foul', 'In play, out(s)',
|
| 125 |
+
'In play, run(s)',
|
| 126 |
+
'Foul Bunt']
|
| 127 |
+
|
| 128 |
+
codes_in = ['In play, out(s)',
|
| 129 |
+
'Swinging Strike',
|
| 130 |
+
'Ball',
|
| 131 |
+
'Foul',
|
| 132 |
+
'In play, no out',
|
| 133 |
+
'Called Strike',
|
| 134 |
+
'Foul Tip',
|
| 135 |
+
'In play, run(s)',
|
| 136 |
+
'Hit By Pitch',
|
| 137 |
+
'Ball In Dirt',
|
| 138 |
+
'Pitchout',
|
| 139 |
+
'Swinging Strike (Blocked)',
|
| 140 |
+
'Foul Bunt',
|
| 141 |
+
'Missed Bunt',
|
| 142 |
+
'Foul Pitchout',
|
| 143 |
+
'Intent Ball',
|
| 144 |
+
'Swinging Pitchout']
|
| 145 |
+
|
| 146 |
+
exit_velo_df['in_zone'] = exit_velo_df['zone'] < 10
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
exit_velo_df = exit_velo_df.drop_duplicates(subset=['play_id'])
|
| 150 |
+
|
| 151 |
+
exit_velo_df_codes = exit_velo_df[exit_velo_df.play_description.isin(codes_in)].dropna(subset=['in_zone'])
|
| 152 |
+
|
| 153 |
+
exit_velo_df_codes['bip'] = ~exit_velo_df_codes.launch_speed.isna()
|
| 154 |
+
conditions = [
|
| 155 |
+
(exit_velo_df_codes['launch_speed'].isna()),
|
| 156 |
+
(exit_velo_df_codes['launch_speed']*1.5 - exit_velo_df_codes['launch_angle'] >= 117 ) & (exit_velo_df_codes['launch_speed'] + exit_velo_df_codes['launch_angle'] >= 124) & (exit_velo_df_codes['launch_speed'] > 98) & (exit_velo_df_codes['launch_angle'] >= 8) & (exit_velo_df_codes['launch_angle'] <= 50)
|
| 157 |
+
]
|
| 158 |
+
|
| 159 |
+
choices = [False,True]
|
| 160 |
+
exit_velo_df_codes['barrel'] = np.select(conditions, choices, default=np.nan)
|
| 161 |
+
|
| 162 |
+
conditions_ss = [
|
| 163 |
+
(exit_velo_df_codes['launch_angle'].isna()),
|
| 164 |
+
(exit_velo_df_codes['launch_angle'] >= 8 ) * (exit_velo_df_codes['launch_angle'] <= 32 )
|
| 165 |
+
]
|
| 166 |
+
|
| 167 |
+
choices_ss = [False,True]
|
| 168 |
+
exit_velo_df_codes['sweet_spot'] = np.select(conditions_ss, choices_ss, default=np.nan)
|
| 169 |
+
conditions_hh = [
|
| 170 |
+
(exit_velo_df_codes['launch_speed'].isna()),
|
| 171 |
+
(exit_velo_df_codes['launch_speed'] >= 94.5 )
|
| 172 |
+
]
|
| 173 |
+
|
| 174 |
+
choices_hh = [False,True]
|
| 175 |
+
exit_velo_df_codes['hard_hit'] = np.select(conditions_hh, choices_hh, default=np.nan)
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
conditions_tb = [
|
| 179 |
+
(exit_velo_df_codes['event_type']=='single'),
|
| 180 |
+
(exit_velo_df_codes['event_type']=='double'),
|
| 181 |
+
(exit_velo_df_codes['event_type']=='triple'),
|
| 182 |
+
(exit_velo_df_codes['event_type']=='home_run'),
|
| 183 |
+
]
|
| 184 |
+
|
| 185 |
+
choices_tb = [1,2,3,4]
|
| 186 |
+
|
| 187 |
+
exit_velo_df_codes['tb'] = np.select(conditions_tb, choices_tb, default=np.nan)
|
| 188 |
+
|
| 189 |
+
conditions_woba = [
|
| 190 |
+
(exit_velo_df_codes['event_type']=='walk'),
|
| 191 |
+
(exit_velo_df_codes['event_type']=='hit_by_pitch'),
|
| 192 |
+
(exit_velo_df_codes['event_type']=='single'),
|
| 193 |
+
(exit_velo_df_codes['event_type']=='double'),
|
| 194 |
+
(exit_velo_df_codes['event_type']=='triple'),
|
| 195 |
+
(exit_velo_df_codes['event_type']=='home_run'),
|
| 196 |
+
]
|
| 197 |
+
|
| 198 |
+
choices_woba = [0.705,
|
| 199 |
+
0.688,
|
| 200 |
+
0.897,
|
| 201 |
+
1.233,
|
| 202 |
+
1.612,
|
| 203 |
+
2.013]
|
| 204 |
+
|
| 205 |
+
exit_velo_df_codes['woba'] = np.select(conditions_woba, choices_woba, default=np.nan)
|
| 206 |
+
|
| 207 |
+
|
| 208 |
+
woba_codes = ['strikeout', 'field_out', 'single', 'walk', 'hit_by_pitch',
|
| 209 |
+
'double', 'sac_fly', 'force_out', 'home_run',
|
| 210 |
+
'grounded_into_double_play', 'fielders_choice', 'field_error',
|
| 211 |
+
'triple', 'sac_bunt', 'double_play',
|
| 212 |
+
'fielders_choice_out', 'strikeout_double_play',
|
| 213 |
+
'sac_fly_double_play', 'other_out']
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
conditions_woba_code = [
|
| 220 |
+
(exit_velo_df_codes['event_type'].isin(woba_codes))
|
| 221 |
+
]
|
| 222 |
+
|
| 223 |
+
choices_woba_code = [1]
|
| 224 |
+
|
| 225 |
+
exit_velo_df_codes['woba_codes'] = np.select(conditions_woba_code, choices_woba_code, default=np.nan)
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
#exit_velo_df_codes['barrel'] = (exit_velo_df_codes.launch_speed >= 98) & (exit_velo_df_codes.launch_angle >= (26 - (-98 + exit_velo_df_codes.launch_speed))) & (exit_velo_df_codes.launch_angle <= 30 + (-98 + exit_velo_df_codes.launch_speed)) & (exit_velo_df_codes.launch_angle >= 8) & (exit_velo_df_codes.launch_angle <= 50)
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
#exit_velo_df_codes['barrel'] = (exit_velo_df_codes.launch_speed >= 98) & (exit_velo_df_codes.launch_angle >= (26 - (-98 + exit_velo_df_codes.launch_speed))) & (exit_velo_df_codes.launch_angle <= 30 + (-98 + exit_velo_df_codes.launch_speed)) & (exit_velo_df_codes.launch_angle >= 8) & (exit_velo_df_codes.launch_angle <= 50)
|
| 233 |
+
exit_velo_df_codes['pitches'] = 1
|
| 234 |
+
exit_velo_df_codes['whiffs'] = [1 if ((x == 'S')|(x == 'W')|(x =='T')) else 0 for x in exit_velo_df_codes.play_code]
|
| 235 |
+
exit_velo_df_codes['csw'] = [1 if ((x == 'S')|(x == 'W')|(x =='T')|(x == 'C')) else 0 for x in exit_velo_df_codes.play_code]
|
| 236 |
+
exit_velo_df_codes['swings'] = [1 if x in swings_in else 0 for x in exit_velo_df_codes.play_description]
|
| 237 |
+
|
| 238 |
+
exit_velo_df_codes['out_zone'] = exit_velo_df_codes.in_zone == False
|
| 239 |
+
exit_velo_df_codes['zone_swing'] = (exit_velo_df_codes.in_zone == True)&(exit_velo_df_codes.swings == 1)
|
| 240 |
+
exit_velo_df_codes['zone_contact'] = (exit_velo_df_codes.in_zone == True)&(exit_velo_df_codes.swings == 1)&(exit_velo_df_codes.whiffs == 0)
|
| 241 |
+
exit_velo_df_codes['ozone_swing'] = (exit_velo_df_codes.in_zone==False)&(exit_velo_df_codes.swings == 1)
|
| 242 |
+
exit_velo_df_codes['ozone_contact'] = (exit_velo_df_codes.in_zone==False)&(exit_velo_df_codes.swings == 1)&(exit_velo_df_codes.whiffs == 0)
|
| 243 |
+
|
| 244 |
+
exit_velo_df_codes_summ = exit_velo_df_codes.groupby(['pitcher_id','pitcher_name','level']).agg(
|
| 245 |
+
pa = ('pa','sum'),
|
| 246 |
+
k = ('k','sum'),
|
| 247 |
+
bb = ('bb','sum'),
|
| 248 |
+
k_minus_bb = ('k_minus_bb','sum'),
|
| 249 |
+
csw = ('csw','sum'),
|
| 250 |
+
bip = ('bip','sum'),
|
| 251 |
+
tb = ('tb','sum'),
|
| 252 |
+
woba = ('woba','sum'),
|
| 253 |
+
woba_codes = ('woba_codes','sum'),
|
| 254 |
+
hard_hit = ('hard_hit','sum'),
|
| 255 |
+
barrel = ('barrel','sum'),
|
| 256 |
+
sweet_spot = ('sweet_spot','sum'),
|
| 257 |
+
max_launch_speed = ('launch_speed','max'),
|
| 258 |
+
launch_speed_90 = ('launch_speed',percentile(90)),
|
| 259 |
+
launch_speed = ('launch_speed','mean'),
|
| 260 |
+
launch_angle = ('launch_angle','mean'),
|
| 261 |
+
pitches = ('pitches','sum'),
|
| 262 |
+
swings = ('swings','sum'),
|
| 263 |
+
in_zone = ('in_zone','sum'),
|
| 264 |
+
out_zone = ('out_zone','sum'),
|
| 265 |
+
whiffs = ('whiffs','sum'),
|
| 266 |
+
zone_swing = ('zone_swing','sum'),
|
| 267 |
+
zone_contact = ('zone_contact','sum'),
|
| 268 |
+
ozone_swing = ('ozone_swing','sum'),
|
| 269 |
+
ozone_contact = ('ozone_contact','sum'),
|
| 270 |
+
).reset_index()
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
exit_velo_df_codes_summ['k_percent'] = [exit_velo_df_codes_summ.k[x]/exit_velo_df_codes_summ.pa[x] if exit_velo_df_codes_summ.pa[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 275 |
+
exit_velo_df_codes_summ['bb_percent'] =[exit_velo_df_codes_summ.bb[x]/exit_velo_df_codes_summ.pa[x] if exit_velo_df_codes_summ.pa[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 276 |
+
exit_velo_df_codes_summ['k_minus_bb_percent'] =[exit_velo_df_codes_summ.k_minus_bb[x]/exit_velo_df_codes_summ.pa[x] if exit_velo_df_codes_summ.pa[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
exit_velo_df_codes_summ['sweet_spot_percent'] = [exit_velo_df_codes_summ.sweet_spot[x]/exit_velo_df_codes_summ.bip[x] if exit_velo_df_codes_summ.bip[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 280 |
+
|
| 281 |
+
exit_velo_df_codes_summ['woba_percent'] = [exit_velo_df_codes_summ.woba[x]/exit_velo_df_codes_summ.woba_codes[x] if exit_velo_df_codes_summ.woba_codes[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
exit_velo_df_codes_summ['hard_hit_percent'] = [exit_velo_df_codes_summ.hard_hit[x]/exit_velo_df_codes_summ.bip[x] if exit_velo_df_codes_summ.bip[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
exit_velo_df_codes_summ['csw_percent'] = [exit_velo_df_codes_summ.csw[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
exit_velo_df_codes_summ['barrel_percent'] = exit_velo_df_codes_summ.barrel = [exit_velo_df_codes_summ.barrel[x]/exit_velo_df_codes_summ.bip[x] if exit_velo_df_codes_summ.bip[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 292 |
+
|
| 293 |
+
exit_velo_df_codes_summ['zone_contact_percent'] = [exit_velo_df_codes_summ.zone_contact[x]/exit_velo_df_codes_summ.zone_swing[x] if exit_velo_df_codes_summ.zone_swing[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 294 |
+
|
| 295 |
+
exit_velo_df_codes_summ['zone_swing_percent'] = [exit_velo_df_codes_summ.zone_swing[x]/exit_velo_df_codes_summ.in_zone[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 296 |
+
|
| 297 |
+
exit_velo_df_codes_summ['zone_percent'] = [exit_velo_df_codes_summ.in_zone[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 298 |
+
|
| 299 |
+
exit_velo_df_codes_summ['chase_percent'] = [exit_velo_df_codes_summ.ozone_swing[x]/(exit_velo_df_codes_summ.pitches[x] - exit_velo_df_codes_summ.in_zone[x]) if (exit_velo_df_codes_summ.pitches[x]- exit_velo_df_codes_summ.in_zone[x]) != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 300 |
+
|
| 301 |
+
exit_velo_df_codes_summ['chase_contact'] = [exit_velo_df_codes_summ.ozone_contact[x]/exit_velo_df_codes_summ.ozone_swing[x] if exit_velo_df_codes_summ.ozone_swing[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 302 |
+
|
| 303 |
+
exit_velo_df_codes_summ['swing_percent'] = [exit_velo_df_codes_summ.swings[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 304 |
+
|
| 305 |
+
exit_velo_df_codes_summ['whiff_rate'] = [exit_velo_df_codes_summ.whiffs[x]/exit_velo_df_codes_summ.swings[x] if exit_velo_df_codes_summ.swings[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 306 |
+
|
| 307 |
+
exit_velo_df_codes_summ['swstr_rate'] = [exit_velo_df_codes_summ.whiffs[x]/exit_velo_df_codes_summ.pitches[x] if exit_velo_df_codes_summ.pitches[x] != 0 else np.nan for x in range(len(exit_velo_df_codes_summ))]
|
| 308 |
+
|
| 309 |
+
exit_velo_df_codes_summ = exit_velo_df_codes_summ.dropna(subset=['bip'])
|
| 310 |
+
|
| 311 |
+
print('whiffing')
|
| 312 |
+
print(exit_velo_df_codes_summ['whiff_rate'])
|
| 313 |
+
|
| 314 |
+
exit_velo_df_codes_summ.head()
|
| 315 |
+
|
| 316 |
+
woba_list = ['woba']
|
| 317 |
+
pa_list = ['k','bb','k_minus_bb','bb_minus_k']
|
| 318 |
+
balls_in_play_list = ['hard_hit','launch_speed','launch_speed_90','launch_angle','barrel','sweet_spot']
|
| 319 |
+
pitches_list = ['zone_percent','swing_percent','sw_str','csw']
|
| 320 |
+
swings_list = ['whiff_percent']
|
| 321 |
+
in_zone_pitches_list = ['zone_swing']
|
| 322 |
+
in_zone_swings_list = ['zone_contact']
|
| 323 |
+
out_zone_pitches_list = ['chase_percent']
|
| 324 |
+
out_zone_swings_list = ['chase_contact']
|
| 325 |
+
|
| 326 |
+
plot_dict = {
|
| 327 |
+
'k':{'x_axis':'Plate Appearances','y_axis':'K%','title':'K%','x_value':'k','x_range':[0.0,0.1,0.2,0.3,0.4],'percent':True,'percentile_label':'k_percent','flip_p':False,'percentile':False},
|
| 328 |
+
'bb':{'x_axis':'Plate Appearances','y_axis':'BB%','title':'BB%','x_value':'bb','x_range':[0.0,0.1,0.2,0.3],'percent':True,'percentile_label':'bb_percent','flip_p':True,'percentile':False},
|
| 329 |
+
'csw':{'x_axis':'Pitches','y_axis':'CSW%','title':'CSW%','x_value':'csw','x_range':[.2,.25,.3,.35,.4],'percent':True,'percentile_label':'csw_percent','flip_p':False,'percentile':False},
|
| 330 |
+
'launch_speed':{'x_axis':'Balls In Play','y_axis':'Exit Velocity','title':'Exit Velocity','x_value':'launch_speed','x_range':[85,90,95,100,105],'percent':True,'percentile_label':'launch_speed','flip_p':True,'percentile':False},
|
| 331 |
+
'launch_speed_90':{'x_axis':'Balls In Play','y_axis':'90th Percentile Exit Velocity','title':'90th Percentile Exit Velocity','x_value':'launch_speed','x_range':[90,95,100,105,110,115,120],'percent':False,'percentile_label':'launch_speed_90','flip_p':True,'percentile':True},
|
| 332 |
+
'sweet_spot':{'x_axis':'Balls In Play','y_axis':'SweetSpot%','title':'SweetSpot%','x_value':'sweet_spot','x_range':[0.2,0.3,0.4,0.5,0.6],'percent':True,'percentile_label':'sweet_spot_percent','flip_p':True,'percentile':False},
|
| 333 |
+
'launch_angle':{'x_axis':'Balls In Play','y_axis':'Launch Angle','title':'Launch Angle','x_value':'launch_angle','x_range':[-20,-10,0,10,20],'percent':False,'percentile_label':'launch_angle','flip_p':True,'percentile':False},
|
| 334 |
+
'barrel':{'x_axis':'Balls In Play','y_axis':'Barrel%','title':'Barrel%','x_value':'barrel','x_range':[0,0.05,0.10,.15,.20,.25,.30],'percent':True,'percentile_label':'barrel_percent','flip_p':False,'percentile':False},
|
| 335 |
+
'zone_percent':{'x_axis':'Pitches','y_axis':'Zone%','title':'Zone%','x_value':'in_zone','x_range':[0.3,0.4,0.5,0.6,0.7],'percent':True,'percentile_label':'zone_percent','flip_p':True,'percentile':False},
|
| 336 |
+
'swing_percent':{'x_axis':'Pitches','y_axis':'Swing%','title':'Swing%','x_value':'swings','x_range':[0.2,0.3,0.4,0.5,0.6,0.7,0.8],'percent':True,'percentile_label':'swing_percent','flip_p':False,'percentile':False},
|
| 337 |
+
'whiff_percent':{'x_axis':'Swings','y_axis':'Whiff%','title':'Whiff%','x_value':'whiffs','x_range':[0.0,0.1,0.2,0.3,0.4,0.5],'percent':True,'percentile_label':'whiff_rate','flip_p':False,'percentile':False},
|
| 338 |
+
'sw_str':{'x_axis':'Pitches','y_axis':'SwStr%','title':'SwStr%','x_value':'whiffs','x_range':[0.0,0.05,0.1,0.15,0.2,0.25],'percent':True,'percentile_label':'swstr_rate','flip_p':False,'percentile':False},
|
| 339 |
+
'zone_swing':{'x_axis':'In-Zone Pitches','y_axis':'Z-Swing%','title':'Z-Swing%','x_value':'zone_swing','x_range':[0.3,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1],'percent':True,'percentile_label':'zone_swing_percent','flip_p':True,'percentile':False},
|
| 340 |
+
'zone_contact':{'x_axis':'In-Zone Swings','y_axis':'Z-Contact%','title':'Z-Contact%','x_value':'zone_contact','x_range':[0.5,0.6,0.7,0.8,0.9,1],'percent':True,'percentile_label':'zone_contact_percent','flip_p':True,'percentile':False},
|
| 341 |
+
'chase_percent':{'x_axis':'Out-of-Zone Pitches','y_axis':'O-Swing%','title':'O-Swing%','x_value':'ozone_swing','x_range':[0.0,0.1,0.2,0.3,0.4,0.5],'percent':True,'percentile_label':'chase_percent','flip_p':False,'percentile':False},
|
| 342 |
+
'chase_contact':{'x_axis':'Out-of-Zone Swings','y_axis':'O-Contact%','title':'O-Contact%','x_value':'ozone_contact','x_range':[0.2,0.3,0.4,0.5,0.6,0.7,0.8],'percent':True,'percentile_label':'chase_contact','flip_p':True,'percentile':False},}
|
| 343 |
+
|
| 344 |
+
test_df = exit_velo_df.sort_values(by='pitcher_name').drop_duplicates(subset='pitcher_id').reset_index(drop=True)[['pitcher_id','pitcher_name']]#['pitcher'].to_dict()
|
| 345 |
+
test_df = test_df.set_index('pitcher_id')
|
| 346 |
+
|
| 347 |
+
#test_df = test_df[test_df.pitcher == 'Chris Bassitt'].append(test_df[test_df.pitcher != 'Chris Bassitt'])
|
| 348 |
+
|
| 349 |
+
batter_dict = test_df['pitcher_name'].to_dict()
|
| 350 |
+
|
| 351 |
+
level_dict = {'MLB':'MLB','AAA':'AAA','AA':'AA','A+':'A+','A':'A'}
|
| 352 |
+
|
| 353 |
+
plot_dict_small = {
|
| 354 |
+
'k':'K%',
|
| 355 |
+
'bb':'BB%',
|
| 356 |
+
'csw':'CSW%',
|
| 357 |
+
'launch_speed':'Exit Velocity',
|
| 358 |
+
'launch_speed_90':'90th Percentile Exit Velocity',
|
| 359 |
+
'sweet_spot':'SweetSpot%',
|
| 360 |
+
'launch_angle':'Launch Angle',
|
| 361 |
+
'zone_percent':'Zone%',
|
| 362 |
+
'barrel':'Barrel%',
|
| 363 |
+
'swing_percent':'Swing%',
|
| 364 |
+
'whiff_percent':'Whiff%',
|
| 365 |
+
'sw_str':'SwStr%',
|
| 366 |
+
'zone_swing':'Z-Swing%',
|
| 367 |
+
'zone_contact':'Z-Contact%',
|
| 368 |
+
'chase_percent':'O-Swing%',
|
| 369 |
+
'chase_contact':'O-Contact%',}
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
def server(input,output,session):
|
| 373 |
+
|
| 374 |
+
@output
|
| 375 |
+
@render.plot(alt="A histogram")
|
| 376 |
+
@reactive.event(input.go, ignore_none=False)
|
| 377 |
+
def plot():
|
| 378 |
+
# np.random.seed(19680801)
|
| 379 |
+
# x = 100 + 15 * np.random.randn(437)
|
| 380 |
+
# fig, ax = plt.subplots()
|
| 381 |
+
# ax.hist(x, input.n(), density=True)
|
| 382 |
+
# return fig
|
| 383 |
+
sns.set_theme(style="whitegrid", palette="pastel")
|
| 384 |
+
if input.id() is "":
|
| 385 |
+
fig = plt.figure(figsize=(12, 12))
|
| 386 |
+
fig.text(s='Please Select a Pitcher',x=0.5,y=0.5)
|
| 387 |
+
return
|
| 388 |
+
|
| 389 |
+
swing_min = int(input.n())
|
| 390 |
+
fig, ax = plt.subplots(1, 1, figsize=(10, 10))
|
| 391 |
+
|
| 392 |
+
fig.set_facecolor('white')
|
| 393 |
+
#ax.set_facecolor('white')
|
| 394 |
+
#fig.patch.set_facecolor('lightblue')
|
| 395 |
+
|
| 396 |
+
print(input.stat_id())
|
| 397 |
+
|
| 398 |
+
if input.stat_id() in pa_list:
|
| 399 |
+
print('we hAVE MADE IT TO THIS PART OF THE CODE')
|
| 400 |
+
|
| 401 |
+
|
| 402 |
+
if input.stat_id() in pa_list:
|
| 403 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.pa==1)&(exit_velo_df_codes.pitcher_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 404 |
+
divisor_x = 'pa'
|
| 405 |
+
print('this is short')
|
| 406 |
+
print(elly_zone_df)
|
| 407 |
+
|
| 408 |
+
|
| 409 |
+
if input.stat_id() in balls_in_play_list:
|
| 410 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.bip)&(exit_velo_df_codes.pitcher_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 411 |
+
divisor_x = 'bip'
|
| 412 |
+
#print('this is short')
|
| 413 |
+
|
| 414 |
+
if input.stat_id() in balls_in_play_list:
|
| 415 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.bip)&(exit_velo_df_codes.pitcher_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 416 |
+
divisor_x = 'bip'
|
| 417 |
+
print('this is short')
|
| 418 |
+
|
| 419 |
+
if input.stat_id() in pitches_list:
|
| 420 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.pitches == 1)&(exit_velo_df_codes.pitcher_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 421 |
+
divisor_x = 'pitches'
|
| 422 |
+
|
| 423 |
+
if input.stat_id() in swings_list:
|
| 424 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.swings == 1)&(exit_velo_df_codes.pitcher_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 425 |
+
divisor_x = 'swings'
|
| 426 |
+
|
| 427 |
+
|
| 428 |
+
if input.stat_id() in in_zone_pitches_list:
|
| 429 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.in_zone)&(exit_velo_df_codes.pitcher_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 430 |
+
divisor_x = 'in_zone'
|
| 431 |
+
|
| 432 |
+
|
| 433 |
+
if input.stat_id() in in_zone_swings_list:
|
| 434 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.zone_swing)&(exit_velo_df_codes.pitcher_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 435 |
+
divisor_x = 'zone_swing'
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
if input.stat_id() in out_zone_pitches_list:
|
| 439 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.in_zone == False)&(exit_velo_df_codes.pitcher_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 440 |
+
divisor_x = 'out_zone'
|
| 441 |
+
|
| 442 |
+
|
| 443 |
+
if input.stat_id() in out_zone_swings_list:
|
| 444 |
+
elly_zone_df = exit_velo_df_codes[(exit_velo_df_codes.ozone_swing)&(exit_velo_df_codes.pitcher_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())]
|
| 445 |
+
divisor_x = 'ozone_swing'
|
| 446 |
+
|
| 447 |
+
# penguins = sns.load_dataset("penguins")
|
| 448 |
+
# sns.histplot(data=penguins, x="flipper_length_mm")
|
| 449 |
+
# print('we made it here:')
|
| 450 |
+
# print(int(input.id()))
|
| 451 |
+
# print(input.stat_id())
|
| 452 |
+
# print(input.level_id())
|
| 453 |
+
# print(exit_velo_df_codes[(exit_velo_df_codes.pitcher_id == int(input.id()))&(exit_velo_df_codes.level==input.level_id())])
|
| 454 |
+
# print(exit_velo_df_codes.columns)
|
| 455 |
+
# print(elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum())
|
| 456 |
+
|
| 457 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ.copy()
|
| 458 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.set_index('pitcher_id','pitcher','level')
|
| 459 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new[divisor_x] >= int(input.n())]
|
| 460 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.level==input.level_id()]
|
| 461 |
+
|
| 462 |
+
exit_velo_df_codes_summ_rank = exit_velo_df_codes_summ_new.rank(method='max',ascending=False)
|
| 463 |
+
exit_velo_df_codes_summ_rank.columns = exit_velo_df_codes_summ_rank.columns+['_rank']
|
| 464 |
+
|
| 465 |
+
exit_velo_df_codes_summ_rank_percent = exit_velo_df_codes_summ_new.rank(pct=True)
|
| 466 |
+
exit_velo_df_codes_summ_rank_percent.columns = exit_velo_df_codes_summ_rank_percent.columns+['_percent']
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.reset_index()
|
| 470 |
+
exit_velo_df_codes_summ_rank = exit_velo_df_codes_summ_rank.reset_index()
|
| 471 |
+
exit_velo_df_codes_summ_rank_percent = exit_velo_df_codes_summ_rank_percent.reset_index()
|
| 472 |
+
print('Table columns:')
|
| 473 |
+
|
| 474 |
+
exit_velo_df_codes_summ_new.pitcher_id = exit_velo_df_codes_summ_new.pitcher_id.astype(int)
|
| 475 |
+
exit_velo_df_codes_summ_rank.pitcher_id = exit_velo_df_codes_summ_rank.pitcher_id.astype(int)
|
| 476 |
+
exit_velo_df_codes_summ_rank_percent.pitcher_id = exit_velo_df_codes_summ_rank_percent.pitcher_id.astype(int)
|
| 477 |
+
|
| 478 |
+
print('Table columns2:')
|
| 479 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.merge(exit_velo_df_codes_summ_rank,left_on=['pitcher_id'],right_on=['pitcher_id'],how='left',suffixes=['','_rank'])
|
| 480 |
+
|
| 481 |
+
exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new.merge(exit_velo_df_codes_summ_rank_percent,left_on=['pitcher_id'],right_on=['pitcher_id'],how='left',suffixes=['','_percent'])
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
print(exit_velo_df_codes_summ_new)
|
| 485 |
+
print(exit_velo_df_codes_summ_rank)
|
| 486 |
+
print(exit_velo_df_codes_summ_rank_percent)
|
| 487 |
+
|
| 488 |
+
|
| 489 |
+
|
| 490 |
+
|
| 491 |
+
#sns.scatterplot(x=data_df.launch_speed_90,y=data_df.zone_contact,color=colour_palette[0],s=75,label=int(input.id()))
|
| 492 |
+
|
| 493 |
+
exit_velo_df_codes_summ_new_select = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.pitcher_id == int(input.id())].reset_index(drop=True)
|
| 494 |
+
print('whiffing')
|
| 495 |
+
print(exit_velo_df_codes)
|
| 496 |
+
print('Player _df:')
|
| 497 |
+
print(exit_velo_df_codes_summ_new_select)
|
| 498 |
+
|
| 499 |
+
if len(exit_velo_df_codes_summ_new_select) < 1:
|
| 500 |
+
ax.text(x=0.5,y=0.5,s='Please Select Different Parameters to Produce a plot',fontsize=18,ha='center')
|
| 501 |
+
return
|
| 502 |
+
|
| 503 |
+
p = inflect.engine()
|
| 504 |
+
|
| 505 |
+
exit_velo_df_codes_summ_new_select = exit_velo_df_codes_summ_new_select.loc[:,~exit_velo_df_codes_summ_new_select.columns.duplicated(keep='last')].copy()
|
| 506 |
+
print('Table for the player:')
|
| 507 |
+
print(list(exit_velo_df_codes_summ_new_select.columns))
|
| 508 |
+
print(plot_dict[input.stat_id()]["percentile_label"])
|
| 509 |
+
print(plot_dict[input.stat_id()]["percentile_label"]+'_percent')
|
| 510 |
+
print(exit_velo_df_codes_summ_new_select)
|
| 511 |
+
print(1*plot_dict[input.stat_id()]["flip_p"])
|
| 512 |
+
print(round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))
|
| 513 |
+
print((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)
|
| 514 |
+
|
| 515 |
+
# print(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+'_percent'])
|
| 516 |
+
|
| 517 |
+
if plot_dict[input.stat_id()]['percent']:
|
| 518 |
+
label_1=f'{input.level_id()} Average {exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].sum()/exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][divisor_x].sum():.1%}'
|
| 519 |
+
label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1%} ({p.ordinal(abs(int((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)))} Percentile)'
|
| 520 |
+
#label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1%}'
|
| 521 |
+
ax.yaxis.set_major_formatter(mtick.PercentFormatter(1))
|
| 522 |
+
|
| 523 |
+
else:
|
| 524 |
+
label_1=f'{input.level_id()} Average {exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].sum()/exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][divisor_x].sum():.1f}'
|
| 525 |
+
label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1f} ({p.ordinal(abs(int((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)))} Percentile)'
|
| 526 |
+
#label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1f}'
|
| 527 |
+
#ax.yaxis.set_major_formatter(mtick.int)
|
| 528 |
+
|
| 529 |
+
|
| 530 |
+
if plot_dict[input.stat_id()]['percentile']:
|
| 531 |
+
label_1=f'{input.level_id()} Average {exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].quantile(0.9):.1f}'
|
| 532 |
+
label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].quantile(0.9):.1f} ({p.ordinal(abs(int((1*plot_dict[input.stat_id()]["flip_p"]-round(exit_velo_df_codes_summ_new_select[plot_dict[input.stat_id()]["percentile_label"]+"_percent"][0],2))*100)))} Percentile)'
|
| 533 |
+
#label_2=f'{batter_dict[int(input.id())]} Average {elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum():.1%}'
|
| 534 |
+
#ax.yaxis.set_major_formatter(mtick.int)
|
| 535 |
+
|
| 536 |
+
|
| 537 |
+
print(plot_dict[input.stat_id()]["x_value"])
|
| 538 |
+
print(divisor_x)
|
| 539 |
+
|
| 540 |
+
# exit_velo_df_codes_summ_new = exit_velo_df_codes_summ.copy()
|
| 541 |
+
# exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.balls_in_play >= int(input.n())]
|
| 542 |
+
# exit_velo_df_codes_summ_new = exit_velo_df_codes_summ_new[exit_velo_df_codes_summ_new.level==input.level_id()]
|
| 543 |
+
|
| 544 |
+
|
| 545 |
+
print('this is here:')
|
| 546 |
+
print(exit_velo_df_codes_summ_new.head())
|
| 547 |
+
print(exit_velo_df_codes_summ_new.columns)
|
| 548 |
+
|
| 549 |
+
|
| 550 |
+
if plot_dict[input.stat_id()]["flip_p"] == False:
|
| 551 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[2],linestyle='dotted',alpha=0.5)
|
| 552 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[3],linestyle='dotted',alpha=0.5)
|
| 553 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[4],linestyle='dotted',alpha=0.5)
|
| 554 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[5],linestyle='dotted',alpha=0.5)
|
| 555 |
+
|
| 556 |
+
|
| 557 |
+
hard_hit_dates = [(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),
|
| 558 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),
|
| 559 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),
|
| 560 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1)]
|
| 561 |
+
hard_hit_text = ['90th %','75th %','25th %','10th %']
|
| 562 |
+
for i, x in enumerate(hard_hit_dates):
|
| 563 |
+
text(min(input.n()+input.n()/100,+input.n()+1), x ,hard_hit_text[i], rotation=0, ha='left',
|
| 564 |
+
bbox=dict(facecolor='white',alpha=0.5, edgecolor=colour_palette[2+i], pad=2))
|
| 565 |
+
|
| 566 |
+
|
| 567 |
+
|
| 568 |
+
if plot_dict[input.stat_id()]["flip_p"] == True:
|
| 569 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[2],linestyle='dotted',alpha=0.5)
|
| 570 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[3],linestyle='dotted',alpha=0.5)
|
| 571 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[4],linestyle='dotted',alpha=0.5)
|
| 572 |
+
ax.hlines(y=(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[5],linestyle='dotted',alpha=0.5)
|
| 573 |
+
|
| 574 |
+
hard_hit_dates = [(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.9),
|
| 575 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.75),
|
| 576 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.25),
|
| 577 |
+
(exit_velo_df_codes_summ_new[plot_dict[input.stat_id()]["percentile_label"]]).quantile(0.1)]
|
| 578 |
+
hard_hit_text = ['10th %','25th %','75th %','90th %']
|
| 579 |
+
for i, x in enumerate(hard_hit_dates):
|
| 580 |
+
text(min(input.n()+input.n()/100,input.n()+input.n()+3), x ,hard_hit_text[i], rotation=0, ha='left',
|
| 581 |
+
bbox=dict(facecolor='white',alpha=0.5, edgecolor=colour_palette[2+i], pad=2))
|
| 582 |
+
|
| 583 |
+
|
| 584 |
+
|
| 585 |
+
|
| 586 |
+
|
| 587 |
+
|
| 588 |
+
if plot_dict[input.stat_id()]["percentile"] == False:
|
| 589 |
+
ax.hlines(y=exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].sum()/exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][divisor_x].sum(),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[1],linestyle='-.',label=label_1)
|
| 590 |
+
|
| 591 |
+
ax.hlines(y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].sum()/elly_zone_df[divisor_x].sum(),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[0],linestyle='--',label=label_2)
|
| 592 |
+
|
| 593 |
+
sns.lineplot(x=range(1,len(elly_zone_df)+1),y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].fillna(0).rolling(window=swing_min).sum()/swing_min,color=colour_palette[0],linewidth=3,ax=ax)
|
| 594 |
+
|
| 595 |
+
|
| 596 |
+
|
| 597 |
+
if plot_dict[input.stat_id()]["percentile"] == True:
|
| 598 |
+
|
| 599 |
+
ax.hlines(y=exit_velo_df_codes[exit_velo_df_codes.level == input.level_id()][plot_dict[input.stat_id()]["x_value"]].quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[1],linestyle='-.',label=label_1)
|
| 600 |
+
|
| 601 |
+
ax.hlines(y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].fillna(0).quantile(0.9),xmin=swing_min,xmax=len(elly_zone_df),color=colour_palette[0],linestyle='--',label=label_2)
|
| 602 |
+
|
| 603 |
+
sns.lineplot(x=range(1,len(elly_zone_df)+1),y=elly_zone_df[plot_dict[input.stat_id()]["x_value"]].fillna(0).rolling(window=swing_min).quantile(0.9),color=colour_palette[0],linewidth=3,ax=ax)
|
| 604 |
+
|
| 605 |
+
|
| 606 |
+
#ax.set_xlim(input.n(),exit_velo_df_small.pitch.max())
|
| 607 |
+
#plt.yticks([0,0.2,0.4,0.6,0.8,1])
|
| 608 |
+
#ax.set_ylim(math.floor((min(exit_velo_df_codes_summ.zone_contact)/5)*100)*5/100,1)
|
| 609 |
+
ax.set_xlim(math.floor(swing_min),len(elly_zone_df))
|
| 610 |
+
ax.set_title(f'{batter_dict[int(input.id())]} - {input.level_id()} - {swing_min} {plot_dict[input.stat_id()]["x_axis"]} Rolling {plot_dict[input.stat_id()]["title"]}', fontsize=16,fontname='Century Gothic',)
|
| 611 |
+
#vals = ax.get_yticks()
|
| 612 |
+
ax.set_xlabel(plot_dict[input.stat_id()]['x_axis'], fontsize=16,fontname='Century Gothic')
|
| 613 |
+
ax.set_ylabel(plot_dict[input.stat_id()]['y_axis'], fontsize=16,fontname='Century Gothic')
|
| 614 |
+
|
| 615 |
+
#fig.axes[0].invert_yaxis()
|
| 616 |
+
|
| 617 |
+
#fig.subplots_adjust(wspace=.02, hspace=.02)
|
| 618 |
+
#ax.xaxis.set_major_formatter(FuncFormatter(lambda x, _: int(x)))
|
| 619 |
+
ax.set_yticks(plot_dict[input.stat_id()]["x_range"])
|
| 620 |
+
#fig.colorbar(plot_dist, ax=ax)
|
| 621 |
+
#fig.colorbar(plot_dist)
|
| 622 |
+
#fig.axes[0].invert_yaxis()
|
| 623 |
+
ax.legend(fontsize='16')
|
| 624 |
+
fig.text(x=0.03,y=0.02,s='By: @TJStats',fontname='Century Gothic')
|
| 625 |
+
fig.text(x=1-0.03,y=0.02,s='Data: MLB',ha='right',fontname='Century Gothic')
|
| 626 |
+
fig.tight_layout()
|
| 627 |
+
|
| 628 |
+
|
| 629 |
+
rolling_pitcher = App(ui.page_fluid(
|
| 630 |
+
ui.tags.base(href=base_url),
|
| 631 |
+
ui.tags.div(
|
| 632 |
+
{"style": "width:90%;margin: 0 auto;max-width: 1600px;"},
|
| 633 |
+
ui.tags.style(
|
| 634 |
+
"""
|
| 635 |
+
h4 {
|
| 636 |
+
margin-top: 1em;font-size:35px;
|
| 637 |
+
}
|
| 638 |
+
h2{
|
| 639 |
+
font-size:25px;
|
| 640 |
+
}
|
| 641 |
+
"""
|
| 642 |
+
),
|
| 643 |
+
shinyswatch.theme.simplex(),
|
| 644 |
+
ui.tags.h4("TJStats"),
|
| 645 |
+
ui.tags.i("Baseball Analytics and Visualizations"),
|
| 646 |
+
ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),
|
| 647 |
+
ui.navset_tab(
|
| 648 |
+
ui.nav_control(
|
| 649 |
+
ui.a(
|
| 650 |
+
"Home",
|
| 651 |
+
href="home/"
|
| 652 |
+
),
|
| 653 |
+
),
|
| 654 |
+
ui.nav_menu(
|
| 655 |
+
"Batter Charts",
|
| 656 |
+
ui.nav_control(
|
| 657 |
+
ui.a(
|
| 658 |
+
"Batting Rolling",
|
| 659 |
+
href="rolling_batter/"
|
| 660 |
+
),
|
| 661 |
+
ui.a(
|
| 662 |
+
"Spray",
|
| 663 |
+
href="spray/"
|
| 664 |
+
),
|
| 665 |
+
ui.a(
|
| 666 |
+
"Decision Value",
|
| 667 |
+
href="decision_value/"
|
| 668 |
+
),
|
| 669 |
+
ui.a(
|
| 670 |
+
"Damage Model",
|
| 671 |
+
href="damage_model/"
|
| 672 |
+
),
|
| 673 |
+
ui.a(
|
| 674 |
+
"Batter Scatter",
|
| 675 |
+
href="batter_scatter/"
|
| 676 |
+
),
|
| 677 |
+
ui.a(
|
| 678 |
+
"EV vs LA Plot",
|
| 679 |
+
href="ev_angle/"
|
| 680 |
+
),
|
| 681 |
+
ui.a(
|
| 682 |
+
"Statcast Compare",
|
| 683 |
+
href="statcast_compare/"
|
| 684 |
+
)
|
| 685 |
+
),
|
| 686 |
+
),
|
| 687 |
+
ui.nav_menu(
|
| 688 |
+
"Pitcher Charts",
|
| 689 |
+
ui.nav_control(
|
| 690 |
+
ui.a(
|
| 691 |
+
"Pitcher Rolling",
|
| 692 |
+
href="rolling_pitcher/"
|
| 693 |
+
),
|
| 694 |
+
ui.a(
|
| 695 |
+
"Pitcher Summary",
|
| 696 |
+
href="pitching_summary_graphic_new/"
|
| 697 |
+
),
|
| 698 |
+
ui.a(
|
| 699 |
+
"Pitcher Scatter",
|
| 700 |
+
href="pitcher_scatter/"
|
| 701 |
+
)
|
| 702 |
+
),
|
| 703 |
+
)),ui.row(
|
| 704 |
+
ui.layout_sidebar(
|
| 705 |
+
|
| 706 |
+
ui.panel_sidebar(
|
| 707 |
+
ui.input_select("id", "Select Pitcher",batter_dict,selected=675911,width=1,size=1,selectize=True),
|
| 708 |
+
ui.input_select("level_id", "Select Level",level_dict,width=1,size=1),
|
| 709 |
+
ui.input_select("stat_id", "Select Stat",plot_dict_small,width=1,size=1),
|
| 710 |
+
ui.input_numeric("n", "Rolling Window Size", value=50),
|
| 711 |
+
ui.input_action_button("go", "Generate",class_="btn-primary"),
|
| 712 |
+
|
| 713 |
+
ui.output_table("result")
|
| 714 |
+
),
|
| 715 |
+
|
| 716 |
+
ui.panel_main(
|
| 717 |
+
ui.output_plot("plot",height = "1000px",width="1000px")
|
| 718 |
+
),
|
| 719 |
+
)),)),server)
|
spray.py
CHANGED
|
@@ -81,6 +81,7 @@ batter_dict = df_2023_bip.sort_values('batter_name').set_index('batter_id')['bat
|
|
| 81 |
def server(input,output,session):
|
| 82 |
@output
|
| 83 |
@render.plot(alt="plot")
|
|
|
|
| 84 |
def plot():
|
| 85 |
|
| 86 |
batter_id_select = int(input.batter_id())
|
|
@@ -342,8 +343,9 @@ spray = App(ui.page_fluid(
|
|
| 342 |
"""
|
| 343 |
),
|
| 344 |
shinyswatch.theme.simplex(),
|
| 345 |
-
ui.tags.h4("
|
| 346 |
-
ui.tags.i("
|
|
|
|
| 347 |
ui.navset_tab(
|
| 348 |
ui.nav_control(
|
| 349 |
ui.a(
|
|
@@ -354,7 +356,11 @@ spray = App(ui.page_fluid(
|
|
| 354 |
ui.nav_menu(
|
| 355 |
"Batter Charts",
|
| 356 |
ui.nav_control(
|
| 357 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 358 |
"Spray",
|
| 359 |
href="spray/"
|
| 360 |
),
|
|
@@ -373,48 +379,29 @@ spray = App(ui.page_fluid(
|
|
| 373 |
ui.a(
|
| 374 |
"EV vs LA Plot",
|
| 375 |
href="ev_angle/"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 376 |
)
|
| 377 |
),
|
| 378 |
),
|
| 379 |
ui.nav_menu(
|
| 380 |
-
"
|
| 381 |
ui.nav_control(
|
| 382 |
ui.a(
|
| 383 |
-
"
|
| 384 |
-
href="
|
| 385 |
),
|
| 386 |
ui.a(
|
| 387 |
-
"
|
| 388 |
-
href="
|
| 389 |
),
|
| 390 |
ui.a(
|
| 391 |
-
"
|
| 392 |
-
href="
|
| 393 |
)
|
| 394 |
),
|
| 395 |
-
),ui.nav_menu(
|
| 396 |
-
"Team Charts",
|
| 397 |
-
ui.nav_control(
|
| 398 |
-
ui.a(
|
| 399 |
-
"Team xG Rates",
|
| 400 |
-
href="team-xg-rates/"
|
| 401 |
-
),
|
| 402 |
-
),
|
| 403 |
-
),ui.nav_control(
|
| 404 |
-
ui.a(
|
| 405 |
-
"Games",
|
| 406 |
-
href="games/"
|
| 407 |
-
),
|
| 408 |
-
),ui.nav_control(
|
| 409 |
-
ui.a(
|
| 410 |
-
"About",
|
| 411 |
-
href="about/"
|
| 412 |
-
),
|
| 413 |
-
),ui.nav_control(
|
| 414 |
-
ui.a(
|
| 415 |
-
"Articles",
|
| 416 |
-
href="articles/"
|
| 417 |
-
),
|
| 418 |
)),ui.row(
|
| 419 |
ui.layout_sidebar(
|
| 420 |
|
|
@@ -424,7 +411,9 @@ spray = App(ui.page_fluid(
|
|
| 424 |
batter_dict,
|
| 425 |
width=1,
|
| 426 |
size=1,
|
| 427 |
-
selectize=True)
|
|
|
|
|
|
|
| 428 |
|
| 429 |
ui.panel_main(
|
| 430 |
ui.navset_tab(
|
|
|
|
| 81 |
def server(input,output,session):
|
| 82 |
@output
|
| 83 |
@render.plot(alt="plot")
|
| 84 |
+
@reactive.event(input.go, ignore_none=False)
|
| 85 |
def plot():
|
| 86 |
|
| 87 |
batter_id_select = int(input.batter_id())
|
|
|
|
| 343 |
"""
|
| 344 |
),
|
| 345 |
shinyswatch.theme.simplex(),
|
| 346 |
+
ui.tags.h4("TJStats"),
|
| 347 |
+
ui.tags.i("Baseball Analytics and Visualizations"),
|
| 348 |
+
ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),
|
| 349 |
ui.navset_tab(
|
| 350 |
ui.nav_control(
|
| 351 |
ui.a(
|
|
|
|
| 356 |
ui.nav_menu(
|
| 357 |
"Batter Charts",
|
| 358 |
ui.nav_control(
|
| 359 |
+
ui.a(
|
| 360 |
+
"Batting Rolling",
|
| 361 |
+
href="rolling_batter/"
|
| 362 |
+
),
|
| 363 |
+
ui.a(
|
| 364 |
"Spray",
|
| 365 |
href="spray/"
|
| 366 |
),
|
|
|
|
| 379 |
ui.a(
|
| 380 |
"EV vs LA Plot",
|
| 381 |
href="ev_angle/"
|
| 382 |
+
),
|
| 383 |
+
ui.a(
|
| 384 |
+
"Statcast Compare",
|
| 385 |
+
href="statcast_compare/"
|
| 386 |
)
|
| 387 |
),
|
| 388 |
),
|
| 389 |
ui.nav_menu(
|
| 390 |
+
"Pitcher Charts",
|
| 391 |
ui.nav_control(
|
| 392 |
ui.a(
|
| 393 |
+
"Pitcher Rolling",
|
| 394 |
+
href="rolling_pitcher/"
|
| 395 |
),
|
| 396 |
ui.a(
|
| 397 |
+
"Pitcher Summary",
|
| 398 |
+
href="pitching_summary_graphic_new/"
|
| 399 |
),
|
| 400 |
ui.a(
|
| 401 |
+
"Pitcher Scatter",
|
| 402 |
+
href="pitcher_scatter/"
|
| 403 |
)
|
| 404 |
),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 405 |
)),ui.row(
|
| 406 |
ui.layout_sidebar(
|
| 407 |
|
|
|
|
| 411 |
batter_dict,
|
| 412 |
width=1,
|
| 413 |
size=1,
|
| 414 |
+
selectize=True),
|
| 415 |
+
ui.input_action_button("go", "Generate",class_="btn-primary",
|
| 416 |
+
)),
|
| 417 |
|
| 418 |
ui.panel_main(
|
| 419 |
ui.navset_tab(
|
statcast_20152023.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
statcast_compare.py
ADDED
|
@@ -0,0 +1,701 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui
|
| 2 |
+
import datasets
|
| 3 |
+
from datasets import load_dataset
|
| 4 |
+
import pandas as pd
|
| 5 |
+
import numpy as np
|
| 6 |
+
import matplotlib.pyplot as plt
|
| 7 |
+
import seaborn as sns
|
| 8 |
+
import numpy as np
|
| 9 |
+
from scipy.stats import gaussian_kde
|
| 10 |
+
import matplotlib
|
| 11 |
+
from matplotlib.ticker import MaxNLocator
|
| 12 |
+
from matplotlib.gridspec import GridSpec
|
| 13 |
+
from scipy.stats import zscore
|
| 14 |
+
import math
|
| 15 |
+
import matplotlib
|
| 16 |
+
from adjustText import adjust_text
|
| 17 |
+
import matplotlib.ticker as mtick
|
| 18 |
+
from shinywidgets import output_widget, render_widget
|
| 19 |
+
import pandas as pd
|
| 20 |
+
from configure import base_url
|
| 21 |
+
import shinyswatch
|
| 22 |
+
import inflect
|
| 23 |
+
from matplotlib.pyplot import text
|
| 24 |
+
|
| 25 |
+
def percentile(n):
|
| 26 |
+
def percentile_(x):
|
| 27 |
+
return np.nanpercentile(x, n)
|
| 28 |
+
percentile_.__name__ = 'percentile_%s' % n
|
| 29 |
+
return percentile_
|
| 30 |
+
|
| 31 |
+
from matplotlib.colors import Normalize
|
| 32 |
+
|
| 33 |
+
print('Running')
|
| 34 |
+
|
| 35 |
+
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ["#4285F4","white","#FBBC04"])
|
| 36 |
+
df = pd.read_csv('statcast_20152023.csv')
|
| 37 |
+
df['last_name'] = df['last_name, first_name'].str.split(',').str[0]
|
| 38 |
+
df['first_name'] = df['last_name, first_name'].str.split(',').str[1].str.strip(' ')
|
| 39 |
+
df['name'] = df['first_name'] +' ' +df['last_name']
|
| 40 |
+
|
| 41 |
+
df[[x for x in df if x[-7:] == 'percent']] = df[[x for x in df if x[-7:] == 'percent']]/100
|
| 42 |
+
df['barrel_batted_rate'] = df['barrel_batted_rate']/100
|
| 43 |
+
|
| 44 |
+
player_dict = df[['player_id','name']].drop_duplicates().sort_values('name').set_index('player_id').to_dict()['name']
|
| 45 |
+
|
| 46 |
+
df_median = df[df.pa>=400]
|
| 47 |
+
|
| 48 |
+
format_dict = {
|
| 49 |
+
'k_percent':{'format':':.1%','average':df.strikeout.sum()/df.pa.sum(),'a_d_good':False,'tab_name':'K%'},
|
| 50 |
+
'bb_percent':{'format':':.1%','average':df.walk.sum()/df.pa.sum(),'a_d_good':True,'tab_name':'BB%'},
|
| 51 |
+
'batting_avg':{'format':':.3f','average':df.hit.sum()/df.ab.sum(),'a_d_good':True,'tab_name':'AVG'},
|
| 52 |
+
'on_base_plus_slg':{'format':':.3f','average':df.on_base_plus_slg.mean()/df.pa.mean(),'a_d_good':True,'tab_name':'OPS'},
|
| 53 |
+
'isolated_power':{'format':':.3f','average':(df.single.sum() + df.double.sum()*2 + df.triple.sum()*3 + df.home_run.sum()*4)/df.ab.sum() - df.hit.sum()/df.ab.sum(),'a_d_good':True,'tab_name':'ISO'},
|
| 54 |
+
'xba':{'format':':.3f','average':df_median.xba.median(),'a_d_good':True,'tab_name':'xBA'},
|
| 55 |
+
'xslg':{'format':':.3f','average':df_median.xslg.median(),'a_d_good':True,'tab_name':'xSLG'},
|
| 56 |
+
'woba':{'format':':.3f','average':df_median.woba.median(),'a_d_good':True,'tab_name':'wOBA'},
|
| 57 |
+
'xwoba':{'format':':.3f','average':df_median.xwoba.median(),'a_d_good':True,'tab_name':'xwOBA'},
|
| 58 |
+
'xobp':{'format':':.3f','average':df_median.xobp.median(),'a_d_good':True,'tab_name':'xOBP'},
|
| 59 |
+
'xiso':{'format':':.3f','average':df_median.xiso.median(),'a_d_good':True,'tab_name':'xISO'},
|
| 60 |
+
'wobacon':{'format':':.3f','average':df_median.wobacon.median(),'a_d_good':True,'tab_name':'wOBACON'},
|
| 61 |
+
'xwobacon':{'format':':.3f','average':df_median.xwobacon.median(),'a_d_good':True,'tab_name':'xwOBACON'},
|
| 62 |
+
'bacon':{'format':':.1f','average':df_median.bacon.median(),'a_d_good':True,'tab_name':'BACON'},
|
| 63 |
+
'xbacon':{'format':':.1f','average':df_median.xbacon.median(),'a_d_good':True,'tab_name':'xBACON'},
|
| 64 |
+
'xbadiff':{'format':':.3f','average':df_median.xbadiff.median(),'a_d_good':True,'tab_name':'BA-xBA'},
|
| 65 |
+
'xslgdiff':{'format':':.3f','average':df_median.xslgdiff.median(),'a_d_good':True,'tab_name':'SLG-xSLG'},
|
| 66 |
+
'wobadiff':{'format':':.3f','average':df_median.wobadiff.median(),'a_d_good':True,'tab_name':'wOBA-xwOBA'},
|
| 67 |
+
'exit_velocity_avg':{'format':':.1f','average':(df.exit_velocity_avg * df.batted_ball).sum() / (df.batted_ball).sum(),'a_d_good':True,'tab_name':'EV'},
|
| 68 |
+
'launch_angle_avg':{'format':':.1f','average':(df.launch_angle_avg * df.batted_ball).sum() / (df.batted_ball).sum(),'a_d_good':True,'tab_name':'LA'},
|
| 69 |
+
'barrel':{'format':':.0f','average':df_median.barrel.median(),'a_d_good':True,'tab_name':'Barrel'},
|
| 70 |
+
'barrel_batted_rate':{'format':':.1%','average':(df.barrel).sum() / (df.batted_ball).sum(),'a_d_good':True,'tab_name':'Barrel%'},
|
| 71 |
+
'avg_best_speed':{'format':':.1f','average':(df.avg_best_speed * df.batted_ball).sum() / (df.batted_ball).sum(),'a_d_good':True,'tab_name':'Best Speed'},
|
| 72 |
+
'avg_hyper_speed':{'format':':.1f','average':(df.avg_hyper_speed * df.batted_ball).sum() / (df.batted_ball).sum(),'a_d_good':True,'tab_name':'Hyper Speed'},
|
| 73 |
+
'out_zone_swing_miss':{'format':':.0f','average':df_median.out_zone_swing_miss.mean(),'a_d_good':True,'tab_name':'O-Whiff%'},
|
| 74 |
+
'out_zone_swing':{'format':':.0f','average':df_median.out_zone_swing.mean(),'a_d_good':True,'tab_name':'O-Swing'},
|
| 75 |
+
'out_zone':{'format':':.0f','average':df_median.out_zone.mean(),'a_d_good':True,'tab_name':'O-Zone'},
|
| 76 |
+
'pitch_count_offspeed':{'format':':.0f','average':df_median.pitch_count_offspeed.mean(),'a_d_good':True,'tab_name':'Pitch Off-Speed'},
|
| 77 |
+
'pitch_count_fastball':{'format':':.0f','average':df_median.pitch_count_fastball.mean(),'a_d_good':True,'tab_name':'Pitch Fastball'},
|
| 78 |
+
'pitch_count_breaking':{'format':':.0f','average':df_median.pitch_count_breaking.mean(),'a_d_good':True,'tab_name':'Pitch Breaking'},
|
| 79 |
+
'pitch_count':{'format':':.0f','average':df_median.pitch_count.mean(),'a_d_good':True,'tab_name':'Pitches'},
|
| 80 |
+
'in_zone_swing_miss':{'format':':.0f','average':df_median.in_zone_swing_miss.mean(),'a_d_good':False,'tab_name':'Z-Whiff'},
|
| 81 |
+
'in_zone_swing':{'format':':.0f','average':df_median.in_zone_swing.mean(),'a_d_good':True,'tab_name':'Z-Swing'},
|
| 82 |
+
'in_zone':{'format':':.0f','average':df_median.in_zone.mean(),'a_d_good':True,'tab_name':'Zone'},
|
| 83 |
+
'edge':{'format':':.0f','average':df_median.edge.mean(),'a_d_good':True,'tab_name':'Edge'},
|
| 84 |
+
'batted_ball':{'format':':.0f','average':df_median.batted_ball.mean(),'a_d_good':True,'tab_name':'Batted Balls'},
|
| 85 |
+
'groundballs':{'format':':.0f','average':df_median.groundballs.mean(),'a_d_good':True,'tab_name':'Groundballs'},
|
| 86 |
+
'flyballs':{'format':':.0f','average':df_median.flyballs.mean(),'a_d_good':True,'tab_name':'Flyballs'},
|
| 87 |
+
'linedrives':{'format':':.0f','average':df_median.linedrives.mean(),'a_d_good':True,'tab_name':'Linedrives'},
|
| 88 |
+
'popups':{'format':':.0f','average':df_median.popups.mean(),'a_d_good':True,'tab_name':'Popups'},
|
| 89 |
+
'n_bolts':{'format':':.0f','average':df_median.n_bolts.mean(),'a_d_good':True,'tab_name':'Bolts'},
|
| 90 |
+
'hp_to_1b':{'format':':.2f','average':df_median.hp_to_1b.mean(),'a_d_good':True,'tab_name':'Home Plate to 1st'},
|
| 91 |
+
'sprint_speed':{'format':':.1f','average':df_median.sprint_speed.mean(),'a_d_good':True,'tab_name':'Sprint Speed'},
|
| 92 |
+
|
| 93 |
+
'slg_percent':{'format':':.1%','average':(df.single.sum() + df.double.sum()*2 + df.triple.sum()*3 + df.home_run.sum()*4)/df.ab.sum(),'a_d_good':True,'tab_name':'SLG'},
|
| 94 |
+
'on_base_percent':{'format':':.1%','average':df_median.on_base_percent.median(),'a_d_good':True,'tab_name':'OBP'},
|
| 95 |
+
'sweet_spot_percent':{'format':':.1%','average':df_median.sweet_spot_percent.median(),'a_d_good':True,'tab_name':'SweetSpot%'},
|
| 96 |
+
'solidcontact_percent':{'format':':.1%','average':df_median.solidcontact_percent.median(),'a_d_good':True,'tab_name':'Solid%'},
|
| 97 |
+
'flareburner_percent':{'format':':.1%','average':df_median.flareburner_percent.median(),'a_d_good':False,'tab_name':'Flare/Burner%'},
|
| 98 |
+
'poorlyunder_percent':{'format':':.1%','average':df_median.poorlyunder_percent.median(),'a_d_good':False,'tab_name':'Under%'},
|
| 99 |
+
'poorlytopped_percent':{'format':':.1%','average':df_median.poorlytopped_percent.median(),'a_d_good':False,'tab_name':'Topped%'},
|
| 100 |
+
'poorlyweak_percent':{'format':':.1%','average':df_median.poorlyweak_percent.median(),'a_d_good':False,'tab_name':'Weak%'},
|
| 101 |
+
'hard_hit_percent':{'format':':.1%','average':df_median.hard_hit_percent.median(),'a_d_good':True,'tab_name':'HardHit%'},
|
| 102 |
+
'z_swing_percent':{'format':':.1%','average':df.in_zone_swing.sum()/df.in_zone.sum(),'a_d_good':True,'tab_name':'Z-Swing%'},
|
| 103 |
+
'z_swing_miss_percent':{'format':':.1%','average':df.in_zone_swing_miss.sum()/df.in_zone_swing.sum(),'a_d_good':False,'tab_name':'Z-Whiff%'},
|
| 104 |
+
|
| 105 |
+
'out_zone_percent':{'format':':.1%','average':df.out_zone.sum()/df.pitch_count.sum(),'a_d_good':True,'tab_name':'O-Zone%'},
|
| 106 |
+
'meatball_swing_percent':{'format':':.1%','average':df_median.meatball_swing_percent.median(),'a_d_good':True,'tab_name':'Meatball Swing%'},
|
| 107 |
+
'meatball_percent':{'format':':.1%','average':df_median.meatball_percent.median(),'a_d_good':True,'tab_name':'Meatball%'},
|
| 108 |
+
'iz_contact_percent':{'format':':.1%','average':1 - df.in_zone_swing_miss.sum()/df.in_zone_swing.sum(),'a_d_good':True,'tab_name':'Z-Contact%'},
|
| 109 |
+
'in_zone_percent':{'format':':.1%','average':df.in_zone.mean()/df.pitch_count.sum(),'a_d_good':True,'tab_name':'Zone%'},
|
| 110 |
+
'oz_swing_percent':{'format':':.1%','average':df.out_zone_swing.sum()/df.out_zone.sum(),'a_d_good':False,'tab_name':'O-Swing%'},
|
| 111 |
+
'oz_swing_miss_percent':{'format':':.1%','average':df.out_zone_swing_miss.sum()/df.out_zone_swing.sum(),'a_d_good':False,'tab_name':'O-Whiff%'},
|
| 112 |
+
'oz_contact_percent':{'format':':.1%','average':1 - df.out_zone_swing_miss.sum()/df.out_zone_swing.sum(),'a_d_good':True,'tab_name':'O-Contact%'},
|
| 113 |
+
'edge_percent':{'format':':.1%','average':df_median.edge_percent.median(),'a_d_good':True,'tab_name':'Edge%'},
|
| 114 |
+
'whiff_percent':{'format':':.1%','average':(df.in_zone_swing_miss.sum() + df.out_zone_swing_miss.sum()) / (df.in_zone_swing.sum() + df.out_zone_swing.sum()),'a_d_good':False,'tab_name':'Whiff%'},
|
| 115 |
+
'swstr_percent':{'format':':.1%','average':(df.in_zone_swing_miss.sum() + df.out_zone_swing_miss.sum()) / (df.pitch_count.sum()),'a_d_good':False,'tab_name':'SwStr%'},
|
| 116 |
+
'swing_percent':{'format':':.1%','average':(df.in_zone_swing.sum() + df.out_zone_swing.sum()) / (df.pitch_count.sum()),'a_d_good':True,'tab_name':'Swing%'},
|
| 117 |
+
'pull_percent':{'format':':.1%','average':df_median.hit.median(),'a_d_good':True,'tab_name':'Pull%'},
|
| 118 |
+
'straightaway_percent':{'format':':.1%','average':df_median.hit.median(),'a_d_good':True,'tab_name':'Straightaway%'},
|
| 119 |
+
'opposite_percent':{'format':':.1%','average':df_median.hit.median(),'a_d_good':True,'tab_name':'Opposite%'},
|
| 120 |
+
'f_strike_percent':{'format':':.1%','average':df_median.hit.median(),'a_d_good':False,'tab_name':'1st Strike%'},
|
| 121 |
+
'groundballs_percent':{'format':':.1%','average':df.groundballs.sum()/df.batted_ball.sum(),'a_d_good':False,'tab_name':'GB%'},
|
| 122 |
+
'flyballs_percent':{'format':':.1%','average':df.flyballs.sum()/df.batted_ball.sum(),'a_d_good':True,'tab_name':'FB%'},
|
| 123 |
+
'linedrives_percent':{'format':':.1%','average':df.linedrives.sum()/df.batted_ball.sum(),'a_d_good':True,'tab_name':'LD%'},
|
| 124 |
+
'popups_percent':{'format':':.1%','average':df.popups.sum()/df.batted_ball.sum(),'a_d_good':False,'tab_name':'PU%'},}
|
| 125 |
+
|
| 126 |
+
column_dict = pd.DataFrame(format_dict.keys(),[format_dict[x]['tab_name'] for x in format_dict.keys()]).reset_index().set_index(0).to_dict()['index']
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def server(input,output,session):
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
@output
|
| 133 |
+
@render.text
|
| 134 |
+
@reactive.event(input.go, ignore_none=False)
|
| 135 |
+
def txt_title():
|
| 136 |
+
if input.player_id() == '':
|
| 137 |
+
return 'Select a Player'
|
| 138 |
+
|
| 139 |
+
player_input = int(input.player_id())
|
| 140 |
+
season_1 = max(2015,int(input.season_1()))
|
| 141 |
+
season_2 = min(2023,int(input.season_2()))
|
| 142 |
+
|
| 143 |
+
if season_1 < season_2:
|
| 144 |
+
season_pick = [season_1,season_2]
|
| 145 |
+
|
| 146 |
+
elif season_1 > season_2:
|
| 147 |
+
season_pick = [season_2,season_1]
|
| 148 |
+
|
| 149 |
+
if len(str(input.player_id())) == 0:
|
| 150 |
+
return 'Select a Batter'
|
| 151 |
+
if str(input.season_1()) == str(input.season_2()):
|
| 152 |
+
return 'Select Different Seasons'
|
| 153 |
+
if len(df[(df.player_id == player_input)&(df.year == season_pick[0])] )== 0 or len(df[(df.player_id == player_input)&(df.year == season_pick[1])] )== 0:
|
| 154 |
+
return 'Select Different Seasons'
|
| 155 |
+
return f'{player_dict[int(input.player_id())]} Statcast Season Comparison'
|
| 156 |
+
|
| 157 |
+
@output
|
| 158 |
+
@render.text
|
| 159 |
+
@reactive.event(input.go, ignore_none=False)
|
| 160 |
+
def txt_title_compare():
|
| 161 |
+
if type(input.player_id()) is int or input.player_id()=='':
|
| 162 |
+
return
|
| 163 |
+
|
| 164 |
+
if type(input.player_id_2()) is int or input.player_id_2()=='':
|
| 165 |
+
return
|
| 166 |
+
|
| 167 |
+
player_input_1 = int(input.player_id())
|
| 168 |
+
player_input_2 = int(input.player_id_2())
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
# season_pick = [input.season_1(),input.season_2()]
|
| 172 |
+
columns_i_want = list(input.row_select())
|
| 173 |
+
print(columns_i_want)
|
| 174 |
+
season_1 = max(2015,int(input.season_1()))
|
| 175 |
+
season_2 = min(2023,int(input.season_2()))
|
| 176 |
+
|
| 177 |
+
player_list = [player_input_1,player_input_2]
|
| 178 |
+
name_list = [player_dict[int(player_input_1)],player_dict[int(player_input_2)]]
|
| 179 |
+
season_pick_list = [season_1,season_2]
|
| 180 |
+
|
| 181 |
+
if len(str(input.player_id())) == 0:
|
| 182 |
+
return 'Select a Batter'
|
| 183 |
+
if len(str(input.player_id_2())) == 0:
|
| 184 |
+
return 'Select a Batter'
|
| 185 |
+
|
| 186 |
+
if str(input.player_id()) == str(input.player_id_2()) and str(input.season_1()) == str(input.season_2()):
|
| 187 |
+
return 'Select Different Seasons'
|
| 188 |
+
if len(df[(df.player_id == player_list[0])&(df.year == season_pick_list[0])] )== 0 or len(df[(df.player_id == player_list[1])&(df.year == season_pick_list[1])])== 0:
|
| 189 |
+
return 'No Data for Specified Batter in Given Season'
|
| 190 |
+
|
| 191 |
+
return f'Statcast Season Comparison'
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
@output
|
| 197 |
+
@render.text
|
| 198 |
+
@reactive.event(input.go, ignore_none=False)
|
| 199 |
+
def text_2022():
|
| 200 |
+
if input.player_id() == '':
|
| 201 |
+
return 'Select a Player'
|
| 202 |
+
return f'{int(input.season_1())} Season Results Compares to MLB Average'
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
@output
|
| 206 |
+
@render.text
|
| 207 |
+
@reactive.event(input.go, ignore_none=False)
|
| 208 |
+
def text_2022_1():
|
| 209 |
+
if type(input.player_id()) is int or input.player_id()=='':
|
| 210 |
+
return
|
| 211 |
+
|
| 212 |
+
if type(input.player_id_2()) is int or input.player_id_2()=='':
|
| 213 |
+
return
|
| 214 |
+
season_1 = max(2015,int(input.season_1()))
|
| 215 |
+
season_2 = min(2023,int(input.season_2()))
|
| 216 |
+
season_pick_list = [season_1,season_2]
|
| 217 |
+
player_input_1 = int(input.player_id())
|
| 218 |
+
player_input_2 = int(input.player_id_2())
|
| 219 |
+
name_list = [player_dict[int(player_input_1)],player_dict[int(player_input_2)]]
|
| 220 |
+
return f"{name_list[0]} '{str(season_pick_list[0])[2:]} Season Results Compares to MLB Average"
|
| 221 |
+
|
| 222 |
+
@output
|
| 223 |
+
@render.text
|
| 224 |
+
@reactive.event(input.go, ignore_none=False)
|
| 225 |
+
def text_2023():
|
| 226 |
+
if input.player_id() == '':
|
| 227 |
+
return 'Select a Player'
|
| 228 |
+
return f'{int(input.season_2())} Season Results Compares to MLB Average'
|
| 229 |
+
|
| 230 |
+
@output
|
| 231 |
+
@render.text
|
| 232 |
+
@reactive.event(input.go, ignore_none=False)
|
| 233 |
+
def text_2023_1():
|
| 234 |
+
if type(input.player_id()) is int or input.player_id()=='':
|
| 235 |
+
return
|
| 236 |
+
|
| 237 |
+
if type(input.player_id_2()) is int or input.player_id_2()=='':
|
| 238 |
+
return
|
| 239 |
+
season_1 = max(2015,int(input.season_1()))
|
| 240 |
+
season_2 = min(2023,int(input.season_2()))
|
| 241 |
+
season_pick_list = [season_1,season_2]
|
| 242 |
+
player_input_1 = int(input.player_id())
|
| 243 |
+
player_input_2 = int(input.player_id_2())
|
| 244 |
+
name_list = [player_dict[int(player_input_1)],player_dict[int(player_input_2)]]
|
| 245 |
+
return f"{name_list[1]} '{str(season_pick_list[1])[2:]} Season Results Compares to MLB Average"
|
| 246 |
+
|
| 247 |
+
@output
|
| 248 |
+
@render.text
|
| 249 |
+
@reactive.event(input.go, ignore_none=False)
|
| 250 |
+
def text_diff():
|
| 251 |
+
if input.player_id() == '':
|
| 252 |
+
return 'Select a Player'
|
| 253 |
+
return f'Difference Compares {int(input.season_2())} Results to {int(input.season_1())} Results'
|
| 254 |
+
|
| 255 |
+
@output
|
| 256 |
+
@render.text
|
| 257 |
+
@reactive.event(input.go, ignore_none=False)
|
| 258 |
+
def text_diff_compare():
|
| 259 |
+
if type(input.player_id()) is int or input.player_id()=='':
|
| 260 |
+
return
|
| 261 |
+
|
| 262 |
+
if type(input.player_id_2()) is int or input.player_id_2()=='':
|
| 263 |
+
return
|
| 264 |
+
player_input_1 = int(input.player_id())
|
| 265 |
+
player_input_2 = int(input.player_id_2())
|
| 266 |
+
name_list = [player_dict[int(player_input_1)],player_dict[int(player_input_2)]]
|
| 267 |
+
return f'Difference Compares {name_list[0]} Results to {name_list[1]} Results'
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
|
| 271 |
+
@output
|
| 272 |
+
@render.table
|
| 273 |
+
@reactive.event(input.go, ignore_none=False)
|
| 274 |
+
def statcast_compare():
|
| 275 |
+
if input.player_id() == '':
|
| 276 |
+
return
|
| 277 |
+
|
| 278 |
+
if len(str(input.player_id())) == 0:
|
| 279 |
+
return
|
| 280 |
+
if len(str(input.player_id_2())) == 0:
|
| 281 |
+
return
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
player_input = int(input.player_id())
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
# season_pick = [input.season_1(),input.season_2()]
|
| 288 |
+
columns_i_want = list(input.row_select())
|
| 289 |
+
print(columns_i_want)
|
| 290 |
+
season_1 = max(2015,int(input.season_1()))
|
| 291 |
+
season_2 = min(2023,int(input.season_2()))
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
if season_1 < season_2:
|
| 295 |
+
season_pick = [season_1,season_2]
|
| 296 |
+
|
| 297 |
+
elif season_1 > season_2:
|
| 298 |
+
season_pick = [season_2,season_1]
|
| 299 |
+
|
| 300 |
+
else:
|
| 301 |
+
return
|
| 302 |
+
|
| 303 |
+
print(df[(df.player_id == player_input)&(df.year == season_pick[0])])
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
if len(df[(df.player_id == player_input)&(df.year == season_pick[0])] )== 0 or len(df[(df.player_id == player_input)&(df.year == season_pick[1])] )== 0:
|
| 307 |
+
return
|
| 308 |
+
|
| 309 |
+
df_compare = pd.concat([df[(df.player_id == player_input)&(df.year == season_pick[0])][[ 'player_age', 'pa']+columns_i_want],
|
| 310 |
+
df[(df.player_id == player_input)&(df.year == season_pick[1])][[ 'player_age', 'pa']+columns_i_want]]).reset_index(drop=True).T
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
print('test')
|
| 314 |
+
print(sum(df.player_id == input.player_id()))
|
| 315 |
+
df_compare.columns = season_pick
|
| 316 |
+
|
| 317 |
+
df_compare['Difference'] = df_compare.loc[columns_i_want][season_pick[1]] - df_compare.loc[columns_i_want][season_pick[0]]
|
| 318 |
+
|
| 319 |
+
df_compare_style = df_compare.style.format(
|
| 320 |
+
"{:.0f}")
|
| 321 |
+
df_compare_style = df_compare_style.set_properties(**{'background-color': 'white',
|
| 322 |
+
'color': 'white'},subset=(['player_age', 'pa'],df_compare_style.columns[2])).set_properties(
|
| 323 |
+
**{'min-width':'100px'},overwrite=False).set_table_styles(
|
| 324 |
+
[{'selector': 'th:first-child', 'text-align': 'center','props': [('background-color', 'white')]}],overwrite=False).set_table_styles(
|
| 325 |
+
[{'selector': 'tr:first-child','text-align': 'center', 'props': [('background-color', 'white')]}],overwrite=False).set_table_styles(
|
| 326 |
+
[{'selector': 'index','text-align': 'center', 'props': [('background-color', 'white')]}],overwrite=False).set_table_styles(
|
| 327 |
+
[{'selector': 'th', 'text-align': 'center','props': [('line-height', '40px'),('min-width', '30px')]}],overwrite=False).set_properties(
|
| 328 |
+
|
| 329 |
+
**{'Height': '20px'},**{'text-align': 'center'},overwrite=False).set_table_styles([{
|
| 330 |
+
'selector': 'caption',
|
| 331 |
+
'props': [
|
| 332 |
+
('color', ''),
|
| 333 |
+
('fontname', 'Century Gothic'),
|
| 334 |
+
('font-size', '20px'),
|
| 335 |
+
('font-style', 'italic'),
|
| 336 |
+
('font-weight', ''),
|
| 337 |
+
('text-align', 'centre'),
|
| 338 |
+
]
|
| 339 |
+
|
| 340 |
+
},{'selector' :'th', 'props':[('text-align', 'center'),('font-size', '20px'),('Height','20px'),('min-width','200px')]},{'selector' :'td', 'props':[('text-align', 'center'),('font-size', '20px'),('min-width','100px')]}],overwrite=False)
|
| 341 |
+
|
| 342 |
+
|
| 343 |
+
for r in columns_i_want:
|
| 344 |
+
if format_dict[r]['a_d_good']:
|
| 345 |
+
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ["#4285F4","white","#FBBC04"])
|
| 346 |
+
else:
|
| 347 |
+
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ["#FBBC04","white","#4285F4"])
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
colormap = plt.get_cmap(cmap)
|
| 351 |
+
norm = Normalize(vmin=0.7, vmax=1.3)
|
| 352 |
+
|
| 353 |
+
normalized_value = norm(df_compare[df_compare.columns[0]][r]/format_dict[r]['average'])
|
| 354 |
+
df_compare_style.format(
|
| 355 |
+
f"{{{format_dict[r]['format']}}}",subset=(r,df_compare_style.columns[0])).set_properties(**{'background-color': '#%02x%02x%02x' % (int(colormap(normalized_value)[0] *255), int(colormap(normalized_value)[1] *255), int(colormap(normalized_value)[2] *255)),
|
| 356 |
+
'color': 'black'},subset=(r,df_compare_style.columns[0]))
|
| 357 |
+
|
| 358 |
+
norm = Normalize(vmin=0.7, vmax=1.3)
|
| 359 |
+
normalized_value = norm(df_compare[df_compare.columns[1]][r]/format_dict[r]['average'])
|
| 360 |
+
df_compare_style.format(
|
| 361 |
+
f"{{{format_dict[r]['format']}}}",subset=(r,df_compare_style.columns[1])).set_properties(**{'background-color': '#%02x%02x%02x' % (int(colormap(normalized_value)[0] *255), int(colormap(normalized_value)[1] *255), int(colormap(normalized_value)[2] *255)),
|
| 362 |
+
'color': 'black'},subset=(r,df_compare_style.columns[1]))
|
| 363 |
+
|
| 364 |
+
norm = Normalize(vmin=0.7, vmax=1.3)
|
| 365 |
+
normalized_value = norm(df_compare[df_compare.columns[1]][r]/df_compare[df_compare.columns[0]][r])
|
| 366 |
+
df_compare_style.format(
|
| 367 |
+
f"{{{format_dict[r]['format']}}}",subset=(r,df_compare_style.columns[2])).set_properties(**{'background-color': '#%02x%02x%02x' % (int(colormap(normalized_value)[0] *255), int(colormap(normalized_value)[1] *255), int(colormap(normalized_value)[2] *255)),
|
| 368 |
+
'color': 'black'},subset=(r,df_compare_style.columns[2]))
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
|
| 372 |
+
|
| 373 |
+
df_compare_style.relabel_index(['Age', 'PA']+[format_dict[x]['tab_name'] for x in columns_i_want]).set_properties(
|
| 374 |
+
**{'border': '1px black solid !important'},overwrite=False).set_table_styles(
|
| 375 |
+
[{"selector": "", "props": [("border", "1px solid")]},
|
| 376 |
+
{"selector": "tbody td", "props": [("border", "1px solid")]},
|
| 377 |
+
{"selector": "th", "props": [("border", "1px solid")]}],overwrite=False)
|
| 378 |
+
|
| 379 |
+
#df_compare = df_compare.fillna(np.nan)
|
| 380 |
+
|
| 381 |
+
return df_compare_style
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
@output
|
| 386 |
+
@render.table
|
| 387 |
+
@reactive.event(input.go, ignore_none=False)
|
| 388 |
+
def statcast_compare_2():
|
| 389 |
+
|
| 390 |
+
# if input.player_id() == 0:
|
| 391 |
+
# return
|
| 392 |
+
if type(input.player_id()) is int or input.player_id()=='':
|
| 393 |
+
return
|
| 394 |
+
|
| 395 |
+
if type(input.player_id_2()) is int or input.player_id_2()=='':
|
| 396 |
+
return
|
| 397 |
+
# if len(str(input.player_id())) == 0:
|
| 398 |
+
# return
|
| 399 |
+
|
| 400 |
+
player_input_1 = int(input.player_id())
|
| 401 |
+
player_input_2 = int(input.player_id_2())
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
# season_pick = [input.season_1(),input.season_2()]
|
| 405 |
+
columns_i_want = list(input.row_select())
|
| 406 |
+
print(columns_i_want)
|
| 407 |
+
season_1 = max(2015,int(input.season_1()))
|
| 408 |
+
season_2 = min(2023,int(input.season_2()))
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
# if season_1 < season_2:
|
| 412 |
+
# season_pick = [season_1,season_2]
|
| 413 |
+
|
| 414 |
+
# elif season_1 > season_2:
|
| 415 |
+
# season_pick = [season_2,season_1]
|
| 416 |
+
|
| 417 |
+
# else:
|
| 418 |
+
# return
|
| 419 |
+
|
| 420 |
+
#print(df[(df.player_id == player_input)&(df.year == season_pick[0])])
|
| 421 |
+
#player_list = ['Elly De La Cruz','Aaron Judge']
|
| 422 |
+
player_list = [player_input_1,player_input_2]
|
| 423 |
+
name_list = [player_dict[int(player_input_1)],player_dict[int(player_input_2)]]
|
| 424 |
+
season_pick_list = [season_1,season_2]
|
| 425 |
+
|
| 426 |
+
if len(df[(df.player_id == player_list[0])&(df.year == season_pick_list[0])] )== 0 or len(df[(df.player_id == player_list[1])&(df.year == season_pick_list[1])] )== 0:
|
| 427 |
+
return
|
| 428 |
+
if str(input.player_id()) == str(input.player_id_2()) and str(input.season_1()) == str(input.season_2()):
|
| 429 |
+
return
|
| 430 |
+
|
| 431 |
+
|
| 432 |
+
df_compare = pd.concat([df[(df.player_id == player_list[0])&(df.year == season_pick_list[0])][[ 'year','player_age', 'pa']+columns_i_want],
|
| 433 |
+
df[(df.player_id == player_list[1])&(df.year == season_pick_list[1])][[ 'year','player_age', 'pa']+columns_i_want]]).reset_index(drop=True).T
|
| 434 |
+
|
| 435 |
+
df_compare.columns = [f"{name_list[0]} '{str(season_pick_list[0])[2:]}",f"{name_list[1]} '{str(season_pick_list[1])[2:]}"]
|
| 436 |
+
df_compare['Difference'] = df_compare.loc[columns_i_want][df_compare.columns [0]] - df_compare.loc[columns_i_want][df_compare.columns[1]]
|
| 437 |
+
#df_compare = df_compare.fillna(np.nan)
|
| 438 |
+
|
| 439 |
+
df_compare_style = df_compare.style.format(
|
| 440 |
+
"{:.0f}")
|
| 441 |
+
df_compare_style = df_compare_style.set_properties(**{'background-color': 'white',
|
| 442 |
+
'color': 'white'},subset=(['year','player_age', 'pa'],df_compare_style.columns[2])).set_properties(
|
| 443 |
+
**{'min-width':'125px'},overwrite=False).set_table_styles(
|
| 444 |
+
[{'selector': 'th:first-child', 'text-align': 'center','props': [('background-color', 'white')]}],overwrite=False).set_table_styles(
|
| 445 |
+
[{'selector': 'tr:first-child','text-align': 'center', 'props': [('background-color', 'white')]}],overwrite=False).set_table_styles(
|
| 446 |
+
[{'selector': 'index','text-align': 'center', 'props': [('background-color', 'white')]}],overwrite=False).set_table_styles(
|
| 447 |
+
[{'selector': 'th', 'text-align': 'center','props': [('line-height', '40px'),('min-width', '30px')]}],overwrite=False).set_properties(
|
| 448 |
+
|
| 449 |
+
**{'Height': '20px'},**{'text-align': 'center'},overwrite=False).set_table_styles([{
|
| 450 |
+
'selector': 'caption',
|
| 451 |
+
'props': [
|
| 452 |
+
('color', ''),
|
| 453 |
+
('fontname', 'Century Gothic'),
|
| 454 |
+
('font-size', '20px'),
|
| 455 |
+
('font-style', 'italic'),
|
| 456 |
+
('font-weight', ''),
|
| 457 |
+
('text-align', 'centre'),
|
| 458 |
+
]
|
| 459 |
+
|
| 460 |
+
},{'selector' :'th', 'props':[('text-align', 'center'),('font-size', '20px'),('Height','20px'),('min-width','200px')]},{'selector' :'td', 'props':[('text-align', 'center'),('font-size', '20px'),('min-width','100px')]}],overwrite=False)
|
| 461 |
+
|
| 462 |
+
|
| 463 |
+
for r in columns_i_want:
|
| 464 |
+
if format_dict[r]['a_d_good']:
|
| 465 |
+
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ["#4285F4","white","#FBBC04"])
|
| 466 |
+
else:
|
| 467 |
+
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ["#FBBC04","white","#4285F4"])
|
| 468 |
+
|
| 469 |
+
|
| 470 |
+
colormap = plt.get_cmap(cmap)
|
| 471 |
+
norm = Normalize(vmin=0.5, vmax=1.5)
|
| 472 |
+
normalized_value = norm(df_compare[df_compare.columns[0]][r]/format_dict[r]['average'])
|
| 473 |
+
df_compare_style.format(
|
| 474 |
+
f"{{{format_dict[r]['format']}}}",subset=(r,df_compare_style.columns[0])).set_properties(**{'background-color': '#%02x%02x%02x' % (int(colormap(normalized_value)[0] *255), int(colormap(normalized_value)[1] *255), int(colormap(normalized_value)[2] *255)),
|
| 475 |
+
'color': 'black'},subset=(r,df_compare_style.columns[0]))
|
| 476 |
+
|
| 477 |
+
norm = Normalize(vmin=0.5, vmax=1.5)
|
| 478 |
+
normalized_value = norm(df_compare[df_compare.columns[1]][r]/format_dict[r]['average'])
|
| 479 |
+
df_compare_style.format(
|
| 480 |
+
f"{{{format_dict[r]['format']}}}",subset=(r,df_compare_style.columns[1])).set_properties(**{'background-color': '#%02x%02x%02x' % (int(colormap(normalized_value)[0] *255), int(colormap(normalized_value)[1] *255), int(colormap(normalized_value)[2] *255)),
|
| 481 |
+
'color': 'black'},subset=(r,df_compare_style.columns[1]))
|
| 482 |
+
|
| 483 |
+
norm = Normalize(vmin=0.8, vmax=1.2)
|
| 484 |
+
normalized_value = norm(df_compare[df_compare.columns[0]][r]/df_compare[df_compare.columns[1]][r])
|
| 485 |
+
df_compare_style.format(
|
| 486 |
+
f"{{{format_dict[r]['format']}}}",subset=(r,df_compare_style.columns[2])).set_properties(**{'background-color': '#%02x%02x%02x' % (int(colormap(normalized_value)[0] *255), int(colormap(normalized_value)[1] *255), int(colormap(normalized_value)[2] *255)),
|
| 487 |
+
'color': 'black'},subset=(r,df_compare_style.columns[2]))
|
| 488 |
+
|
| 489 |
+
df_compare_style = df_compare_style
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
df_compare_style.relabel_index(['Year','Age', 'PA']+[format_dict[x]['tab_name'] for x in columns_i_want]).set_properties(
|
| 493 |
+
**{'border': '1px black solid !important'},overwrite=False).set_table_styles(
|
| 494 |
+
[{"selector": "", "props": [("border", "1px solid")]},
|
| 495 |
+
{"selector": "tbody td", "props": [("border", "1px solid")]},
|
| 496 |
+
{"selector": "th", "props": [("border", "1px solid")]}],overwrite=False)
|
| 497 |
+
|
| 498 |
+
#df_compare = df_compare.fillna(np.nan)
|
| 499 |
+
|
| 500 |
+
return df_compare_style
|
| 501 |
+
|
| 502 |
+
@output
|
| 503 |
+
@render.table
|
| 504 |
+
@reactive.event(input.go, ignore_none=False)
|
| 505 |
+
def colour_scale():
|
| 506 |
+
off_b2b_df = pd.DataFrame(data={'one':-0.30,'two':0,'three':0.30},index=[0])
|
| 507 |
+
off_b2b_df_style = off_b2b_df.style.set_properties(**{'border': '3 px'},overwrite=False).set_table_styles([{
|
| 508 |
+
'selector': 'caption',
|
| 509 |
+
'props': [
|
| 510 |
+
('color', ''),
|
| 511 |
+
('fontname', 'Century Gothic'),
|
| 512 |
+
('font-size', '20px'),
|
| 513 |
+
('font-style', 'italic'),
|
| 514 |
+
('font-weight', ''),
|
| 515 |
+
('text-align', 'centre'),
|
| 516 |
+
]
|
| 517 |
+
|
| 518 |
+
},{'selector' :'th', 'props':[('text-align', 'center'),('Height','px'),('color','black'),(
|
| 519 |
+
'border', '1px black solid !important')]},{'selector' :'td', 'props':[('text-align', 'center'),('font-size', '18px'),('color','black')]}],overwrite=False).set_properties(
|
| 520 |
+
**{'background-color':'White','index':'White','min-width':'150px'},overwrite=False).set_table_styles(
|
| 521 |
+
[{'selector': 'th:first-child', 'props': [('background-color', 'white')]}],overwrite=False).set_table_styles(
|
| 522 |
+
[{'selector': 'tr:first-child', 'props': [('background-color', 'white')]}],overwrite=False).set_table_styles(
|
| 523 |
+
[{'selector': 'tr', 'props': [('line-height', '20px')]}],overwrite=False).set_properties(
|
| 524 |
+
**{'Height': '8px'},**{'text-align': 'center'},overwrite=False).set_properties(
|
| 525 |
+
**{'background-color':'#4285F4'},subset=off_b2b_df.columns[0]).set_properties(
|
| 526 |
+
**{'background-color':'white'},subset=off_b2b_df.columns[1]).set_properties(
|
| 527 |
+
**{'background-color':'#FBBC04'},subset=off_b2b_df.columns[2]).set_properties(
|
| 528 |
+
**{'color':'black'},subset=off_b2b_df.columns[:]).hide_index().set_table_styles([
|
| 529 |
+
{'selector': 'thead', 'props': [('display', 'none')]}
|
| 530 |
+
]).set_properties(**{'border': '3 px','color':'black'},overwrite=False).set_properties(
|
| 531 |
+
**{'border': '1px black solid !important'},subset = ((list(off_b2b_df.index[:]),off_b2b_df.columns[:]))).set_properties(
|
| 532 |
+
**{'min-width':'130'},subset = ((list(off_b2b_df.index[:]),off_b2b_df.columns[:])),overwrite=False).set_properties(**{
|
| 533 |
+
'color': 'black'},overwrite=False).set_properties(
|
| 534 |
+
**{'border': '1px black solid !important'},subset = ((list(off_b2b_df.index[:]),off_b2b_df.columns[:]))) .format(
|
| 535 |
+
"{:+.0%}")
|
| 536 |
+
return off_b2b_df_style
|
| 537 |
+
|
| 538 |
+
@output
|
| 539 |
+
@render.table
|
| 540 |
+
@reactive.event(input.go, ignore_none=False)
|
| 541 |
+
def colour_scale_2():
|
| 542 |
+
off_b2b_df = pd.DataFrame(data={'one':-0.30,'two':0,'three':0.30},index=[0])
|
| 543 |
+
off_b2b_df_style = off_b2b_df.style.set_properties(**{'border': '3 px'},overwrite=False).set_table_styles([{
|
| 544 |
+
'selector': 'caption',
|
| 545 |
+
'props': [
|
| 546 |
+
('color', ''),
|
| 547 |
+
('fontname', 'Century Gothic'),
|
| 548 |
+
('font-size', '20px'),
|
| 549 |
+
('font-style', 'italic'),
|
| 550 |
+
('font-weight', ''),
|
| 551 |
+
('text-align', 'centre'),
|
| 552 |
+
]
|
| 553 |
+
|
| 554 |
+
},{'selector' :'th', 'props':[('text-align', 'center'),('Height','px'),('color','black'),(
|
| 555 |
+
'border', '1px black solid !important')]},{'selector' :'td', 'props':[('text-align', 'center'),('font-size', '18px'),('color','black')]}],overwrite=False).set_properties(
|
| 556 |
+
**{'background-color':'White','index':'White','min-width':'150px'},overwrite=False).set_table_styles(
|
| 557 |
+
[{'selector': 'th:first-child', 'props': [('background-color', 'white')]}],overwrite=False).set_table_styles(
|
| 558 |
+
[{'selector': 'tr:first-child', 'props': [('background-color', 'white')]}],overwrite=False).set_table_styles(
|
| 559 |
+
[{'selector': 'tr', 'props': [('line-height', '20px')]}],overwrite=False).set_properties(
|
| 560 |
+
**{'Height': '8px'},**{'text-align': 'center'},overwrite=False).set_properties(
|
| 561 |
+
**{'background-color':'#4285F4'},subset=off_b2b_df.columns[0]).set_properties(
|
| 562 |
+
**{'background-color':'white'},subset=off_b2b_df.columns[1]).set_properties(
|
| 563 |
+
**{'background-color':'#FBBC04'},subset=off_b2b_df.columns[2]).set_properties(
|
| 564 |
+
**{'color':'black'},subset=off_b2b_df.columns[:]).hide_index().set_table_styles([
|
| 565 |
+
{'selector': 'thead', 'props': [('display', 'none')]}
|
| 566 |
+
]).set_properties(**{'border': '3 px','color':'black'},overwrite=False).set_properties(
|
| 567 |
+
**{'border': '1px black solid !important'},subset = ((list(off_b2b_df.index[:]),off_b2b_df.columns[:]))).set_properties(
|
| 568 |
+
**{'min-width':'130'},subset = ((list(off_b2b_df.index[:]),off_b2b_df.columns[:])),overwrite=False).set_properties(**{
|
| 569 |
+
'color': 'black'},overwrite=False).set_properties(
|
| 570 |
+
**{'border': '1px black solid !important'},subset = ((list(off_b2b_df.index[:]),off_b2b_df.columns[:]))) .format(
|
| 571 |
+
"{:+.0%}")
|
| 572 |
+
|
| 573 |
+
|
| 574 |
+
return off_b2b_df_style
|
| 575 |
+
# test = test.fillna(0)
|
| 576 |
+
#test['PP TOI'] = ["%d:%02d" % (int(x),(x*60)%60) if x>0 else '0:00' for x in test['PP TOI']]
|
| 577 |
+
|
| 578 |
+
|
| 579 |
+
statcast_compare = App(ui.page_fluid(
|
| 580 |
+
ui.tags.base(href=base_url),
|
| 581 |
+
ui.tags.div(
|
| 582 |
+
{"style": "width:90%;margin: 0 auto;max-width: 1600px;"},
|
| 583 |
+
ui.tags.style(
|
| 584 |
+
"""
|
| 585 |
+
h4 {
|
| 586 |
+
margin-top: 1em;font-size:35px;
|
| 587 |
+
}
|
| 588 |
+
h2{
|
| 589 |
+
font-size:25px;
|
| 590 |
+
}
|
| 591 |
+
"""
|
| 592 |
+
),
|
| 593 |
+
shinyswatch.theme.simplex(),
|
| 594 |
+
ui.tags.h4("TJStats"),
|
| 595 |
+
ui.tags.i("Baseball Analytics and Visualizations"),
|
| 596 |
+
ui.markdown("""<a href='https://www.patreon.com/tj_stats'>Support me on Patreon for Access to 2024 Apps</a><sup>1</sup>"""),
|
| 597 |
+
ui.navset_tab(
|
| 598 |
+
ui.nav_control(
|
| 599 |
+
ui.a(
|
| 600 |
+
"Home",
|
| 601 |
+
href="home/"
|
| 602 |
+
),
|
| 603 |
+
),
|
| 604 |
+
ui.nav_menu(
|
| 605 |
+
"Batter Charts",
|
| 606 |
+
ui.nav_control(
|
| 607 |
+
ui.a(
|
| 608 |
+
"Batting Rolling",
|
| 609 |
+
href="rolling_batter/"
|
| 610 |
+
),
|
| 611 |
+
ui.a(
|
| 612 |
+
"Spray",
|
| 613 |
+
href="spray/"
|
| 614 |
+
),
|
| 615 |
+
ui.a(
|
| 616 |
+
"Decision Value",
|
| 617 |
+
href="decision_value/"
|
| 618 |
+
),
|
| 619 |
+
ui.a(
|
| 620 |
+
"Damage Model",
|
| 621 |
+
href="damage_model/"
|
| 622 |
+
),
|
| 623 |
+
ui.a(
|
| 624 |
+
"Batter Scatter",
|
| 625 |
+
href="batter_scatter/"
|
| 626 |
+
),
|
| 627 |
+
ui.a(
|
| 628 |
+
"EV vs LA Plot",
|
| 629 |
+
href="ev_angle/"
|
| 630 |
+
),
|
| 631 |
+
ui.a(
|
| 632 |
+
"Statcast Compare",
|
| 633 |
+
href="statcast_compare/"
|
| 634 |
+
)
|
| 635 |
+
),
|
| 636 |
+
),
|
| 637 |
+
ui.nav_menu(
|
| 638 |
+
"Pitcher Charts",
|
| 639 |
+
ui.nav_control(
|
| 640 |
+
ui.a(
|
| 641 |
+
"Pitcher Rolling",
|
| 642 |
+
href="rolling_pitcher/"
|
| 643 |
+
),
|
| 644 |
+
ui.a(
|
| 645 |
+
"Pitcher Summary",
|
| 646 |
+
href="pitching_summary_graphic_new/"
|
| 647 |
+
),
|
| 648 |
+
ui.a(
|
| 649 |
+
"Pitcher Scatter",
|
| 650 |
+
href="pitcher_scatter/"
|
| 651 |
+
)
|
| 652 |
+
),
|
| 653 |
+
)),ui.row(
|
| 654 |
+
ui.layout_sidebar(
|
| 655 |
+
|
| 656 |
+
|
| 657 |
+
|
| 658 |
+
ui.panel_sidebar(
|
| 659 |
+
#ui.input_date_range("date_range_id", "Date range input",start = statcast_df.game_date.min(), end = statcast_df.game_date.max()),
|
| 660 |
+
ui.input_select("player_id", "Select Player 1",player_dict,width=1,size=1,selectize=True,multiple=False,selected=592450),
|
| 661 |
+
ui.input_select("player_id_2", "Select Player 2 (For Player Compare Tab)",player_dict,width=1,size=1,selectize=True,multiple=False,selected=592450),
|
| 662 |
+
ui.input_numeric("season_1", "Season 1", value=2022,min=2015,max=2023),
|
| 663 |
+
ui.input_numeric("season_2", "Season 2", value=2023,min=2015,max=2023),
|
| 664 |
+
ui.input_select("row_select", "Select Stats",
|
| 665 |
+
column_dict,width=1,size=1,selectize=True,
|
| 666 |
+
multiple=True,
|
| 667 |
+
selected=['k_percent','bb_percent','woba','xwoba','iz_contact_percent','oz_swing_percent','whiff_percent']),
|
| 668 |
+
ui.input_action_button("go", "Generate",class_="btn-primary",
|
| 669 |
+
)),
|
| 670 |
+
|
| 671 |
+
ui.panel_main(ui.tags.h3(""),
|
| 672 |
+
ui.navset_tab(
|
| 673 |
+
ui.nav("Single Player",
|
| 674 |
+
ui.div({"style": "font-size:2.1em;"},ui.output_text("txt_title")),
|
| 675 |
+
#ui.tags.h2("Fantasy Hockey Schedule Summary"),
|
| 676 |
+
ui.tags.h5("Created By: @TJStats, Data: MLB"),
|
| 677 |
+
#ui.div({"style": "font-size:1.6em;"},ui.output_text("txt")),
|
| 678 |
+
ui.output_table("statcast_compare"),
|
| 679 |
+
#ui.tags.h5('Legend'),
|
| 680 |
+
ui.tags.h3(""),
|
| 681 |
+
ui.tags.h5('Colour Scale:'),
|
| 682 |
+
ui.output_table("colour_scale"),
|
| 683 |
+
ui.div({"style": "font-size:1em;"},ui.output_text("text_2022")),
|
| 684 |
+
ui.div({"style": "font-size:1em;"},ui.output_text("text_2023")),
|
| 685 |
+
ui.div({"style": "font-size:1em;"},ui.output_text("text_diff"))),
|
| 686 |
+
|
| 687 |
+
ui.nav("Player Compare",
|
| 688 |
+
ui.div({"style": "font-size:2.1em;"},ui.output_text("txt_title_compare")),
|
| 689 |
+
#ui.tags.h2("Fantasy Hockey Schedule Summary"),
|
| 690 |
+
ui.tags.h5("Created By: @TJStats, Data: MLB"),
|
| 691 |
+
#ui.div({"style": "font-size:1.6em;"},ui.output_text("txt")),
|
| 692 |
+
ui.output_table("statcast_compare_2"),
|
| 693 |
+
ui.tags.h3(""),
|
| 694 |
+
ui.tags.h5('Colour Scale:'),
|
| 695 |
+
ui.output_table("colour_scale_2"),
|
| 696 |
+
ui.div({"style": "font-size:1em;"},ui.output_text("text_2022_1")),
|
| 697 |
+
ui.div({"style": "font-size:1em;"},ui.output_text("text_2023_1")),
|
| 698 |
+
ui.div({"style": "font-size:1em;"},ui.output_text("text_diff_compare")))
|
| 699 |
+
)
|
| 700 |
+
),
|
| 701 |
+
)),)),server)
|
statcast_pitch_summary.csv
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
,pitch_name,pitches,swings,whiff,csw,out_zone,chase,pitch_velocity,spin_rate,exit_velocity,pitch_velocity_std,spin_rate_std,exit_velocity_std,pfx_x,pfx_z,extension,release_x,release_z,zone_percent,xwOBA,pitch,whiff_rate,csw_rate,chase_percent,pitch_percent
|
| 2 |
+
0,4-Seam Fastball,230929.0,111299.0,24750.0,64996.0,105087,25567.0,94.19093625756828,2282.8438012609377,83.74365680792623,2.4786532395740055,166.7404996241093,13.467960960835905,-0.2653508555392431,1.3087501028552622,6.512675018103453,-0.8060702360690668,5.833463472307033,0.5448427871770111,0.40198316316909566,,0.2223739656241296,0.28145447301984594,0.243293651926499,0.16085685746486877
|
| 3 |
+
1,Slider,126071.0,60687.0,20453.0,39120.0,69402,21991.0,85.11885798600645,2408.7220697139937,81.64673187847214,3.3273820646525754,254.3806272913541,15.94645745407159,0.25503050110660713,0.1340754872641023,6.415392251066094,-0.9940703310302156,5.742570977542618,0.4494213578063155,0.36039362273548514,,0.3370244039085801,0.3103013381348605,0.31686406731794475,0.08781653615376793
|
| 4 |
+
2,Sinker,110881.0,50193.0,7235.0,31514.0,49458,12493.0,93.31904199285688,2150.2386523758873,83.87984289276808,2.892717921060944,168.33013944189094,15.447823735442787,-0.5781807359307359,0.6536634199134199,6.419496957697072,-0.8578973665223666,5.636833874458874,0.5539452205517627,0.3630746667835847,,0.14414360568206722,0.28421460845410845,0.25259816409883135,0.07723572705273965
|
| 5 |
+
3,Changeup,78512.0,39518.0,12295.0,19910.0,47870,16185.0,85.45176986078029,1789.468468007219,80.71900963512873,3.4111014745537154,305.36591130209456,16.47019099576866,-0.396463507833397,0.474831486434849,6.458975699314073,-0.568952999617883,5.749977200356643,0.3902588139392704,0.34566906762705085,,0.3111240447391062,0.2535918076217648,0.3381031961562565,0.054688642800522144
|
| 6 |
+
4,Cutter,55805.0,27665.0,6714.0,15683.0,27167,7768.0,89.22766029952471,2387.958273978037,82.31847715218278,3.2905648267822385,206.66655976707708,15.415902082385763,0.12278660592581965,0.639279718774661,6.36341704390314,-0.9624070952005166,5.842086232871798,0.5123017650748141,0.36965886615098403,,0.24268931863365262,0.28103216557656124,0.2859351419000994,0.03887176115094684
|
| 7 |
+
5,Curveball,51679.0,21553.0,6701.0,16148.0,29175,8309.0,79.2795228051164,2529.729456384324,81.09545750923714,3.845559953202193,303.1060225022905,16.897464856909227,0.3128750556318814,-0.7793070686352289,6.357315891472868,-0.6464800015480177,5.931275179473287,0.4354573424408367,0.36297365675534693,,0.3109079942467406,0.31246734650438285,0.2847986289631534,0.03599773755971296
|
| 8 |
+
6,Sweeper,30983.0,14387.0,4714.0,9472.0,17497,5501.0,81.87104541200013,2573.167409915042,79.5424205782126,2.9169456469962296,242.3612934233338,16.05341300254306,0.626698834844915,0.10297743924087403,6.440382997994436,-1.0498637962753767,5.449356421263274,0.4352709550398606,0.34346382040489076,,0.32765691249044276,0.3057160378271956,0.3143967537292107,0.021581646371109865
|
| 9 |
+
7,Split-Finger,15570.0,8130.0,2828.0,3995.0,9884,3589.0,86.61461143224149,1356.4218921536972,81.58194705023973,3.210384887902199,330.0858364738183,17.118239986777326,-0.8700796403339756,0.2713461785484907,6.4252829218107,-1.7122678227360306,5.850094412331407,0.36518946692357096,0.3443354243542435,,0.34784747847478475,0.25658317276814385,0.36311210036422503,0.01084550346958592
|
| 10 |
+
8,Knuckle Curve,12154.0,5439.0,1805.0,3752.0,7059,2292.0,81.43667105479678,2469.610794751176,81.99410356180783,3.3353055020455646,284.5354624498529,16.265551322730666,0.4980812901102518,-0.7952756294224125,6.3597479614529275,-1.0805693598815205,5.938246667763699,0.41920355438538753,0.37180873734683006,,0.33186247471961755,0.30870495310185947,0.3246918827029324,0.00846604040907818
|
| 11 |
+
9,Slurve,2330.0,984.0,273.0,767.0,1262,365.0,82.12420600858368,2524.4065886432595,80.52035398230089,2.558741806283268,306.1556785170377,16.356798085102586,0.3707424892703863,-0.25004291845493565,6.026536796536797,-0.7101459227467811,5.605351931330472,0.45836909871244635,0.3978255208333333,,0.2774390243902439,0.32918454935622316,0.28922345483359746,0.00162299441773508
|
| 12 |
+
10,Other,1099.0,553.0,39.0,192.0,576,161.0,66.9722474977252,1636.2980856882407,84.21832669322708,9.112324944344907,265.8499548102452,16.429985208785887,-0.4521565059144677,1.3244676979071883,4.820714940421631,-1.647452229299363,6.3448225659690625,0.47588717015468607,0.44125391849529777,,0.0705244122965642,0.17470427661510465,0.2795138888888889,0.0007655239764338425
|
| 13 |
+
11,Forkball,778.0,358.0,199.0,247.0,590,214.0,82.96773778920308,1079.639686684073,77.80469798657718,1.5813830914720153,180.5812639240975,16.635375590080205,-0.6015809768637532,0.10609254498714654,6.465681233933163,-1.8326992287917736,5.97146529562982,0.2416452442159383,0.31482432432432433,,0.5558659217877095,0.31748071979434445,0.36271186440677966,0.0005419268914154044
|
| 14 |
+
12,Eephus,522.0,226.0,7.0,79.0,339,92.0,47.99444444444444,1168.9176245210729,85.22054794520548,6.869976110853827,235.4947805488423,16.73137435394275,0.008371647509578542,1.393352490421456,4.532567049808429,-1.4747318007662835,6.651915708812261,0.3505747126436782,0.3907325581395349,,0.030973451327433628,0.15134099616858238,0.2713864306784661,0.00036360647470288056
|
| 15 |
+
13,Knuckleball,190.0,81.0,20.0,44.0,97,18.0,75.68631578947368,337.4578947368421,79.05087719298245,4.960630411514611,393.10082691661756,16.785560576270584,-0.2866315789473684,-0.16442105263157897,6.280526315789474,-1.598,5.561368421052632,0.48947368421052634,0.3248333333333333,,0.24691358024691357,0.23157894736842105,0.18556701030927836,0.00013234718427882627
|
| 16 |
+
14,Screwball,74.0,34.0,5.0,10.0,41,10.0,80.18648648648649,2094.9594594594596,83.2423076923077,1.450679984566931,133.61049905317344,13.026010074521608,-0.7537837837837837,-0.37148648648648647,6.20945945945946,-1.2633783783783783,6.0683783783783785,0.44594594594594594,0.40330000000000005,,0.14705882352941177,0.13513513513513514,0.24390243902439024,5.1545745455963914e-05
|
| 17 |
+
15,Slow Curve,51.0,21.0,1.0,2.0,36,8.0,59.17450980392157,2058.7551020408164,77.07000000000001,11.825867293983224,429.4794101880903,17.29673227428872,0.06490196078431372,-0.7588235294117648,5.652941176470589,-0.72,6.276274509803921,0.29411764705882354,0.21123076923076922,,0.047619047619047616,0.0392156862745098,0.2222222222222222,3.5524770516948104e-05
|
| 18 |
+
16,Pitch Out,46.0,0.0,0.0,0.0,46,0.0,90.01086956521739,2212.1304347826085,,2.9954950072006916,143.40565751983308,,-0.6036956521739131,1.2219565217391304,6.254347826086956,-1.803478260869565,5.74195652173913,0.0,,,,0.0,0.0,3.204194987803162e-05
|
| 19 |
+
17,All,717944.0,341263.0,88076.0,206012.0,365586,104563.0,88.99871914935127,2249.1345844165853,82.5959800585916,6.109454214467076,339.2167730889254,15.224075051082256,-0.12493170194679414,0.5903365570869159,6.43911718239724,-0.8595453392605805,5.7720100196491035,0.4902945076496217,0.37195099345233773,1.0,0.25808833656153757,0.2869471713671261,0.28601478174765993,0.5000940361572508
|
summary_pitcher.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
summary_pitcher_level.csv
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
test_data_mlb.ipynb
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cells": [
|
| 3 |
+
{
|
| 4 |
+
"cell_type": "code",
|
| 5 |
+
"execution_count": 1,
|
| 6 |
+
"metadata": {},
|
| 7 |
+
"outputs": [],
|
| 8 |
+
"source": [
|
| 9 |
+
"import time\n",
|
| 10 |
+
"import requests\n",
|
| 11 |
+
"import pandas as pd\n",
|
| 12 |
+
"import seaborn as sns\n",
|
| 13 |
+
"import matplotlib.pyplot as plt\n",
|
| 14 |
+
"from matplotlib.pyplot import figure\n",
|
| 15 |
+
"from matplotlib.offsetbox import OffsetImage, AnnotationBbox\n",
|
| 16 |
+
"from scipy import stats\n",
|
| 17 |
+
"import matplotlib.lines as mlines\n",
|
| 18 |
+
"import matplotlib.transforms as mtransforms\n",
|
| 19 |
+
"import numpy as np\n",
|
| 20 |
+
"import time\n",
|
| 21 |
+
"#import plotly.express as px\n",
|
| 22 |
+
"#!pip install chart_studio\n",
|
| 23 |
+
"#import chart_studio.tools as tls\n",
|
| 24 |
+
"from bs4 import BeautifulSoup\n",
|
| 25 |
+
"import matplotlib.pyplot as plt\n",
|
| 26 |
+
"import numpy as np\n",
|
| 27 |
+
"import matplotlib.font_manager as font_manager\n",
|
| 28 |
+
"from datetime import datetime\n",
|
| 29 |
+
"import pytz\n",
|
| 30 |
+
"from matplotlib.ticker import MaxNLocator\n",
|
| 31 |
+
"from matplotlib.patches import Ellipse\n",
|
| 32 |
+
"import matplotlib.transforms as transforms\n",
|
| 33 |
+
"from matplotlib.gridspec import GridSpec\n",
|
| 34 |
+
"from datasets import load_dataset"
|
| 35 |
+
]
|
| 36 |
+
},
|
| 37 |
+
{
|
| 38 |
+
"cell_type": "code",
|
| 39 |
+
"execution_count": 2,
|
| 40 |
+
"metadata": {},
|
| 41 |
+
"outputs": [
|
| 42 |
+
{
|
| 43 |
+
"name": "stdout",
|
| 44 |
+
"output_type": "stream",
|
| 45 |
+
"text": [
|
| 46 |
+
"Starting Everything:\n"
|
| 47 |
+
]
|
| 48 |
+
},
|
| 49 |
+
{
|
| 50 |
+
"name": "stderr",
|
| 51 |
+
"output_type": "stream",
|
| 52 |
+
"text": [
|
| 53 |
+
"Found cached dataset csv (C:/Users/thoma/.cache/huggingface/datasets/nesticot___csv/nesticot--mlb_data-a391519415fcbccf/0.0.0/6954658bab30a358235fa864b05cf819af0e179325c740e4bc853bcc7ec513e1)\n",
|
| 54 |
+
"100%|ββββββββββ| 1/1 [00:00<00:00, 2.02it/s]\n"
|
| 55 |
+
]
|
| 56 |
+
}
|
| 57 |
+
],
|
| 58 |
+
"source": [
|
| 59 |
+
"\n",
|
| 60 |
+
"colour_palette = ['#FFB000','#648FFF','#785EF0',\n",
|
| 61 |
+
" '#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']\n",
|
| 62 |
+
"\n",
|
| 63 |
+
"print('Starting Everything:')\n",
|
| 64 |
+
"# exit_velo_df = milb_a_ev_df.append([triple_a_ev_df,double_a_ev_df,a_high_a_ev_df,single_a_ev_df]).reset_index(drop=True)\n",
|
| 65 |
+
"# player_df_all = mlb_a_player_df.append([triple_a_player_df,double_a_player_df,a_high_a_player_df,single_a_player_df]).reset_index(drop=True)\n",
|
| 66 |
+
"# exit_velo_df = pd.read_csv('exit_velo_df_all.csv',index_col=[0])\n",
|
| 67 |
+
"# player_df_all = pd.read_csv('player_df_all.csv',index_col=[0])\n",
|
| 68 |
+
"\n",
|
| 69 |
+
"# pa_df = pd.read_csv('pa_df_all.csv',index_col=[0])\n",
|
| 70 |
+
"# pa_df_full_na = pa_df.dropna()\n",
|
| 71 |
+
"\n",
|
| 72 |
+
"### Import Datasets\n",
|
| 73 |
+
"dataset = load_dataset('nesticot/mlb_data', data_files=['a_pitch_data_2023.csv',\n",
|
| 74 |
+
" ])\n",
|
| 75 |
+
"dataset_train = dataset['train']\n",
|
| 76 |
+
"exit_velo_df = dataset_train.to_pandas().set_index(list(dataset_train.features.keys())[0]).reset_index(drop=True)\n",
|
| 77 |
+
"colour_palette = ['#FFB000','#648FFF','#785EF0',\n",
|
| 78 |
+
" '#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED']\n",
|
| 79 |
+
"\n",
|
| 80 |
+
"\n"
|
| 81 |
+
]
|
| 82 |
+
},
|
| 83 |
+
{
|
| 84 |
+
"cell_type": "code",
|
| 85 |
+
"execution_count": 9,
|
| 86 |
+
"metadata": {},
|
| 87 |
+
"outputs": [
|
| 88 |
+
{
|
| 89 |
+
"data": {
|
| 90 |
+
"text/plain": [
|
| 91 |
+
"0 True\n",
|
| 92 |
+
"1 True\n",
|
| 93 |
+
"2 True\n",
|
| 94 |
+
"3 True\n",
|
| 95 |
+
"4 True\n",
|
| 96 |
+
" ... \n",
|
| 97 |
+
"575260 True\n",
|
| 98 |
+
"575261 True\n",
|
| 99 |
+
"575262 True\n",
|
| 100 |
+
"575263 True\n",
|
| 101 |
+
"575264 True\n",
|
| 102 |
+
"Name: is_pitch, Length: 575265, dtype: bool"
|
| 103 |
+
]
|
| 104 |
+
},
|
| 105 |
+
"execution_count": 9,
|
| 106 |
+
"metadata": {},
|
| 107 |
+
"output_type": "execute_result"
|
| 108 |
+
}
|
| 109 |
+
],
|
| 110 |
+
"source": [
|
| 111 |
+
"exit_velo_df['is_pitch']"
|
| 112 |
+
]
|
| 113 |
+
},
|
| 114 |
+
{
|
| 115 |
+
"cell_type": "code",
|
| 116 |
+
"execution_count": 18,
|
| 117 |
+
"metadata": {},
|
| 118 |
+
"outputs": [],
|
| 119 |
+
"source": [
|
| 120 |
+
"tl_df = exit_velo_df[exit_velo_df['batter_id'] == 699073].groupby(['batter_id','batter_name','batter_hand']).agg(\n",
|
| 121 |
+
" pitches = ('is_pitch','sum'),\n",
|
| 122 |
+
" swings = ('is_swing','sum'),\n",
|
| 123 |
+
" whiffs = ('is_whiff','sum')\n",
|
| 124 |
+
")"
|
| 125 |
+
]
|
| 126 |
+
},
|
| 127 |
+
{
|
| 128 |
+
"cell_type": "code",
|
| 129 |
+
"execution_count": 19,
|
| 130 |
+
"metadata": {},
|
| 131 |
+
"outputs": [],
|
| 132 |
+
"source": [
|
| 133 |
+
"tl_df['whiff_rate'] = tl_df['whiffs'] / tl_df['swings']"
|
| 134 |
+
]
|
| 135 |
+
},
|
| 136 |
+
{
|
| 137 |
+
"cell_type": "code",
|
| 138 |
+
"execution_count": 20,
|
| 139 |
+
"metadata": {},
|
| 140 |
+
"outputs": [
|
| 141 |
+
{
|
| 142 |
+
"data": {
|
| 143 |
+
"text/html": [
|
| 144 |
+
"<div>\n",
|
| 145 |
+
"<style scoped>\n",
|
| 146 |
+
" .dataframe tbody tr th:only-of-type {\n",
|
| 147 |
+
" vertical-align: middle;\n",
|
| 148 |
+
" }\n",
|
| 149 |
+
"\n",
|
| 150 |
+
" .dataframe tbody tr th {\n",
|
| 151 |
+
" vertical-align: top;\n",
|
| 152 |
+
" }\n",
|
| 153 |
+
"\n",
|
| 154 |
+
" .dataframe thead th {\n",
|
| 155 |
+
" text-align: right;\n",
|
| 156 |
+
" }\n",
|
| 157 |
+
"</style>\n",
|
| 158 |
+
"<table border=\"1\" class=\"dataframe\">\n",
|
| 159 |
+
" <thead>\n",
|
| 160 |
+
" <tr style=\"text-align: right;\">\n",
|
| 161 |
+
" <th></th>\n",
|
| 162 |
+
" <th></th>\n",
|
| 163 |
+
" <th></th>\n",
|
| 164 |
+
" <th>pitches</th>\n",
|
| 165 |
+
" <th>swings</th>\n",
|
| 166 |
+
" <th>whiffs</th>\n",
|
| 167 |
+
" <th>whiff_rate</th>\n",
|
| 168 |
+
" </tr>\n",
|
| 169 |
+
" <tr>\n",
|
| 170 |
+
" <th>batter_id</th>\n",
|
| 171 |
+
" <th>batter_name</th>\n",
|
| 172 |
+
" <th>batter_hand</th>\n",
|
| 173 |
+
" <th></th>\n",
|
| 174 |
+
" <th></th>\n",
|
| 175 |
+
" <th></th>\n",
|
| 176 |
+
" <th></th>\n",
|
| 177 |
+
" </tr>\n",
|
| 178 |
+
" </thead>\n",
|
| 179 |
+
" <tbody>\n",
|
| 180 |
+
" <tr>\n",
|
| 181 |
+
" <th rowspan=\"2\" valign=\"top\">699073</th>\n",
|
| 182 |
+
" <th rowspan=\"2\" valign=\"top\">Thayron Liranzo</th>\n",
|
| 183 |
+
" <th>L</th>\n",
|
| 184 |
+
" <td>1344</td>\n",
|
| 185 |
+
" <td>554</td>\n",
|
| 186 |
+
" <td>189</td>\n",
|
| 187 |
+
" <td>0.341155</td>\n",
|
| 188 |
+
" </tr>\n",
|
| 189 |
+
" <tr>\n",
|
| 190 |
+
" <th>R</th>\n",
|
| 191 |
+
" <td>343</td>\n",
|
| 192 |
+
" <td>160</td>\n",
|
| 193 |
+
" <td>60</td>\n",
|
| 194 |
+
" <td>0.375</td>\n",
|
| 195 |
+
" </tr>\n",
|
| 196 |
+
" </tbody>\n",
|
| 197 |
+
"</table>\n",
|
| 198 |
+
"</div>"
|
| 199 |
+
],
|
| 200 |
+
"text/plain": [
|
| 201 |
+
" pitches swings whiffs whiff_rate\n",
|
| 202 |
+
"batter_id batter_name batter_hand \n",
|
| 203 |
+
"699073 Thayron Liranzo L 1344 554 189 0.341155\n",
|
| 204 |
+
" R 343 160 60 0.375"
|
| 205 |
+
]
|
| 206 |
+
},
|
| 207 |
+
"execution_count": 20,
|
| 208 |
+
"metadata": {},
|
| 209 |
+
"output_type": "execute_result"
|
| 210 |
+
}
|
| 211 |
+
],
|
| 212 |
+
"source": [
|
| 213 |
+
"tl_df"
|
| 214 |
+
]
|
| 215 |
+
}
|
| 216 |
+
],
|
| 217 |
+
"metadata": {
|
| 218 |
+
"kernelspec": {
|
| 219 |
+
"display_name": "Python 3",
|
| 220 |
+
"language": "python",
|
| 221 |
+
"name": "python3"
|
| 222 |
+
},
|
| 223 |
+
"language_info": {
|
| 224 |
+
"codemirror_mode": {
|
| 225 |
+
"name": "ipython",
|
| 226 |
+
"version": 3
|
| 227 |
+
},
|
| 228 |
+
"file_extension": ".py",
|
| 229 |
+
"mimetype": "text/x-python",
|
| 230 |
+
"name": "python",
|
| 231 |
+
"nbconvert_exporter": "python",
|
| 232 |
+
"pygments_lexer": "ipython3",
|
| 233 |
+
"version": "3.9.13"
|
| 234 |
+
}
|
| 235 |
+
},
|
| 236 |
+
"nbformat": 4,
|
| 237 |
+
"nbformat_minor": 2
|
| 238 |
+
}
|