{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Load in the Pitch() function from the mlpsoccer module to avoid having to build your own football pitch in a plot\n", "\n", "from mplsoccer.pitch import Pitch\n", "\n", "# pandas and numpy modules are loaded as pd and np to help with data handling\n", "\n", "import pandas as pd\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Using pandas' read_csv() function you can read in the snippets of tracking data'\n", "# The function need you to write r'PATH TO YOUR DATA'\n", "\n", "home = pd.read_csv(r'C:\\\\Users\\\\YOUR_USER\\\\Desktop\\\\Github\\\\DFDA\\\\Data\\\\Tracking Data\\\\home.csv')\n", "\n", "away = pd.read_csv(r'C:\\\\Users\\\\YOUR_USER\\\\Desktop\\\\Github\\\\DFDA\\\\Data\\\\Tracking Data\\\\away.csv')\n", "\n", "info = pd.read_csv(r'C:\\\\Users\\\\YOUR_USER\\\\Desktop\\\\Github\\\\DFDA\\\\Data\\\\Tracking Data\\\\info.csv')\n", "\n", "# Again using head() you can take a look at the data\n", "home.head()" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# You can select a series of frames by first using unique() and then [:x] to select x number of frames\n", "frames = info.frameIdx.unique()[:1]\n", "frames" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Subsetting the full dataset to only include the select frames is done by the isin() function\n", "\n", "homePlot = home[home['frameIdx'].isin(frames)]\n", "awayPlot = away[away['frameIdx'].isin(frames)]\n", "ballPlot = info[info['frameIdx'].isin(frames)]\n", "\n", "# To allow for an easier annotation of shirt numbers later you can extract the x, y, and shirtnumber column from each team\n", "\n", "homeNumbers=homePlot[[\"x\",\"y\",\"number\",\"team\"]]\n", "awayNumbers=awayPlot[[\"x\",\"y\",\"number\",\"team\"]]\n", "\n", "# And then combine them, one data set after the other by using pandas' concat() function\n", "\n", "numbers = pd.concat([homeNumbers, awayNumbers], ignore_index=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# To understand how the code below adds shirt numbers to the plot - a breif description of loops are required\n", "# If you already understand loops just skip to the plot\n", "\n", "# You can make python go through certain things one step at a time by using loops\n", "# The following loop prints all the shirt numbers in the numbers data frame one at a time\n", "\n", "for i in range(0,len(numbers)):\n", " print(numbers.team[i],numbers.number[i], \"- row\", i+1, \"done\") \n", " # This prints the team, the shirt number and the row number+1 (as python starts counting from 0)\n", " \n", "# It might go faster than you can notice, but the code does run one row at a time" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "scrolled": false }, "outputs": [], "source": [ "# If you have chosen a single frame, the following will plot that frame\n", "# If you have chosen multiple frames, the plot will include all frames and be useless\n", "\n", "# First start by plotting a pitch, this time using second spectrum coordinates and setting the length and width of the pitch\n", "\n", "pitch = Pitch(pitch_type='secondspectrum', # Using second spectrum to fit with tracking data\n", " pitch_width=68, pitch_length=105, # Setting pitch size to danish standard\n", " goal_type='box', # Add \"nets\" to goals\n", " pitch_color='grass', # Setting the color to be grass \n", " line_color='white', # Setting the lines on the pitch to be white\n", " stripe=True, # Add stripes to the grass color\n", " corner_arcs=True, # Add corner arcs\n", " )\n", "\n", "fig, ax = pitch.draw(figsize=(16,10.4))\n", "\n", "# Then for each team plot the players by using a pitch.scatter() function\n", "\n", "scatter = pitch.scatter(homePlot.x, # x coordinate\n", " homePlot.y, # y coordinate\n", " ax=ax, # What plot to add them to \n", " edgecolor='black', # Edge color of the point\n", " c=homePlot.GK, # Color of the point\n", " s=250, # Size of the points\n", " cmap='autumn' # Color map - field players will be red and keeper yellow\n", " )\n", "\n", "scatter = pitch.scatter(awayPlot.x, # x coordinate\n", " awayPlot.y, # y coordinate\n", " ax=ax, # What plot to add them to \n", " edgecolor='black', # Edge color of the point\n", " c=awayPlot.GK, # Color of the point\n", " s=250, # Size of the points\n", " cmap='Set2' #Color map - field players will be green and keeper grey\n", " )\n", "\n", "\n", "# Add the ball first at a larger size, then a smaller size to create a point inside a point\n", "\n", "scatter = pitch.scatter(ballPlot.xBall, # x coordinate\n", " ballPlot.yBall, # y coordinate\n", " ax=ax, # What plot to add them to \n", " edgecolor='black', # Edge color of the point\n", " c='black', # Color of the point\n", " s=100 # Size of the points\n", " )\n", "\n", "scatter = pitch.scatter(ballPlot.xBall, # x coordinate\n", " ballPlot.yBall, # y coordinate\n", " ax=ax, # What plot to add them to \n", " edgecolor='black', # Edge color of the point\n", " c='white', # Color of the point\n", " s=40 # Size of the points\n", " )\n", "\n", "\n", "# Lastly loop over all the rows in the numbers dataframe and annotate each player with his shirt number\n", "\n", "for i in range(0,len(numbers)): # Loops from 0 to the number of rows - remember that python starts counting at 0\n", " annotate = pitch.annotate(text=numbers.number[i], # Which text is printed - here the shirtnumber of row number i\n", " xy=(numbers.x[i],numbers.y[i]), # Where to print it - here x,y of row number i\n", " ax=ax, # What plot to add them to \n", " va='center', # Align the text at vertical center of x,y\n", " ha='center' # Align the text at horizontal center of x,y\n", " )" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# To move from a single plot to an animation you need to choose a series of frames\n", "# The snippet variable has been created to split the data into segments each containing a goal - so you can subset on that\n", "# Or you can choose a series of frames like above - or subset based on the game clock by looking at info.gameClock\n", "\n", "# To plot it faster, you can plot every second frame - hence you can use [::2]\n", "\n", "frames = info.loc[info[\"snippet\"]==1].frameIdx.unique()[::2]\n", "\n", "df_ball=info[info['frameIdx'].isin(frames)]\n", "df_home=home[home['frameIdx'].isin(frames)]\n", "df_away=away[away['frameIdx'].isin(frames)]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# The function which creates and saves the plot are located in the Jupyter Notebook folder\n", "# To some extend it follows the ideas already used to plot a single frame\n", "\n", "from functions import save_match_clip\n", "\n", "# Saving the animation to your locals folder - might take a while depending on the number of frames!\n", "# Creators run time was 8+ minutes for all frames in snippet 1\n", "\n", "save_match_clip(df_ball, df_home, df_away, fpath=\"C:\\\\Users\\\\YOUR_USER\\\\Desktop\\\\Github\\\\DFDA\\\\Local\", fname='Clip', frames_per_second=12.5)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.3" }, "vscode": { "interpreter": { "hash": "697d41c126625c3517a4890c41e0bb959a0eeddb5d944aeb5ca46094479b57e8" } } }, "nbformat": 4, "nbformat_minor": 4 }