{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name": "FastWideOpenSpaces.ipynb", "provenance": [], "collapsed_sections": [], "toc_visible": true, "machine_shape": "hm" }, "kernelspec": { "name": "python3", "display_name": "Python 3" }, "accelerator": "GPU" }, "cells": [ { "cell_type": "markdown", "metadata": { "id": "KkFlDuzYFaja", "colab_type": "text" }, "source": [ "##Import data and convert locations to numpy arrays with dimensions (players,frames,2)." ] }, { "cell_type": "code", "metadata": { "id": "rrTirNum9zsa", "colab_type": "code", "colab": {} }, "source": [ "import pandas as pd\n", "import numpy as np\n", "import torch\n", "\n", "away_data = pd.read_csv('https://raw.githubusercontent.com/metrica-sports/sample-data/master/data/Sample_Game_1/Sample_Game_1_RawTrackingData_Away_Team.csv', skiprows=2)\n", "home_data = pd.read_csv('https://raw.githubusercontent.com/metrica-sports/sample-data/master/data/Sample_Game_1/Sample_Game_1_RawTrackingData_Home_Team.csv', skiprows=2)\n", "\n", "locs_home = np.array([np.asarray(home_data.iloc[:,range(3 + j*2,3 + j*2 +2)]) for j in range(14)]) * np.array([105,68])\n", "locs_away = np.array([np.asarray(away_data.iloc[:,range(3 + j*2,3 + j*2 +2)]) for j in range(14)]) * np.array([105,68])\n", "locs_ball = np.asarray(home_data.iloc[:,range(31,33)]) * np.array([105,68])\n", "tt = home_data['Time [s]']\n", "event_data = pd.read_csv('https://raw.githubusercontent.com/metrica-sports/sample-data/master/data/Sample_Game_1/Sample_Game_1_RawEventsData.csv')" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "QaQ4hNPUE5_B", "colab_type": "text" }, "source": [ "## Pre-compute quantities required for pitch-control\n", "\n", "Precompute required pitch control quantities for all frames simultaneously. Mostly correspond to quantities in appendix of paper, should be clear from variable names what corresponds to what." ] }, { "cell_type": "code", "metadata": { "id": "hooGYdpOmnmz", "colab_type": "code", "colab": {} }, "source": [ "jitter = 1e-12 # to avoid division by zero when players are standing still\n", "\n", "# GPU versions of data\n", "xy_home = torch.Tensor(locs_home).cuda()\n", "xy_away = torch.Tensor(locs_away).cuda()\n", "xy_ball = torch.Tensor(locs_ball).cuda()\n", "ttt = torch.Tensor(tt).cuda()\n", "# x & y velocity components\n", "dt = ttt[1:] - ttt[:-1]\n", "sxy_home = (xy_home[:,1:,:] - xy_home[:,:-1,:])/dt[:,None] + jitter\n", "sxy_away = (xy_away[:,1:,:] - xy_away[:,:-1,:])/dt[:,None] + jitter\n", "# velocities\n", "s_home = torch.sqrt(torch.sum(sxy_home**2,2))\n", "s_away = torch.sqrt(torch.sum(sxy_away**2,2))\n", "# angles of travel\n", "theta_home = torch.acos(sxy_home[:,:,0] / s_home)\n", "theta_away = torch.acos(sxy_away[:,:,0] / s_away)\n", "# means for player influence functions\n", "mu_home = xy_home[:,:-1,:] + 0.5*sxy_home\n", "mu_away = xy_away[:,:-1,:] + 0.5*sxy_away\n", "# proportion of max. speed\n", "Srat_home = torch.min((s_home / 13.0)**2,torch.Tensor([1]).cuda())\n", "Srat_away = torch.min((s_away / 13.0)**2,torch.Tensor([1]).cuda())\n", "# influence radius\n", "Ri_home = torch.min(4 + torch.sqrt(torch.sum((xy_ball - xy_home)**2,2))**3 / 972,torch.Tensor([10]).cuda())\n", "Ri_away = torch.min(4 + torch.sqrt(torch.sum((xy_ball - xy_away)**2,2))**3 / 972,torch.Tensor([10]).cuda())\n", "# inverses of covariance matrices -- Sigma^{-1} = RS^{-1}S^{-1}R^T. only need RS^{-1} to evaluate gaussian.\n", "RSinv_home = torch.Tensor(s_home.shape[0],s_home.shape[1],2,2).cuda()\n", "RSinv_away = torch.Tensor(s_home.shape[0],s_home.shape[1],2,2).cuda()\n", "\n", "S1_home = 2 / ((1+Srat_home) * Ri_home[:,:-1])\n", "S2_home = 2 / ((1-Srat_home) * Ri_home[:,:-1])\n", "S1_away = 2 / ((1+Srat_away) * Ri_away[:,:-1])\n", "S2_away = 2 / ((1-Srat_away) * Ri_away[:,:-1])\n", "\n", "RSinv_home[:,:,0,0] = S1_home * torch.cos(theta_home)\n", "RSinv_home[:,:,1,0] = S1_home * torch.sin(theta_home)\n", "RSinv_home[:,:,0,1] = - S2_home * torch.sin(theta_home)\n", "RSinv_home[:,:,1,1] = S2_home * torch.cos(theta_home)\n", "\n", "RSinv_away[:,:,0,0] = S1_away * torch.cos(theta_away)\n", "RSinv_away[:,:,1,0] = S1_away * torch.sin(theta_away)\n", "RSinv_away[:,:,0,1] = - S2_away * torch.sin(theta_away)\n", "RSinv_away[:,:,1,1] = S2_away * torch.cos(theta_away)\n", "# denominators for individual player influence functions (see eq 1 in paper). Note the normalising factors for the multivariate normal distns (eq 12) \n", "#cancel, so don't need to bother computing them.\n", "denominators_h = torch.exp(-0.5 * torch.sum(((xy_home[:,:-1,None,:] - mu_home[:,:,None,:]).matmul(RSinv_home))**2,-1))\n", "denominators_a = torch.exp(-0.5 * torch.sum(((xy_away[:,:-1,None,:] - mu_away[:,:,None,:]).matmul(RSinv_away))**2,-1))\n", "\n", "# set up query points for evaluating pitch control\n", "n_grid_points_x = 50\n", "n_grid_points_y = 30\n", "xy_query = torch.stack([torch.linspace(0,105,n_grid_points_x).cuda().repeat(n_grid_points_y),torch.repeat_interleave(torch.linspace(0,68,n_grid_points_y).cuda(),n_grid_points_x)],1)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "z6ddJqZx61gO", "colab_type": "text" }, "source": [ "Now we can compute the pitch control at the query points for whichever frames we care about. There might be a memory error if you use a finer grid of query points, but we can process the whole match under the current settings. If there's a memory error, try reducing the batch size." ] }, { "cell_type": "markdown", "metadata": { "id": "03_jqJz_Cv6a", "colab_type": "text" }, "source": [ "# Standard Wide Open Spaces implementation" ] }, { "cell_type": "code", "metadata": { "id": "5Rpb-5z5Gntb", "colab_type": "code", "colab": {} }, "source": [ "# specify frames of interest\n", "first_frame = 0\n", "n_frames = sxy_home.shape[1]\n", "\n", "# add some dimensions to query array for broadcasting purposes\n", "xyq = xy_query[None,None,:,:]\n", "pitch_control = torch.Tensor(n_frames,xy_query.shape[0]).cuda()\n", "#batch_size sets number of frames to be processed at once. decrease if there's a cuda memory error.\n", "batch_size = 2000\n", "for f in range(int(n_frames/batch_size) + 1):\n", " # subtract means from query points\n", " xminmu_h = mu_home[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),None,:] - xyq\n", " # multiply (mu - x) obtained above by RS^{-1}\n", " mm_h = xminmu_h.matmul(RSinv_home[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),:,:])\n", " infl_h = torch.exp(-0.5 * torch.sum(mm_h**2,-1))\n", " infl_h = infl_h / denominators_h[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),:]\n", " xminmu_a = mu_away[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),None,:] - xyq\n", " mm_a = xminmu_a.matmul(RSinv_away[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),:,:])\n", " infl_a = torch.exp(-0.5 * torch.sum(mm_a**2,-1))\n", " infl_a = infl_a / denominators_a[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),:]\n", " isnan_h = torch.isnan(infl_h)\n", " isnan_a = torch.isnan(infl_a)\n", " infl_h[isnan_h] = 0\n", " infl_a[isnan_a] = 0\n", " pitch_control[(f*batch_size):(np.minimum((f+1)*batch_size,int(n_frames))),:] = torch.sigmoid(torch.sum(infl_h,0) - torch.sum(infl_a,0))\n" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "Y-YuLSL6DAXz", "colab_type": "text" }, "source": [ "# Modified Wide Open Spaces\n", " - Includes pitch control per player\n", " - Gives more control of distant empty areas to one team or the other, rather than sharing it between the two teams." ] }, { "cell_type": "code", "metadata": { "id": "k2zY4evDZCaE", "colab_type": "code", "colab": {} }, "source": [ "# specify frames of interest\n", "first_frame = 0\n", "n_frames = sxy_home.shape[1]\n", "return_pcpp = False\n", "\n", "# add some dimensions to query array for broadcasting purposes\n", "xyq = xy_query[None,None,:,:]\n", "pitch_control = torch.Tensor(n_frames,xy_query.shape[0]).cuda()\n", "if return_pcpp:\n", " pcpp = torch.Tensor(28,n_frames,xy_query.shape[0]).cuda()\n", "#batch_size sets number of frames to be processed at once. decrease if there's a cuda memory error.\n", "batch_size = 1000\n", "for f in range(int(n_frames/batch_size) + 1):\n", " # subtract means from query points\n", " xminmu_h = mu_home[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),None,:] - xyq\n", " # multiply (mu - x) obtained above by RS^{-1}\n", " mm_h = xminmu_h.matmul(RSinv_home[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),:,:])\n", " infl_h = torch.exp(-0.5 * torch.sum(mm_h**2,-1))\n", " infl_h = infl_h / denominators_h[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),:]\n", " xminmu_a = mu_away[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),None,:] - xyq\n", " mm_a = xminmu_a.matmul(RSinv_away[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),:,:])\n", " infl_a = torch.exp(-0.5 * torch.sum(mm_a**2,-1))\n", " infl_a = infl_a / denominators_a[:,(first_frame + f*batch_size):(np.minimum(first_frame + (f+1)*batch_size,int(first_frame + n_frames))),:]\n", " isnan_h = torch.isnan(infl_h)\n", " isnan_a = torch.isnan(infl_a)\n", " infl_h[isnan_h] = 0\n", " infl_a[isnan_a] = 0\n", " ## rather than putting influence functions through a sigmoid function, just set individual player's control over a location to be\n", " ## their proportion of the total influence at that location.\n", " pc = torch.cat([infl_h,infl_a]) / torch.sum(torch.cat([infl_h,infl_a]),0)\n", " if return_pcpp:\n", " pcpp[:,(f*batch_size):(np.minimum((f+1)*batch_size,int(n_frames))),:] = pc\n", " ## the home team's control over a location is then just the sum of this new per-player control over all players from the home team.\n", " pitch_control[(f*batch_size):(np.minimum((f+1)*batch_size,int(n_frames))),:] = torch.sum(pc[0:14],0)\n" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "tRZ2dsxsSZ9n", "colab_type": "text" }, "source": [ "# Optional post-processing to increase resolution\n", "\n", "Optionally, you can increase the resolution using bicubic interpolation. You might lose a bit of accuracy, but it's a lot faster than computing pitch control explicitly on a finer grid. Again, you might need to play with the batch size to avoid memory errors if you push the resolution higher." ] }, { "cell_type": "code", "metadata": { "id": "hpq8NumkSZUs", "colab_type": "code", "colab": {} }, "source": [ "pc = pitch_control.reshape(pitch_control.shape[0],n_grid_points_y,n_grid_points_x)\n", "\n", "#upsample resolution to 105x68\n", "n_interp_x = 105\n", "n_interp_y = 68\n", "#pre-allocate tensor containing upsampled pitch control maps\n", "pc_int = torch.Tensor(pc.shape[0],1,n_interp_y,n_interp_x)\n", "\n", "batch_size = 20000\n", "for f in range(int(n_frames/batch_size) + 1):\n", " pc_int[(f*batch_size):(np.minimum((f+1)*batch_size,int(n_frames)))] = torch.nn.functional.interpolate(\n", " pc[(f*batch_size):(np.minimum((f+1)*batch_size,int(n_frames))),None,:,:],\n", " size=(n_interp_y,n_interp_x),\n", " mode='bicubic')" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "1N5jvftdKRuR", "colab_type": "text" }, "source": [ "# Adding the value layer\n", "\n", " - multplying the pitch control surface by an expected threat surface weights pitch control in an area by the probability of scoring within 5 actions if the ball is controlled there." ] }, { "cell_type": "code", "metadata": { "id": "RBVDkviuK03G", "colab_type": "code", "colab": { "base_uri": "https://localhost:8080/", "height": 72 }, "outputId": "884ecc4f-a388-49be-a404-f2db22534e82" }, "source": [ "import pickle\n", "xTMap = torch.tensor(pickle.load(open('xTMap.p','rb'))).cuda()\n", "xTMap_interp = torch.nn.functional.interpolate(xTMap[None,None,:,:],(n_grid_points_y,n_grid_points_x),mode='bilinear')\n", "xTflipped = torch.flip(xTMap_interp[0,0],[0,1])\n", "expected_threat = xTMap_interp[0,0].reshape((1,-1))\n", "expected_threat_away = xTflipped.reshape((1,-1))\n", "\n", "second_half_start_frame = event_data.loc[list(event_data.loc[:,'Period']).index(2),'Start Frame']\n", "\n", "# flip direction of play in second half, so home team is always playing left to right\n", "pitch_control[second_half_start_frame:] = torch.flip(pitch_control[second_half_start_frame:],[0,1])\n", "\n", "passes_only = event_data[event_data.Type == 'PASS']\n", "team_passes = np.array(passes_only.Team)\n", "poss_change_idx = np.where(team_passes[:-1] != team_passes[1:])[0]\n", "poss_change_frames = np.r_[0,np.array(passes_only.iloc[poss_change_idx+1,4])-1,pitch_control.shape[0]-1]\n", "\n", "first_team = team_passes[0]\n", "cur_team = 1 if first_team == 'Away' else 0\n", "\n", "team_in_poss = torch.zeros(pitch_control.shape[0]).cuda()\n", "\n", "for j in range(len(poss_change_frames)-1):\n", " team_in_poss[poss_change_frames[j]:poss_change_frames[j+1]] = cur_team\n", " cur_team = (cur_team + 1)%2\n", "\n", "xTweights = expected_threat * (1 - team_in_poss)[:,None] + expected_threat_away * team_in_poss[:,None]\n", "\n", "pitch_control_possession = team_in_poss[:,None] *(team_in_poss[:,None] - pitch_control) - (team_in_poss[:,None] - 1) * pitch_control\n", "\n", "weighted_pitch_control = pitch_control_possession * xTweights" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "/usr/local/lib/python3.6/dist-packages/torch/nn/functional.py:2973: UserWarning: Default upsampling behavior when mode=bilinear is changed to align_corners=False since 0.4.0. Please specify align_corners=True if the old behavior is desired. See the documentation of nn.Upsample for details.\n", " \"See the documentation of nn.Upsample for details.\".format(mode))\n" ], "name": "stderr" } ] }, { "cell_type": "markdown", "metadata": { "id": "LuEGMPff7krq", "colab_type": "text" }, "source": [ "# Adding the decision/transition layer" ] }, { "cell_type": "code", "metadata": { "id": "Hp9_r3z07ueq", "colab_type": "code", "colab": {} }, "source": [ "transition_layer = torch.exp( - torch.sqrt(torch.sum((xy_query[None,:,:] - xy_ball[:-1,None,:]) ** 2,axis = 2)) / (2*14))\n", "transition_layer = transition_layer / transition_layer.sum(1,True)\n", "weighted_pitch_control = weighted_pitch_control * transition_layer\n", "weighted_pitch_control = torch.max(weighted_pitch_control,torch.zeros_like(weighted_pitch_control))" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "nZOCJw6-D7xS", "colab_type": "text" }, "source": [ "# Plotting the results\n", " - Allows you to make short mp4 clips of pitch control for short passages of play or plot single frames with plotly." ] }, { "cell_type": "markdown", "metadata": { "id": "3CIfQ_ehduYe", "colab_type": "text" }, "source": [ "Here's a way to plot the results using matplotlib. We need to install Tom Decroos's matplotsoccer first. I've basically copied Rob Hickman's ggplot version of this as far as design goes." ] }, { "cell_type": "code", "metadata": { "id": "gSFm7NjrcoBI", "colab_type": "code", "colab": { "base_uri": "https://localhost:8080/", "height": 193 }, "outputId": "5f49a229-7a4a-4d3d-e2d7-51eff674cf0c" }, "source": [ "!pip install matplotsoccer" ], "execution_count": null, "outputs": [ { "output_type": "stream", "text": [ "Collecting matplotsoccer\n", " Downloading https://files.pythonhosted.org/packages/71/27/fbe1ee8008fd03186cfa888b42cb776f675f1a2b1efd255c01b85925dd44/matplotsoccer-0.0.8.tar.gz\n", "Building wheels for collected packages: matplotsoccer\n", " Building wheel for matplotsoccer (setup.py) ... \u001b[?25l\u001b[?25hdone\n", " Created wheel for matplotsoccer: filename=matplotsoccer-0.0.8-cp36-none-any.whl size=5984 sha256=61350904fb17ac6716c250bfa180d56b9ec2a14745b848f60f8a8a3ffa8c29ca\n", " Stored in directory: /root/.cache/pip/wheels/69/af/8d/ee61635d6f863657abe8cd0c22622c408a4b980d5af1974f1f\n", "Successfully built matplotsoccer\n", "Installing collected packages: matplotsoccer\n", "Successfully installed matplotsoccer-0.0.8\n" ], "name": "stdout" } ] }, { "cell_type": "code", "metadata": { "id": "RyjwBq5ohLR5", "colab_type": "code", "colab": { "base_uri": "https://localhost:8080/", "height": 540 }, "outputId": "58234205-219a-4448-eb40-1b8c0cea06dd" }, "source": [ "import matplotlib.pyplot as plt\n", "import matplotlib.animation\n", "from matplotsoccer import field\n", "from IPython.core.display import HTML\n", "\n", "xx = np.linspace(0,105,n_grid_points_x)\n", "yy = np.linspace(0,68,n_grid_points_y)\n", "\n", "locs_ball_reduced = locs_ball[first_frame:(first_frame + n_frames),:]\n", "locs_home_reduced = locs_home[:,first_frame:(first_frame + n_frames),:]\n", "locs_away_reduced = locs_away[:,first_frame:(first_frame + n_frames),:]\n", "\n", "first_frame_to_plot = 24000\n", "n_frames_to_plot = 500\n", "\n", "fig, ax=plt.subplots()\n", "field(ax=ax,show = False)\n", "ball_points = ax.scatter(locs_ball_reduced[first_frame_to_plot,0],locs_ball_reduced[first_frame_to_plot,1],color = 'black',zorder = 15, s = 16)\n", "ball_points2 = ax.scatter(locs_ball_reduced[first_frame_to_plot,0],locs_ball_reduced[first_frame_to_plot,1],color = 'white',zorder = 15, s = 9)\n", "home_points = ax.scatter(locs_home_reduced[:,first_frame_to_plot,0],locs_home_reduced[:,first_frame_to_plot,1],color = 'red',zorder = 10)\n", "away_points = ax.scatter(locs_away_reduced[:,first_frame_to_plot,0],locs_away_reduced[:,first_frame_to_plot,1],color = 'blue',zorder = 10)\n", "p = [ax.contourf(xx,\n", " yy,\n", " pitch_control[first_frame_to_plot].reshape(n_grid_points_y,n_grid_points_x).cpu(),\n", " extent = (0,105,0,68),\n", " levels = np.linspace(0,1,100),\n", " cmap = 'coolwarm')]\n", "\n", "def update(i):\n", " fr = i + first_frame_to_plot\n", " for tp in p[0].collections:\n", " tp.remove()\n", " p[0] = ax.contourf(xx,\n", " yy,\n", " pitch_control[fr].reshape(n_grid_points_y,n_grid_points_x).cpu(),\n", " extent = (0,105,0,68),\n", " levels = np.linspace(0,1,100),\n", " cmap = 'coolwarm')\n", " ball_points.set_offsets(np.c_[[locs_ball[fr,0]],[locs_ball[fr,1]]])\n", " ball_points2.set_offsets(np.c_[[locs_ball[fr,0]],[locs_ball[fr,1]]])\n", " home_points.set_offsets(np.c_[locs_home[:,fr,0],locs_home[:,fr,1]])\n", " away_points.set_offsets(np.c_[locs_away[:,fr,0],locs_away[:,fr,1]])\n", " return p[0].collections + [ball_points,home_points,away_points]\n", "\n", "ani = matplotlib.animation.FuncAnimation(fig, update, frames=n_frames_to_plot, \n", " interval=40, blit=True, repeat=False)\n", "HTML(ani.to_html5_video())" ], "execution_count": null, "outputs": [ { "output_type": "execute_result", "data": { "text/html": [ "" ], "text/plain": [ "" ] }, "metadata": { "tags": [] }, "execution_count": 70 }, { "output_type": "display_data", "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "tags": [], "needs_background": "light" } } ] }, { "cell_type": "markdown", "metadata": { "id": "8s4JUVs97KxU", "colab_type": "text" }, "source": [ "Here's a plot of the results for a single frame. Pretty much just to try out plotly. Almost definitely not the best way to do it, but there are maybe nice interactive things you could build on top of it." ] }, { "cell_type": "code", "metadata": { "id": "1xbUSaOUVnrT", "colab_type": "code", "colab": { "base_uri": "https://localhost:8080/", "height": 542 }, "outputId": "b45d4201-ff59-4900-8f91-a9019cec2ce3" }, "source": [ "import plotly.graph_objects as go\n", "xx = np.linspace(0,105,n_grid_points_x)\n", "yy = np.linspace(0,68,n_grid_points_y)\n", "\n", "frame_to_plot = 2154\n", "\n", "pl_ocean=[[0, '#ff9900'],\n", "[0.25, '#ffcc66'],\n", "[0.5, '#FFFFFF'],\n", "[0.75, '#9999ff'],\n", "[1, '#6666ff']]\n", "\n", "fig = go.Figure(go.Contour(x=xx, y=yy,z = pitch_control[frame_to_plot].reshape(n_grid_points_y,n_grid_points_x).cpu().numpy(),\n", " colorscale = pl_ocean,\n", " contours_coloring='heatmap',\n", " contours = dict(start=0, \n", " end=1, \n", " size=0.1,\n", " showlines=False),\n", " line_width = 0))\n", "\n", "fig.add_trace(go.Scatter(x=locs_home[:,first_frame + frame_to_plot,0], y=locs_home[:,first_frame + frame_to_plot,1],\n", " mode='markers',\n", " showlegend = False,\n", " marker = dict(color = '#000066',\n", " size = 10)))\n", "\n", "fig.add_trace(go.Scatter(x=locs_away[:,first_frame + frame_to_plot,0], y=locs_away[:,first_frame + frame_to_plot,1],\n", " mode='markers',\n", " showlegend = False,\n", " marker = dict(color = '#b34700',\n", " size = 10)))\n", "\n", "fig.add_trace(go.Scatter(x=locs_ball[[first_frame + frame_to_plot],0], y=locs_ball[[first_frame + frame_to_plot],1],\n", " mode='markers',\n", " showlegend = False,\n", " marker = dict(color = 'black',\n", " size = 10)))\n", "\n", "fig.add_trace(go.Scatter(x=locs_ball[[first_frame + frame_to_plot],0], y=locs_ball[[first_frame + frame_to_plot],1],\n", " mode='markers',\n", " showlegend = False,\n", " marker = dict(color = 'white',\n", " size = 5)))\n", "fig.update_layout(title=\"Pitch Control\",\n", " plot_bgcolor = 'white')\n", "fig.update_xaxes(showgrid=False, zeroline=False,showticklabels=False)\n", "fig.update_yaxes(showgrid=False, zeroline=False,showticklabels=False)\n", "fig.show()" ], "execution_count": null, "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", " \n", " \n", " \n", "
\n", " \n", "
\n", "\n", "" ] }, "metadata": { "tags": [] } } ] }, { "cell_type": "markdown", "metadata": { "id": "jDFM5Fd7FvYv", "colab_type": "text" }, "source": [ "The following cell plots the pitch control for a single player in a single frame -- you need to have set return_pcpp to True in the modified wide open spaces cell above to get the pitch control per player." ] }, { "cell_type": "code", "metadata": { "id": "YTh1LV_r2Vi0", "colab_type": "code", "colab": { "base_uri": "https://localhost:8080/", "height": 17 }, "outputId": "a86dcd6c-9516-4668-9330-6f149b3a0392" }, "source": [ "import plotly.graph_objects as go\n", "xx = np.linspace(0,105,n_grid_points_x)\n", "yy = np.linspace(0,68,n_grid_points_y)\n", "\n", "frame_to_plot = 110\n", "player_to_plot = 18\n", "\n", "pl_ocean=[[0, '#ff9900'],\n", "[0.25, '#ffcc66'],\n", "[0.5, '#FFFFFF'],\n", "[0.75, '#9999ff'],\n", "[1, '#6666ff']]\n", "\n", "fig = go.Figure(go.Contour(x=xx, y=yy,z = pcpp[player_to_plot,frame_to_plot].reshape(n_grid_points_y,n_grid_points_x).cpu().numpy(),\n", " colorscale = pl_ocean,\n", " contours_coloring='heatmap',\n", " contours = dict(start=0, \n", " end=1, \n", " size=0.1,\n", " showlines=False),\n", " line_width = 0))\n", "\n", "fig.add_trace(go.Scatter(x=locs_home[:,first_frame + frame_to_plot,0], y=locs_home[:,first_frame + frame_to_plot,1],\n", " mode='markers',\n", " showlegend = False,\n", " marker = dict(color = '#000066',\n", " size = 10)))\n", "\n", "fig.add_trace(go.Scatter(x=locs_away[:,first_frame + frame_to_plot,0], y=locs_away[:,first_frame + frame_to_plot,1],\n", " mode='markers',\n", " showlegend = False,\n", " marker = dict(color = '#b34700',\n", " size = 10)))\n", "\n", "fig.add_trace(go.Scatter(x=locs_ball[[first_frame + frame_to_plot],0], y=locs_ball[[first_frame + frame_to_plot],1],\n", " mode='markers',\n", " showlegend = False,\n", " marker = dict(color = 'black',\n", " size = 10)))\n", "\n", "fig.add_trace(go.Scatter(x=locs_ball[[first_frame + frame_to_plot],0], y=locs_ball[[first_frame + frame_to_plot],1],\n", " mode='markers',\n", " showlegend = False,\n", " marker = dict(color = 'white',\n", " size = 5)))\n", "fig.update_layout(title=\"Pitch Control\",\n", " plot_bgcolor = 'white')\n", "fig.update_xaxes(showgrid=False, zeroline=False,showticklabels=False)\n", "fig.update_yaxes(showgrid=False, zeroline=False,showticklabels=False)\n", "fig.show()" ], "execution_count": null, "outputs": [ { "output_type": "display_data", "data": { "text/html": [ "\n", "\n", "\n", "
\n", " \n", " \n", " \n", "
\n", " \n", "
\n", "\n", "" ] }, "metadata": { "tags": [] } } ] } ] }