Modules


Calculate Packing


class packing.packing(sender_xy: numpy.array, receiver_xy: numpy.array, defending_team_xy: pandas.core.frame.DataFrame, col_label_x: str, col_label_y: str, defend_side: str)[source]

Find the packing for a given pass

Parameters
sender_xyndarray

Sender XY coordinates as numpy array

receiver_xyndarray

Receiver XY coordinates as numpy array

defending_team_xyDataFrame

DataFrame with the defending team coordinates Do not include any passing team XY or other columns as it’ll have an impact on plotting function.

col_label_xString

The column label for defending team’s X coordinate in defending_team_xy

col_label_yString

The column label for defending team’s Y coordinate in defending_team_xy

defend_sideString

The side of the defending team on the football pitch. Left/Right, not case sensitive

goal_centerDict

Center of goal selected based on defend_side {‘left’: [0, 0.5], ‘right’: [1, 0.5]}

Returns
packing_dfDataFrame

Returns a dataframe with the following new columns along with existing columns that was provided. New Columns : [triangle_area, rect_length, rect_width, area_diff, method_1, method2_dist, method_2, method3_angle_s, method3_angle_r, method_3, method_1_update, packing_rate, col_label_x, col_label_y]

packing_rateFloat

Packing rate for that given pass scenario Packing rate will be multiplied by a factor based on the pass type: 1.0 : Forward Pass -1.0 : Back Pass 0.5 : Side pass

pass_pressureInteger

Defending players who are closer to sender/receiver but not involved in packing. Indicator to see if players take high risk pass. For eg: packing rate could be lower but pass pressure can be higher if pass sender/receiver are heavily marked.

How packing is calculated?


A brief explanation on how packing rate is calculated. There are 3 methods which look at different aspects of the pass and defending player’s relationship to the pass.

Method 1 looks at the space between the sender and receiver and checks if any defending players is within that space. A bounding box (green box here) is created between the sender and receiver and the defending players inside the box (and certain distance threshold outside the box) are marked as 1.

Method 2 looks at the distance between the defender and line of pass. If the defender is within a certain distance (method2_radius), they are marked as 1. This helps to consider defenders who are only within the range of the pass.

Method 3 looks at the angle between the sender & receiver to the defender. In order to consider the lines of the defender, instead of just making a vertical line, based on the direction of pass angles are calculated. This is seen with an example here.

Method 1 gets a final update based on a specific condition as mentioned in this section.


Method 1


packing.calculate_packing.method_1(self, box_a, box_b, box_c, box_d, df_method1, col_label_x, col_label_y, rect_thresh=0.01)

Method 1 : Draw a rectangle box between sender and receiver to see if any player is inside the bounding box. A rect_thresh of 0.01 is used to consider players on the edge of the box.

Parameters
box_andarray

A ndarray of [‘sender_x’, ‘sender_y’]

box_bndarray

A ndarray of [‘sender_x’, ‘receiver_y’]

box_cndarray

A ndarray of [‘receiver_x’, ‘receiver_y’]

box_dndarray

A ndarray of [‘receiver_x’, ‘sender_y’]

df_method1DataFrame

A copy of defending_team_xy dataframe

col_label_xString

The column label for defending team’s X coordinate in defending_team_xy

col_label_yString

The column label for defending team’s Y coordinate in defending_team_xy

rect_threshFloat, default 0.015

A threshold to check if any player is outside/on the edge of the box within the threshold distance

Returns
df_method1DataFrame

A copy of original DataFrame with 1/0 for Method 1 and the following new columns : triangle_area : Float, rect_length : Float, rect_width : Float, method_1 : Binary

Method 2


packing.calculate_packing.method_2(self, sender_xy, receiver_xy, df_method2, col_label_x, col_label_y, method2_radius=0.12)

Method 2 : Check if player is within a certain distance to line of pass, so that the pass can potentially be intersected (assuming the speed of pass is not a factor).

For a given defender, assume the defender xy to be center of circle. Find the perpendicular distance from player xy to the line of pass. If the distance is <= method2_radius, then method_2 returns as 1, else 0.

Parameters
sender_xyndarray

A ndarray of [‘sender_x’, ‘sender_y’]

receiver_xyndarray

A ndarray of [‘receiver_x’, ‘receiver_y’]

df_method2DataFrame

