def warn(*args, **kwargs):
pass
import warnings
warnings.warn = warn
import random
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import plotly.figure_factory as ff
import pybaseball as pb
import os
from PIL import Image
from rembg import remove
from io import BytesIO
import requests
b_id = 677594
batter = pb.playerid_reverse_lookup([b_id]).name_first.iloc[0].title().replace(' ', '') + ' ' + pb.playerid_reverse_lookup([b_id]).name_last.iloc[0].title()
Gathering player lookup table. This may take a moment.
team_logo_dict = {
'ARI': 109, 'ATL': 144, 'BAL': 110, 'BOS': 111, 'CHC': 112, 'CWS': 145, 'CIN': 113, 'CLE': 114, 'COL': 115, 'DET': 116,
'HOU': 117, 'KC': 118, 'LAA': 108, 'LAD': 119, 'MIA': 146, 'MIL': 158, 'MIN': 142, 'NYM': 121, 'NYY': 147, 'OAK': 133,
'PHI': 143, 'PIT': 134, 'SD': 135, 'SF': 137, 'SEA': 136, 'STL': 138, 'TB': 139, 'TEX': 140, 'TOR': 141, 'WSH': 120
}
df = pd.read_csv("C:/Users/chris/Documents/Datasets/BaseballR/2022/2022merged.csv", dtype={61:"str",151:"str",152:"str",153:"str",154:"str",155:"str",156:"str"})
df_scores_pre_post = df.groupby(['game_pk.x', 'inning_topbot', 'inning']).agg({'bat_score': 'first', 'post_bat_score': 'last'}).reset_index()
df_scores_pre_post.columns = ['game_pk.x', 'inning_topbot', 'inning', 'pre_inn_bat_score', 'post_inn_bat_score']
df_ABbyteam = df.groupby(['game_pk.x', 'inning_topbot', 'inning', 'atBatIndex']).agg({'matchup.batter.fullName': 'last'}).reset_index()
df_ABbyteam['AB_num_by_team'] = df_ABbyteam.groupby(['game_pk.x', 'inning_topbot']).cumcount()
df_ABbyteam['AB_num_by_team'] = df_ABbyteam['AB_num_by_team'] + 1
df_ABbyteam.drop(columns=['matchup.batter.fullName'], inplace=True)
df = pd.merge(left=df, right=df_scores_pre_post, left_on=['game_pk.x', 'inning_topbot', 'inning'], right_on=['game_pk.x', 'inning_topbot', 'inning'])
df = pd.merge(left=df, right=df_ABbyteam, left_on=['game_pk.x', 'inning_topbot', 'inning', 'atBatIndex'], right_on=['game_pk.x', 'inning_topbot', 'inning', 'atBatIndex'])
df['runs_scored_rest_of_inn'] = df['post_inn_bat_score'] - df['bat_score']
df['spot_in_order'] = df['AB_num_by_team'] % 9
df.loc[df['spot_in_order'] == 0, 'spot_in_order'] = 9
df['on_1b_tf'] = df['on_1b'].notnull()
df['on_2b_tf'] = df['on_2b'].notnull()
df['on_3b_tf'] = df['on_3b'].notnull()
df['batter'] = df['batter'].astype(int)
df['balls'] = df['balls'].astype(int)
df['strikes'] = df['strikes'].astype(int)
df['inning'] = df['inning'].astype(int)
df['at_bat_number'] = df['at_bat_number'].astype(int)
df['spot_in_order'] = df['spot_in_order'].astype(str)
df['home_score'] = df['home_score'].astype(int)
df['away_score'] = df['away_score'].astype(int)
df['runs_scored_rest_of_inn'] = df['runs_scored_rest_of_inn'].astype(int)
cols = ['game_pk.x', 'game_date.x', 'startTime', 'pitchNumber', 'details.description', 'balls', 'strikes',
'count.outs.start', 'matchup.pitcher.fullName', 'matchup.batter.fullName', 'delta_home_win_exp', 'delta_run_exp',
'inning_topbot', 'inning', 'on_1b_tf', 'on_2b_tf', 'on_3b_tf', 'at_bat_number', 'spot_in_order', 'home_score', 'away_score',
'runs_scored_rest_of_inn']
starters = df.groupby(['game_pk.x', 'inning_topbot']).agg({'matchup.pitcher.fullName': 'first'}).reset_index()
starters.columns = ['game_pk.x', 'inning_topbot', 'pitching_team_starter']
df2 = pd.merge(df, starters, left_on=['game_pk.x', 'inning_topbot'], right_on=['game_pk.x', 'inning_topbot'])
df2['isStartingPitcher'] = np.where(df2['pitching_team_starter'] == df2['matchup.pitcher.fullName'], 1, 0)
groupbydict = {
'fielding_team': 'first',
'game_date.y': 'last',
'pitchNumber': 'count',
'at_bat_number': 'nunique',
'delta_home_win_exp': ['sum', 'mean'],
'delta_run_exp': ['sum', 'mean'],
'inning_topbot': 'last',
'isStartingPitcher': 'last'
}
df1 = df2.groupby(['game_pk.x', 'matchup.pitcher.fullName']).agg(groupbydict).reset_index()
df1.columns = ['game_id', 'pitcher', 'team', 'game_date', 'pitch_count', 'batters_faced', 'total_delta_win_exp', 'delta_win_exp_per_pitch', 'total_delta_runs_exp', 'delta_runs_exp_per_pitch', 'home_away', 'isStartingPitcher']
df1['cum_delta_win_exp'] = np.where(df1.home_away == 'Bot', df1.total_delta_win_exp*-1, df1.total_delta_win_exp)
df1['delta_win_exp_per_pitch'] = np.where(df1.home_away == 'Bot', df1.delta_win_exp_per_pitch*-1, df1.delta_win_exp_per_pitch)
df1['home_away'] = np.where(df1.home_away == 'Bot', 'Away', 'Home')
df1.head(5)
game_id | pitcher | team | game_date | pitch_count | batters_faced | total_delta_win_exp | delta_win_exp_per_pitch | total_delta_runs_exp | delta_runs_exp_per_pitch | home_away | isStartingPitcher | cum_delta_win_exp | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 661032 | Aaron Loup | Los Angeles Angels | 2022-04-26 | 15 | 3 | 0.021 | 0.001400 | -0.476 | -0.031733 | Home | 0 | 0.021 |
1 | 661032 | Anthony Gose | Cleveland Guardians | 2022-04-26 | 14 | 4 | -0.002 | 0.000143 | 0.635 | 0.045357 | Away | 0 | 0.002 |
2 | 661032 | Enyel De Los Santos | Cleveland Guardians | 2022-04-26 | 12 | 4 | -0.008 | 0.000667 | -0.575 | -0.047917 | Away | 0 | 0.008 |
3 | 661032 | Jimmy Herget | Los Angeles Angels | 2022-04-26 | 11 | 3 | -0.024 | -0.002182 | 0.949 | 0.086273 | Home | 0 | -0.024 |
4 | 661032 | Patrick Sandoval | Los Angeles Angels | 2022-04-26 | 90 | 24 | 0.300 | 0.003333 | -3.260 | -0.036222 | Home | 1 | 0.300 |
groupbydict = {
'batting_team': 'first',
'batter': 'last',
'game_date.y': 'last',
'pitchNumber': 'count',
'at_bat_number': 'nunique',
'delta_home_win_exp': ['sum', 'mean'],
'delta_run_exp': ['sum', 'mean'],
'inning_topbot': 'last',
}
df1_hitters = df2.groupby(['matchup.batter.fullName', 'game_pk.x']).agg(groupbydict).reset_index()
df1_hitters.columns = ['batter', 'game_id', 'team', 'batter_id', 'game_date', 'pitch_count', 'plate_apps', 'total_delta_win_exp', 'delta_win_exp_per_pitch', 'total_delta_runs_exp', 'delta_runs_exp_per_pitch', 'home_away']
df1_hitters['total_delta_win_exp'] = np.where(df1_hitters.home_away == 'Bot', df1_hitters.total_delta_win_exp, df1_hitters.total_delta_win_exp*-1)
df1_hitters['delta_win_exp_per_pitch'] = np.where(df1_hitters.home_away == 'Bot', df1_hitters.delta_win_exp_per_pitch, df1_hitters.delta_win_exp_per_pitch*-1)
df1_hitters['home_away'] = np.where(df1_hitters.home_away == 'Bot', 'Home', 'Away')
df1_hitters.sort_values(by=['total_delta_runs_exp'], ascending=False)[df1_hitters.batter_id == b_id].head()
batter | game_id | team | batter_id | game_date | pitch_count | plate_apps | total_delta_win_exp | delta_win_exp_per_pitch | total_delta_runs_exp | delta_runs_exp_per_pitch | home_away | |
---|---|---|---|---|---|---|---|---|---|---|---|---|
25579 | Julio Rodriguez | 661810 | Seattle Mariners | 677594 | 2022-07-15 | 17 | 5 | 0.204 | 0.012000 | 4.219 | 0.248176 | Away |
25664 | Julio Rodriguez | 662567 | Seattle Mariners | 677594 | 2022-05-15 | 21 | 5 | 0.348 | 0.016571 | 3.102 | 0.147714 | Away |
25661 | Julio Rodriguez | 662360 | Seattle Mariners | 677594 | 2022-06-21 | 13 | 5 | 0.094 | 0.007231 | 2.686 | 0.206615 | Away |
25563 | Julio Rodriguez | 661365 | Seattle Mariners | 677594 | 2022-05-01 | 13 | 4 | 0.149 | 0.011462 | 2.441 | 0.187769 | Away |
25685 | Julio Rodriguez | 663369 | Seattle Mariners | 677594 | 2022-08-16 | 20 | 5 | 0.066 | 0.003300 | 2.431 | 0.127947 | Away |
groupbydict = {
'batting_team': 'first',
'fielding_team': 'first',
'batter': 'last',
'game_date.y': 'last',
'startTime': 'first',
'pitchNumber': 'count',
'delta_home_win_exp': 'last',
'delta_run_exp': 'sum',
'inning_topbot': 'last',
'des': 'last',
'count.balls.end': 'last',
'count.strikes.end': 'last',
'count.outs.start': 'last',
'about.inning': 'last',
'bat_score': 'last',
'fld_score': 'last',
'home_team.y': 'last',
'away_team.y': 'last',
'result.event': 'last',
'hitData.trajectory': 'last'
}
df3_hitters = df2.groupby(['matchup.batter.fullName', 'game_pk.x', 'at_bat_number']).agg(groupbydict).reset_index()
df3_hitters.columns = ['batter', 'game_id', 'PA_of_game', 'team', 'opp', 'batter_id', 'game_date', 'time', 'pitch_count', 'delta_win_exp',
'delta_runs_exp', 'home_away', 'desc', 'balls', 'strikes', 'outs', 'inning','bat_score', 'field_score', 'home_team',
'away_team', 'result', 'trajectory']
df3_hitters['delta_win_exp'] = np.where(df3_hitters.home_away == 'Bot', df3_hitters.delta_win_exp, df3_hitters.delta_win_exp*-1)
df3_hitters['inning'] = df3_hitters.home_away + ' ' + df3_hitters.inning.astype('string')
df3_hitters['count'] = df3_hitters.balls.astype('string') + '-' + df3_hitters.strikes.astype('string')
df3_hitters['bat_score'] = round(df3_hitters['bat_score'].astype('int'), 0)
df3_hitters['field_score'] = round(df3_hitters['field_score'].astype('int'), 0)
df3_hitters['score'] = df3_hitters.bat_score.astype('string') + '-' + df3_hitters.field_score.astype('string')
df3_hitters['bat_code'] = np.where(df3_hitters.home_away == 'Bot', df3_hitters.home_team, df3_hitters.away_team)
df3_hitters['field_code'] = np.where(df3_hitters.home_away == 'Bot', df3_hitters.away_team, df3_hitters.home_team)
df3_hitters['home_away'] = np.where(df3_hitters.home_away == 'Bot', 'Home', 'Away')
df3_hitters['PA_num'] = df3_hitters.sort_values(by=['time'], ascending=True).groupby(['batter_id']).cumcount()
df3_hitters['PA_num'] = df3_hitters['PA_num'] + 1
df3_hitters['cum_win_exp_added'] = df3_hitters.sort_values(by=['time'], ascending=True).groupby(['batter_id'])['delta_win_exp'].cumsum()
df3_hitters['cum_runs_exp_added'] = df3_hitters.sort_values(by=['time'], ascending=True).groupby(['batter_id'])['delta_runs_exp'].cumsum()
df3_hitters['result_cat'] = df3_hitters['result']
df3_hitters['result_cat'] = np.where(df3_hitters.result_cat == 'Intent Walk', 'Walk', df3_hitters.result_cat)
df3_hitters['result_cat'] = np.where(df3_hitters.result_cat == 'Strikeout Double Play', 'Strikeout', df3_hitters.result_cat)
df3_hitters['result_cat'] = np.where(df3_hitters.result_cat == 'Batter Out', 'Strikeout', df3_hitters.result_cat)
df3_hitters['result_cat'] = np.where(df3_hitters.result_cat == 'Field Out', 'Strikeout', df3_hitters.result_cat)
df3_hitters['result_cat'] = np.where(df3_hitters.result_cat == 'Sac Fly', 'Sacrifice', df3_hitters.result_cat)
df3_hitters['result_cat'] = np.where(df3_hitters.result_cat == 'Sac Bunt', 'Sacrifice', df3_hitters.result_cat)
results1 = ['Strikeout', 'Groundout', 'Flyout', 'Lineout', 'Pop Out', 'Forceout', 'Grounded Into DP', 'Field Error', 'Fielders Choice',
'Double Play', 'Fielders Choice Out', 'Bunt Groundout', 'Strikeout Double Play', 'Bunt Pop Out', 'Sac Fly Double Play',
'Bunt Lineout', 'Triple Play', 'Field Out', 'Sac Bunt Double Play', 'Batter Out']
results2 = ['Single', 'Walk', 'Double', 'Home Run', 'Hit By Pitch', 'Sac Fly', 'Sac Bunt', 'Triple', 'Intent Walk']
df3_hitters['result_cat'] = np.where((df3_hitters.result_cat.isin(results1) & (df3_hitters.trajectory == 'ground_ball')), 'Groundout', df3_hitters.result_cat)
df3_hitters['result_cat'] = np.where((df3_hitters.result_cat.isin(results1) & (df3_hitters.trajectory == 'fly_ball')), 'Flyout', df3_hitters.result_cat)
df3_hitters['result_cat'] = np.where((df3_hitters.result_cat.isin(results1) & (df3_hitters.trajectory == 'line_drive')), 'Lineout', df3_hitters.result_cat)
df3_hitters['result_cat'] = np.where((df3_hitters.result_cat.isin(results1) & (df3_hitters.trajectory == 'popup')), 'Pop Out', df3_hitters.result_cat)
df3_hitters['result_cat'] = np.where((df3_hitters.result_cat.isin(results1) & (df3_hitters.trajectory == 'bunt_grounder')), 'Groundout', df3_hitters.result_cat)
df3_hitters['result_cat'] = np.where((df3_hitters.result_cat.isin(results1) & (df3_hitters.trajectory == 'bunt_popup')), 'Pop Out', df3_hitters.result_cat)
df3_hitters['result_cat'] = np.where((df3_hitters.result_cat.isin(results1) & (df3_hitters.trajectory == 'bunt_line_drive')), 'Lineout', df3_hitters.result_cat)
df3_hitters = df3_hitters[df3_hitters['result'].isin(results1+results2)]
df3_hitters.sort_values(by=['delta_win_exp'], ascending=False)[df3_hitters.batter_id == b_id].head()
batter | game_id | PA_of_game | team | opp | batter_id | game_date | time | pitch_count | delta_win_exp | ... | result | trajectory | count | score | bat_code | field_code | PA_num | cum_win_exp_added | cum_runs_exp_added | result_cat | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
96221 | Julio Rodriguez | 662120 | 75 | Seattle Mariners | Atlanta Braves | 677594 | 2022-09-11 | 2022-09-11T23:31:09.465Z | 3 | 0.469 | ... | Home Run | line_drive | 1-1 | 6-7 | SEA | ATL | 528 | 2.771 | 29.905 | Home Run |
96365 | Julio Rodriguez | 662165 | 53 | Seattle Mariners | Texas Rangers | 677594 | 2022-07-27 | 2022-07-27T21:38:36.935Z | 3 | 0.338 | ... | Home Run | fly_ball | 1-1 | 1-2 | SEA | TEX | 391 | 0.926 | 19.611 | Home Run |
96308 | Julio Rodriguez | 662144 | 59 | Seattle Mariners | Washington Nationals | 677594 | 2022-08-24 | 2022-08-24T22:35:37.970Z | 2 | 0.336 | ... | Home Run | fly_ball | 0-1 | 0-1 | SEA | WSH | 456 | 1.629 | 24.218 | Home Run |
96265 | Julio Rodriguez | 662134 | 46 | Seattle Mariners | Oakland Athletics | 677594 | 2022-05-24 | 2022-05-25T03:25:24.636Z | 1 | 0.293 | ... | Home Run | fly_ball | 0-0 | 3-4 | SEA | OAK | 175 | 0.173 | 3.645 | Home Run |
96269 | Julio Rodriguez | 662135 | 23 | Seattle Mariners | Oakland Athletics | 677594 | 2022-05-23 | 2022-05-24T02:29:19.931Z | 1 | 0.241 | ... | Home Run | fly_ball | 0-0 | 0-0 | SEA | OAK | 169 | 0.005 | 3.015 | Home Run |
5 rows × 31 columns
groupbydict = {
'team': 'first',
'bat_code': 'first',
'batter_id': 'last',
'pitch_count': 'sum',
'PA_of_game': 'count',
'delta_win_exp': 'sum',
'delta_runs_exp': 'sum',
}
df2_hitters = df3_hitters.groupby(['batter']).agg(groupbydict).reset_index()
df2_hitters.columns = ['batter', 'team', 'team_code', 'batter_id', 'pitch_count', 'plate_apps', 'total_delta_win_exp', 'total_delta_runs_exp']
df2_hitters['delta_win_exp_per_pa'] = df2_hitters['total_delta_win_exp']/df2_hitters['plate_apps']
df2_hitters['delta_runs_exp_per_pa'] = df2_hitters['total_delta_runs_exp']/df2_hitters['plate_apps']
b_team = df2_hitters[df2_hitters.batter_id == b_id].team_code.iloc[0]
df2_hitters[df2_hitters.batter_id == b_id]
batter | team | team_code | batter_id | pitch_count | plate_apps | total_delta_win_exp | total_delta_runs_exp | delta_win_exp_per_pa | delta_runs_exp_per_pa | |
---|---|---|---|---|---|---|---|---|---|---|
359 | Julio Rodriguez | Seattle Mariners | SEA | 677594 | 2078 | 558 | 3.134 | 32.889 | 0.005616 | 0.058941 |
#df2_hitters.sort_values(by=['total_delta_win_exp'], ascending=False).head(5)
df2_hitters.sort_values(by=['total_delta_runs_exp'], ascending=False).head(5)
batter | team | team_code | batter_id | pitch_count | plate_apps | total_delta_win_exp | total_delta_runs_exp | delta_win_exp_per_pa | delta_runs_exp_per_pa | |
---|---|---|---|---|---|---|---|---|---|---|
2 | Aaron Judge | New York Yankees | NYY | 592450 | 2893 | 677 | 7.000 | 80.608 | 0.010340 | 0.119066 |
527 | Paul Goldschmidt | St. Louis Cardinals | STL | 502671 | 2690 | 648 | 4.180 | 61.778 | 0.006451 | 0.095336 |
219 | Freddie Freeman | Los Angeles Dodgers | LAD | 518692 | 2751 | 702 | 2.375 | 56.725 | 0.003383 | 0.080805 |
478 | Mike Trout | Los Angeles Angels | LAA | 545361 | 2101 | 495 | 4.434 | 42.807 | 0.008958 | 0.086479 |
684 | Yordan Alvarez | Houston Astros | HOU | 670541 | 2194 | 555 | 4.941 | 42.453 | 0.008903 | 0.076492 |
batter_ids = df3_hitters.batter_id.unique().tolist()
fig = go.Figure()
for i in batter_ids:
fig.add_trace(go.Scatter(x=df3_hitters[df3_hitters.batter_id == i].sort_values(by=['PA_num'], ascending=True).PA_num,
y=df3_hitters[df3_hitters.batter_id == i].sort_values(by=['PA_num'], ascending=True).cum_runs_exp_added,
line=dict(color='lightgray', width=2),
mode='lines'))
fig.add_trace(go.Scatter(x=df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True).PA_num,
y=df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True).cum_runs_exp_added,
line=dict(color='royalblue', width=4),
mode='lines'))
fig.add_trace(go.Scatter(x=[df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True)['PA_num'].iloc[-1]],
y=[df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True)['cum_runs_exp_added'].iloc[-1]],
line=dict(color='royalblue', width=4),
mode='lines+markers+text',
marker=dict(size=54, line=dict(width=4, color='black')),
text=round(df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True)['cum_runs_exp_added'].iloc[-1], 1),
textposition='middle center',
textfont=dict(size=18, color='white')))
fig.update_layout(title_text=f'<b>Expected Runs Added</b><br><sup>Cumulative, 2022</sup>',
title_font_size=25,
title_x=0.5,
title_y=0.92,
xaxis_title='Plate Appearances',
#yaxis_title='Expected Runs Added',
xaxis=dict(tickmode='linear', tick0=0, dtick=100, showgrid=False, zeroline=False, title_font=dict(size=18)),
yaxis=dict(tickmode='linear', tick0=0, dtick=10, showgrid=False, zeroline=False, title_font=dict(size=18)),
font=dict(size=18, color='black'),
showlegend=False,
width=1000,
height=650)
fig.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True, ticks='outside', tickson='labels', ticklen=5, tickwidth=2, fixedrange=True)
fig.update_yaxes(showline=True, linewidth=2, linecolor='black', mirror=True, ticks='outside', tickson='labels', ticklen=5, tickwidth=2, fixedrange=True)
fig.show()
batter_ids = df3_hitters.batter_id.unique().tolist()
fig9 = go.Figure()
for i in batter_ids:
fig9.add_trace(go.Scatter(x=df3_hitters[df3_hitters.batter_id == i].sort_values(by=['PA_num'], ascending=True).PA_num,
y=df3_hitters[df3_hitters.batter_id == i].sort_values(by=['PA_num'], ascending=True).cum_win_exp_added,
line=dict(color='lightgray', width=2),
mode='lines'))
fig9.add_trace(go.Scatter(x=df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True).PA_num,
y=df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True).cum_win_exp_added,
line=dict(color='royalblue', width=4),
mode='lines'))
fig9.add_trace(go.Scatter(x=[df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True)['PA_num'].iloc[-1]],
y=[df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True)['cum_win_exp_added'].iloc[-1]],
line=dict(color='royalblue', width=4),
mode='lines+markers+text',
marker=dict(size=54, line=dict(width=4, color='black')),
text=round(df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True)['cum_win_exp_added'].iloc[-1], 1),
textposition='middle center',
textfont=dict(size=18, color='white')))
fig9.update_layout(title_text=f'<b>Expected Win Percentage Added</b><br><sup>Cumulative, 2022</sup>',
title_font_size=25,
title_x=0.5,
title_y=0.92,
xaxis_title='Plate Appearances',
#yaxis_title='Expected Runs Added',
xaxis=dict(tickmode='linear', tick0=0, dtick=100, showgrid=False, zeroline=False, title_font=dict(size=18)),
yaxis=dict(tickmode='linear', tick0=0, dtick=1, showgrid=False, zeroline=False, title_font=dict(size=18)),
font=dict(size=18, color='black'),
showlegend=False,
width=1000,
height=650)
fig9.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True, ticks='outside', tickson='labels', ticklen=5, tickwidth=2, fixedrange=True)
fig9.update_yaxes(showline=True, linewidth=2, linecolor='black', mirror=True, ticks='outside', tickson='labels', ticklen=5, tickwidth=2, fixedrange=True)
fig9.show()
fig1 = go.Figure()
fig1.add_trace(go.Scatter(x=df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True).PA_num,
y=df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True).cum_runs_exp_added,
line=dict(color='royalblue', width=5),
mode='lines'))
fig1.add_trace(go.Scatter(x=[df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True)['PA_num'].iloc[-1]],
y=[df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True)['cum_runs_exp_added'].iloc[-1]],
line=dict(color='royalblue', width=4),
mode='lines+markers+text',
marker=dict(size=54, line=dict(width=4, color='black')),
text=round(df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['PA_num'], ascending=True)['cum_runs_exp_added'].iloc[-1], 1),
textposition='middle center',
textfont=dict(size=18, color='white')))
fig1.update_layout(title_text=f'<b>Cumulative Expected Runs Added</b>',
title_font_size=25,
title_x=0.5,
title_y=0.92,
xaxis_title='Plate Appearances',
xaxis=dict(tickmode='linear', tick0=0, dtick=100, showgrid=False, zeroline=False, title_font=dict(size=18)),
yaxis=dict(tickmode='linear', tick0=0, dtick=5, showgrid=False, zeroline=False, title_font=dict(size=18)),
font=dict(size=18, color='black'),
showlegend=False,
width=1200,
height=650)
fig1.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True, ticks='outside', tickson='labels', ticklen=5, tickwidth=2, fixedrange=True)
fig1.update_yaxes(showline=True, linewidth=2, linecolor='black', mirror=True, ticks='outside', tickson='labels', ticklen=5, tickwidth=2, fixedrange=True)
fig1.show()
def shorten_desc(x):
match x.count('scores') + x.count('homers'):
case 0:
return x.split('.')[0] + '.'
case 1:
return x.split('.')[0] + '. One (1) run scores.'
case 2:
return x.split('.')[0] + '. Two (2) runs score.'
case 3:
return x.split('.')[0] + '. Three (3) runs score.'
case 4:
return x.split('.')[0] + '. Four (4) runs score.'
top_plays_cols = ['game_date', 'field_code', 'score', 'inning', 'outs', 'desc', 'delta_win_exp']
df_top_plays = df3_hitters[df3_hitters.batter_id == b_id].sort_values(by=['delta_win_exp'], ascending=False)[top_plays_cols].head(5)
df_top_plays['desc'] = df_top_plays['desc'].apply(lambda x: shorten_desc(x))
for index, row in df_top_plays.iterrows():
rowlist = row['desc'].split('.')
if len(rowlist[1]) > 0:
print(rowlist[1][0])
df_top_plays['delta_win_exp'] = round(df_top_plays['delta_win_exp']*100,1).astype('string') + '%'
fig2 = go.Figure(data=[go.Table(columnwidth=[90, 70, 70, 80, 60, 300, 170],
header=dict(values=['<b>Date', '<b>Opp.', '<b>Score', '<b>Inning', '<b>Outs', '<b>Description', '<b>Win Exp. Added'],
align='center',
line=dict(color='black', width=2),
fill_color='royalblue',
font=dict(color='white', size=16),
height=40),
cells=dict(values=[df_top_plays['game_date']+'<br>', df_top_plays['field_code']+'<br>', df_top_plays['score']+'<br>',
df_top_plays['inning']+'<br>', df_top_plays['outs'].astype('str')+'<br>', df_top_plays['desc'],
'<b>'+df_top_plays['delta_win_exp']],
align=['center', 'center', 'center', 'center', 'center', 'left', 'center'],
fill_color='aliceblue',
line_color='aliceblue',
height=30))])
fig2.update_layout(title_text=f'<b>Top 5 Plays</b><br><sup>By Win Expectancy Added</sup>',
title_font_size=25,
title_x=0.5,
title_y=0.92,
xaxis=dict(tickmode='linear', tick0=0, dtick=100, showgrid=False, zeroline=False, title_font=dict(size=18)),
yaxis=dict(tickmode='linear', tick0=0, dtick=5, showgrid=False, zeroline=False, title_font=dict(size=18)),
font=dict(size=14, color='black'),
showlegend=False,
width=1000,
height=650)
fig2.show()
cutoff = 150
data = df2_hitters[df2_hitters.plate_apps >= cutoff].sort_values(by=['delta_runs_exp_per_pa']).reset_index(drop=True)
range_vals = [df2_hitters[df2_hitters.plate_apps >= cutoff]['delta_runs_exp_per_pa'].min(), df2_hitters[df2_hitters.plate_apps >= cutoff]['delta_runs_exp_per_pa'].max()]
value = data.loc[data['batter_id'] == b_id, 'delta_runs_exp_per_pa'].iat[0]
percentile = data['batter_id'].to_list().index(b_id)/len(data['batter_id'].to_list())
fig3 = ff.create_distplot(hist_data=[data['delta_runs_exp_per_pa']],
group_labels=['delta_runs_exp_per_pa'],
bin_size=[0.005],
show_hist=False,
show_rug=False)
x_kde = fig3.data[0].x
y_kde = fig3.data[0].y
x1 = [i for i in fig3.data[0].x if i < value]
y1 = fig3.data[0].y[:len(x1)]
fig3 = go.Figure()
fig3.add_trace(go.Scatter(x=x_kde,
y=y_kde,
line=dict(color='royalblue', width=5),
fill='tozeroy',
mode='none',
fillcolor='lightgrey'))
fig3.add_trace(go.Scatter(x=x1,
y=y1,
line=dict(color='royalblue', width=5),
fill='tozeroy',
mode='none',
fillcolor='royalblue'))
fig3.add_trace(go.Scatter(x=x_kde,
y=y_kde,
line=dict(color='black', width=2),
mode='lines'))
fig3.update_layout(title_text=f'<b>Expected Runs Added/PA</b><br><sup>Rank vs. MLB, Minimum {cutoff} PA</sup>',
title_font_size=25,
title_x=0.5,
title_y=0.92,
xaxis=dict(tickmode='linear', dtick=0.05, range=range_vals, showgrid=False, zeroline=False, title_font=dict(size=18)),
yaxis=dict(tickmode='linear', dtick=2, showgrid=False, zeroline=False, title_font=dict(size=18), showticklabels=False),
font=dict(size=18, color='black'),
showlegend=False,
width=800,
height=650)
match str(round(percentile*100,1))[-1]:
case '1':
suffix = 'st'
case '2':
suffix = 'nd'
case '3':
suffix = 'rd'
case '4' | '5' | '6' | '7' | '8' | '9':
suffix = 'th'
case '0':
match str(round(percentile*100,1))[-3]:
case '1':
suffix = 'st'
case '2':
suffix = 'nd'
case '3':
suffix = 'rd'
case '4' | '5' | '6' | '7' | '8' | '9' | '0':
suffix = 'th'
fig3.add_annotation(text=f'<b>{round(percentile*100,1)}{suffix} Percentile</b><br>xRA/PA: {round(value,4)}',
showarrow=False,
xref='paper', yref='paper',
x=0.04, y=0.95, align='center',
bgcolor='white',
bordercolor='black',
borderpad=5,
borderwidth=2)
fig3.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True, ticks='outside', tickson='labels', ticklen=5, tickwidth=2, fixedrange=True)
fig3.update_yaxes(showline=True, linewidth=2, linecolor='black', mirror=True, fixedrange=True)
fig3.show()
cutoff = 150
data = df2_hitters[df2_hitters.plate_apps >= cutoff].sort_values(by=['delta_win_exp_per_pa']).reset_index(drop=True)
range_vals = [df2_hitters[df2_hitters.plate_apps >= cutoff]['delta_win_exp_per_pa'].min(), df2_hitters[df2_hitters.plate_apps >= cutoff]['delta_win_exp_per_pa'].max()]
value = data.loc[data['batter_id'] == b_id, 'delta_win_exp_per_pa'].iat[0]
percentile = data['batter_id'].to_list().index(b_id)/len(data['batter_id'].to_list())
fig10 = ff.create_distplot(hist_data=[data['delta_win_exp_per_pa']],
group_labels=['delta_win_exp_per_pa'],
bin_size=[0.005],
show_hist=False,
show_rug=False)
x_kde = fig10.data[0].x
y_kde = fig10.data[0].y
x1 = [i for i in fig10.data[0].x if i < value]
y1 = fig10.data[0].y[:len(x1)]
fig10 = go.Figure()
fig10.add_trace(go.Scatter(x=x_kde,
y=y_kde,
line=dict(color='royalblue', width=5),
fill='tozeroy',
mode='none',
fillcolor='lightgrey'))
fig10.add_trace(go.Scatter(x=x1,
y=y1,
line=dict(color='royalblue', width=5),
fill='tozeroy',
mode='none',
fillcolor='royalblue'))
fig10.add_trace(go.Scatter(x=x_kde,
y=y_kde,
line=dict(color='black', width=2),
mode='lines'))
fig10.update_layout(title_text=f'<b>Expected Win Percentage Added/PA</b><br><sup>Rank vs. MLB, Minimum {cutoff} PA</sup>',
title_font_size=25,
title_x=0.5,
title_y=0.92,
xaxis=dict(tickmode='linear', dtick=0.005, range=range_vals, showgrid=False, zeroline=False, title_font=dict(size=18)),
yaxis=dict(tickmode='linear', dtick=2, showgrid=False, zeroline=False, title_font=dict(size=18), showticklabels=False),
font=dict(size=18, color='black'),
showlegend=False,
width=800,
height=650)
match str(round(percentile*100,1))[-1]:
case '1':
suffix = 'st'
case '2':
suffix = 'nd'
case '3':
suffix = 'rd'
case '4' | '5' | '6' | '7' | '8' | '9':
suffix = 'th'
case '0':
match str(round(percentile*100,1))[-3]:
case '1':
suffix = 'st'
case '2':
suffix = 'nd'
case '3':
suffix = 'rd'
case '4' | '5' | '6' | '7' | '8' | '9' | '0':
suffix = 'th'
fig10.add_annotation(text=f'<b>{round(percentile*100,1)}{suffix} Percentile</b><br>xWP/PA: {round(value,4)}',
showarrow=False,
xref='paper', yref='paper',
x=0.04, y=0.95, align='center',
bgcolor='white',
bordercolor='black',
borderpad=5,
borderwidth=2)
fig10.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True, ticks='outside', tickson='labels', ticklen=5, tickwidth=2, fixedrange=True)
fig10.update_yaxes(showline=True, linewidth=2, linecolor='black', mirror=True, fixedrange=True)
fig10.show()
def get_percentile(row, result_dict):
#if row['result'] in ['Strikeout', 'Groundout', 'Lineout', 'Pop Out', 'Flyout']:
#return 1 - result_dict[row['result']].index(row['rate'])/len(result_dict[row['result']])
return result_dict[row['result']].index(row['rate'])/len(result_dict[row['result']])
cutoff = 150
batter_ids = df2_hitters[df2_hitters.plate_apps >= cutoff].batter_id.unique().tolist()
results = df3_hitters.result_cat.unique().tolist()
groupbydict = {
'batter_id': 'last',
'delta_runs_exp': 'sum',
'time': 'count'
}
data = pd.DataFrame()
for i in batter_ids:
df = df3_hitters[df3_hitters.batter_id == i].groupby(['result_cat']).agg(groupbydict).reset_index()
df.columns = ['result', 'batter_id', 'total_runs_exp', 'total_PA']
df['runs_per_PA'] = df['total_runs_exp']/df['total_PA']
for result in results:
if result not in df.result.to_list():
df.loc[len(df.index)] = [result, i, 0.0, 0,0.000]
df['rate'] = round(df['total_PA']/df['total_PA'].sum(),3)
df['runs_per_PA'] = round(df['runs_per_PA'],3)
data = data.append(df)
data['total_runs_exp'] = round(data['total_runs_exp'],1)
data['color'] = np.where(data['total_runs_exp'] >= 0, 'royalblue', 'darkred')
data = data.reset_index(drop=True)
data = pd.merge(data, df2_hitters[['batter', 'batter_id']], on=['batter_id'])
result_dict = {}
for i in results:
result_dict[i] = data[data.result == i]['rate'].sort_values(ascending=True).reset_index(drop=True).to_list()
data['rate_percentile'] = data.apply(lambda row: get_percentile(row, result_dict), axis=1)
data
result | batter_id | total_runs_exp | total_PA | runs_per_PA | rate | color | batter | rate_percentile | |
---|---|---|---|---|---|---|---|---|---|
0 | Double | 572041 | 18.7 | 26 | 0.718 | 0.049 | royalblue | AJ Pollock | 0.688249 |
1 | Flyout | 572041 | -15.3 | 62 | -0.247 | 0.118 | darkred | AJ Pollock | 0.405276 |
2 | Groundout | 572041 | -34.6 | 140 | -0.247 | 0.266 | darkred | AJ Pollock | 0.808153 |
3 | Hit By Pitch | 572041 | 0.8 | 2 | 0.398 | 0.004 | royalblue | AJ Pollock | 0.124700 |
4 | Home Run | 572041 | 18.7 | 14 | 1.339 | 0.027 | royalblue | AJ Pollock | 0.496403 |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
4999 | Sacrifice | 656716 | 0.1 | 2 | 0.028 | 0.011 | royalblue | Zach McKinstry | 0.652278 |
5000 | Single | 656716 | 7.7 | 19 | 0.405 | 0.103 | royalblue | Zach McKinstry | 0.100719 |
5001 | Strikeout | 656716 | -13.6 | 52 | -0.262 | 0.281 | darkred | Zach McKinstry | 0.805755 |
5002 | Triple | 656716 | 2.1 | 3 | 0.687 | 0.016 | royalblue | Zach McKinstry | 0.985612 |
5003 | Walk | 656716 | 6.0 | 16 | 0.375 | 0.086 | royalblue | Zach McKinstry | 0.592326 |
5004 rows × 9 columns
bar_data = data[data.batter_id == b_id].sort_values(by='total_runs_exp', ascending=False).reset_index(drop=True)
fig4 = go.Figure(go.Bar(x=bar_data.result,
y=bar_data.total_runs_exp,
text=abs(bar_data.total_runs_exp),
textposition='auto'))
fig4.update_layout(title_text=f'Total Delta Expected Runs by PA Outcome',
title_font_size=25,
title_x=0.5,
title_y=0.92,
xaxis=dict(tickmode='linear', showgrid=False, zeroline=False, title_font=dict(size=18)),
yaxis=dict(tickmode='linear', showgrid=False, zeroline=False, title_font=dict(size=18), showticklabels=False),
font=dict(size=18, color='black'),
showlegend=False,
width=1200,
height=650)
fig4.update_traces(marker_color=bar_data['color'],
marker_line_color='black',
marker_line_width=2,
opacity=1,
textangle=0)
fig4.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True, ticks='outside', tickson='labels', ticklen=5, tickwidth=2, fixedrange=True)
fig4.update_yaxes(showline=True, linewidth=2, linecolor='black', mirror=True, fixedrange=True)
fig4.show()
bar_data = data[(data.batter_id == b_id) & data.result.isin(['Single', 'Double', 'Triple', 'Home Run', 'Walk', 'Hit By Pitch'])].reset_index(drop=True)
bar_data = bar_data.sort_values(by='total_runs_exp', ascending=True).reset_index(drop=True)
fig7 = go.Figure(go.Bar(x=bar_data.total_runs_exp,
y=bar_data.result,
orientation='h',
text=bar_data.total_runs_exp.astype('string'),
textposition='auto'))
fig7.update_layout(title_text=f'<b>Total Expected Runs Added</b><br><sup>By PA Outcome - Reached Base</sup>',
title_font_size=25,
title_x=0.5,
title_y=0.92,
xaxis=dict(tickmode='linear', showgrid=False, zeroline=False, title_font=dict(size=18), showticklabels=False),
yaxis=dict(tickmode='linear', showgrid=False, zeroline=False, title_font=dict(size=18)),
font=dict(size=18, color='black'),
showlegend=False,
width=700,
height=600)
fig7.update_traces(marker_color=bar_data['color'],
marker_line_color='black',
marker_line_width=2,
opacity=1,
textangle=0)
fig7.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True, fixedrange=True)
fig7.update_yaxes(showline=True, linewidth=2, linecolor='black', mirror=True, fixedrange=True, ticks='outside', tickson='labels', ticklen=5, tickwidth=2)
fig7.show()
bar_data = data[(data.batter_id == b_id) & data.result.isin(['Strikeout', 'Groundout', 'Flyout', 'Pop Out', 'Lineout', 'Sacrifice'])].reset_index(drop=True)
bar_data = bar_data.sort_values(by='total_runs_exp', ascending=False).reset_index(drop=True)
fig8 = go.Figure(go.Bar(x=0-bar_data.total_runs_exp,
y=bar_data.result,
orientation='h',
text=abs(bar_data.total_runs_exp).astype('string'),
textposition='auto'))
fig8.update_layout(title_text=f'<b>Total Expected Runs Lost</b><br><sup>By PA Outcome - Outs</sup>',
title_font_size=25,
title_x=0.5,
title_y=0.92,
xaxis=dict(tickmode='linear', showgrid=False, zeroline=False, title_font=dict(size=18), showticklabels=False),
yaxis=dict(tickmode='linear', showgrid=False, zeroline=False, title_font=dict(size=18)),
font=dict(size=18, color='black'),
showlegend=False,
width=700,
height=600)
fig8.update_traces(marker_color=bar_data['color'],
marker_line_color='black',
marker_line_width=2,
opacity=1,
textangle=0)
fig8.update_xaxes(showline=True, linewidth=2, linecolor='black', mirror=True, fixedrange=True)
fig8.update_yaxes(showline=True, linewidth=2, linecolor='black', mirror=True, fixedrange=True, ticks='outside', tickson='labels', ticklen=5, tickwidth=2)
fig8.show()
ordered_results = ['Single', 'Double', 'Triple', 'Home Run', 'Walk', 'Hit By Pitch']
polar_data = pd.DataFrame()
for i in ordered_results:
polar_data = polar_data.append(data[(data.batter_id == b_id) & (data.result == i)][['result', 'rate_percentile']])
polar_data['rate_percentile'] = round(polar_data['rate_percentile']*100,0).astype('int')
fig5 = go.Figure()
fig5.add_trace(go.Barpolar(r=polar_data.rate_percentile.to_list(),
width=[60,60,60,60,60,60],
theta0=0,
dtheta=60,
marker_color='royalblue',
marker_line_color="white",
marker_line_width=2,
marker_opacity=1))
fig5.add_trace(go.Barpolar(r=[100,100,100,100,100,100],
width=[60,60,60,60,60,60],
theta0=0,
dtheta=60,
marker_color='lightgrey',
marker_line_color="white",
marker_line_width=2,
marker_opacity=1))
fig5.update_layout(template=None,
polar=dict(radialaxis=dict(range=[0, 100], showticklabels=False, ticks=''),
angularaxis=dict(showticklabels=True, ticks='outside')),
showlegend=False,
polar_radialaxis_showgrid=False,
polar_radialaxis_showline=False,
polar_angularaxis_showgrid=False,
polar_angularaxis_linewidth=2,
polar_angularaxis_linecolor='black',
polar_angularaxis_tickcolor='black',
polar_angularaxis_tickwidth=2,
polar_angularaxis_ticktext=ordered_results,
polar_angularaxis_tickvals=[0,60,120,180,240,300],
polar_angularaxis_tickmode='array',
title='<b>PA Outcome Rates - Reached Base</b><br><sup>Percentile Rankings vs. MLB</sup>',
title_font_size=25,
title_x=0.5,
title_y=0.92,
font=dict(size=18, color='black'),
dragmode=False,
width=700,
height=600)
annot_coords = [(0.85, 0.5), (0.725, 0.875), (0.275, 0.875), (0.15, 0.5), (0.275, 0.125), (0.725, 0.125)]
for i in range(len(annot_coords)):
fig5.add_annotation(text=f'{polar_data.rate_percentile.to_list()[i]}%',
showarrow=False,
xref='paper', yref='paper',
x=annot_coords[i][0], y=annot_coords[i][1], align='center',
bgcolor='white',
bordercolor='black',
borderpad=4,
borderwidth=2)
fig5.show()
ordered_results = ['Strikeout', 'Groundout', 'Flyout', 'Pop Out', 'Lineout', 'Sacrifice']
polar_data = pd.DataFrame()
for i in ordered_results:
polar_data = polar_data.append(data[(data.batter_id == b_id) & (data.result == i)][['result', 'rate_percentile']])
polar_data['rate_percentile'] = round(polar_data['rate_percentile']*100,0).astype('int')
fig6 = go.Figure()
fig6.add_trace(go.Barpolar(r=polar_data.rate_percentile.to_list(),
width=[60,60,60,60,60,60],
theta0=0,
dtheta=60,
marker_color='darkred',
marker_line_color="white",
marker_line_width=2,
marker_opacity=1))
fig6.add_trace(go.Barpolar(r=[100,100,100,100,100,100],
width=[60,60,60,60,60,60],
theta0=0,
dtheta=60,
marker_color='lightgrey',
marker_line_color="white",
marker_line_width=2,
marker_opacity=1))
fig6.update_layout(template=None,
polar=dict(radialaxis=dict(range=[0, 100], showticklabels=False, ticks=''),
angularaxis=dict(showticklabels=True, ticks='outside')),
showlegend=False,
polar_radialaxis_showgrid=False,
polar_radialaxis_showline=False,
polar_angularaxis_showgrid=False,
polar_angularaxis_linewidth=2,
polar_angularaxis_linecolor='black',
polar_angularaxis_tickcolor='black',
polar_angularaxis_tickwidth=2,
polar_angularaxis_ticktext=ordered_results,
polar_angularaxis_tickvals=[0,60,120,180,240,300],
polar_angularaxis_tickmode='array',
title='<b>PA Outcome Rates - Outs</b><br><sup>Percentile Rankings vs. MLB</sup>',
title_font_size=25,
title_x=0.5,
title_y=0.92,
font=dict(size=18, color='black'),
dragmode=False,
width=700,
height=600)
annot_coords = [(0.85, 0.5), (0.725, 0.875), (0.275, 0.875), (0.15, 0.5), (0.275, 0.125), (0.725, 0.125)]
for i in range(len(annot_coords)):
fig6.add_annotation(text=f'{polar_data.rate_percentile.to_list()[i]}%',
showarrow=False,
xref='paper', yref='paper',
x=annot_coords[i][0], y=annot_coords[i][1], align='center',
bgcolor='white',
bordercolor='black',
borderpad=4,
borderwidth=2)
fig6.show()
fig.write_image('fig.png')
fig3.write_image('fig3.png')
fig2.write_image('fig2.png')
fig5.write_image('fig5.png')
fig6.write_image('fig6.png')
fig7.write_image('fig7.png')
fig8.write_image('fig8.png')
images = [Image.open(x) for x in ['fig.png', 'fig3.png', 'fig2.png']]
total_width = -210
heights = []
for i in images:
total_width += i.size[0]
heights.append(i.size[1])
max_height = max(heights)
new_im = Image.new('RGB', (total_width, max_height))
x_offset = 0
for im in images:
new_im.paste(im, (x_offset,0))
x_offset += im.size[0]-75
new_im.save('top.png')
images = [Image.open(x) for x in ['fig7.png', 'fig8.png', 'fig5.png', 'fig6.png']]
total_width = -210
heights = []
for i in images:
total_width += i.size[0]
heights.append(i.size[1])
max_height = max(heights)
new_im = Image.new('RGB', (total_width, max_height))
x_offset = 0
for im in images:
new_im.paste(im, (x_offset,0))
x_offset += im.size[0]-70
new_im.save('bot.png')
images = [Image.open(x) for x in ['top.png', 'bot.png']]
total_height = 0
widths = []
for i in images:
total_height += i.size[1]
widths.append(i.size[0])
min_width = min(widths)
new_im = Image.new('RGB', (min_width, total_height))
y_offset = 0
for im in images:
new_im.paste(im, (0,y_offset))
y_offset += im.size[1]
new_im.save('base.png')
url = f'https://img.mlbstatic.com/mlb-photos/image/upload/d_people:generic:headshot:67:current.png/w_213,q_auto:best/v1/people/{b_id}/headshot/67/current'
response = requests.get(url)
img = Image.open(BytesIO(response.content))
output = remove(img)
#output.save('headshot.png')
fig0 = go.Figure()
img_width = 2590
img_height = 1250
hs_width = 213
hs_height = 320
logo_width = 320
logo_height = 320
scale_factor = 1
fig0.update_xaxes(
visible=False,
range=[0, img_width * scale_factor]
)
fig0.update_yaxes(
visible=False,
range=[0, (img_height * scale_factor)+300],
)
fig0.add_layout_image(
dict(
xref='x',
yref='y',
x=0,
y=(img_height * scale_factor)+50,
sizex=(img_width * scale_factor),
sizey=(img_height * scale_factor),
opacity=1,
layer='below',
source=Image.open('base.png'))
)
fig0.add_layout_image(
dict(
xref='x',
yref='y',
x=50,
y=img_height+300,
sizex=hs_width,
sizey=hs_height,
sizing='stretch',
opacity=1,
layer='below',
source=output)
)
fig0.add_layout_image(
dict(
xref='x',
yref='y',
x=img_width-hs_width-125,
y=img_height+300,
sizex=logo_width,
sizey=logo_height,
sizing='stretch',
opacity=1,
layer='below',
source=f'https://www.mlbstatic.com/team-logos/team-primary-on-light/{team_logo_dict[b_team]}.svg')
)
fig0.update_layout(
width=(img_width * scale_factor),
height=(img_height * scale_factor)+300,
margin={"l": 0, "r": 0, "t": 0, "b": 0},
title=f'<b>2022 Season Summary</b><br><sup>{batter}</sup>',
title_font_size=45,
font=dict(size=18, color='black'),
title_x=0.5,
title_y=0.925,
plot_bgcolor='white'
)
fig0.add_annotation(text=f'<b>Data: MLB Stats API, Statcast',
showarrow=False,
xref='paper', yref='paper',
x=.5, y=.025, align='center',
bgcolor='white',
#bordercolor='black',
borderpad=4,
borderwidth=2,
font=dict(size=24)
)
fig0.show()
fig0.write_image(f'{b_id}-season-summary.png')
files_to_remove = ['fig.png', 'fig3.png', 'fig2.png', 'fig5.png', 'fig6.png', 'fig7.png', 'fig8.png', 'top.png', 'bot.png', 'base.png']
for i in files_to_remove:
os.remove(i)