Spaces:
Running
Running
patrickramos
commited on
Commit
•
43049df
1
Parent(s):
7763ddf
Update app
Browse files- data.py +6 -10
- demo.py +12 -11
- gradio_function.py +141 -111
data.py
CHANGED
@@ -69,14 +69,10 @@ df['swing'] = ~df['description'].isin(['B', 'BB', 'LS', 'inv_K', 'bunt_K', 'HBP'
|
|
69 |
df['csw'] = df['description'].isin(['SS', 'K', 'LS', 'inv_K'])
|
70 |
df['normal_pitch'] = ~df['description'].isin(['obstruction', 'illegal_pitch', 'defensive_interference']) # guess
|
71 |
|
72 |
-
|
73 |
-
whiff_rate = (
|
|
|
|
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
pitch_stats = pd.merge(
|
79 |
-
whiff_rate,
|
80 |
-
csw_rate,
|
81 |
-
on=['name', 'pitch_name']
|
82 |
-
).set_index(['name', 'pitch_name'])
|
|
|
69 |
df['csw'] = df['description'].isin(['SS', 'K', 'LS', 'inv_K'])
|
70 |
df['normal_pitch'] = ~df['description'].isin(['obstruction', 'illegal_pitch', 'defensive_interference']) # guess
|
71 |
|
72 |
+
df_by_player_pitch = df.groupby(['name', 'pitch_name'])
|
73 |
+
whiff_rate = (df_by_player_pitch['whiff'].sum() / df_by_player_pitch['swing'].sum() * 100).round(1).rename('Whiff%')
|
74 |
+
csw_rate = (df_by_player_pitch['csw'].sum() / df_by_player_pitch['normal_pitch'].sum() * 100).round(1).rename('CSW%')
|
75 |
+
velo = df_by_player_pitch['release_speed'].apply(lambda x: round(x.mean(), 1)).rename('Velocity')
|
76 |
|
77 |
+
pitch_stats = pd.concat([whiff_rate, csw_rate, velo], axis=1)
|
78 |
+
league_pitch_stats = pd.DataFrame(df.groupby('pitch_name')['release_speed'].apply(lambda x: round(x.mean(), 1)).rename('Velocity'))
|
|
|
|
|
|
|
|
|
|
|
|
demo.py
CHANGED
@@ -33,16 +33,14 @@ with gr.Blocks(css=css) as demo:
|
|
33 |
# NPB data visualization demo
|
34 |
[Data from SportsNavi](https://sports.yahoo.co.jp/)
|
35 |
''')
|
36 |
-
player = gr.Dropdown(choices=sorted(player_df['name'].dropna().tolist()), label='Player')
|
37 |
player_info = gr.Markdown()
|
38 |
download_file = gr.DownloadButton(label='Download player data')
|
39 |
-
with gr.
|
40 |
-
with gr.
|
41 |
-
gr.
|
42 |
-
|
43 |
-
|
44 |
-
gr.Markdown('## Pitch Velocity')
|
45 |
-
pitch_velo_summary = gr.Plot(show_label=False, elem_classes='pitch-velo-summary')
|
46 |
|
47 |
|
48 |
max_pitch_maps = len(jp_pitch_to_en_pitch)
|
@@ -70,18 +68,21 @@ with gr.Blocks(css=css) as demo:
|
|
70 |
pitch_names.append(gr.Markdown(f'### Pitch {col+1}', visible=visible))
|
71 |
pitch_infos.append(gr.DataFrame(pd.DataFrame([{'Whiff%': None, 'CSW%': None}]), interactive=False, visible=visible))
|
72 |
pitch_velos.append(gr.Plot(show_label=False, elem_classes='pitch-velo', visible=visible))
|
73 |
-
pitch_maps.append(gr.Plot(label='Pitch
|
|
|
|
|
|
|
74 |
|
75 |
gr.Markdown('## Bugs and other notes')
|
76 |
with gr.Accordion('Click to open', open=False):
|
77 |
gr.Markdown('''
|
78 |
-
- No padding in pie charts leads to hovertext getting cut off near the bottom for some players
|
79 |
- Y axis ticks messy when no velocity distribution is plotted
|
80 |
- Topmost distribution in summary velo distribution plot is clipped
|
|
|
81 |
'''
|
82 |
)
|
83 |
|
84 |
-
player.input(get_data, inputs=player, outputs=[player_info, download_file, usage, *pitch_groups, *pitch_names, *pitch_infos, *pitch_velos, *pitch_maps,
|
85 |
|
86 |
demo.launch(
|
87 |
share=True,
|
|
|
33 |
# NPB data visualization demo
|
34 |
[Data from SportsNavi](https://sports.yahoo.co.jp/)
|
35 |
''')
|
36 |
+
player = gr.Dropdown(value=None, choices=sorted(player_df['name'].dropna().tolist()), label='Player')
|
37 |
player_info = gr.Markdown()
|
38 |
download_file = gr.DownloadButton(label='Download player data')
|
39 |
+
with gr.Group():
|
40 |
+
with gr.Row():
|
41 |
+
usage = gr.Plot(label='Pitch Distribution')#, elem_classes='pitch-usage')
|
42 |
+
pitch_velo_summary = gr.Plot(label='Velocity Summary')#, elem_classes='pitch-velo-summary')
|
43 |
+
pitch_loc_summary = gr.Plot(label='Overall Location')
|
|
|
|
|
44 |
|
45 |
|
46 |
max_pitch_maps = len(jp_pitch_to_en_pitch)
|
|
|
68 |
pitch_names.append(gr.Markdown(f'### Pitch {col+1}', visible=visible))
|
69 |
pitch_infos.append(gr.DataFrame(pd.DataFrame([{'Whiff%': None, 'CSW%': None}]), interactive=False, visible=visible))
|
70 |
pitch_velos.append(gr.Plot(show_label=False, elem_classes='pitch-velo', visible=visible))
|
71 |
+
pitch_maps.append(gr.Plot(label='Pitch Location', elem_classes='pitch-loc', visible=visible))
|
72 |
+
|
73 |
+
gr.Markdown('## Pitch Velocity')
|
74 |
+
velo_stats = gr.DataFrame(pd.DataFrame([{'Avg. Velo': None, 'League Avg. Velo': None}]), interactive=False, label='Pitch Velocity')
|
75 |
|
76 |
gr.Markdown('## Bugs and other notes')
|
77 |
with gr.Accordion('Click to open', open=False):
|
78 |
gr.Markdown('''
|
|
|
79 |
- Y axis ticks messy when no velocity distribution is plotted
|
80 |
- Topmost distribution in summary velo distribution plot is clipped
|
81 |
+
- DataFrame precision inconsistent
|
82 |
'''
|
83 |
)
|
84 |
|
85 |
+
player.input(get_data, inputs=player, outputs=[player_info, download_file, usage, pitch_velo_summary, pitch_loc_summary, *pitch_groups, *pitch_names, *pitch_infos, *pitch_velos, *pitch_maps, velo_stats])
|
86 |
|
87 |
demo.launch(
|
88 |
share=True,
|
gradio_function.py
CHANGED
@@ -8,7 +8,7 @@ import pandas as pd
|
|
8 |
import gradio as gr
|
9 |
|
10 |
from translate import max_pitch_types
|
11 |
-
from data import df, pitch_stats
|
12 |
|
13 |
# GRADIO FUNCTIONS
|
14 |
|
@@ -48,42 +48,54 @@ colorscale = [
|
|
48 |
]
|
49 |
|
50 |
|
51 |
-
def plot_pitch_map(player=None, loc=None, pitch_type=None, pitch_name=None):
|
52 |
assert not ((loc is None and player is None) or (loc is not None and player is not None)), 'exactly one of `player` or `loc` must be specified'
|
53 |
|
54 |
if loc is None and player is not None:
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
|
|
|
|
|
|
60 |
|
61 |
fig = go.Figure()
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
fig.update_layout(
|
88 |
xaxis=dict(range=[-plot_s/2, plot_s/2+1], showticklabels=False),
|
89 |
yaxis=dict(range=[-plot_s/2, plot_s/2+1], scaleanchor='x', scaleratio=1, showticklabels=False),
|
@@ -93,24 +105,24 @@ def plot_pitch_map(player=None, loc=None, pitch_type=None, pitch_name=None):
|
|
93 |
return fig
|
94 |
|
95 |
|
96 |
-
def plot_empty_pitch_map():
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
|
112 |
# velo distribution
|
113 |
-
def plot_pitch_velo(player=None, velos=None, pitch_type=None, pitch_name=None):
|
114 |
assert not ((velos is None and player is None) or (velos is not None and player is not None)), 'exactly one of `player` or `loc` must be specified'
|
115 |
|
116 |
if velos is None and player is not None:
|
@@ -118,13 +130,27 @@ def plot_pitch_velo(player=None, velos=None, pitch_type=None, pitch_name=None):
|
|
118 |
pitch_val = pitch_type or pitch_name
|
119 |
pitch_col = 'pitch_type' if pitch_type else 'pitch_name'
|
120 |
velos = df.set_index(['name', pitch_col]).loc[(player, pitch_val), 'release_speed']
|
|
|
|
|
|
|
121 |
|
122 |
-
fig = go.Figure(
|
123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
124 |
fig.update_layout(
|
125 |
xaxis=dict(
|
126 |
title='Velocity',
|
127 |
-
range=
|
128 |
scaleratio=2
|
129 |
),
|
130 |
yaxis=dict(
|
@@ -143,39 +169,39 @@ def plot_pitch_velo(player=None, velos=None, pitch_type=None, pitch_name=None):
|
|
143 |
return fig
|
144 |
|
145 |
|
146 |
-
def plot_empty_pitch_velo():
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
def plot_all_pitch_velo(player=None, player_df=None, pitch_counts=None, min_pitches=
|
179 |
# assert not ((player is None and player_df is None) or (player is not None and player_df is not None)), 'exactly one of `player` or `player_df` must be specified'
|
180 |
|
181 |
if player_df is None and player is not None:
|
@@ -221,7 +247,7 @@ def plot_all_pitch_velo(player=None, player_df=None, pitch_counts=None, min_pitc
|
|
221 |
fig.add_trace(go.Scatter(
|
222 |
x=[velo_center],
|
223 |
y=[pitch_name],
|
224 |
-
text=['No visualization as less than
|
225 |
textposition='top center',
|
226 |
hovertext=False,
|
227 |
mode="lines+text",
|
@@ -230,16 +256,6 @@ def plot_all_pitch_velo(player=None, player_df=None, pitch_counts=None, min_pitc
|
|
230 |
name=pitch_name,
|
231 |
))
|
232 |
|
233 |
-
fig.add_trace(go.Violin(
|
234 |
-
x=player_df['release_speed'],
|
235 |
-
y=[player]*len(player_df),
|
236 |
-
side='positive',
|
237 |
-
orientation='h',
|
238 |
-
meanline_visible=True,
|
239 |
-
points=False,
|
240 |
-
legendrank=0,
|
241 |
-
name=player
|
242 |
-
))
|
243 |
fig.add_trace(go.Violin(
|
244 |
x=league_df['release_speed'],
|
245 |
y=[player]*len(league_df),
|
@@ -253,9 +269,20 @@ def plot_all_pitch_velo(player=None, player_df=None, pitch_counts=None, min_pitc
|
|
253 |
# visible='legendonly',
|
254 |
name='NPB',
|
255 |
))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
256 |
|
257 |
-
fig.update_xaxes(title='Velocity')
|
258 |
-
fig.
|
|
|
259 |
|
260 |
return fig
|
261 |
|
@@ -263,14 +290,18 @@ def plot_all_pitch_velo(player=None, player_df=None, pitch_counts=None, min_pitc
|
|
263 |
def get_data(player):
|
264 |
player_name = f'# {player}'
|
265 |
|
266 |
-
_df = df.set_index('name').loc[player]
|
267 |
_df.to_csv(f'files/npb.csv', index=False)
|
268 |
-
_df_by_pitch_name = _df.set_index('pitch_name')
|
269 |
|
270 |
usage_fig = px.pie(_df['pitch_name'], names='pitch_name')
|
271 |
usage_fig.update_traces(texttemplate='%{percent:.1%}', hovertemplate=f'<b>{player}</b><br>' + 'threw a <b>%{label}</b><br><b>%{percent:.1%}</b> of the time (<b>%{value}</b> pitches)')
|
272 |
|
273 |
pitch_counts = _df['pitch_name'].value_counts()
|
|
|
|
|
|
|
|
|
274 |
pitch_groups = []
|
275 |
pitch_names = []
|
276 |
pitch_infos = []
|
@@ -288,16 +319,15 @@ def get_data(player):
|
|
288 |
visible=True
|
289 |
))
|
290 |
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
pitch_maps.append(gr.update(value=plot_empty_pitch_map(), label=pitch_name, visible=True))
|
301 |
|
302 |
for _ in range(max_pitch_types - len(pitch_names)):
|
303 |
pitch_groups.append(gr.update(visible=False))
|
@@ -307,6 +337,6 @@ def get_data(player):
|
|
307 |
pitch_velos.append(gr.update(value=None, visible=False))
|
308 |
pitch_maps.append(gr.update(value=None, visible=False))
|
309 |
|
310 |
-
|
311 |
|
312 |
-
return player_name, 'files/npb.csv', usage_fig, *pitch_groups, *pitch_names, *pitch_infos, *pitch_velos, *pitch_maps,
|
|
|
8 |
import gradio as gr
|
9 |
|
10 |
from translate import max_pitch_types
|
11 |
+
from data import df, pitch_stats, league_pitch_stats
|
12 |
|
13 |
# GRADIO FUNCTIONS
|
14 |
|
|
|
48 |
]
|
49 |
|
50 |
|
51 |
+
def plot_pitch_map(player=None, loc=None, pitch_type=None, pitch_name=None, all_pitches=False, min_pitches=2):
|
52 |
assert not ((loc is None and player is None) or (loc is not None and player is not None)), 'exactly one of `player` or `loc` must be specified'
|
53 |
|
54 |
if loc is None and player is not None:
|
55 |
+
if all_pitches:
|
56 |
+
assert not (pitch_type is not None or pitch_name is not None), 'cannot have `pitch_type` or `pitch_name` when `all_pitches` is `True`'
|
57 |
+
loc = df.sort_values('name').set_index('name').loc[player, ['plate_x', 'plate_z']]
|
58 |
+
else:
|
59 |
+
assert not ((pitch_type is None and pitch_name is None) or (pitch_type is not None and pitch_name is not None)), 'exactly one of `pitch_type` or `pitch_name` must be specified'
|
60 |
+
pitch_val = pitch_type or pitch_name
|
61 |
+
pitch_col = 'pitch_type' if pitch_type else 'pitch_name'
|
62 |
+
loc = df.sort_values('name').set_index(['name', pitch_col]).loc[(player, pitch_val), ['plate_x', 'plate_z']]
|
63 |
|
64 |
fig = go.Figure()
|
65 |
+
if len(loc) >= min_pitches:
|
66 |
+
Z = fit_pred_kde(loc.to_numpy().T, X, Y)
|
67 |
+
fig.add_shape(
|
68 |
+
type="rect",
|
69 |
+
**coordinatify(sz_h, sz_w),
|
70 |
+
line_color='gray',
|
71 |
+
# fillcolor='rgba(220, 220, 220, 0.75)', #gainsboro
|
72 |
+
)
|
73 |
+
fig.add_shape(
|
74 |
+
type="rect",
|
75 |
+
**coordinatify(h_h, h_w),
|
76 |
+
line_color='dimgray',
|
77 |
+
)
|
78 |
+
fig.add_trace(go.Contour(
|
79 |
+
z=Z,
|
80 |
+
x=kde_range,
|
81 |
+
y=kde_range,
|
82 |
+
colorscale=colorscale,
|
83 |
+
zmin=1e-5,
|
84 |
+
zmax=Z.max(),
|
85 |
+
contours={
|
86 |
+
'start': 1e-5,
|
87 |
+
'end': Z.max(),
|
88 |
+
'size': Z.max() / 6
|
89 |
+
},
|
90 |
+
showscale=False
|
91 |
+
))
|
92 |
+
else:
|
93 |
+
fig.add_annotation(
|
94 |
+
x=0,
|
95 |
+
y=0,
|
96 |
+
text=f'No visualization<br>as less than {min_pitches} pitches thrown',
|
97 |
+
showarrow=False
|
98 |
+
)
|
99 |
fig.update_layout(
|
100 |
xaxis=dict(range=[-plot_s/2, plot_s/2+1], showticklabels=False),
|
101 |
yaxis=dict(range=[-plot_s/2, plot_s/2+1], scaleanchor='x', scaleratio=1, showticklabels=False),
|
|
|
105 |
return fig
|
106 |
|
107 |
|
108 |
+
# def plot_empty_pitch_map():
|
109 |
+
# fig = go.Figure()
|
110 |
+
# fig.add_annotation(
|
111 |
+
# x=0,
|
112 |
+
# y=0,
|
113 |
+
# text='No visualization<br>as less than 10 pitches thrown',
|
114 |
+
# showarrow=False
|
115 |
+
# )
|
116 |
+
# fig.update_layout(
|
117 |
+
# xaxis=dict(range=[-plot_s/2, plot_s/2+1], showticklabels=False),
|
118 |
+
# yaxis=dict(range=[-plot_s/2, plot_s/2+1], scaleanchor='x', scaleratio=1, showticklabels=False),
|
119 |
+
# # width=384,
|
120 |
+
# # height=384
|
121 |
+
# )
|
122 |
+
# return fig
|
123 |
|
124 |
# velo distribution
|
125 |
+
def plot_pitch_velo(player=None, velos=None, pitch_type=None, pitch_name=None, min_pitches=2):
|
126 |
assert not ((velos is None and player is None) or (velos is not None and player is not None)), 'exactly one of `player` or `loc` must be specified'
|
127 |
|
128 |
if velos is None and player is not None:
|
|
|
130 |
pitch_val = pitch_type or pitch_name
|
131 |
pitch_col = 'pitch_type' if pitch_type else 'pitch_name'
|
132 |
velos = df.set_index(['name', pitch_col]).loc[(player, pitch_val), 'release_speed']
|
133 |
+
|
134 |
+
if isinstance(velos, int):
|
135 |
+
velos = [velos]
|
136 |
|
137 |
+
fig = go.Figure()
|
138 |
+
if len(velos) >= min_pitches:
|
139 |
+
fig = fig.add_trace(go.Violin(x=velos, side='positive', hoveron='points', points=False, meanline_visible=True, name='Velocity Distribution'))
|
140 |
+
median = velos.median()
|
141 |
+
x_range = [median-25, median+25]
|
142 |
+
else:
|
143 |
+
fig.add_annotation(
|
144 |
+
x=(170+125)/2,
|
145 |
+
y=0.3/2,
|
146 |
+
text=f'No visualization<br>as less than {min_pitches} pitches thrown',
|
147 |
+
showarrow=False,
|
148 |
+
)
|
149 |
+
x_range = [125, 170]
|
150 |
fig.update_layout(
|
151 |
xaxis=dict(
|
152 |
title='Velocity',
|
153 |
+
range=x_range,
|
154 |
scaleratio=2
|
155 |
),
|
156 |
yaxis=dict(
|
|
|
169 |
return fig
|
170 |
|
171 |
|
172 |
+
# def plot_empty_pitch_velo():
|
173 |
+
# fig = go.Figure()
|
174 |
+
# fig.add_annotation(
|
175 |
+
# x=(170+125)/2,
|
176 |
+
# y=0.3/2,
|
177 |
+
# text='No visualization<br>as less than 10 pitches thrown',
|
178 |
+
# showarrow=False,
|
179 |
+
# )
|
180 |
+
# fig.update_layout(
|
181 |
+
# xaxis=dict(
|
182 |
+
# title='Velocity',
|
183 |
+
# range=[125, 170],
|
184 |
+
# scaleratio=2
|
185 |
+
# ),
|
186 |
+
# yaxis=dict(
|
187 |
+
# title='Frequency',
|
188 |
+
# range=[0, 0.3],
|
189 |
+
# scaleanchor='x',
|
190 |
+
# scaleratio=1,
|
191 |
+
# # tickvals=np.linspace(0, 0.3, 3),
|
192 |
+
# # ticktext=np.linspace(0, 0.3, 3),
|
193 |
+
# tickvals=[0.15],
|
194 |
+
# ticktext=[0.15]
|
195 |
+
# ),
|
196 |
+
# autosize=True,
|
197 |
+
# # width=512,
|
198 |
+
# # height=256,
|
199 |
+
# modebar_remove=['zoom', 'autoScale', 'resetScale'],
|
200 |
+
# )
|
201 |
+
# return fig
|
202 |
+
|
203 |
+
|
204 |
+
def plot_all_pitch_velo(player=None, player_df=None, pitch_counts=None, min_pitches=2):
|
205 |
# assert not ((player is None and player_df is None) or (player is not None and player_df is not None)), 'exactly one of `player` or `player_df` must be specified'
|
206 |
|
207 |
if player_df is None and player is not None:
|
|
|
247 |
fig.add_trace(go.Scatter(
|
248 |
x=[velo_center],
|
249 |
y=[pitch_name],
|
250 |
+
text=[f'No visualization as less than {min_pitches} pitches thrown'],
|
251 |
textposition='top center',
|
252 |
hovertext=False,
|
253 |
mode="lines+text",
|
|
|
256 |
name=pitch_name,
|
257 |
))
|
258 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
259 |
fig.add_trace(go.Violin(
|
260 |
x=league_df['release_speed'],
|
261 |
y=[player]*len(league_df),
|
|
|
269 |
# visible='legendonly',
|
270 |
name='NPB',
|
271 |
))
|
272 |
+
fig.add_trace(go.Violin(
|
273 |
+
x=player_df['release_speed'],
|
274 |
+
y=[player]*len(player_df),
|
275 |
+
side='positive',
|
276 |
+
orientation='h',
|
277 |
+
meanline_visible=True,
|
278 |
+
points=False,
|
279 |
+
legendrank=0,
|
280 |
+
name=player
|
281 |
+
))
|
282 |
|
283 |
+
fig.update_xaxes(title='Velocity', range=[player_df['release_speed'].dropna().min() - 2, player_df['release_speed'].dropna().max() + 2])
|
284 |
+
fig.update_yaxes(range=[0, len(pitch_counts)+1-0.25], visible=False)
|
285 |
+
fig.update_layout(violingap=0, violingroupgap=0, legend=dict(orientation='h', y=-0.15, yanchor='top'))
|
286 |
|
287 |
return fig
|
288 |
|
|
|
290 |
def get_data(player):
|
291 |
player_name = f'# {player}'
|
292 |
|
293 |
+
_df = df.sort_values('name').set_index('name').loc[player]
|
294 |
_df.to_csv(f'files/npb.csv', index=False)
|
295 |
+
_df_by_pitch_name = _df.set_index('pitch_name').sort_values('pitch_name')
|
296 |
|
297 |
usage_fig = px.pie(_df['pitch_name'], names='pitch_name')
|
298 |
usage_fig.update_traces(texttemplate='%{percent:.1%}', hovertemplate=f'<b>{player}</b><br>' + 'threw a <b>%{label}</b><br><b>%{percent:.1%}</b> of the time (<b>%{value}</b> pitches)')
|
299 |
|
300 |
pitch_counts = _df['pitch_name'].value_counts()
|
301 |
+
|
302 |
+
pitch_velo_summary = plot_all_pitch_velo(player=player, player_df=_df_by_pitch_name, pitch_counts=pitch_counts.sort_values(ascending=True))
|
303 |
+
pitch_loc_summary = plot_pitch_map(player, all_pitches=True)
|
304 |
+
|
305 |
pitch_groups = []
|
306 |
pitch_names = []
|
307 |
pitch_infos = []
|
|
|
319 |
visible=True
|
320 |
))
|
321 |
|
322 |
+
pitch_velos.append(gr.update(
|
323 |
+
value=plot_pitch_velo(velos=_df_by_pitch_name.loc[pitch_name, 'release_speed']),
|
324 |
+
visible=True
|
325 |
+
))
|
326 |
+
pitch_maps.append(gr.update(
|
327 |
+
value=plot_pitch_map(player, pitch_name=pitch_name),
|
328 |
+
label='Pitch location',
|
329 |
+
visible=True
|
330 |
+
))
|
|
|
331 |
|
332 |
for _ in range(max_pitch_types - len(pitch_names)):
|
333 |
pitch_groups.append(gr.update(visible=False))
|
|
|
337 |
pitch_velos.append(gr.update(value=None, visible=False))
|
338 |
pitch_maps.append(gr.update(value=None, visible=False))
|
339 |
|
340 |
+
velo_stats = pd.concat([pitch_stats.loc[player, 'Velocity'].rename('Avg. Velo'), league_pitch_stats['Velocity'].rename('League Avg. Velo')], join='inner', axis=1).rename_axis(['Pitch']).reset_index()
|
341 |
|
342 |
+
return player_name, 'files/npb.csv', usage_fig, pitch_velo_summary, pitch_loc_summary, *pitch_groups, *pitch_names, *pitch_infos, *pitch_velos, *pitch_maps, velo_stats
|