npb_data_app / pitch_leaderboard.py
patrickramos's picture
Add features to pitch leaderboard
bb5e4ba
raw
history blame
4.59 kB
import gradio as gr
import polars as pl
from datetime import datetime
from itertools import chain
from data import data_df
from stats import compute_pitch_stats, filter_data_by_date_and_game_kind
from convert import ball_kind
STATS = ['Count', 'Usage', 'SwStr%', 'Whiff%', 'CSW%', 'GB%', 'FB%', 'LD%']
PCT_STATS = ['Usage', 'SwStr%', 'Whiff%', 'CSW%', 'GB%', 'FB%', 'LD%']
STATS_WITH_PCTLS = ['SwStr%', 'Whiff%', 'CSW%', 'GB%', 'FB%', 'LD%']
todo = '''
**To-do**
- Color cells according to percentiles
'''
def gr_create_pitch_leaderboard(start_date, end_date, min_pitches, pitcher_lr, include_pitches):
assert pitcher_lr in ['Both', 'Left', 'Right']
data = data_df.filter(pl.col('ballKind_code') != '-')
data = filter_data_by_date_and_game_kind(data, start_date=start_date, end_date=end_date, game_kind='Regular Season')
if pitcher_lr != 'Both':
data = data.filter(pl.col('batLR') == pitcher_lr[0].lower())
# both, left, right = [
# (
# compute_pitch_stats(df, player_type='pitcher', min_pitches=min_pitches, pitch_class_type='specific')
# .filter(pl.col('qualified') & (pl.col('ballKind').is_in(include_pitches)))
# .drop('qualified')
# .rename({'pitcher_name': 'Pitcher', 'count': 'Count', 'usage': 'Usage', 'ballKind': 'Pitch', 'general_ballKind': 'Pitch (General)'} | {f'{stat}_pctl': f'{stat} (Pctl)' for stat in STATS_WITH_PCTLS})
# .with_columns(
# pl.col(stat).mul(100).round(1)
# for stat in PCT_STATS + [f'{stat} (Pctl)' for stat in STATS_WITH_PCTLS]
# )
# [['pitId', 'ballKind_code', 'Pitcher', 'Pitch', 'Pitch (General)', 'Count', 'Usage'] + STATS_WITH_PCTLS]
# )
# for df
# in [data, data.filter(pl.col('batLR') == 'l'), data.filter(pl.col('batLR') == 'r')]
# ]
# pitch_stats = (
# both
# .join(left, on=['pitId', 'ballKind_code'], suffix=' (LHH)', how='full')
# .join(right, on=['pitId', 'ballKind_code'], suffix=' (RHH)', how='full')
# .drop('pitId', 'ballKind_code', *list(chain.from_iterable([[f'{col} ({handedness}HH)' for col in ['pitId', 'ballKind_code', 'Pitcher', 'Pitch', 'Pitch (General)']] for handedness in ('L', 'R')])))
# )
pitch_stats = (
compute_pitch_stats(data, player_type='pitcher', min_pitches=min_pitches, pitch_class_type='specific')
.filter(pl.col('qualified') & (pl.col('ballKind').is_in(include_pitches)))
.drop('pitId', 'ballKind_code', 'qualified')
.rename({'pitcher_name': 'Pitcher', 'count': 'Count', 'usage': 'Usage', 'ballKind': 'Pitch', 'general_ballKind': 'Pitch (General)'} | {f'{stat}_pctl': f'{stat} (Pctl)' for stat in STATS_WITH_PCTLS})
.with_columns(
pl.col(stat).mul(100).round(1)
for stat in PCT_STATS + [f'{stat} (Pctl)' for stat in STATS_WITH_PCTLS]
)
[['Pitcher', 'Pitch', 'Pitch (General)', 'Count', 'Usage'] + STATS_WITH_PCTLS]
)
return pitch_stats
def create_pitch_leaderboard():
now = datetime.now()
start_datetime_init = datetime(now.year, 1, 1)
end_datetime_init = now
pitch_types = [pitch_type for pitch_type in ball_kind.values() if pitch_type != '-']
with gr.Blocks() as app:
gr.Markdown('# Pitch Leaderboard')
with gr.Row():
start_date = gr.DateTime(start_datetime_init, include_time=False, type='datetime', label='Start')
end_date = gr.DateTime(end_datetime_init, include_time=False, type='datetime', label='End')
with gr.Row():
include_pitches = gr.CheckboxGroup(pitch_types, value=pitch_types, label='Pitches', scale=3)
with gr.Column(scale=1):
all_pitches = gr.Button('Select/Deselect all pitches', scale=1)
min_pitches = gr.Number(100, label='Min. Pitches', precision=0, minimum=0)
pitcher_lr = gr.Radio(['Both', 'Left', 'Right'], value='Both', label='Batter handedness')
search = gr.Button('Search')
leaderboard = gr.DataFrame(
pl.DataFrame({'Pitcher': [], 'Pitch': []}),
# gr_create_pitch_leaderboard(start_date=start_date_init, end_date=end_date_init, min_pitches=100),
column_widths=[200]*3 + [100]*(3*len(STATS)),
show_copy_button=True,
show_search=True,
pinned_columns=2
)
search.click(gr_create_pitch_leaderboard, inputs=[start_date, end_date, min_pitches, pitcher_lr, include_pitches], outputs=leaderboard)
all_pitches.click(lambda _pitch_types : [] if _pitch_types == pitch_types else pitch_types, inputs=include_pitches, outputs=include_pitches)
gr.Markdown(todo)
return app
if __name__ == '__main__':
app = create_pitch_leaderboard()
app.launch()