# Load in the Pitch() function from the mlpsoccer module to avoid having to build your own football pitch in a plot
from mplsoccer.pitch import Pitch
# Load in the opta functions from the kloppy module to help with loading event data
from kloppy import opta
# pandas and numpy modules are loaded as pd and np to help with data handling
# Both of these shorthands are conventions in python
import pandas as pd
import numpy as np
# Using the opta.load() function you can now load in the event data without working with troublesome XML files
# Adding additional columns to the to_pandas() function to get player and team names added as columns
dataset = opta.load(
f7_data="C:\\Users\\YOUR_USER\\Desktop\\Github\\DFDA\\Data\\Event Data\\F7.xml",
f24_data="C:\\Users\\YOUR_USER\\Desktop\\Github\\DFDA\\Data\\Event Data\\F24.xml",
coordinates="opta"
).to_pandas(
additional_columns={
'player': lambda event: str(event.player),
'team': lambda event: str(event.team)
}
)
# Subsetting the data to shots by Vejle BK as you have done in earlier notebooks
shots=dataset.loc[(dataset['event_type']=='SHOT') & (dataset['team']=='Vejle BK')]
# To be able to color your plot based on results - you need to convert the result column to a numerical column
# In the code below, this is solved by adding a new colum named "goal"
# Firstly the new column is assigned and given a value of 0
shots=shots.assign(goal=0)
# Then you can use numpy's where() function to change the value of goal to 1 when ever the result of the shot was a goal
# where() works just like a if() statement in other languanges
shots['goal']=np.where(shots.result=='GOAL',1,0)
# Using head(10) you can inspect if you have done it right - the last of the 10 rows should have a column with goal set to 1
shots.head(10)
# To plot a football pitch you can simply use the Pitch() function from mlpsoccer
pitch = Pitch()
# To actually show the plot you need to call the function pitch.draw()
# And as a convention in python plots are called fig and ax
fig, ax = pitch.draw()
# Going from a white pitch to something resembling a real pitch is as simple as changing a few settings in the pitch() function
# Like with head(x) Pitch() has multiple settings which can be used to alter the look of the pitch
# See this link to for further options:
# https://mplsoccer.readthedocs.io/en/latest/gallery/pitch_setup/plot_pitches.html
pitch = Pitch(pitch_color='grass', # Setting the color to be grass
line_color='white', # Setting the lines on the pitch to be white
goal_type='box', # Add "nets" to goals
stripe=True, # Add stripes to the grass color
corner_arcs=True, # Add corner arcs
pitch_type='opta' # Set coordinates to be opta based for your event data to work
)
fig, ax = pitch.draw(figsize=(16, 11) # Increase the size of the plot
)
# To plot some events on the pitch you first call the code from above to create a pitch
pitch = Pitch(pitch_color='grass', line_color='white', stripe=True, pitch_type='opta',corner_arcs=True,goal_type='box')
fig, ax = pitch.draw(figsize=(16, 11))
# Then add something to it
# Below is the code to add the shots you subsetted before as points - using scatter
scatter = pitch.scatter(shots.coordinates_x, # x coordinate
shots.coordinates_y, # y coordinate
ax=ax, # What plot to add them to
edgecolor='black', # Edge color of the point
c=shots.goal, # Color of the point
s=250 # Size of the points
)
# Moving to something other than shots - you can extract all the crosses from Vejle in open play in this way
cross=dataset.loc[(dataset['pass_type']=='CROSS') &
(dataset['team']=='Vejle BK') &
(dataset['set_piece_type'].isna()) # if set_piece_type is empty then the cross is in open play
]
# Subsetting in completed and incompleted passes
incomplete = cross[cross['result']=='INCOMPLETE'] # == means is equal to
complete = cross[cross['result']!='INCOMPLETE'] # != means NOT equal to
# Extract Vejle BK by picking the first value of unique teams in the cross dataset
# Given that python starts counting at 0 the first value are called by writing [0] rather than [1]
team = cross.team.unique()[0]
# To plot some events on the pitch you first call the code from above to create a pitch
pitch = Pitch(pitch_color='grass', line_color='white', stripe=True, pitch_type='opta',corner_arcs=True,goal_type='box')
fig, ax = pitch.draw(figsize=(15, 10))
# After creating the pitch you can add the completed crosses by using pitch.lines()
lc1 = pitch.lines(complete.coordinates_x, # x coordinate start
complete.coordinates_y, # y coordinate start
complete.end_coordinates_x, # x coordinate end
complete.end_coordinates_y, # y coordinate end
ax=ax, # What plot to add them to
lw=10, # Linewidth
comet=True, # Setting line style to be comet - wider at end than at start
color='blue', # Color of the line
capstyle="round", # Rounding off end rather than a flat end
label='Completed', # What to call the lines in the legend
zorder=1 # What order to add the lines to the plot
)
# Similar to the completed crosses you can then add incomplete and assign them a red color
lc2 = pitch.lines(incomplete.coordinates_x,
incomplete.coordinates_y,
incomplete.end_coordinates_x,
incomplete.end_coordinates_y,
ax=ax, lw=10,
comet=True, capstyle="round",color='red', label='Incomplete', zorder=1)
# You can then add a few more details to make the plot a bit nicer to look at
# Start by repeating the process from above
# To plot some events on the pitch you first call the code from above to create a pitch
pitch = Pitch(pitch_color='grass', line_color='white', stripe=True, pitch_type='opta',corner_arcs=True,goal_type='box')
fig, ax = pitch.draw(figsize=(15, 10))
# After creating the pitch you can add the completed crosses by using pitch.lines()
lc1 = pitch.lines(complete.coordinates_x, # x coordinate start
complete.coordinates_y, # y coordinate start
complete.end_coordinates_x, # x coordinate end
complete.end_coordinates_y, # y coordinate end
ax=ax, # What plot to add them to
lw=10, # Linewidth
comet=True, # Setting line style to be comet - wider at end than at start
color='blue', # Color of the line
capstyle="round", # Rounding off end rather than a flat end
label='Completed', # What to call the lines in the legend
zorder=1 # What order to add the lines to the plot
)
# Similar to the completed crosses you can then add incomplete and assign them a red color
lc2 = pitch.lines(incomplete.coordinates_x,
incomplete.coordinates_y,
incomplete.end_coordinates_x,
incomplete.end_coordinates_y,
ax=ax, lw=10,
comet=True, capstyle="round",color='red', label='Incomplete', zorder=1)
# Adding a title by using the team name extracted above
# Writing "f'SOMETHING'" allows you to combine variables and text
# You simply need to enclose a variable in {} to use it
ax_title = ax.set_title(f'{team} crosses', fontsize=30)
# Adding some points without color fill might help pick out each cross a bit better
# First you can add colorless points to the end coordinate
scatter = pitch.scatter(cross.end_coordinates_x, # x coordinate
cross.end_coordinates_y, # y coordinate
ax=ax, # Which plot to add them to
edgecolor='black', # Edge color of the point
c="None", # Fill color - set to none to get an empty point
s=100, # Size of the point
zorder=2 # Plotting at a higher level than the lines to ensure the point are on top
)
# Likewise you can add smaller points to the start coordinates
scatter = pitch.scatter(cross.coordinates_x, cross.coordinates_y, ax=ax, edgecolor='black',c="None", s=50, zorder=2)
# Lastly you can add a legend to tell the enduser what the different color of lines means
ax.legend(fontsize=17, # Size of text in the legend
bbox_to_anchor=(0.2, 0.925) # Placement of the legend - here placed at the top right by setting
#(x value low and y value close to one)
)
# You can then simply right click the plot and copy it into your presentation or report
# Or you can begin subsetting crosses from the left or right side - seen from the offensive point of view
# Do this by only including crosses with a y coordinate < 50 (right side) or y coordinate > 50 (left side)