@@ -54,7 +54,7 @@ def find_assists(df):
54
54
55
55
56
56
# Locate and build a dataframe of all set plays, ignoring kick-offs and throw-ins
57
- def find_set_plays (df ):
57
+ def find_set_plays (df , mode ):
58
58
sp_df = pd .DataFrame ()
59
59
60
60
count = 0
@@ -100,9 +100,14 @@ def find_set_plays(df):
100
100
),
101
101
ignore_index = True ,
102
102
)
103
+ if mode == "progressive" :
104
+ df = df .drop (index + 1 )
103
105
except Exception as e :
104
106
print (e )
105
107
108
+ if mode == "progressive" :
109
+ sp_df = df
110
+
106
111
sp_df .loc [sp_df .To .isnull (), "Type" ] = "Incomplete"
107
112
return sp_df
108
113
@@ -130,7 +135,7 @@ def left_justify_events(df, team_on_left):
130
135
return df
131
136
132
137
133
- # Once number of clusters is auto-calculated, graph the cluseters
138
+ # Once number of clusters is auto-calculated, graph the clusters
134
139
def create_cluster_graph (df , num_clusters ):
135
140
# creates a new trace for each set of data
136
141
fig = make_subplots (
@@ -206,6 +211,67 @@ def drawAnnotations(df):
206
211
return annotations_list
207
212
208
213
214
+ def find_progressive_passes (df ):
215
+ # df = df.loc[(df['End_X'] - df['location_x']) > 1000] # limit passes to those greater than 10M forward
216
+ df_own_half = df .loc [
217
+ (df ["End_X" ] < 0.5 ) & (df ["Start_X" ] < 0.5 )
218
+ ] # passes in own half
219
+ df_diff_half = df .loc [
220
+ (df ["End_X" ] > 0.5 ) & (df ["Start_X" ] < 0.5 )
221
+ ] # passes in different half
222
+ df_opp_half = df .loc [
223
+ (df ["End_X" ] > 0.5 ) & (df ["Start_X" ] > 0.5 )
224
+ ] # passes in opponent's half
225
+ goal_x = float (1 )
226
+ goal_y = float (0.5 )
227
+
228
+ # Passes in own half
229
+ if len (df_own_half ) > 0 :
230
+ # dist = math.hypot(x2 - x1, y2 - y1)
231
+ df_own_half ["orig_distance_to_goal" ] = df_own_half .apply (
232
+ lambda x : math .hypot (x ["Start_X" ] - goal_x , x ["Start_Y" ] - goal_y ), axis = 1
233
+ )
234
+ df_own_half ["end_distance_to_goal" ] = df_own_half .apply (
235
+ lambda x : math .hypot (x ["End_X" ] - goal_x , x ["End_Y" ] - goal_y ), axis = 1
236
+ )
237
+ df_own_half ["distance" ] = (
238
+ df_own_half ["orig_distance_to_goal" ] - df_own_half ["end_distance_to_goal" ]
239
+ )
240
+ df_own_half = df_own_half .loc [(df_own_half ["distance" ]) >= 0.30 ]
241
+
242
+ # Passes in both halves
243
+ if len (df_diff_half ) > 0 :
244
+ df_diff_half ["orig_distance_to_goal" ] = df_diff_half .apply (
245
+ lambda x : math .hypot (x ["Start_X" ] - goal_x , x ["Start_Y" ] - goal_y ), axis = 1
246
+ )
247
+ df_diff_half ["end_distance_to_goal" ] = df_diff_half .apply (
248
+ lambda x : math .hypot (x ["End_X" ] - goal_x , x ["End_Y" ] - goal_y ), axis = 1
249
+ )
250
+
251
+ df_diff_half ["distance" ] = (
252
+ df_diff_half ["orig_distance_to_goal" ] - df_diff_half ["end_distance_to_goal" ]
253
+ )
254
+ df_diff_half = df_diff_half .loc [(df_diff_half ["distance" ]) >= 0.15 ]
255
+
256
+ # Passes in opposition half
257
+ if len (df_opp_half ) > 0 :
258
+ df_opp_half ["orig_distance_to_goal" ] = df_opp_half .apply (
259
+ lambda x : math .hypot (x ["Start_X" ] - goal_x , x ["Start_Y" ] - goal_y ), axis = 1
260
+ )
261
+ df_opp_half ["end_distance_to_goal" ] = df_opp_half .apply (
262
+ lambda x : math .hypot (x ["End_X" ] - goal_x , x ["End_Y" ] - goal_y ), axis = 1
263
+ )
264
+ df_opp_half ["distance" ] = (
265
+ df_opp_half ["orig_distance_to_goal" ] - df_opp_half ["end_distance_to_goal" ]
266
+ )
267
+ df_opp_half = df_opp_half .loc [(df_opp_half ["distance" ]) >= 0.12 ]
268
+
269
+ df_list = [df_own_half , df_diff_half , df_opp_half ] # List of your dataframes
270
+ df_combo = pd .concat (df_list )
271
+
272
+ return df_combo
273
+
274
+
209
275
# Main function - graph all football events which occur in a match
210
276
def plotEvents (eventType , filename , team , team_on_left ):
211
277
# Read in event csv data file
@@ -225,7 +291,7 @@ def plotEvents(eventType, filename, team, team_on_left):
225
291
226
292
# For events involving the graphing of movement of the ball from one location to another
227
293
if (
228
- (eventType == "Progressive Passes Into Final 3rd " )
294
+ (eventType == "Progressive Passes" )
229
295
or (eventType == "Crosses" )
230
296
or (eventType == "Set Plays" )
231
297
or (eventType == "Assists to Shots" )
@@ -235,14 +301,15 @@ def plotEvents(eventType, filename, team, team_on_left):
235
301
if eventType == "Assists to Shots" :
236
302
df = find_assists (events_df )
237
303
elif eventType == "Set Plays" :
238
- df = find_set_plays (events_df )
239
- elif eventType == "Progressive Passes Into Final 3rd" :
240
- df = events_df .loc [events_df ["Type" ] == "PASS" ]
304
+ df = find_set_plays (events_df , "normal" )
305
+ elif eventType == "Progressive Passes" :
306
+ df = find_set_plays (
307
+ events_df , "progressive"
308
+ ) # take out set plays as they include corners and throw-ins
309
+ df = df [(df ["Start_Y" ] > 0 ) & (df ["Start_Y" ] < 1 )]
310
+ df = df .loc [events_df ["Type" ] == "PASS" ]
241
311
df .reset_index (drop = True , inplace = True )
242
- df = df .loc [
243
- (df ["End_X" ] - df ["Start_X" ]) > 0.1
244
- ] # limit passes to those greater than 10M forward
245
- df = df .loc [df ["End_X" ] > 0.7 ]
312
+ df = find_progressive_passes (df )
246
313
elif eventType == "Crosses" :
247
314
df = events_df .loc [events_df ["Subtype" ].str .contains ("CROSS" , na = False )]
248
315
df .reset_index (drop = True , inplace = True )
@@ -279,7 +346,7 @@ def plotEvents(eventType, filename, team, team_on_left):
279
346
"Crosses" ,
280
347
"Set Plays" ,
281
348
"Assists to Shots" ,
282
- "Progressive Passes Into Final 3rd " ,
349
+ "Progressive Passes" ,
283
350
]:
284
351
colorfactor = df ["Type" ]
285
352
fig = px .scatter (
@@ -299,7 +366,6 @@ def plotEvents(eventType, filename, team, team_on_left):
299
366
"Start_X" : False ,
300
367
"Start_Y" : False ,
301
368
"size" : False ,
302
- "Type" : False ,
303
369
"From" : True ,
304
370
"To" : True ,
305
371
},
@@ -377,6 +443,14 @@ def plotEvents(eventType, filename, team, team_on_left):
377
443
# Metrica data starts 0, 0 at top left corner. Need to account for that or markers will be wrong
378
444
fig .update_yaxes (autorange = "reversed" )
379
445
446
+ # Add corner flags to prevent zoom and pitch distortion
447
+ fig .add_scatter (
448
+ x = [0 , 0 , 1 , 1 ],
449
+ y = [0 , 1 , 0 , 1 ],
450
+ mode = "markers" ,
451
+ marker = dict (size = 2 , color = "grey" ),
452
+ )
453
+
380
454
# Remove side color scale and hide zero and gridlines
381
455
fig .update_layout (
382
456
coloraxis_showscale = False ,
@@ -423,7 +497,7 @@ def plotEvents(eventType, filename, team, team_on_left):
423
497
fig .update_xaxes (title_text = "" )
424
498
fig .update_yaxes (title_text = "" )
425
499
image_file = "assets/Pitch.png"
426
- fig .update_yaxes (scaleanchor = "x" , scaleratio = 0.65 )
500
+ fig .update_yaxes (scaleanchor = "x" , scaleratio = 0.70 )
427
501
428
502
from PIL import Image
429
503
0 commit comments