|
|
from shiny import App, ui, render, reactive |
|
|
import polars as pl |
|
|
import numpy as np |
|
|
import pandas as pd |
|
|
import api_scraper |
|
|
scrape = api_scraper.MLB_Scrape() |
|
|
from functions import df_update |
|
|
from functions import pitch_summary_functions |
|
|
update = df_update.df_update() |
|
|
from stuff_model import feature_engineering as fe |
|
|
from stuff_model import stuff_apply |
|
|
import requests |
|
|
import joblib |
|
|
from matplotlib.gridspec import GridSpec |
|
|
import math |
|
|
from pytabulator import TableOptions, Tabulator, output_tabulator, render_tabulator, theme |
|
|
theme.tabulator_site() |
|
|
|
|
|
colour_palette = ['#FFB000','#648FFF','#785EF0', |
|
|
'#DC267F','#FE6100','#3D1EB2','#894D80','#16AA02','#B5592B','#A3C1ED'] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
season = 2024 |
|
|
|
|
|
df_mlb = pl.read_parquet("data/data_mlb_2024.parquet")[:] |
|
|
df_aaa = pl.read_parquet("data/data_aaa_2024.parquet")[:] |
|
|
df_a = pl.read_parquet("data/data_a_2024.parquet")[:] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def df_final(df:pl.dataframe,year_input:int,sport_id:int): |
|
|
|
|
|
df_schedule = scrape.get_schedule(year_input=[year_input],sport_id=[sport_id]) |
|
|
df = df.join(df_schedule, on='game_id', how='left') |
|
|
|
|
|
df = df.with_columns( |
|
|
pl.when((pl.col('batter_team_id') == pl.col('away_id'))) |
|
|
.then(pl.lit('Away')) |
|
|
.when((pl.col('batter_team_id') == pl.col('home_id'))) |
|
|
.then(pl.lit('Home')) |
|
|
.otherwise(None) |
|
|
.alias('home_away') |
|
|
) |
|
|
|
|
|
df = df.with_columns( |
|
|
pl.when((pl.col('pitcher_team_id') == pl.col('away_id'))) |
|
|
.then(pl.lit('Away')) |
|
|
.when((pl.col('pitcher_team_id') == pl.col('home_id'))) |
|
|
.then(pl.lit('Home')) |
|
|
.otherwise(None) |
|
|
.alias('home_away_pitcher') |
|
|
) |
|
|
|
|
|
|
|
|
print('schedule') |
|
|
|
|
|
df = df.with_columns([ |
|
|
(60.5 - df["extension"]).alias("release_pos_y") |
|
|
]) |
|
|
|
|
|
|
|
|
delta_t = (df["release_pos_y"] - df["y0"]) / df["vy0"] |
|
|
|
|
|
|
|
|
df = df.with_columns([ |
|
|
(df["x0"] + df["vx0"] * delta_t + 0.5 * df["ax"] * delta_t ** 2).alias("release_pos_x"), |
|
|
(df["z0"] + df["vz0"] * delta_t + 0.5 * df["az"] * delta_t ** 2).alias("release_pos_z") |
|
|
]) |
|
|
|
|
|
df_stuff = stuff_apply.stuff_apply(fe.feature_engineering(df)) |
|
|
print('stuff') |
|
|
df_up = update.update(df) |
|
|
print('update') |
|
|
df_total = df_up.join(df_stuff[['play_id','tj_stuff_plus']], on='play_id', how='left') |
|
|
print('total') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return df_total |
|
|
|
|
|
|
|
|
df_mlb_total = df_final(df=df_mlb,year_input=season,sport_id=1) |
|
|
df_aaa_total = df_final(df=df_aaa,year_input=season,sport_id=11) |
|
|
df_a_total = df_final(df=df_a.drop_nulls(subset=['start_speed']),year_input=season,sport_id=14) |
|
|
|
|
|
rounding_dict = { |
|
|
'pa': 0, |
|
|
'bip': 0, |
|
|
'hits': 0, |
|
|
'k': 0, |
|
|
'bb': 0, |
|
|
'max_launch_speed': 1, |
|
|
'launch_speed_90': 1, |
|
|
'launch_speed': 1, |
|
|
'pitches': 0, |
|
|
'tj_stuff_plus_avg': 0, |
|
|
'avg': 3, |
|
|
'obp': 3, |
|
|
'slg': 3, |
|
|
'ops': 3, |
|
|
'k_percent': 3, |
|
|
'bb_percent': 3, |
|
|
'k_minus_bb_percent': 3, |
|
|
'sweet_spot_percent': 3, |
|
|
'woba_percent': 3, |
|
|
'xwoba_percent': 3, |
|
|
'woba_percent_contact': 3, |
|
|
'xwoba_percent_contact': 3, |
|
|
'hard_hit_percent': 3, |
|
|
'barrel_percent': 3, |
|
|
'zone_contact_percent': 3, |
|
|
'zone_swing_percent': 3, |
|
|
'zone_percent': 3, |
|
|
'chase_percent': 3, |
|
|
'chase_contact': 3, |
|
|
'swing_percent': 3, |
|
|
'whiff_rate': 3, |
|
|
'swstr_rate': 3, |
|
|
'ground_ball_percent': 3, |
|
|
'line_drive_percent': 3, |
|
|
'fly_ball_percent': 3, |
|
|
'pop_up_percent': 3, |
|
|
'pulled_fly_ball_percent': 3, |
|
|
'heart_zone_swing_percent': 3, |
|
|
'shadow_zone_swing_percent': 3, |
|
|
'chase_zone_swing_percent': 3, |
|
|
'waste_zone_swing_percent': 3, |
|
|
'heart_zone_whiff_percent': 3, |
|
|
'shadow_zone_whiff_percent': 3, |
|
|
'chase_zone_whiff_percent': 3, |
|
|
'waste_zone_whiff_percent': 3, |
|
|
'start_speed_avg': 1, |
|
|
'vb_avg': 1, |
|
|
'ivb_avg': 1, |
|
|
'hb_avg': 1, |
|
|
'z0_avg': 1, |
|
|
'x0_avg': 1, |
|
|
'vaa_avg': 1, |
|
|
'haa_avg': 1, |
|
|
'spin_rate_avg': 0, |
|
|
'extension_avg': 1 |
|
|
} |
|
|
|
|
|
columns = [ |
|
|
{ "title": "PA", "field": "pa", "width": 150}, |
|
|
{ "title": "BBE", "field": "bip", "width": 150 }, |
|
|
{ "title": "H", "field": "hits", "width": 150 }, |
|
|
{ "title": "K", "field": "k", "width": 150 }, |
|
|
{ "title": "BB", "field": "bb", "width": 150 }, |
|
|
{ "title": "Max EV", "field": "max_launch_speed", "width": 150 }, |
|
|
{ "title": "90th% EV", "field": "launch_speed_90", "width": 150 }, |
|
|
{ "title": "EV", "field": "launch_speed", "width": 150 }, |
|
|
{ "title": "Pitches", "field": "pitches", "width": 150 }, |
|
|
{ "title": "AVG", "field": "avg", "width": 150 }, |
|
|
{ "title": "OBP", "field": "obp", "width": 150 }, |
|
|
{ "title": "SLG", "field": "slg", "width": 150 }, |
|
|
{ "title": "OPS", "field": "ops", "width": 150 }, |
|
|
{ "title": "K%", "field": "k_percent", "width": 150,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "BB%", "field": "bb_percent", "width": 150,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "K-BB%", "field": "k_minus_bb_percent", "width": 150,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "SwSpot%", "field": "sweet_spot_percent", "width": 150,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "wOBA", "field": "woba_percent", "width": 150 }, |
|
|
{ "title": "xwOBA", "field": "xwoba_percent", "width": 150 }, |
|
|
{ "title": "wOBACON", "field": "woba_percent_contact", "width": 150 }, |
|
|
{ "title": "xwOBACON", "field": "xwoba_percent_contact", "width": 150 }, |
|
|
{ "title": "HardHit%", "field": "hard_hit_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Barrel%", "field": "barrel_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Z-Contact%", "field": "zone_contact_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Z-Swing%", "field": "zone_swing_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Zone%", "field": "zone_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "O-Swing%", "field": "chase_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "O-Contact%", "field": "chase_contact", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Swing%", "field": "swing_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Whiff%", "field": "whiff_rate", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "SwStr%", "field": "swstr_rate", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "GB%", "field": "ground_ball_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "LD%", "field": "line_drive_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "FB%", "field": "fly_ball_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "PU%", "field": "pop_up_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
|
|
|
{ "title": "Pull LD+FB%", "field": "pulled_fly_ball_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
|
|
|
|
|
|
{ "title": "Heart Swing%", "field": "heart_zone_swing_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Shadow Swing%", "field": "shadow_zone_swing_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Chase Swing%", "field": "chase_zone_swing_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Waste Swing%", "field": "waste_zone_swing_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Heart Whiff%", "field": "heart_zone_whiff_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Shadow Whiff%", "field": "shadow_zone_whiff_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Chase Whiff%", "field": "chase_zone_whiff_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "Waste Whiff%", "field": "waste_zone_whiff_percent", "width": 150 ,"formatter": "money", "formatterParams":{"decimal":".","thousand":".","symbol":"%","symbolAfter":"%","negativeSign":True,"precision":1}}, |
|
|
{ "title": "tjStuff+", "field": "tj_stuff_plus_avg", "width": 150 }, |
|
|
{ "title": "Velocity", "field": "start_speed_avg", "width": 150 }, |
|
|
{ "title": "Extension", "field": "extension_avg", "width": 150 }, |
|
|
{ "title": "VB", "field": "vb_avg", "width": 150 }, |
|
|
{ "title": "iVB", "field": "ivb_avg", "width": 150 }, |
|
|
{ "title": "HB", "field": "hb_avg", "width": 150 }, |
|
|
{ "title": "vRel", "field": "z0_avg", "width": 150 }, |
|
|
{ "title": "hRel", "field": "x0_avg", "width": 150 }, |
|
|
{ "title": "VAA", "field": "vaa_avg", "width": 150 }, |
|
|
{ "title": "HAA", "field": "haa_avg", "width": 150 }, |
|
|
{ "title": "Spin Rate", "field": "spin_rate_avg", "width": 150 }, |
|
|
{ "title": "Extension", "field": "extension_avg", "width": 150 }, |
|
|
|
|
|
] |
|
|
|
|
|
stat_titles = dict(zip([col["field"] for col in columns],[col["title"] for col in columns])) |
|
|
|
|
|
stat_selection = [key for key in stat_titles.keys()] |
|
|
|
|
|
agg_titles = {'batter_id':'Batter ID', |
|
|
'batter_name':'Batter Name', |
|
|
'batter_team':'Batter Team', |
|
|
'batter_hand':'Batter Hand', |
|
|
'pitcher_id':'Pitcher ID', |
|
|
'pitcher_name':'Pitcher Name', |
|
|
'pitcher_team':'Pitcher Team', |
|
|
'pitcher_hand':'Pitcher Hand', |
|
|
'pitch_type':'Pitch Type', |
|
|
'pitch_group':'Pitch Group', |
|
|
'home_away_batter':'Home/Away Batter', |
|
|
'home_away_pitcher':'Home/Away Pitcher', |
|
|
'is_swing':'Is Swing?', |
|
|
'is_bip':'Is BIP?', |
|
|
'in_zone_final':'In Zone?', |
|
|
'attack_zone_final':'Attack Zone'} |
|
|
|
|
|
|
|
|
columns_group = [ |
|
|
{ "title": "Batter ID", "field": "batter_id", "width": 150, "headerFilter":"input","frozen":True,}, |
|
|
{ "title": "Batter Name", "field": "batter_name", "width": 200,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Batter Team", "field": "batter_team", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Batter Hand", "field": "batter_hand", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Pitcher ID", "field": "pitcher_id", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Pitcher Name", "field": "pitcher_name", "width": 200,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Pitcher Team", "field": "pitcher_team", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Pitcher Hand", "field": "pitcher_hand", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Pitch Type", "field": "pitch_type", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Pitch Group", "field": "pitch_group", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Home/Away Batter", "field": "home_away_batter", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Home/Away Pitcher", "field": "home_away_pitcher", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Is Swing?", "field": "is_swing", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Is BIP?", "field": "is_bip", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "In Zone?", "field": "in_zone_final", "width": 150,"frozen":True, "headerFilter":"input" }, |
|
|
{ "title": "Attack Zone", "field": "attack_zone_final", "width": 150,"frozen":True, "headerFilter":"input" } |
|
|
] |
|
|
|
|
|
|
|
|
app_ui = ui.page_sidebar( |
|
|
ui.sidebar( |
|
|
ui.input_selectize( |
|
|
"level_input", |
|
|
"Select Level:", |
|
|
choices=['MLB','AAA','A'], |
|
|
multiple=False, |
|
|
selected=['MLB'] |
|
|
), |
|
|
ui.input_selectize( |
|
|
"list_input", |
|
|
"Select Aggregation:", |
|
|
choices=agg_titles, |
|
|
multiple=True, |
|
|
selected=['batter_id', 'batter_name'] |
|
|
), |
|
|
ui.input_selectize( |
|
|
"list_stats", |
|
|
"Select Stats:", |
|
|
choices=stat_titles, |
|
|
multiple=True, |
|
|
selected=['pa'] |
|
|
), |
|
|
ui.input_date_range( |
|
|
"date_id", |
|
|
"Select Date Range", |
|
|
start=f'{season}-01-01', |
|
|
end=f'{season}-12-01', |
|
|
min=f'{season}-01-01', |
|
|
max=f'{season}-12-01', |
|
|
), |
|
|
ui.hr(), |
|
|
ui.h4("Filters"), |
|
|
ui.div( |
|
|
{"id": "filter-container"}, |
|
|
ui.div( |
|
|
{"class": "filter-row", "id": "filter_row_1"}, |
|
|
ui.row( |
|
|
ui.column(5, |
|
|
ui.input_select( |
|
|
"filter_column_1", |
|
|
"Metric", |
|
|
choices={} |
|
|
) |
|
|
), |
|
|
ui.column(3, |
|
|
ui.input_select( |
|
|
"filter_operator_1", |
|
|
"Operator", |
|
|
choices=[">=", "<="] |
|
|
), |
|
|
), |
|
|
ui.column(3, |
|
|
ui.input_numeric( |
|
|
"filter_value_1", |
|
|
"Value", |
|
|
value=0 |
|
|
) |
|
|
), |
|
|
ui.column(1, |
|
|
ui.markdown(" "), |
|
|
|
|
|
|
|
|
ui.input_action_button( |
|
|
f"delete_filter_1", |
|
|
"", |
|
|
class_="btn-danger btn-sm", |
|
|
style="padding: 3px 6px;", |
|
|
icon='✖' |
|
|
|
|
|
) |
|
|
) |
|
|
) |
|
|
) |
|
|
), |
|
|
ui.input_action_button( |
|
|
"add_filter", |
|
|
"Add Filter", |
|
|
class_="btn-secondary" |
|
|
), |
|
|
ui.br(), |
|
|
ui.br(), |
|
|
ui.input_action_button( |
|
|
"generate_table", |
|
|
"Generate Table", |
|
|
class_="btn-primary" |
|
|
), |
|
|
width="400px" |
|
|
), |
|
|
ui.navset_tab( |
|
|
ui.nav_panel("Leaderboard", |
|
|
ui.card( |
|
|
|
|
|
output_tabulator("tabulator") |
|
|
) |
|
|
), |
|
|
|
|
|
) |
|
|
) |
|
|
|
|
|
def server(input, output, session): |
|
|
|
|
|
filter_count = reactive.value(1) |
|
|
|
|
|
active_filters = reactive.value([1]) |
|
|
|
|
|
@reactive.effect |
|
|
@reactive.event(input.list_stats) |
|
|
def _(): |
|
|
stat_choices = {k: k for k in input.list_stats()} |
|
|
filtered_stat_choices = {key: stat_titles[key] for key in stat_choices} |
|
|
ui.update_select("filter_column_1", choices=filtered_stat_choices) |
|
|
|
|
|
@reactive.effect |
|
|
@reactive.event(input.add_filter) |
|
|
def _(): |
|
|
current_count = filter_count.get() |
|
|
new_count = current_count + 1 |
|
|
|
|
|
stat_choices = {k: k for k in input.list_stats()} |
|
|
filtered_stat_choices = {key: stat_titles[key] for key in stat_choices} |
|
|
|
|
|
ui.insert_ui( |
|
|
selector="#filter-container", |
|
|
where="beforeEnd", |
|
|
ui=ui.div( |
|
|
{"class": "filter-row", "id": f"filter_row_{new_count}"}, |
|
|
ui.row( |
|
|
ui.column(5, |
|
|
ui.input_select( |
|
|
f"filter_column_{new_count}", |
|
|
"Metric", |
|
|
choices=filtered_stat_choices |
|
|
), |
|
|
), |
|
|
ui.column(3, |
|
|
ui.input_select( |
|
|
f"filter_operator_{new_count}", |
|
|
"Operator", |
|
|
choices=[">=", "<="] |
|
|
), |
|
|
), |
|
|
ui.column(3, |
|
|
ui.input_numeric( |
|
|
f"filter_value_{new_count}", |
|
|
"Value", |
|
|
value=0 |
|
|
) |
|
|
), |
|
|
ui.column(1, |
|
|
ui.markdown(" "), |
|
|
|
|
|
|
|
|
ui.input_action_button( |
|
|
f"delete_filter_{new_count}", |
|
|
"", |
|
|
class_="btn-danger btn-sm", |
|
|
style="padding: 3px 6px;", |
|
|
icon='✖' |
|
|
|
|
|
) |
|
|
) |
|
|
) |
|
|
) |
|
|
) |
|
|
filter_count.set(new_count) |
|
|
current_filters = active_filters.get() |
|
|
current_filters.append(new_count) |
|
|
active_filters.set(current_filters) |
|
|
|
|
|
@reactive.effect |
|
|
def _(): |
|
|
|
|
|
for i in range(1, filter_count.get() + 1): |
|
|
try: |
|
|
if getattr(input, f"delete_filter_{i}")() > 0: |
|
|
|
|
|
ui.remove_ui(f"#filter_row_{i}") |
|
|
|
|
|
current_filters = active_filters.get() |
|
|
if i in current_filters: |
|
|
current_filters.remove(i) |
|
|
active_filters.set(current_filters) |
|
|
except: |
|
|
continue |
|
|
|
|
|
@output |
|
|
@render_tabulator |
|
|
@reactive.event(input.generate_table, ignore_none=False) |
|
|
def tabulator(): |
|
|
columns_c = columns.copy() |
|
|
selection_list = list(input.list_input()) |
|
|
start_date = str(input.date_id()[0]) |
|
|
end_date = str(input.date_id()[1]) |
|
|
|
|
|
|
|
|
if input.level_input() == "MLB": |
|
|
df_agg = update.update_summary_select(df=df_mlb_total.filter((pl.col('game_date')>=start_date)&(pl.col('game_date')<=end_date)), |
|
|
selection=selection_list) |
|
|
|
|
|
elif input.level_input() == "AAA": |
|
|
df_agg = update.update_summary_select(df=df_aaa_total.filter((pl.col('game_date')>=start_date)&(pl.col('game_date')<=end_date)), |
|
|
selection=selection_list) |
|
|
|
|
|
elif input.level_input() == "A": |
|
|
df_agg = update.update_summary_select(df=df_a_total.filter((pl.col('game_date')>=start_date)&(pl.col('game_date')<=end_date)), |
|
|
selection=selection_list) |
|
|
|
|
|
|
|
|
df_agg = df_agg.select(selection_list + list(input.list_stats())) |
|
|
|
|
|
|
|
|
for i in active_filters.get(): |
|
|
try: |
|
|
col_name = getattr(input, f"filter_column_{i}")() |
|
|
if col_name: |
|
|
operator = getattr(input, f"filter_operator_{i}")() |
|
|
if col_name in [col["field"] for col in columns_c if col.get("formatter") == "money"]: |
|
|
value = getattr(input, f"filter_value_{i}")()/100 |
|
|
else: |
|
|
value = getattr(input, f"filter_value_{i}")() |
|
|
|
|
|
if operator == ">=": |
|
|
df_agg = df_agg.filter(pl.col(col_name) >= value) |
|
|
elif operator == "<=": |
|
|
df_agg = df_agg.filter(pl.col(col_name) <= value) |
|
|
except: |
|
|
continue |
|
|
|
|
|
for col in df_agg.columns[len(selection_list):]: |
|
|
if col in rounding_dict: |
|
|
df_agg = df_agg.with_columns(pl.col(col).round(rounding_dict[col])) |
|
|
|
|
|
for column in columns_c: |
|
|
if column.get("formatter") == "money" and column.get("field") in df_agg.columns: |
|
|
df_agg = df_agg.with_columns(pl.col(column.get("field"))*100) |
|
|
|
|
|
col_group = [] |
|
|
for column in columns_group: |
|
|
if column.get("field") in df_agg.columns: |
|
|
col_group.append(column) |
|
|
|
|
|
col_group_stats = [] |
|
|
for column in columns_c: |
|
|
if column.get("field") in df_agg.columns: |
|
|
col_group_stats.append(column) |
|
|
|
|
|
columns_c = col_group + col_group_stats |
|
|
|
|
|
|
|
|
df_agg = df_agg.with_columns( |
|
|
[df_agg[col].cast(pl.Int8) for col in df_agg.columns if df_agg[col].dtype == pl.Boolean] |
|
|
) |
|
|
|
|
|
return Tabulator( |
|
|
df_agg.to_pandas(), |
|
|
|
|
|
table_options=TableOptions( |
|
|
height=800, |
|
|
|
|
|
columns=columns_c, |
|
|
) |
|
|
) |
|
|
|
|
|
app = App(app_ui, server) |
|
|
|