A copy of defending_team_xy dataframe, updated from Method 1

radiusFloat, default 0.150

search radius for find if player can potentially intersect the pass by being within a given distance

Returns
df_method2DataFrame

A copy of original DataFrame with 1/0 for Method 2 and the following new columns : method2_dist : Distance of player to line of pass, method_2 : Binary, (1/0)

Method 3


packing.calculate_packing.method_3(self, sender_xy, receiver_xy, df_method3, col_label_x, col_label_y)

Method 3 : Check defender angle with respect to sender & receiver. One of the draw back of method_2 is that defender can be close to line to pass but still be beyond the sender or receiver (one of angle b/w defender & sender/receiver > 90). This method checks this condition.

Parameters
sender_xyndarray

A ndarray of [‘sender_x’, ‘sender_y’]

receiver_xyndarray

A ndarray of [‘receiver_x’, ‘receiver_y’]

df_method3DataFrame

A copy of defending_team_xy dataframe, updated from Method 2

Returns
df_method3DataFrame

A copy of original DataFrame with 1/0 for Method 3 and the following new columns : method3_angle_s : Angle between defender & sender, method3_angle_r : Angle between defender & receiver, method_3 : Binary, (1/0)

Method 1 - Update


packing.calculate_packing.update_method_1(self, df_update)

Method 1 Update : For special cases where bounding box from Method 1 is almost a line i.e: either width/length <= 0.07 units (both sender and receiver are in similar X or Y coordinate). In this case, update the value of method_1 value to 1 if both method_2 and method_3 are 1.

Parameters
df_updateDataFrame

The copy of DataFrame after Methods 1,2 & 3.

Returns
df_updateDataFrame

Final Dataframe with updated 1/0 for Method 1

Pass Pressure


packing.calculate_packing.get_pass_pressure(self, sender_xy, receiver_xy, defending_team_xy, col_label_x, col_label_y)

For defender who are not in the packing rate, if they are close (<=0.05 units) to the sender/receiver, they’re considered to have an influence on the pass by increasing the pressure of the pass.

Parameters
sender_xyndarray

Sender XY coordinates as numpy array

receiver_xyndarray

Receiver XY coordinates as numpy array

defending_team_xyDataFrame

DataFrame with the defending team coordinates

col_label_xString

The column label for defending team’s X coordinate in defending_team_xy

col_label_yString

The column label for defending team’s Y coordinate in defending_team_xy

Returns
total_pressureInt

Total count of defenders applying pressure on the sender & receiver, but not involved in packing rate.

Visualize Packing


class plot_packing.plot_packing(passer_team_df, packing_df, col_label_x, col_label_y, packing_rate, pass_pressure, sender_xy, receiver_xy, x_range, y_range, path_to_save, pass_frame=None, bcg_img=None, file_name='packing')[source]

Plot the player location on the pitch and highlight the defending team players that might have been calculated in packing.

Parameters
passer_team_dfDataFrame

DataFrame with the passing team coordinates Column name with id or _id are considered player ids (Only 1 column with such name).

packing_dfDataFrame

Resulting DataFrame from packing module (should not be altered). Column name with id or _id are considered player ids (Only 1 column with such name).

col_label_xString

The column label for defending team’s X coordinate in defending_team_xy

col_label_yString

The column label for defending team’s Y coordinate in defending_team_xy

packing_rateFloat

Resulting output from packing module (should not be altered)

pass_pressureInt

Defending players who are closer to sender/receiver but not involved in packing

sender_xyndarray

Sender XY coordinates as numpy array

receiver_xyndarray

Receiver XY coordinates as numpy array

x_range[start, end] list

List of range of x-axis of the pitch, Eg: [0, 100] or [-5250, 5250]

y_range[start, end] list

List of range of y-axis of the pitch, Eg: [0, 100]`or `[3400, -3400]

path_to_save :

A path to save the output html file. Path should end with a /

pass_frameString, Optional, default None

Identifier to display pass event time on plot

bcg_imgString, default None

Path to background image

file_nameString, default packing

Filename to save the plot

Returns
Defending players who have been calcuated in packing will be marked in a green border.
show() :

Plot is shown on the browser. If module is run on jupyter notebook, plot will be shown in the notebook.

save() :

Plot saved locally to path specified under path_to_save. Note: If file_name is not changed every time module is run, plots will be overwritten.