Notebook first written: 20/01/2022
Notebook last updated: 01/02/2022
Click here to jump straight into the Data Engineering section and skip the Notebook Brief and Data Sources sections.
This notebook parses and engineers Second Spectrum Tracking data for two matches, that have been provided by Watford F.C, using pandas for data manipulation through DataFrames.
For more information about this notebook and the author, I am available through all the following channels:
A static version of this notebook can be found here. This notebook has an accompanying watford
GitHub repository and for my full repository of football analysis, see my football_analysis
GitHub repository.
This notebook was written using Python 3 and requires the following libraries:
Jupyter notebooks
for this notebook environment with which this project is presented;NumPy
for multidimensional array computing; andpandas
for data analysis and manipulation.All packages used for this notebook can be obtained by downloading and installing the Conda distribution, available on all platforms (Windows, Linux and Mac OSX). Step-by-step guides on how to install Anaconda can be found for Windows here and Mac here, as well as in the Anaconda documentation itself here.
# Python ≥3.5 (ideally)
import platform
import sys, getopt
assert sys.version_info >= (3, 5)
import csv
# Import Dependencies
%matplotlib inline
# Math Operations
import numpy as np
import math
from math import pi
# Datetime
import datetime
from datetime import date
import time
# Data Preprocessing
import pandas as pd
import pandas_profiling as pp
import os
import re
import chardet
import random
from io import BytesIO
from pathlib import Path
# Kloppy
from kloppy import secondspectrum
# Reading Directories
import glob
import os
# Working with JSON
import json
from pandas import json_normalize
# Data Visualisation
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.colors import LinearSegmentedColormap
from matplotlib import patches
import seaborn as sns
import missingno as msno
import moviepy.editor as mpy
from moviepy.video.io.bindings import mplfig_to_npimage
# Requests and downloads
import tqdm
import requests
# Machine Learning
import scipy.signal as signal
from scipy.spatial import Voronoi, voronoi_plot_2d, Delaunay
# Display in Jupyter
from IPython.display import Image, YouTubeVideo
from IPython.core.display import HTML
# Ignore Warnings
import warnings
warnings.filterwarnings(action="ignore", message="^internal gelsd")
# Print message
print('Setup Complete')
Setup Complete
# Python / module versions used here for reference
print('Python: {}'.format(platform.python_version()))
print('NumPy: {}'.format(np.__version__))
print('pandas: {}'.format(pd.__version__))
print('matplotlib: {}'.format(mpl.__version__))
Python: 3.7.6 NumPy: 1.19.1 pandas: 1.1.3 matplotlib: 3.3.1
# Set up initial paths to subfolders
base_dir = os.path.join('..', '..')
data_dir = os.path.join(base_dir, 'data')
data_dir_second_spectrum = os.path.join(base_dir, 'data', 'second_spectrum')
data_dir_opta = os.path.join(base_dir, 'data', 'opta')
scripts_dir = os.path.join(base_dir, 'scripts')
scripts_dir_second_spectrum = os.path.join(base_dir, 'scripts', 'second_spectrum')
scripts_dir_metrica_sports = os.path.join(base_dir, 'scripts', 'metrica_sports')
img_dir = os.path.join(base_dir, 'img')
fig_dir = os.path.join(base_dir, 'img', 'fig')
fig_dir_second_spectrum = os.path.join(base_dir, 'img', 'fig', 'second_spectrum')
video_dir = os.path.join(base_dir, 'video')
video_dir_second_spectrum = os.path.join(base_dir, 'video', 'fig', 'second_spectrum')
Custom libraries for working with the Second Spectrum data, that were initially written by Laurie Shaw, to work with the Metrica Sports data. See the following for his original code [link].
The modifications to this data include the ability to create Pitch Control models without Tracking data.
# Custom libraries for working with Tracking data
## Define path of scripts
sys.path.insert(0, os.path.abspath(scripts_dir_second_spectrum))
## Second Spectrum scripts - custom scripts derived from Laurie Shaw's Metrica scripts
import Second_Spectrum_IO as sio
import Second_Spectrum_Viz as sviz
import Second_Spectrum_Velocities as svel
import Second_Spectrum_PitchControl as spc
import Second_Spectrum_EPV as sepv
"""
## Laurie Shaw's custom libraries for working with Metrica Sports data
import Metrica_IO as mio
import Metrica_Viz as mviz
import Metrica_Velocities as mvel
import Metrica_PitchControl as mpc
import Metrica_EPV as mepv
"""
"\n## Laurie Shaw's custom libraries for working with Metrica Sports data\nimport Metrica_IO as mio\nimport Metrica_Viz as mviz\nimport Metrica_Velocities as mvel\nimport Metrica_PitchControl as mpc\nimport Metrica_EPV as mepv\n"
# Define function to read in Tracking data using Kloppy
def read_in_tracking_data(filename):
"""
Function to read in Second Spectrum Tracking data as a pandas DataFrame using Kloppy. Written by Edd Webster / @eddwebster
"""
# Read in exported CSV file if exists, if not, read in the raw JSON and XML files using Kloppy
## If not already saved as a CSV file
if not os.path.exists(os.path.join(data_dir_second_spectrum, 'raw', 'data', f'g{filename}_SecondSpectrum_Data.csv')):
### Load meta data (XML) and raw data (JSONL) files
dataset = secondspectrum.load(
meta_data = f'../../data/second_spectrum/raw/metadata/g{filename}_SecondSpectrum_Metadata.xml',
raw_data = f'../../data/second_spectrum/raw/data/g{filename}_SecondSpectrum_Data.jsonl',
#meta_data = os.path.join(data_dir_second_spectrum, 'raw', 'data', f'{filename}_SecondSpectrum_Metadata.xml')
#raw_data = os.path.join(data_dir_second_spectrum, 'raw', 'data', f'{filename}_SecondSpectrum_Data.jsonl')
### Optional arguments
additional_meta_data = f'../../data/second_spectrum/raw/metadata/g{filename}_SecondSpectrum_Metadata.json',
#additional_meta_data = os.path.join(data_dir_second_spectrum, 'raw', 'data', f'{filename}_SecondSpectrum_Metadata.json')
#sample_rate=1/25,
#limit=100,
coordinates='secondspectrum',
#only_alive=True
)
### Convert DataFrame to pandas
df_tracking = dataset.to_pandas()
### Export raw DataFrame
df_tracking.to_csv(os.path.join(data_dir_second_spectrum, 'raw', 'data', f'g{filename}_SecondSpectrum_Data.csv'), index=None, header=True)
## If already, read in CSV file
else:
df_tracking = pd.read_csv(os.path.join(data_dir_second_spectrum, 'raw', 'data', f'g{filename}_SecondSpectrum_Data.csv'))
## Return DataFrame
return df_tracking
# Define function to create separate DataFrames of the Tracking data for Home and Away
def create_home_and_away_dfs(df_tracking):
"""
Function to separate the Tracking data into Home and Away DataFrames. Written by Edd Webster / @eddwebster
"""
# Define columns that are found in both DataFrames
lst_cols = ['Frame' ,'Period', 'Time [s]', 'Ball State', 'Ball Owning Team ID', 'ball_x', 'ball_y']
# Define columns in Home DataFrame
## Create an empty list for home columns
lst_cols_home = []
## Extract Home columns from the DataFrame
for col in df_tracking.columns:
if 'Home' in col:
lst_cols_home.append(col)
# Define columns in Away DataFrame
## Create an empty list for home columns
lst_cols_away = []
## Extract Home columns from the DataFrame
for col in df_tracking.columns:
if 'Away' in col:
lst_cols_away.append(col)
# Select columns of interest for Home and Away DataFrames
df_tracking_home = df_tracking[lst_cols + lst_cols_home]
df_tracking_away = df_tracking[lst_cols + lst_cols_away]
# Return DataFrame
return df_tracking_home, df_tracking_away
# Define function to reverse the direction of players
def to_single_playing_direction(home, away):
'''
Flip coordinates in second half so that each team always shoots in the same direction through the match.
'''
for team in [home, away]:
second_half_idx = team.Period.idxmax(2)
columns = [c for c in team.columns if c[-1].lower() in ['x','y']]
team.loc[second_half_idx:,columns] *= -1
return home, away
# Display all columns of displayed pandas DataFrames
pd.set_option('display.max_columns', None)
#pd.set_option('display.max_rows', None)
pd.options.mode.chained_assignment = None
This notebook parses and engineers Second Spectrum using pandas and Kloppy
, for two Premier League matches featuring Crystal Palace during the 21/22 season. The two datasets provided are for the following matches:
Once engineered, this dataset will be visualised and analysed in congunction with the corresponding Event data, as part of an opposition analysis piece.
Notebook Conventions:
DataFrame
object are prefixed with df_
.DataFrame
objects (e.g., a list, a set or a dict) are prefixed with dfs_
.Second Spectrum are a football analytics, data provider ...
The tracking data represents the location of every player on the pitch with a temporal frequency of 25 Hz and the corresponding match time for each tracking frame is specified.
UPDATE THIS
The Second Spectrum Tracking dataset has fourteen features (columns) with the following definitions and data types:
Feature | Data type | Definition | |
---|---|---|---|
Frame |
object | ||
Period |
object | ||
Time [s] |
object | ||
Home_11_x |
object | ||
Home_11_y |
object | ||
Away_8_x |
object | ||
Away_8_y |
object |
UPDATE THIS
The following cells take the raw meta data (XML
) and tracking data (JSONL
) files and read them in as a unified pandas DataFrame, using the custom read_in_tracking_data
function that I have written (see the functions section at the top of the notebook), powered by Kloppy
.
# Show files in directory
print(glob.glob(os.path.join(data_dir_second_spectrum, 'raw', 'data/*.csv')))
['../../data/second_spectrum/raw/data/g2210334_SecondSpectrum_Data.csv', '../../data/second_spectrum/raw/data/g2210324_SecondSpectrum_Data.csv']
# Read in Tracking data using the custom 'read_in_tracking_data' function, powered by Kloppy
df_tracking_cry_bri = read_in_tracking_data(filename='2210324')
df_tracking_cry_lei = read_in_tracking_data(filename='2210334')
To avoid duplication commands and to get an idea of what the output of the Tracking data looks like, this section just goes through tone the two Tracking data files, Crystal Palace vs. Leicester City (g2210334).
First check the quality of the dataset by looking first and last rows in pandas using the head()
and tail()
methods.
# Display the first five rows of the DataFrame, df_tracking_cry_lei
df_tracking_cry_lei.head()
period_id | timestamp | ball_state | ball_owning_team_id | ball_x | ball_y | 66975_x | 66975_y | 66975_d | 66975_s | 55494_x | 55494_y | 55494_d | 55494_s | 80146_x | 80146_y | 80146_d | 80146_s | 40836_x | 40836_y | 40836_d | 40836_s | 50471_x | 50471_y | 50471_d | 50471_s | 199670_x | 199670_y | 199670_d | 199670_s | 244723_x | 244723_y | 244723_d | 244723_s | 82403_x | 82403_y | 82403_d | 82403_s | 209036_x | 209036_y | 209036_d | 209036_s | 232787_x | 232787_y | 232787_d | 232787_s | 174874_x | 174874_y | 174874_d | 174874_s | 101668_x | 101668_y | 101668_d | 101668_s | 17745_x | 17745_y | 17745_d | 17745_s | 197469_x | 197469_y | 197469_d | 197469_s | 218031_x | 218031_y | 218031_d | 218031_s | 40146_x | 40146_y | 40146_d | 40146_s | 173515_x | 173515_y | 173515_d | 173515_s | 201666_x | 201666_y | 201666_d | 201666_s | 93100_x | 93100_y | 93100_d | 93100_s | 166989_x | 166989_y | 166989_d | 166989_s | 219352_x | 219352_y | 219352_d | 219352_s | 166477_x | 166477_y | 166477_d | 166477_s | 443661_x | 443661_y | 443661_d | 443661_s | 225902_x | 225902_y | 225902_d | 225902_s | 172780_x | 172780_y | 172780_d | 172780_s | 86417_x | 86417_y | 86417_d | 86417_s | 51938_x | 51938_y | 51938_d | 51938_s | 54861_x | 54861_y | 54861_d | 54861_s | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 0.00 | dead | 31 | 0.04 | 0.09 | 0.60 | -9.08 | NaN | NaN | 11.43 | 10.22 | NaN | NaN | 0.21 | 9.47 | NaN | NaN | 42.10 | -0.19 | NaN | NaN | -0.36 | 0.35 | NaN | NaN | -0.57 | -20.55 | NaN | NaN | 7.63 | -25.09 | NaN | NaN | 1.22 | -30.53 | NaN | NaN | 14.71 | -12.51 | NaN | NaN | 0.51 | 2.82 | NaN | NaN | 19.24 | 0.54 | NaN | NaN | -0.93 | 9.31 | NaN | NaN | -50.43 | 0.12 | NaN | NaN | -10.17 | -19.71 | NaN | NaN | -16.67 | -16.05 | NaN | NaN | -13.94 | 9.96 | NaN | NaN | 0.49 | -10.92 | NaN | NaN | -3.18 | 10.89 | NaN | NaN | -15.10 | -0.99 | NaN | NaN | -8.98 | 0.43 | NaN | NaN | -0.99 | -20.88 | NaN | NaN | -14.75 | -27.43 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | 1 | 0.04 | alive | 31 | 0.83 | 0.09 | 0.56 | -9.07 | NaN | NaN | 11.41 | 10.22 | NaN | NaN | 0.22 | 9.47 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.35 | 0.35 | NaN | NaN | -0.64 | -20.47 | NaN | NaN | 7.62 | -25.09 | NaN | NaN | 1.24 | -30.53 | NaN | NaN | 14.70 | -12.52 | NaN | NaN | 0.50 | 2.81 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | -0.89 | 9.25 | NaN | NaN | -50.42 | 0.12 | NaN | NaN | -10.13 | -19.70 | NaN | NaN | -16.68 | -16.06 | NaN | NaN | -13.94 | 9.96 | NaN | NaN | 0.59 | -10.86 | NaN | NaN | -3.19 | 10.91 | NaN | NaN | -15.13 | -0.99 | NaN | NaN | -8.95 | 0.44 | NaN | NaN | -0.99 | -20.83 | NaN | NaN | -14.75 | -27.42 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2 | 1 | 0.08 | alive | 31 | 1.34 | 0.09 | 0.53 | -9.07 | NaN | NaN | 11.41 | 10.21 | NaN | NaN | 0.22 | 9.46 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.34 | 0.34 | NaN | NaN | -0.71 | -20.40 | NaN | NaN | 7.62 | -25.08 | NaN | NaN | 1.25 | -30.53 | NaN | NaN | 14.70 | -12.53 | NaN | NaN | 0.49 | 2.80 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | -0.84 | 9.19 | NaN | NaN | -50.43 | 0.13 | NaN | NaN | -10.09 | -19.69 | NaN | NaN | -16.69 | -16.07 | NaN | NaN | -13.93 | 9.99 | NaN | NaN | 0.70 | -10.80 | NaN | NaN | -3.20 | 10.94 | NaN | NaN | -15.16 | -0.99 | NaN | NaN | -8.92 | 0.44 | NaN | NaN | -1.00 | -20.78 | NaN | NaN | -14.76 | -27.42 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
3 | 1 | 0.12 | alive | 31 | 2.03 | 0.10 | 0.50 | -9.06 | NaN | NaN | 11.39 | 10.21 | NaN | NaN | 0.23 | 9.45 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.35 | 0.34 | NaN | NaN | -0.79 | -20.31 | NaN | NaN | 7.60 | -25.08 | NaN | NaN | 1.26 | -30.53 | NaN | NaN | 14.69 | -12.54 | NaN | NaN | 0.47 | 2.78 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | -0.77 | 9.12 | NaN | NaN | -50.42 | 0.13 | NaN | NaN | -10.05 | -19.67 | NaN | NaN | -16.69 | -16.09 | NaN | NaN | -13.92 | 10.02 | NaN | NaN | 0.80 | -10.75 | NaN | NaN | -3.22 | 10.96 | NaN | NaN | -15.16 | -1.00 | NaN | NaN | -8.88 | 0.44 | NaN | NaN | -1.02 | -20.73 | NaN | NaN | -14.76 | -27.41 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
4 | 1 | 0.16 | alive | 31 | 2.85 | 0.11 | 0.46 | -9.06 | NaN | NaN | 11.40 | 10.19 | NaN | NaN | 0.25 | 9.43 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.36 | 0.33 | NaN | NaN | -0.88 | -20.23 | NaN | NaN | 7.59 | -25.07 | NaN | NaN | 1.27 | -30.53 | NaN | NaN | 14.68 | -12.56 | NaN | NaN | 0.46 | 2.77 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | -0.70 | 9.04 | NaN | NaN | -50.42 | 0.13 | NaN | NaN | -10.02 | -19.65 | NaN | NaN | -16.70 | -16.09 | NaN | NaN | -13.93 | 10.04 | NaN | NaN | 0.91 | -10.69 | NaN | NaN | -3.22 | 11.00 | NaN | NaN | -15.20 | -1.01 | NaN | NaN | -8.91 | 0.45 | NaN | NaN | -1.04 | -20.69 | NaN | NaN | -14.78 | -27.40 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
# Display the last five rows of the DataFrame, df_tracking_cry_lei
df_tracking_cry_lei.tail()
period_id | timestamp | ball_state | ball_owning_team_id | ball_x | ball_y | 66975_x | 66975_y | 66975_d | 66975_s | 55494_x | 55494_y | 55494_d | 55494_s | 80146_x | 80146_y | 80146_d | 80146_s | 40836_x | 40836_y | 40836_d | 40836_s | 50471_x | 50471_y | 50471_d | 50471_s | 199670_x | 199670_y | 199670_d | 199670_s | 244723_x | 244723_y | 244723_d | 244723_s | 82403_x | 82403_y | 82403_d | 82403_s | 209036_x | 209036_y | 209036_d | 209036_s | 232787_x | 232787_y | 232787_d | 232787_s | 174874_x | 174874_y | 174874_d | 174874_s | 101668_x | 101668_y | 101668_d | 101668_s | 17745_x | 17745_y | 17745_d | 17745_s | 197469_x | 197469_y | 197469_d | 197469_s | 218031_x | 218031_y | 218031_d | 218031_s | 40146_x | 40146_y | 40146_d | 40146_s | 173515_x | 173515_y | 173515_d | 173515_s | 201666_x | 201666_y | 201666_d | 201666_s | 93100_x | 93100_y | 93100_d | 93100_s | 166989_x | 166989_y | 166989_d | 166989_s | 219352_x | 219352_y | 219352_d | 219352_s | 166477_x | 166477_y | 166477_d | 166477_s | 443661_x | 443661_y | 443661_d | 443661_s | 225902_x | 225902_y | 225902_d | 225902_s | 172780_x | 172780_y | 172780_d | 172780_s | 86417_x | 86417_y | 86417_d | 86417_s | 51938_x | 51938_y | 51938_d | 51938_s | 54861_x | 54861_y | 54861_d | 54861_s | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
145616 | 2 | 3003.92 | dead | 31 | 0.0 | 0.0 | -3.75 | -18.83 | NaN | NaN | -3.06 | -30.92 | NaN | NaN | NaN | NaN | NaN | NaN | -37.09 | -5.20 | NaN | NaN | -3.05 | -3.99 | NaN | NaN | NaN | NaN | NaN | NaN | -1.01 | 8.49 | NaN | NaN | 6.16 | 13.24 | NaN | NaN | -13.54 | -3.99 | NaN | NaN | NaN | NaN | NaN | NaN | -19.02 | -27.61 | NaN | NaN | -2.28 | -26.67 | NaN | NaN | 41.40 | -1.46 | NaN | NaN | NaN | NaN | NaN | NaN | 19.16 | -4.38 | NaN | NaN | 17.66 | -23.71 | NaN | NaN | NaN | NaN | NaN | NaN | -1.41 | -22.07 | NaN | NaN | 20.40 | -14.86 | NaN | NaN | 7.23 | -20.32 | NaN | NaN | NaN | NaN | NaN | NaN | 16.14 | 6.60 | NaN | NaN | 3.14 | -31.79 | NaN | NaN | 9.04 | -6.38 | NaN | NaN | -2.69 | -16.54 | NaN | NaN | 14.74 | -21.58 | NaN | NaN | 6.37 | 4.74 | NaN | NaN | 20.38 | -2.36 | NaN | NaN |
145617 | 2 | 3003.96 | dead | 31 | 0.0 | 0.0 | -3.76 | -18.77 | NaN | NaN | -3.07 | -30.87 | NaN | NaN | NaN | NaN | NaN | NaN | -37.07 | -5.24 | NaN | NaN | -3.05 | -3.97 | NaN | NaN | NaN | NaN | NaN | NaN | -0.98 | 8.54 | NaN | NaN | 6.20 | 13.26 | NaN | NaN | -13.55 | -3.97 | NaN | NaN | NaN | NaN | NaN | NaN | -19.02 | -27.58 | NaN | NaN | -2.31 | -26.62 | NaN | NaN | 41.38 | -1.46 | NaN | NaN | NaN | NaN | NaN | NaN | 19.14 | -4.34 | NaN | NaN | 17.68 | -23.67 | NaN | NaN | NaN | NaN | NaN | NaN | -1.36 | -22.05 | NaN | NaN | 20.38 | -14.81 | NaN | NaN | 7.22 | -20.26 | NaN | NaN | NaN | NaN | NaN | NaN | 16.17 | 6.64 | NaN | NaN | 3.25 | -31.75 | NaN | NaN | 9.04 | -6.34 | NaN | NaN | -2.69 | -16.50 | NaN | NaN | 14.74 | -21.55 | NaN | NaN | 6.40 | 4.78 | NaN | NaN | 20.36 | -2.30 | NaN | NaN |
145618 | 2 | 3004.00 | dead | 31 | 0.0 | 0.0 | -3.78 | -18.73 | NaN | NaN | -3.09 | -30.82 | NaN | NaN | NaN | NaN | NaN | NaN | -37.04 | -5.27 | NaN | NaN | -3.04 | -3.95 | NaN | NaN | NaN | NaN | NaN | NaN | -0.95 | 8.58 | NaN | NaN | 6.23 | 13.29 | NaN | NaN | -13.56 | -3.95 | NaN | NaN | NaN | NaN | NaN | NaN | -19.00 | -27.55 | NaN | NaN | -2.34 | -26.58 | NaN | NaN | 41.37 | -1.45 | NaN | NaN | NaN | NaN | NaN | NaN | 19.12 | -4.30 | NaN | NaN | 17.70 | -23.64 | NaN | NaN | NaN | NaN | NaN | NaN | -1.32 | -22.02 | NaN | NaN | 20.36 | -14.77 | NaN | NaN | 7.22 | -20.21 | NaN | NaN | NaN | NaN | NaN | NaN | 16.19 | 6.68 | NaN | NaN | 3.35 | -31.71 | NaN | NaN | 9.04 | -6.29 | NaN | NaN | -2.69 | -16.45 | NaN | NaN | 14.75 | -21.53 | NaN | NaN | 6.42 | 4.82 | NaN | NaN | 20.34 | -2.24 | NaN | NaN |
145619 | 2 | 3004.04 | dead | 31 | 0.0 | 0.0 | -3.80 | -18.67 | NaN | NaN | -3.10 | -30.77 | NaN | NaN | NaN | NaN | NaN | NaN | -37.01 | -5.31 | NaN | NaN | -3.04 | -3.92 | NaN | NaN | NaN | NaN | NaN | NaN | -0.92 | 8.63 | NaN | NaN | 6.26 | 13.32 | NaN | NaN | -13.57 | -3.92 | NaN | NaN | NaN | NaN | NaN | NaN | -18.99 | -27.51 | NaN | NaN | -2.36 | -26.54 | NaN | NaN | 41.35 | -1.44 | NaN | NaN | NaN | NaN | NaN | NaN | 19.09 | -4.25 | NaN | NaN | 17.72 | -23.60 | NaN | NaN | NaN | NaN | NaN | NaN | -1.27 | -21.99 | NaN | NaN | 20.33 | -14.72 | NaN | NaN | 7.21 | -20.15 | NaN | NaN | NaN | NaN | NaN | NaN | 16.21 | 6.72 | NaN | NaN | 3.45 | -31.67 | NaN | NaN | 9.03 | -6.25 | NaN | NaN | -2.68 | -16.41 | NaN | NaN | 14.75 | -21.50 | NaN | NaN | 6.45 | 4.86 | NaN | NaN | 20.31 | -2.18 | NaN | NaN |
145620 | 2 | 3004.08 | dead | 31 | 0.0 | 0.0 | -3.81 | -18.62 | NaN | NaN | -3.12 | -30.72 | NaN | NaN | NaN | NaN | NaN | NaN | -36.98 | -5.34 | NaN | NaN | -3.04 | -3.90 | NaN | NaN | NaN | NaN | NaN | NaN | -0.90 | 8.68 | NaN | NaN | 6.29 | 13.34 | NaN | NaN | -13.58 | -3.90 | NaN | NaN | NaN | NaN | NaN | NaN | -18.98 | -27.48 | NaN | NaN | -2.38 | -26.50 | NaN | NaN | 41.34 | -1.43 | NaN | NaN | NaN | NaN | NaN | NaN | 19.07 | -4.20 | NaN | NaN | 17.74 | -23.56 | NaN | NaN | NaN | NaN | NaN | NaN | -1.23 | -21.97 | NaN | NaN | 20.31 | -14.68 | NaN | NaN | 7.20 | -20.10 | NaN | NaN | NaN | NaN | NaN | NaN | 16.23 | 6.76 | NaN | NaN | 3.54 | -31.63 | NaN | NaN | 9.03 | -6.20 | NaN | NaN | -2.68 | -16.36 | NaN | NaN | 14.76 | -21.48 | NaN | NaN | 6.48 | 4.90 | NaN | NaN | 20.29 | -2.13 | NaN | NaN |
# Print the shape of the DataFrame, df_tracking_cry_lei
print(df_tracking_cry_lei.shape)
(145621, 118)
The DataFrame has 145,621 rows, which is what we would expected - 25 fps × 60 secs × 90 mins = 135,000 (excluding injury time).
# Print the column names of the DataFrame, df_tracking_cry_lei
print(df_tracking_cry_lei.columns)
Index(['period_id', 'timestamp', 'ball_state', 'ball_owning_team_id', 'ball_x', 'ball_y', '66975_x', '66975_y', '66975_d', '66975_s', ... '86417_d', '86417_s', '51938_x', '51938_y', '51938_d', '51938_s', '54861_x', '54861_y', '54861_d', '54861_s'], dtype='object', length=118)
# Data types of the features of the raw DataFrame, df_tracking_cry_lei
df_tracking_cry_lei.dtypes
period_id int64 timestamp float64 ball_state object ball_owning_team_id int64 ball_x float64 ... 51938_s float64 54861_x float64 54861_y float64 54861_d float64 54861_s float64 Length: 118, dtype: object
Full details of these attributes and their data types is discussed further in the Data Dictionary.
# Displays all columns
with pd.option_context('display.max_rows', None, 'display.max_columns', None):
print(df_tracking_cry_lei.dtypes)
period_id int64 timestamp float64 ball_state object ball_owning_team_id int64 ball_x float64 ball_y float64 66975_x float64 66975_y float64 66975_d float64 66975_s float64 55494_x float64 55494_y float64 55494_d float64 55494_s float64 80146_x float64 80146_y float64 80146_d float64 80146_s float64 40836_x float64 40836_y float64 40836_d float64 40836_s float64 50471_x float64 50471_y float64 50471_d float64 50471_s float64 199670_x float64 199670_y float64 199670_d float64 199670_s float64 244723_x float64 244723_y float64 244723_d float64 244723_s float64 82403_x float64 82403_y float64 82403_d float64 82403_s float64 209036_x float64 209036_y float64 209036_d float64 209036_s float64 232787_x float64 232787_y float64 232787_d float64 232787_s float64 174874_x float64 174874_y float64 174874_d float64 174874_s float64 101668_x float64 101668_y float64 101668_d float64 101668_s float64 17745_x float64 17745_y float64 17745_d float64 17745_s float64 197469_x float64 197469_y float64 197469_d float64 197469_s float64 218031_x float64 218031_y float64 218031_d float64 218031_s float64 40146_x float64 40146_y float64 40146_d float64 40146_s float64 173515_x float64 173515_y float64 173515_d float64 173515_s float64 201666_x float64 201666_y float64 201666_d float64 201666_s float64 93100_x float64 93100_y float64 93100_d float64 93100_s float64 166989_x float64 166989_y float64 166989_d float64 166989_s float64 219352_x float64 219352_y float64 219352_d float64 219352_s float64 166477_x float64 166477_y float64 166477_d float64 166477_s float64 443661_x float64 443661_y float64 443661_d float64 443661_s float64 225902_x float64 225902_y float64 225902_d float64 225902_s float64 172780_x float64 172780_y float64 172780_d float64 172780_s float64 86417_x float64 86417_y float64 86417_d float64 86417_s float64 51938_x float64 51938_y float64 51938_d float64 51938_s float64 54861_x float64 54861_y float64 54861_d float64 54861_s float64 dtype: object
# Info for the raw DataFrame, df_tracking_cry_lei
df_tracking_cry_lei.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 145621 entries, 0 to 145620 Columns: 118 entries, period_id to 54861_s dtypes: float64(115), int64(2), object(1) memory usage: 131.1+ MB
# Plot visualisation of the missing values for each feature of the raw DataFrame, df_tracking_cry_lei
msno.matrix(df_tracking_cry_lei, figsize = (30, 7))
<AxesSubplot:>
# Counts of missing values
null_value_stats = df_tracking_cry_lei.isnull().sum(axis=0)
null_value_stats[null_value_stats != 0]
66975_d 145621 66975_s 145621 55494_d 145621 55494_s 145621 80146_x 63487 ... 51938_s 145621 54861_x 128694 54861_y 128694 54861_d 145621 54861_s 145621 Length: 80, dtype: int64
The next step is to wrangle the dataset to into a format that’s suitable for analysis.
This section is broken down into the following subsections:
4.1. Reset Index
4.2. Rename Columns
4.3. Rename Player Columns Using Metadata
4.4. Separate Home and Away DataFrames
4.5. Calculate Player Velocities
4.6. Reverse direction of players
Reset index, the existing index will not be dropped and will be used to identify the frame number.
# Reset index, the existing index will be used for the frame number
df_tracking_cry_bri = df_tracking_cry_bri.reset_index(drop=False)
df_tracking_cry_lei = df_tracking_cry_lei.reset_index(drop=False)
To work with Laurie Shaw's Metrica Sports Tracking data libraries, LaurieOnTracking
, the data needs to be engineered to match the Metrica schema, which is the following:
Feature | Data type | Definition | |
---|---|---|---|
Frame |
int64 | ||
Period |
int64 | ||
Time [s] |
float64 | ||
Home/Away_No._x (repeated 14 times) |
float64 | ||
Home/Away_No._y (repeated 14 times) |
float64 | ||
ball_x |
float64 | ||
ball_y |
float64 |
To learn more about the Metrica Sports schema, see the official documentation [link].
# Rename columns
df_tracking_cry_lei = df_tracking_cry_lei.rename(columns={'period_id': 'Period',
'timestamp': 'Time [s]',
'ball_state': 'Ball State',
'ball_owning_team_id': 'Ball Owning Team ID',
'index': 'Frame'
}
)
# Rename columns
df_tracking_cry_bri = df_tracking_cry_bri.rename(columns={'period_id': 'Period',
'timestamp': 'Time [s]',
'ball_state': 'Ball State',
'ball_owning_team_id': 'Ball Owning Team ID',
'index': 'Frame'
}
)
# Display DataFrame
df_tracking_cry_bri.head()
Frame | Period | Time [s] | Ball State | Ball Owning Team ID | ball_x | ball_y | 50471_x | 50471_y | 50471_d | 50471_s | 66975_x | 66975_y | 66975_d | 66975_s | 199670_x | 199670_y | 199670_d | 199670_s | 80146_x | 80146_y | 80146_d | 80146_s | 174874_x | 174874_y | 174874_d | 174874_s | 244723_x | 244723_y | 244723_d | 244723_s | 232787_x | 232787_y | 232787_d | 232787_s | 55494_x | 55494_y | 55494_d | 55494_s | 82403_x | 82403_y | 82403_d | 82403_s | 209036_x | 209036_y | 209036_d | 209036_s | 40836_x | 40836_y | 40836_d | 40836_s | 116216_x | 116216_y | 116216_d | 116216_s | 115382_x | 115382_y | 115382_d | 115382_s | 179268_x | 179268_y | 179268_d | 179268_s | 60307_x | 60307_y | 60307_d | 60307_s | 111478_x | 111478_y | 111478_d | 111478_s | 61933_x | 61933_y | 61933_d | 61933_s | 39155_x | 39155_y | 39155_d | 39155_s | 83299_x | 83299_y | 83299_d | 83299_s | 50175_x | 50175_y | 50175_d | 50175_s | 215059_x | 215059_y | 215059_d | 215059_s | 78916_x | 78916_y | 78916_d | 78916_s | 235382_x | 235382_y | 235382_d | 235382_s | 243505_x | 243505_y | 243505_d | 243505_s | 54861_x | 54861_y | 54861_d | 54861_s | 55037_x | 55037_y | 55037_d | 55037_s | 233425_x | 233425_y | 233425_d | 233425_s | 86417_x | 86417_y | 86417_d | 86417_s | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0.00 | dead | 31 | -0.06 | 0.02 | -9.41 | 2.46 | NaN | NaN | -7.85 | -6.82 | NaN | NaN | 0.11 | 9.07 | NaN | NaN | -1.60 | -28.63 | NaN | NaN | -19.66 | -13.13 | NaN | NaN | -19.06 | 6.63 | NaN | NaN | -2.85 | -14.11 | NaN | NaN | -15.20 | -25.35 | NaN | NaN | -2.93 | 19.45 | NaN | NaN | -19.64 | -3.11 | NaN | NaN | -45.54 | -0.09 | NaN | NaN | -0.03 | -10.30 | NaN | NaN | -0.26 | 0.34 | NaN | NaN | 14.59 | -19.99 | NaN | NaN | 7.07 | 6.07 | NaN | NaN | 3.79 | 20.08 | NaN | NaN | 14.57 | 8.56 | NaN | NaN | 7.25 | -7.72 | NaN | NaN | 21.50 | -6.01 | NaN | NaN | 0.35 | -17.21 | NaN | NaN | 41.03 | -0.00 | NaN | NaN | 0.24 | -29.41 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | 1 | 1 | 0.04 | alive | 36 | 1.11 | -0.36 | -9.43 | 2.46 | NaN | NaN | -7.85 | -6.84 | NaN | NaN | 0.15 | 9.03 | NaN | NaN | -1.59 | -28.66 | NaN | NaN | -19.65 | -13.14 | NaN | NaN | -19.06 | 6.62 | NaN | NaN | -2.84 | -14.08 | NaN | NaN | -15.21 | -25.35 | NaN | NaN | -2.89 | 19.46 | NaN | NaN | -19.63 | -3.10 | NaN | NaN | -45.53 | -0.09 | NaN | NaN | -0.09 | -10.30 | NaN | NaN | -0.27 | 0.35 | NaN | NaN | 14.57 | -20.00 | NaN | NaN | 7.07 | 6.07 | NaN | NaN | 3.78 | 20.07 | NaN | NaN | 14.53 | 8.57 | NaN | NaN | 7.25 | -7.72 | NaN | NaN | 21.49 | -6.01 | NaN | NaN | 0.35 | -17.20 | NaN | NaN | 41.01 | -0.01 | NaN | NaN | 0.20 | -29.44 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2 | 2 | 1 | 0.08 | alive | 36 | 1.57 | -0.52 | -9.44 | 2.46 | NaN | NaN | -7.86 | -6.86 | NaN | NaN | 0.19 | 8.99 | NaN | NaN | -1.57 | -28.68 | NaN | NaN | -19.64 | -13.14 | NaN | NaN | -19.07 | 6.62 | NaN | NaN | -2.84 | -14.05 | NaN | NaN | -15.23 | -25.36 | NaN | NaN | -2.85 | 19.47 | NaN | NaN | -19.63 | -3.10 | NaN | NaN | -45.52 | -0.09 | NaN | NaN | -0.16 | -10.29 | NaN | NaN | -0.28 | 0.33 | NaN | NaN | 14.55 | -20.00 | NaN | NaN | 7.07 | 6.08 | NaN | NaN | 3.77 | 20.06 | NaN | NaN | 14.50 | 8.58 | NaN | NaN | 7.25 | -7.71 | NaN | NaN | 21.48 | -6.00 | NaN | NaN | 0.34 | -17.20 | NaN | NaN | 40.99 | -0.01 | NaN | NaN | 0.16 | -29.47 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
3 | 3 | 1 | 0.12 | alive | 36 | 2.23 | -0.73 | -9.45 | 2.46 | NaN | NaN | -7.87 | -6.88 | NaN | NaN | 0.24 | 8.93 | NaN | NaN | -1.56 | -28.70 | NaN | NaN | -19.62 | -13.14 | NaN | NaN | -19.07 | 6.62 | NaN | NaN | -2.83 | -14.02 | NaN | NaN | -15.25 | -25.36 | NaN | NaN | -2.80 | 19.48 | NaN | NaN | -19.62 | -3.10 | NaN | NaN | -45.50 | -0.10 | NaN | NaN | -0.24 | -10.29 | NaN | NaN | -0.29 | 0.32 | NaN | NaN | 14.53 | -20.01 | NaN | NaN | 7.07 | 6.09 | NaN | NaN | 3.77 | 20.06 | NaN | NaN | 14.46 | 8.59 | NaN | NaN | 7.25 | -7.71 | NaN | NaN | 21.47 | -5.98 | NaN | NaN | 0.33 | -17.19 | NaN | NaN | 40.98 | -0.01 | NaN | NaN | 0.10 | -29.50 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
4 | 4 | 1 | 0.16 | alive | 36 | 2.90 | -0.94 | -9.47 | 2.46 | NaN | NaN | -7.87 | -6.90 | NaN | NaN | 0.29 | 8.87 | NaN | NaN | -1.55 | -28.73 | NaN | NaN | -19.61 | -13.14 | NaN | NaN | -19.07 | 6.62 | NaN | NaN | -2.83 | -14.00 | NaN | NaN | -15.28 | -25.36 | NaN | NaN | -2.76 | 19.49 | NaN | NaN | -19.62 | -3.11 | NaN | NaN | -45.49 | -0.10 | NaN | NaN | -0.32 | -10.29 | NaN | NaN | -0.30 | 0.32 | NaN | NaN | 14.51 | -20.01 | NaN | NaN | 7.07 | 6.09 | NaN | NaN | 3.76 | 20.05 | NaN | NaN | 14.43 | 8.59 | NaN | NaN | 7.24 | -7.71 | NaN | NaN | 21.47 | -5.97 | NaN | NaN | 0.32 | -17.18 | NaN | NaN | 40.96 | -0.01 | NaN | NaN | 0.04 | -29.54 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
For compatibility with Laurie Shaw's Metrica Sports Tracking data library LaurieOnTracking
, the column of each player is given a Home_
or Away_
prefix.
# Read in the Meta data for the corresponding Tracking data
## Define the file name
file_name = 'g2210334'
## Read in the Meta data
df_meta_cry_lei = pd.read_csv(os.path.join(data_dir_second_spectrum, 'raw', 'metadata', f'{file_name}_SecondSpectrum_Metadata_Players.csv'))
## Prepare DataFrame to create dictionary
## Convert data types
df_meta_cry_lei['optaId'] = df_meta_cry_lei['optaId'].astype(int)
df_meta_cry_lei['name'] = df_meta_cry_lei['name'].astype(str)
## Create a dictionary of Qualifier IDs and Qualifier Names from the reference dataset
dict_home_away_players = dict(zip(df_meta_cry_lei['optaId'], df_meta_cry_lei['HA']))
dict_home_away_players
{86417: 'Home', 54861: 'Home', 105666: 'Home', 66975: 'Home', 108413: 'Home', 443661: 'Home', 244723: 'Home', 80146: 'Home', 209036: 'Home', 40836: 'Home', 50471: 'Home', 174874: 'Home', 55494: 'Home', 55037: 'Home', 49413: 'Home', 232787: 'Home', 82403: 'Home', 199670: 'Home', 58786: 'Home', 57328: 'Home', 245419: 'Away', 166477: 'Away', 219352: 'Away', 166989: 'Away', 173515: 'Away', 215413: 'Away', 111931: 'Away', 218031: 'Away', 168580: 'Away', 51938: 'Away', 95463: 'Away', 101668: 'Away', 155569: 'Away', 172780: 'Away', 17745: 'Away', 197469: 'Away', 93100: 'Away', 201666: 'Away', 225902: 'Away', 40146: 'Away'}
# Map the values to the data (current a hard coded way, will replace with a loop shortly)
dict_home_away_players_cry_lei = {'86417_x': 'Home_15_x',
'54861_x': 'Home_20_x',
'105666_x': 'Home_1_x',
'66975_x': 'Home_4_x',
'108413_x': 'Home_12_x',
'443661_x': 'Home_7_x',
'244723_x': 'Home_3_x',
'80146_x': 'Home_9_x',
'209036_x': 'Home_6_x',
'40836_x': 'Home_13_x',
'50471_x': 'Home_18_x',
'174874_x': 'Home_16_x',
'55494_x': 'Home_2_x',
'55037_x': 'Home_8_x',
'49413_x': 'Home_5_x',
'232787_x': 'Home_23_x',
'82403_x': 'Home_11_x',
'199670_x': 'Home_22_x',
'58786_x': 'Home_34_x',
'57328_x': 'Home_17_x',
'86417_y': 'Home_15_y',
'54861_y': 'Home_20_y',
'105666_y': 'Home_1_y',
'66975_y': 'Home_4_y',
'108413_y': 'Home_12_y',
'443661_y': 'Home_7_y',
'244723_y': 'Home_3_y',
'80146_y': 'Home_9_y',
'209036_y': 'Home_6_y',
'40836_y': 'Home_13_y',
'50471_y': 'Home_18_y',
'174874_y': 'Home_16_y',
'55494_y': 'Home_2_y',
'55037_y': 'Home_8_y',
'49413_y': 'Home_5_y',
'232787_y': 'Home_23_y',
'82403_y': 'Home_11_y',
'199670_y': 'Home_22_y',
'58786_y': 'Home_34_y',
'57328_y': 'Home_17_y',
'86417_d': 'Home_15_d',
'54861_d': 'Home_20_d',
'105666_d': 'Home_1_d',
'66975_d': 'Home_4_d',
'108413_d': 'Home_12_d',
'443661_d': 'Home_7_d',
'244723_d': 'Home_3_d',
'80146_d': 'Home_9_d',
'209036_d': 'Home_6_d',
'40836_d': 'Home_13_d',
'50471_d': 'Home_18_d',
'174874_d': 'Home_16_d',
'55494_d': 'Home_2_d',
'55037_d': 'Home_8_d',
'49413_d': 'Home_5_d',
'232787_d': 'Home_23_d',
'82403_d': 'Home_11_d',
'199670_d': 'Home_22_d',
'58786_d': 'Home_34_d',
'57328_d': 'Home_17_d',
'86417_s': 'Home_15_s',
'54861_s': 'Home_20_s',
'105666_s': 'Home_1_s',
'66975_s': 'Home_4_s',
'108413_s': 'Home_12_s',
'443661_s': 'Home_7_s',
'244723_s': 'Home_3_s',
'80146_s': 'Home_9_s',
'209036_s': 'Home_6_s',
'40836_s': 'Home_13_s',
'50471_s': 'Home_18_s',
'174874_s': 'Home_16_s',
'55494_s': 'Home_2_s',
'55037_s': 'Home_8_s',
'49413_s': 'Home_5_s',
'232787_s': 'Home_23_s',
'82403_s': 'Home_11_s',
'199670_s': 'Home_22_s',
'58786_s': 'Home_34_s',
'57328_s': 'Home_17_s',
'245419_x': 'Away_29_x',
'166477_x': 'Away_27_x',
'219352_x': 'Away_37_x',
'166989_x': 'Away_8_x',
'173515_x': 'Away_14_x',
'215413_x': 'Away_22_x',
'111931_x': 'Away_21_x',
'218031_x': 'Away_4_x',
'168580_x': 'Away_17_x',
'51938_x': 'Away_11_x',
'95463_x': 'Away_12_x',
'101668_x': 'Away_9_x',
'155569_x': 'Away_18_x',
'172780_x': 'Away_10_x',
'17745_x': 'Away_1_x',
'197469_x': 'Away_20_x',
'93100_x': 'Away_23_x',
'201666_x': 'Away_7_x',
'225902_x': 'Away_42_x',
'40146_x': 'Away_5_x',
'245419_y': 'Away_29_y',
'166477_y': 'Away_27_y',
'219352_y': 'Away_37_y',
'166989_y': 'Away_8_y',
'173515_y': 'Away_14_y',
'215413_y': 'Away_22_y',
'111931_y': 'Away_21_y',
'218031_y': 'Away_4_y',
'168580_y': 'Away_17_y',
'51938_y': 'Away_11_y',
'95463_y': 'Away_12_y',
'101668_y': 'Away_9_y',
'155569_y': 'Away_18_y',
'172780_y': 'Away_10_y',
'17745_y': 'Away_1_y',
'197469_y': 'Away_20_y',
'93100_y': 'Away_23_y',
'201666_y': 'Away_7_y',
'225902_y': 'Away_42_y',
'40146_y': 'Away_5_y',
'245419_d': 'Away_29_d',
'166477_d': 'Away_27_d',
'219352_d': 'Away_37_d',
'166989_d': 'Away_8_d',
'173515_d': 'Away_14_d',
'215413_d': 'Away_22_d',
'111931_d': 'Away_21_d',
'218031_d': 'Away_4_d',
'168580_d': 'Away_17_d',
'51938_d': 'Away_11_d',
'95463_d': 'Away_12_d',
'101668_d': 'Away_9_d',
'155569_d': 'Away_18_d',
'172780_d': 'Away_10_d',
'17745_d': 'Away_1_d',
'197469_d': 'Away_20_d',
'93100_d': 'Away_23_d',
'201666_d': 'Away_7_d',
'225902_d': 'Away_42_d',
'40146_d': 'Away_5_d',
'245419_s': 'Away_29_s',
'166477_s': 'Away_27_s',
'219352_s': 'Away_37_s',
'166989_s': 'Away_8_s',
'173515_s': 'Away_14_s',
'215413_s': 'Away_22_s',
'111931_s': 'Away_21_s',
'218031_s': 'Away_4_s',
'168580_s': 'Away_17_s',
'51938_s': 'Away_11_s',
'95463_s': 'Away_12_s',
'101668_s': 'Away_9_s',
'155569_s': 'Away_18_s',
'172780_s': 'Away_10_s',
'17745_s': 'Away_1_s',
'197469_s': 'Away_20_s',
'93100_s': 'Away_23_s',
'201666_s': 'Away_7_s',
'225902_s': 'Away_42_s',
'40146_s': 'Away_5_s'
}
## Map Qualifier Names to Qualifier IDs
df_tracking_cry_lei = df_tracking_cry_lei.rename(columns=dict_home_away_players_cry_lei)
## Display columns
df_tracking_cry_lei.columns
Index(['Frame', 'Period', 'Time [s]', 'Ball State', 'Ball Owning Team ID', 'ball_x', 'ball_y', 'Home_4_x', 'Home_4_y', 'Home_4_d', ... 'Home_15_d', 'Home_15_s', 'Away_11_x', 'Away_11_y', 'Away_11_d', 'Away_11_s', 'Home_20_x', 'Home_20_y', 'Home_20_d', 'Home_20_s'], dtype='object', length=119)
# Read in the Meta data for the corresponding Tracking data
## Define the file name
file_name = 'g2210324'
## Read in the Meta data
df_meta_cry_bri = pd.read_csv(os.path.join(data_dir_second_spectrum, 'raw', 'metadata', f'{file_name}_SecondSpectrum_Metadata_Players.csv'))
## Prepare DataFrame to create dictionary
## Convert data types
df_meta_cry_bri['optaId'] = df_meta_cry_bri['optaId'].astype(int)
df_meta_cry_bri['name'] = df_meta_cry_bri['name'].astype(str)
## Create a dictionary of Qualifier IDs and Qualifier Names from the reference dataset
dict_home_away_players = dict(zip(df_meta_cry_bri['optaId'], df_meta_cry_bri['HA']))
dict_home_away_players
{55037: 'Home', 50471: 'Home', 80146: 'Home', 82403: 'Home', 108413: 'Home', 199670: 'Home', 232787: 'Home', 174874: 'Home', 244723: 'Home', 66975: 'Home', 55494: 'Home', 209036: 'Home', 54861: 'Home', 86417: 'Home', 105666: 'Home', 231747: 'Home', 40836: 'Home', 58786: 'Home', 443661: 'Home', 57328: 'Home', 39155: 'Away', 116216: 'Away', 61933: 'Away', 78916: 'Away', 243505: 'Away', 233425: 'Away', 49262: 'Away', 50175: 'Away', 111478: 'Away', 243016: 'Away', 235382: 'Away', 232792: 'Away', 209045: 'Away', 60307: 'Away', 433589: 'Away', 115382: 'Away', 83299: 'Away', 215059: 'Away', 109345: 'Away', 179268: 'Away'}
# Map the values to the data (current a hard coded way, will replace with a loop shortly)
dict_home_away_players_cry_bri = {'55037_x': 'Home_8_x',
'50471_x': 'Home_18_x',
'80146_x': 'Home_9_x',
'82403_x': 'Home_11_x',
'108413_x': 'Home_12_x',
'199670_x': 'Home_22_x',
'232787_x': 'Home_23_x',
'174874_x': 'Home_16_x',
'244723_x': 'Home_3_x',
'66975_x': 'Home_4_x',
'55494_x': 'Home_2_x',
'209036_x': 'Home_6_x',
'54861_x': 'Home_20_x',
'86417_x': 'Home_15_x',
'105666_x': 'Home_1_x',
'231747_x': 'Home_14_x',
'40836_x': 'Home_13_x',
'58786_x': 'Home_34_x',
'443661_x': 'Home_17_x',
'57328_x': 'Home_17_x',
'55037_y': 'Home_8_y',
'50471_y': 'Home_18_y',
'80146_y': 'Home_9_y',
'82403_y': 'Home_11_y',
'108413_y': 'Home_12_y',
'199670_y': 'Home_22_y',
'232787_y': 'Home_23_y',
'174874_y': 'Home_16_y',
'244723_y': 'Home_3_y',
'66975_y': 'Home_4_y',
'55494_y': 'Home_2_y',
'209036_y': 'Home_6_y',
'54861_y': 'Home_20_y',
'86417_y': 'Home_15_y',
'105666_y': 'Home_1_y',
'231747_y': 'Home_14_y',
'40836_y': 'Home_13_y',
'58786_y': 'Home_34_y',
'443661_y': 'Home_17_y',
'57328_y': 'Home_17_y',
'55037_d': 'Home_8_d',
'50471_d': 'Home_18_d',
'80146_d': 'Home_9_d',
'82403_d': 'Home_11_d',
'108413_d': 'Home_12_d',
'199670_d': 'Home_22_d',
'232787_d': 'Home_23_d',
'174874_d': 'Home_16_d',
'244723_d': 'Home_3_d',
'66975_d': 'Home_4_d',
'55494_d': 'Home_2_d',
'209036_d': 'Home_6_d',
'54861_d': 'Home_20_d',
'86417_d': 'Home_15_d',
'105666_d': 'Home_1_d',
'231747_d': 'Home_14_d',
'40836_d': 'Home_13_d',
'58786_d': 'Home_34_d',
'443661_d': 'Home_17_d',
'57328_d': 'Home_17_d',
'55037_s': 'Home_8_s',
'50471_s': 'Home_18_s',
'80146_s': 'Home_9_s',
'82403_s': 'Home_11_s',
'108413_s': 'Home_12_s',
'199670_s': 'Home_22_s',
'232787_s': 'Home_23_s',
'174874_s': 'Home_16_s',
'244723_s': 'Home_3_s',
'66975_s': 'Home_4_s',
'55494_s': 'Home_2_s',
'209036_s': 'Home_6_s',
'54861_s': 'Home_20_s',
'86417_s': 'Home_15_s',
'105666_s': 'Home_1_s',
'231747_s': 'Home_14_s',
'40836_s': 'Home_13_s',
'58786_s': 'Home_34_s',
'443661_s': 'Home_17_s',
'57328_s': 'Home_17_s',
'39155_x': 'Away_14_x',
'116216_x': 'Away_11_x',
'61933_x': 'Away_24_x',
'78916_x': 'Away_33_x',
'243505_x': 'Away_15_x',
'233425_x': 'Away_7_x',
'49262_x': 'Away_23_x',
'50175_x': 'Away_18_x',
'111478_x': 'Away_34_x',
'243016_x': 'Away_10_x',
'235382_x': 'Away_17_x',
'232792_x': 'Away_2_x',
'209045_x': 'Away_30_x',
'60307_x': 'Away_13_x',
'433589_x': 'Away_28_x',
'115382_x': 'Away_9_x',
'83299_x': 'Away_5_x',
'215059_x': 'Away_1_x',
'109345_x': 'Away_20_x',
'179268_x': 'Away_3_x',
'39155_y': 'Away_14_y',
'116216_y': 'Away_11_y',
'61933_y': 'Away_24_y',
'78916_y': 'Away_33_y',
'243505_y': 'Away_15_y',
'233425_y': 'Away_7_y',
'49262_y': 'Away_23_y',
'50175_y': 'Away_18_y',
'111478_y': 'Away_34_y',
'243016_y': 'Away_10_y',
'235382_y': 'Away_17_y',
'232792_y': 'Away_2_y',
'209045_y': 'Away_30_y',
'60307_y': 'Away_13_y',
'433589_y': 'Away_28_y',
'115382_y': 'Away_9_y',
'83299_y': 'Away_5_y',
'215059_y': 'Away_1_y',
'109345_y': 'Away_20_y',
'179268_y': 'Away_3_y',
'39155_d': 'Away_14_d',
'116216_d': 'Away_11_d',
'61933_d': 'Away_24_d',
'78916_d': 'Away_33_d',
'243505_d': 'Away_15_d',
'233425_d': 'Away_7_d',
'49262_d': 'Away_23_d',
'50175_d': 'Away_18_d',
'111478_d': 'Away_34_d',
'243016_d': 'Away_10_d',
'235382_d': 'Away_17_d',
'232792_d': 'Away_2_d',
'209045_d': 'Away_30_d',
'60307_d': 'Away_13_d',
'433589_d': 'Away_28_d',
'115382_d': 'Away_9_d',
'83299_d': 'Away_5_d',
'215059_d': 'Away_1_d',
'109345_d': 'Away_20_d',
'179268_d': 'Away_3_d',
'39155_s': 'Away_14_s',
'116216_s': 'Away_11_s',
'61933_s': 'Away_24_s',
'78916_s': 'Away_33_s',
'243505_s': 'Away_15_s',
'233425_s': 'Away_7_s',
'49262_s': 'Away_23_s',
'50175_s': 'Away_18_s',
'111478_s': 'Away_34_s',
'243016_s': 'Away_10_s',
'235382_s': 'Away_17_s',
'232792_s': 'Away_2_s',
'209045_s': 'Away_30_s',
'60307_s': 'Away_13_s',
'433589_s': 'Away_28_s',
'115382_s': 'Away_9_s',
'83299_s': 'Away_5_s',
'215059_s': 'Away_1_s',
'109345_s': 'Away_20_s',
'179268_s': 'Away_3_s'
}
## Map Qualifier Names to Qualifier IDs
df_tracking_cry_bri = df_tracking_cry_bri.rename(columns=dict_home_away_players_cry_bri)
## Display columns
df_tracking_cry_bri.columns
Index(['Frame', 'Period', 'Time [s]', 'Ball State', 'Ball Owning Team ID', 'ball_x', 'ball_y', 'Home_18_x', 'Home_18_y', 'Home_18_d', ... 'Home_8_d', 'Home_8_s', 'Away_7_x', 'Away_7_y', 'Away_7_d', 'Away_7_s', 'Home_15_x', 'Home_15_y', 'Home_15_d', 'Home_15_s'], dtype='object', length=119)
For compatibility with Laurie Shaw's Metrica Sports Tracking data library LaurieOnTracking
, Home and Away DataFrames are separated. This is done using the custom create_home_and_away_dfs
that I have written (see the top the top of the notebook)
# Separate Home and Away DataFrames
df_tracking_home_cry_lei, df_tracking_away_cry_lei = create_home_and_away_dfs(df_tracking_cry_lei)
df_tracking_home_cry_bri, df_tracking_away_cry_bri = create_home_and_away_dfs(df_tracking_cry_bri)
# Display Home DataFrame
df_tracking_home_cry_lei.head()
Frame | Period | Time [s] | Ball State | Ball Owning Team ID | ball_x | ball_y | Home_4_x | Home_4_y | Home_4_d | Home_4_s | Home_2_x | Home_2_y | Home_2_d | Home_2_s | Home_9_x | Home_9_y | Home_9_d | Home_9_s | Home_13_x | Home_13_y | Home_13_d | Home_13_s | Home_18_x | Home_18_y | Home_18_d | Home_18_s | Home_22_x | Home_22_y | Home_22_d | Home_22_s | Home_3_x | Home_3_y | Home_3_d | Home_3_s | Home_11_x | Home_11_y | Home_11_d | Home_11_s | Home_6_x | Home_6_y | Home_6_d | Home_6_s | Home_23_x | Home_23_y | Home_23_d | Home_23_s | Home_16_x | Home_16_y | Home_16_d | Home_16_s | Home_7_x | Home_7_y | Home_7_d | Home_7_s | Home_15_x | Home_15_y | Home_15_d | Home_15_s | Home_20_x | Home_20_y | Home_20_d | Home_20_s | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0.00 | dead | 31 | 0.04 | 0.09 | 0.60 | -9.08 | NaN | NaN | 11.43 | 10.22 | NaN | NaN | 0.21 | 9.47 | NaN | NaN | 42.10 | -0.19 | NaN | NaN | -0.36 | 0.35 | NaN | NaN | -0.57 | -20.55 | NaN | NaN | 7.63 | -25.09 | NaN | NaN | 1.22 | -30.53 | NaN | NaN | 14.71 | -12.51 | NaN | NaN | 0.51 | 2.82 | NaN | NaN | 19.24 | 0.54 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | 1 | 1 | 0.04 | alive | 31 | 0.83 | 0.09 | 0.56 | -9.07 | NaN | NaN | 11.41 | 10.22 | NaN | NaN | 0.22 | 9.47 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.35 | 0.35 | NaN | NaN | -0.64 | -20.47 | NaN | NaN | 7.62 | -25.09 | NaN | NaN | 1.24 | -30.53 | NaN | NaN | 14.70 | -12.52 | NaN | NaN | 0.50 | 2.81 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2 | 2 | 1 | 0.08 | alive | 31 | 1.34 | 0.09 | 0.53 | -9.07 | NaN | NaN | 11.41 | 10.21 | NaN | NaN | 0.22 | 9.46 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.34 | 0.34 | NaN | NaN | -0.71 | -20.40 | NaN | NaN | 7.62 | -25.08 | NaN | NaN | 1.25 | -30.53 | NaN | NaN | 14.70 | -12.53 | NaN | NaN | 0.49 | 2.80 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
3 | 3 | 1 | 0.12 | alive | 31 | 2.03 | 0.10 | 0.50 | -9.06 | NaN | NaN | 11.39 | 10.21 | NaN | NaN | 0.23 | 9.45 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.35 | 0.34 | NaN | NaN | -0.79 | -20.31 | NaN | NaN | 7.60 | -25.08 | NaN | NaN | 1.26 | -30.53 | NaN | NaN | 14.69 | -12.54 | NaN | NaN | 0.47 | 2.78 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
4 | 4 | 1 | 0.16 | alive | 31 | 2.85 | 0.11 | 0.46 | -9.06 | NaN | NaN | 11.40 | 10.19 | NaN | NaN | 0.25 | 9.43 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.36 | 0.33 | NaN | NaN | -0.88 | -20.23 | NaN | NaN | 7.59 | -25.07 | NaN | NaN | 1.27 | -30.53 | NaN | NaN | 14.68 | -12.56 | NaN | NaN | 0.46 | 2.77 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
# Display Away DataFrame
df_tracking_away_cry_lei.head()
Frame | Period | Time [s] | Ball State | Ball Owning Team ID | ball_x | ball_y | Away_9_x | Away_9_y | Away_9_d | Away_9_s | Away_1_x | Away_1_y | Away_1_d | Away_1_s | Away_20_x | Away_20_y | Away_20_d | Away_20_s | Away_4_x | Away_4_y | Away_4_d | Away_4_s | Away_5_x | Away_5_y | Away_5_d | Away_5_s | Away_14_x | Away_14_y | Away_14_d | Away_14_s | Away_7_x | Away_7_y | Away_7_d | Away_7_s | Away_23_x | Away_23_y | Away_23_d | Away_23_s | Away_8_x | Away_8_y | Away_8_d | Away_8_s | Away_37_x | Away_37_y | Away_37_d | Away_37_s | Away_27_x | Away_27_y | Away_27_d | Away_27_s | Away_42_x | Away_42_y | Away_42_d | Away_42_s | Away_10_x | Away_10_y | Away_10_d | Away_10_s | Away_11_x | Away_11_y | Away_11_d | Away_11_s | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0.00 | dead | 31 | 0.04 | 0.09 | -0.93 | 9.31 | NaN | NaN | -50.43 | 0.12 | NaN | NaN | -10.17 | -19.71 | NaN | NaN | -16.67 | -16.05 | NaN | NaN | -13.94 | 9.96 | NaN | NaN | 0.49 | -10.92 | NaN | NaN | -3.18 | 10.89 | NaN | NaN | -15.10 | -0.99 | NaN | NaN | -8.98 | 0.43 | NaN | NaN | -0.99 | -20.88 | NaN | NaN | -14.75 | -27.43 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | 1 | 1 | 0.04 | alive | 31 | 0.83 | 0.09 | -0.89 | 9.25 | NaN | NaN | -50.42 | 0.12 | NaN | NaN | -10.13 | -19.70 | NaN | NaN | -16.68 | -16.06 | NaN | NaN | -13.94 | 9.96 | NaN | NaN | 0.59 | -10.86 | NaN | NaN | -3.19 | 10.91 | NaN | NaN | -15.13 | -0.99 | NaN | NaN | -8.95 | 0.44 | NaN | NaN | -0.99 | -20.83 | NaN | NaN | -14.75 | -27.42 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2 | 2 | 1 | 0.08 | alive | 31 | 1.34 | 0.09 | -0.84 | 9.19 | NaN | NaN | -50.43 | 0.13 | NaN | NaN | -10.09 | -19.69 | NaN | NaN | -16.69 | -16.07 | NaN | NaN | -13.93 | 9.99 | NaN | NaN | 0.70 | -10.80 | NaN | NaN | -3.20 | 10.94 | NaN | NaN | -15.16 | -0.99 | NaN | NaN | -8.92 | 0.44 | NaN | NaN | -1.00 | -20.78 | NaN | NaN | -14.76 | -27.42 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
3 | 3 | 1 | 0.12 | alive | 31 | 2.03 | 0.10 | -0.77 | 9.12 | NaN | NaN | -50.42 | 0.13 | NaN | NaN | -10.05 | -19.67 | NaN | NaN | -16.69 | -16.09 | NaN | NaN | -13.92 | 10.02 | NaN | NaN | 0.80 | -10.75 | NaN | NaN | -3.22 | 10.96 | NaN | NaN | -15.16 | -1.00 | NaN | NaN | -8.88 | 0.44 | NaN | NaN | -1.02 | -20.73 | NaN | NaN | -14.76 | -27.41 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
4 | 4 | 1 | 0.16 | alive | 31 | 2.85 | 0.11 | -0.70 | 9.04 | NaN | NaN | -50.42 | 0.13 | NaN | NaN | -10.02 | -19.65 | NaN | NaN | -16.70 | -16.09 | NaN | NaN | -13.93 | 10.04 | NaN | NaN | 0.91 | -10.69 | NaN | NaN | -3.22 | 11.00 | NaN | NaN | -15.20 | -1.01 | NaN | NaN | -8.91 | 0.45 | NaN | NaN | -1.04 | -20.69 | NaN | NaN | -14.78 | -27.40 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Determine the velocity of each player at a given timestep using the calc_player_velocities
function from the mvel
library.
# Calculate player velocities
## 03/10/2021: Crystal Palace (2) vs. (2) Leicester City (g2210334)
### Home
df_tracking_home_cry_lei = svel.calc_player_velocities(df_tracking_home_cry_lei,
smoothing=True,
filter_='moving_average',
maxspeed=12
) # filter_='Savitzky-Golay'
### Away
df_tracking_away_cry_lei = svel.calc_player_velocities(df_tracking_away_cry_lei,
smoothing=True,
filter_='moving_average',
maxspeed=12
)
## 27/09/2021: Crystal Palace (1) vs. (1) Brighton & Hove Albion (g2210324)
### Home
df_tracking_home_cry_bri = svel.calc_player_velocities(df_tracking_home_cry_bri,
smoothing=True,
filter_='moving_average',
maxspeed=12
) # filter_='Savitzky-Golay'
### Away
df_tracking_away_cry_bri = svel.calc_player_velocities(df_tracking_away_cry_bri,
smoothing=True,
filter_='moving_average',
maxspeed=12
)
# Display Home DataFrame
df_tracking_home_cry_lei.head()
Frame | Period | Time [s] | Ball State | Ball Owning Team ID | ball_x | ball_y | Home_4_x | Home_4_y | Home_4_d | Home_4_s | Home_2_x | Home_2_y | Home_2_d | Home_2_s | Home_9_x | Home_9_y | Home_9_d | Home_9_s | Home_13_x | Home_13_y | Home_13_d | Home_13_s | Home_18_x | Home_18_y | Home_18_d | Home_18_s | Home_22_x | Home_22_y | Home_22_d | Home_22_s | Home_3_x | Home_3_y | Home_3_d | Home_3_s | Home_11_x | Home_11_y | Home_11_d | Home_11_s | Home_6_x | Home_6_y | Home_6_d | Home_6_s | Home_23_x | Home_23_y | Home_23_d | Home_23_s | Home_16_x | Home_16_y | Home_16_d | Home_16_s | Home_7_x | Home_7_y | Home_7_d | Home_7_s | Home_15_x | Home_15_y | Home_15_d | Home_15_s | Home_20_x | Home_20_y | Home_20_d | Home_20_s | Home_4_vx | Home_4_vy | Home_4_speed | Home_2_vx | Home_2_vy | Home_2_speed | Home_9_vx | Home_9_vy | Home_9_speed | Home_13_vx | Home_13_vy | Home_13_speed | Home_18_vx | Home_18_vy | Home_18_speed | Home_22_vx | Home_22_vy | Home_22_speed | Home_3_vx | Home_3_vy | Home_3_speed | Home_11_vx | Home_11_vy | Home_11_speed | Home_6_vx | Home_6_vy | Home_6_speed | Home_23_vx | Home_23_vy | Home_23_speed | Home_16_vx | Home_16_vy | Home_16_speed | Home_7_vx | Home_7_vy | Home_7_speed | Home_15_vx | Home_15_vy | Home_15_speed | Home_20_vx | Home_20_vy | Home_20_speed | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0.00 | dead | 31 | 0.04 | 0.09 | 0.60 | -9.08 | NaN | NaN | 11.43 | 10.22 | NaN | NaN | 0.21 | 9.47 | NaN | NaN | 42.10 | -0.19 | NaN | NaN | -0.36 | 0.35 | NaN | NaN | -0.57 | -20.55 | NaN | NaN | 7.63 | -25.09 | NaN | NaN | 1.22 | -30.53 | NaN | NaN | 14.71 | -12.51 | NaN | NaN | 0.51 | 2.82 | NaN | NaN | 19.24 | 0.54 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | 1 | 1 | 0.04 | alive | 31 | 0.83 | 0.09 | 0.56 | -9.07 | NaN | NaN | 11.41 | 10.22 | NaN | NaN | 0.22 | 9.47 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.35 | 0.35 | NaN | NaN | -0.64 | -20.47 | NaN | NaN | 7.62 | -25.09 | NaN | NaN | 1.24 | -30.53 | NaN | NaN | 14.70 | -12.52 | NaN | NaN | 0.50 | 2.81 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | -1.00 | 0.25 | 1.030776 | -0.50 | 0.00 | 0.500000 | 0.25 | 0.00 | 0.250000 | 0.25 | 0.0 | 0.25 | 0.25 | 0.00 | 0.250000 | -1.75 | 2.00 | 2.657536 | -0.25 | 0.00 | 0.250000 | 0.50 | 0.0 | 0.50 | -0.25 | -0.25 | 0.353553 | -0.25 | -0.25 | 0.353553 | -0.25 | 0.0 | 0.25 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2 | 2 | 1 | 0.08 | alive | 31 | 1.34 | 0.09 | 0.53 | -9.07 | NaN | NaN | 11.41 | 10.21 | NaN | NaN | 0.22 | 9.46 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.34 | 0.34 | NaN | NaN | -0.71 | -20.40 | NaN | NaN | 7.62 | -25.08 | NaN | NaN | 1.25 | -30.53 | NaN | NaN | 14.70 | -12.53 | NaN | NaN | 0.49 | 2.80 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | -0.75 | 0.00 | 0.750000 | 0.00 | -0.25 | 0.250000 | 0.00 | -0.25 | 0.250000 | 0.00 | 0.0 | 0.00 | 0.25 | -0.25 | 0.353553 | -1.75 | 1.75 | 2.474874 | 0.00 | 0.25 | 0.250000 | 0.25 | 0.0 | 0.25 | 0.00 | -0.25 | 0.250000 | -0.25 | -0.25 | 0.353553 | 0.00 | 0.0 | 0.00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
3 | 3 | 1 | 0.12 | alive | 31 | 2.03 | 0.10 | 0.50 | -9.06 | NaN | NaN | 11.39 | 10.21 | NaN | NaN | 0.23 | 9.45 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.35 | 0.34 | NaN | NaN | -0.79 | -20.31 | NaN | NaN | 7.60 | -25.08 | NaN | NaN | 1.26 | -30.53 | NaN | NaN | 14.69 | -12.54 | NaN | NaN | 0.47 | 2.78 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | -0.75 | 0.25 | 0.790569 | -0.50 | 0.00 | 0.500000 | 0.25 | -0.25 | 0.353553 | 0.00 | 0.0 | 0.00 | -0.25 | 0.00 | 0.250000 | -2.00 | 2.25 | 3.010399 | -0.50 | 0.00 | 0.500000 | 0.25 | 0.0 | 0.25 | -0.25 | -0.25 | 0.353553 | -0.50 | -0.50 | 0.707107 | 0.00 | 0.0 | 0.00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
4 | 4 | 1 | 0.16 | alive | 31 | 2.85 | 0.11 | 0.46 | -9.06 | NaN | NaN | 11.40 | 10.19 | NaN | NaN | 0.25 | 9.43 | NaN | NaN | 42.11 | -0.19 | NaN | NaN | -0.36 | 0.33 | NaN | NaN | -0.88 | -20.23 | NaN | NaN | 7.59 | -25.07 | NaN | NaN | 1.27 | -30.53 | NaN | NaN | 14.68 | -12.56 | NaN | NaN | 0.46 | 2.77 | NaN | NaN | 19.23 | 0.54 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | -1.00 | 0.00 | 1.000000 | 0.25 | -0.50 | 0.559017 | 0.50 | -0.50 | 0.707107 | 0.00 | 0.0 | 0.00 | -0.25 | -0.25 | 0.353553 | -2.25 | 2.00 | 3.010399 | -0.25 | 0.25 | 0.353553 | 0.25 | 0.0 | 0.25 | -0.25 | -0.50 | 0.559017 | -0.25 | -0.25 | 0.353553 | 0.00 | 0.0 | 0.00 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
# Display Away DataFrame
df_tracking_away_cry_lei.head()
Frame | Period | Time [s] | Ball State | Ball Owning Team ID | ball_x | ball_y | Away_9_x | Away_9_y | Away_9_d | Away_9_s | Away_1_x | Away_1_y | Away_1_d | Away_1_s | Away_20_x | Away_20_y | Away_20_d | Away_20_s | Away_4_x | Away_4_y | Away_4_d | Away_4_s | Away_5_x | Away_5_y | Away_5_d | Away_5_s | Away_14_x | Away_14_y | Away_14_d | Away_14_s | Away_7_x | Away_7_y | Away_7_d | Away_7_s | Away_23_x | Away_23_y | Away_23_d | Away_23_s | Away_8_x | Away_8_y | Away_8_d | Away_8_s | Away_37_x | Away_37_y | Away_37_d | Away_37_s | Away_27_x | Away_27_y | Away_27_d | Away_27_s | Away_42_x | Away_42_y | Away_42_d | Away_42_s | Away_10_x | Away_10_y | Away_10_d | Away_10_s | Away_11_x | Away_11_y | Away_11_d | Away_11_s | Away_9_vx | Away_9_vy | Away_9_speed | Away_1_vx | Away_1_vy | Away_1_speed | Away_20_vx | Away_20_vy | Away_20_speed | Away_4_vx | Away_4_vy | Away_4_speed | Away_5_vx | Away_5_vy | Away_5_speed | Away_14_vx | Away_14_vy | Away_14_speed | Away_7_vx | Away_7_vy | Away_7_speed | Away_23_vx | Away_23_vy | Away_23_speed | Away_8_vx | Away_8_vy | Away_8_speed | Away_37_vx | Away_37_vy | Away_37_speed | Away_27_vx | Away_27_vy | Away_27_speed | Away_42_vx | Away_42_vy | Away_42_speed | Away_10_vx | Away_10_vy | Away_10_speed | Away_11_vx | Away_11_vy | Away_11_speed | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0.00 | dead | 31 | 0.04 | 0.09 | -0.93 | 9.31 | NaN | NaN | -50.43 | 0.12 | NaN | NaN | -10.17 | -19.71 | NaN | NaN | -16.67 | -16.05 | NaN | NaN | -13.94 | 9.96 | NaN | NaN | 0.49 | -10.92 | NaN | NaN | -3.18 | 10.89 | NaN | NaN | -15.10 | -0.99 | NaN | NaN | -8.98 | 0.43 | NaN | NaN | -0.99 | -20.88 | NaN | NaN | -14.75 | -27.43 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | 1 | 1 | 0.04 | alive | 31 | 0.83 | 0.09 | -0.89 | 9.25 | NaN | NaN | -50.42 | 0.12 | NaN | NaN | -10.13 | -19.70 | NaN | NaN | -16.68 | -16.06 | NaN | NaN | -13.94 | 9.96 | NaN | NaN | 0.59 | -10.86 | NaN | NaN | -3.19 | 10.91 | NaN | NaN | -15.13 | -0.99 | NaN | NaN | -8.95 | 0.44 | NaN | NaN | -0.99 | -20.83 | NaN | NaN | -14.75 | -27.42 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.00 | -1.50 | 1.802776 | 0.25 | 0.00 | 0.250000 | 1.00 | 0.25 | 1.030776 | -0.25 | -0.25 | 0.353553 | 0.00 | 0.00 | 0.000000 | 2.50 | 1.50 | 2.915476 | -0.25 | 0.50 | 0.559017 | -0.75 | 0.00 | 0.750000 | 0.75 | 0.25 | 0.790569 | 0.00 | 1.25 | 1.250000 | 0.00 | 0.25 | 0.250000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2 | 2 | 1 | 0.08 | alive | 31 | 1.34 | 0.09 | -0.84 | 9.19 | NaN | NaN | -50.43 | 0.13 | NaN | NaN | -10.09 | -19.69 | NaN | NaN | -16.69 | -16.07 | NaN | NaN | -13.93 | 9.99 | NaN | NaN | 0.70 | -10.80 | NaN | NaN | -3.20 | 10.94 | NaN | NaN | -15.16 | -0.99 | NaN | NaN | -8.92 | 0.44 | NaN | NaN | -1.00 | -20.78 | NaN | NaN | -14.76 | -27.42 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.25 | -1.50 | 1.952562 | -0.25 | 0.25 | 0.353553 | 1.00 | 0.25 | 1.030776 | -0.25 | -0.25 | 0.353553 | 0.25 | 0.75 | 0.790569 | 2.75 | 1.50 | 3.132491 | -0.25 | 0.75 | 0.790569 | -0.75 | 0.00 | 0.750000 | 0.75 | 0.00 | 0.750000 | -0.25 | 1.25 | 1.274755 | -0.25 | 0.00 | 0.250000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
3 | 3 | 1 | 0.12 | alive | 31 | 2.03 | 0.10 | -0.77 | 9.12 | NaN | NaN | -50.42 | 0.13 | NaN | NaN | -10.05 | -19.67 | NaN | NaN | -16.69 | -16.09 | NaN | NaN | -13.92 | 10.02 | NaN | NaN | 0.80 | -10.75 | NaN | NaN | -3.22 | 10.96 | NaN | NaN | -15.16 | -1.00 | NaN | NaN | -8.88 | 0.44 | NaN | NaN | -1.02 | -20.73 | NaN | NaN | -14.76 | -27.41 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.75 | -1.75 | 2.474874 | 0.25 | 0.00 | 0.250000 | 1.00 | 0.50 | 1.118034 | 0.00 | -0.50 | 0.500000 | 0.25 | 0.75 | 0.790569 | 2.50 | 1.25 | 2.795085 | -0.50 | 0.50 | 0.707107 | 0.00 | -0.25 | 0.250000 | 1.00 | 0.00 | 1.000000 | -0.50 | 1.25 | 1.346291 | 0.00 | 0.25 | 0.250000 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
4 | 4 | 1 | 0.16 | alive | 31 | 2.85 | 0.11 | -0.70 | 9.04 | NaN | NaN | -50.42 | 0.13 | NaN | NaN | -10.02 | -19.65 | NaN | NaN | -16.70 | -16.09 | NaN | NaN | -13.93 | 10.04 | NaN | NaN | 0.91 | -10.69 | NaN | NaN | -3.22 | 11.00 | NaN | NaN | -15.20 | -1.01 | NaN | NaN | -8.91 | 0.45 | NaN | NaN | -1.04 | -20.69 | NaN | NaN | -14.78 | -27.40 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.75 | -2.00 | 2.657536 | 0.00 | 0.00 | 0.000000 | 0.75 | 0.50 | 0.901388 | -0.25 | 0.00 | 0.250000 | -0.25 | 0.50 | 0.559017 | 2.75 | 1.50 | 3.132491 | 0.00 | 1.00 | 1.000000 | -1.00 | -0.25 | 1.030776 | -0.75 | 0.25 | 0.790569 | -0.50 | 1.00 | 1.118034 | -0.50 | 0.25 | 0.559017 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
Exploring the df_tracking_home
and df_tracking_away
DataFrames, we can see the additional columns for velocity have been added to the DataFrame. These columns are of the following, but for all the players:
Away_101668_vx
Away_101668_vy
Away_101668_speed
i.e. total speedThese new metrics are all measured in meters per second.
Reverse direction of a player so that home team is alway attacking right->left i.e. no switch at half time
# Reverse direction of a player so that home team is alway attacking right->left i.e. no switch at half time
df_tracking_home_cry_lei, df_tracking_away_cry_lei = to_single_playing_direction(df_tracking_home_cry_lei, df_tracking_away_cry_lei)
df_tracking_home_cry_bri, df_tracking_away_cry_bri = to_single_playing_direction(df_tracking_home_cry_bri, df_tracking_away_cry_bri)
# Export DataFrame
## 03/10/2021: Crystal Palace (2) vs. (2) Leicester City (g2210334)
df_tracking_home_cry_lei.to_csv(os.path.join(data_dir_second_spectrum, 'engineered', 'data', 'g2210334_SecondSpectrum_Trackingdata_Home.csv'), index=None, header=True)
df_tracking_away_cry_lei.to_csv(os.path.join(data_dir_second_spectrum, 'engineered', 'data', 'g2210334_SecondSpectrum_Trackingdata_Away.csv'), index=None, header=True)
## 27/09/2021: Crystal Palace (1) vs. (1) Brighton & Hove Albion (g2210324)
df_tracking_home_cry_bri.to_csv(os.path.join(data_dir_second_spectrum, 'engineered', 'data', 'g2210324_SecondSpectrum_Trackingdata_Home.csv'), index=None, header=True)
df_tracking_away_cry_bri.to_csv(os.path.join(data_dir_second_spectrum, 'engineered', 'data', 'g2210324_SecondSpectrum_Trackingdata_Away.csv'), index=None, header=True)
This notebook parses and engineers Second Spectrum Tracking data using pandas, Kloppy
, and a custom library of function, based on Laurie Shaw's Metrica Sports Tracking data library LaurieOnTracking
, that has been modified to be compatible with Second Spectrum Tracking data, which is a different format.
Kloppy
*Visit my website eddwebster.com or my GitHub Repository for more projects. If you'd like to get in contact, my Twitter handle is @eddwebster and my email is: edd.j.webster@gmail.com.*