{"id":420,"date":"2019-01-31T07:33:29","date_gmt":"2019-01-31T07:33:29","guid":{"rendered":"https:\/\/fcpython.com\/?p=420"},"modified":"2020-12-18T20:08:55","modified_gmt":"2020-12-18T20:08:55","slug":"convex-hulls-football-python","status":"publish","type":"post","link":"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python","title":{"rendered":"Convex Hulls for Football in Python"},"content":{"rendered":"<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Building on what you can do with event data from the <a href=\"https:\/\/fcpython.com\/blog\/parsing-opta-f24-files-introduction-xml-python\">Opta (or any other) event feed<\/a>, we&#8217;re going to look at one way of visualising a team&#8217;s defensive actions. Popularised in the football analytics community by Thom Lawrence (please let us know if we should add anyone else!), convex hulls display the smallest area needed to cover a set of points:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<blockquote class=\"twitter-tweet\">\n<p dir=\"ltr\" lang=\"en\">Been a while since I did some of these, but behold: <a href=\"https:\/\/twitter.com\/hashtag\/USMNT?src=hash&amp;ref_src=twsrc%5Etfw\">#USMNT<\/a> 0-2 Colombia. US asking for trouble on their left. <a href=\"https:\/\/t.co\/JnpqlnkelR\">pic.twitter.com\/JnpqlnkelR<\/a><\/p>\n<p>\u2014 Thom Lawrence \u00f0\u0178\u008d\u2039\u00f0\u0178\u2018\u20ac (@lemonwatcher) <a href=\"https:\/\/twitter.com\/lemonwatcher\/status\/740340965249024000?ref_src=twsrc%5Etfw\">June 8, 2016<\/a><\/p><\/blockquote>\n<p><script async src=\"https:\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script><\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>In this tutorial, we&#8217;re going to go through selecting and preparing our data to create these, before plotting the hull. We&#8217;ll then apply this to a for loop to chart each player together to see where a team is being forced to defend.<\/p>\n<p>For this article, we&#8217;ll be making use of the ConvexHull tools within the <a href=\"https:\/\/docs.scipy.org\/doc\/scipy\/reference\/index.html\">Scipy<\/a> module. The wider module is a phenomenal resource for more complex maths needs in Python, so give it a look if you&#8217;re interested.<\/p>\n<p>Outside of ConvexHull, we&#8217;ll need pandas and numpy for importing and manipulating data, while Matplotlib will plot our data. Let&#8217;s import them and get started:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In\u00a0[1]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython3\">\n<pre><span class=\"kn\">from<\/span> <span class=\"nn\">scipy.spatial<\/span> <span class=\"k\">import<\/span> <span class=\"n\">ConvexHull<\/span>\r\n\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">pandas<\/span> <span class=\"k\">as<\/span> <span class=\"nn\">pd<\/span>\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">numpy<\/span> <span class=\"k\">as<\/span> <span class=\"nn\">np<\/span>\r\n\r\n<span class=\"kn\">import<\/span> <span class=\"nn\">matplotlib.pyplot<\/span> <span class=\"k\">as<\/span> <span class=\"nn\">plt<\/span>\r\n<span class=\"kn\">from<\/span> <span class=\"nn\">matplotlib.patches<\/span> <span class=\"k\">import<\/span> <span class=\"n\">Arc<\/span>\r\n\r\n\r\n<span class=\"o\">%<\/span><span class=\"k\">matplotlib<\/span> inline\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>With the modules ready, we&#8217;re going to import our data. For this example, our data contains all defensive actions in one match, split by player and team.<\/p>\n<p>Let&#8217;s take a look at how it is structured with .head():<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In\u00a0[2]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython3\">\n<pre><span class=\"n\">defdata<\/span> <span class=\"o\">=<\/span> <span class=\"n\">pd<\/span><span class=\"o\">.<\/span><span class=\"n\">read_csv<\/span><span class=\"p\">(<\/span><span class=\"s2\">\"def_table.csv\"<\/span><span class=\"p\">)<\/span>\r\n<span class=\"n\">defdata<\/span><span class=\"o\">.<\/span><span class=\"n\">head<\/span><span class=\"p\">()<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt output_prompt\">Out[2]:<\/div>\n<div class=\"output_html rendered_html output_subarea output_execute_result\">\n<div>\n<style>\n    .dataframe thead tr:only-child th {<br \/>        text-align: right;<br \/>    }<\/p>\n<p>    .dataframe thead th {<br \/>        text-align: left;<br \/>    }<\/p>\n<p>    .dataframe tbody tr th {<br \/>        vertical-align: top;<br \/>    }<br \/><\/style>\n<table class=\"dataframe\" border=\"1\">\n<thead>\n<tr style=\"text-align: right;\">\n<th><\/th>\n<th>player<\/th>\n<th>team<\/th>\n<th>minute<\/th>\n<th>x<\/th>\n<th>y<\/th>\n<th>outcome<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<th>0<\/th>\n<td>50471<\/td>\n<td>Team A<\/td>\n<td>1<\/td>\n<td>38.9<\/td>\n<td>31.8<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>1<\/th>\n<td>19197<\/td>\n<td>Team A<\/td>\n<td>6<\/td>\n<td>52.6<\/td>\n<td>68.4<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>2<\/th>\n<td>42593<\/td>\n<td>Team B<\/td>\n<td>6<\/td>\n<td>39.8<\/td>\n<td>83.9<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>3<\/th>\n<td>19188<\/td>\n<td>Team A<\/td>\n<td>7<\/td>\n<td>3.5<\/td>\n<td>37.9<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>4<\/th>\n<td>82403<\/td>\n<td>Team A<\/td>\n<td>8<\/td>\n<td>17.9<\/td>\n<td>98.5<\/td>\n<td>1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>So each row is a defensive action, and we can see the x\/y coordinates and who did it.<\/p>\n<p>We just want one player&#8217;s actions, so we&#8217;ll create a new dataframe for the first player ID &#8211; 50471:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In\u00a0[3]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython3\">\n<pre><span class=\"n\">player50471<\/span> <span class=\"o\">=<\/span> <span class=\"n\">defdata<\/span><span class=\"o\">.<\/span><span class=\"n\">loc<\/span><span class=\"p\">[(<\/span><span class=\"n\">defdata<\/span><span class=\"p\">[<\/span><span class=\"s1\">'player'<\/span><span class=\"p\">]<\/span> <span class=\"o\">==<\/span> <span class=\"mi\">50471<\/span><span class=\"p\">)]<\/span>\r\n\r\n<span class=\"n\">player50471<\/span><span class=\"o\">.<\/span><span class=\"n\">head<\/span><span class=\"p\">()<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt output_prompt\">Out[3]:<\/div>\n<div class=\"output_html rendered_html output_subarea output_execute_result\">\n<div>\n<style>\n    .dataframe thead tr:only-child th {<br \/>        text-align: right;<br \/>    }<\/p>\n<p>    .dataframe thead th {<br \/>        text-align: left;<br \/>    }<\/p>\n<p>    .dataframe tbody tr th {<br \/>        vertical-align: top;<br \/>    }<br \/><\/style>\n<table class=\"dataframe\" border=\"1\">\n<thead>\n<tr style=\"text-align: right;\">\n<th><\/th>\n<th>player<\/th>\n<th>team<\/th>\n<th>minute<\/th>\n<th>x<\/th>\n<th>y<\/th>\n<th>outcome<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<th>0<\/th>\n<td>50471<\/td>\n<td>Team A<\/td>\n<td>1<\/td>\n<td>38.9<\/td>\n<td>31.8<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>12<\/th>\n<td>50471<\/td>\n<td>Team A<\/td>\n<td>22<\/td>\n<td>30.0<\/td>\n<td>33.2<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>13<\/th>\n<td>50471<\/td>\n<td>Team A<\/td>\n<td>25<\/td>\n<td>64.7<\/td>\n<td>94.9<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>51<\/th>\n<td>50471<\/td>\n<td>Team A<\/td>\n<td>65<\/td>\n<td>31.2<\/td>\n<td>32.2<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>56<\/th>\n<td>50471<\/td>\n<td>Team A<\/td>\n<td>72<\/td>\n<td>46.5<\/td>\n<td>22.6<\/td>\n<td>1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>To create a convex hull, we need to build it from a list of coordinates. We have our coordinates in the dataframe already, but need them to look something close to the below:<\/p>\n<p>(38.9, 31.8), (30.0, 33.2), (64.7, 94.9) and so on&#8230;<\/p>\n<p>Thanks to the pandas module, this is made easy by adding .values to the end of the data that we want to see in arrays, rather than columns:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In\u00a0[4]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython3\">\n<pre><span class=\"n\">defpoints<\/span> <span class=\"o\">=<\/span> <span class=\"n\">player50471<\/span><span class=\"p\">[[<\/span><span class=\"s1\">'x'<\/span><span class=\"p\">,<\/span> <span class=\"s1\">'y'<\/span><span class=\"p\">]]<\/span><span class=\"o\">.<\/span><span class=\"n\">values<\/span>\r\n\r\n<span class=\"n\">defpoints<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt output_prompt\">Out[4]:<\/div>\n<div class=\"output_text output_subarea output_execute_result\">\n<pre>array([[38.9, 31.8],\r\n       [30. , 33.2],\r\n       [64.7, 94.9],\r\n       [31.2, 32.2],\r\n       [46.5, 22.6],\r\n       [30.3, 49.8],\r\n       [22.9, 92.5]])<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Our data is now ready to be used to create our convex hull. By itself, it is actually pretty boring &#8211; it simply creates an object that does nothing at all by itself. Let&#8217;s see how this is done below:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In\u00a0[5]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython3\">\n<pre><span class=\"c1\">#Create a convex hull object and assign it to the variable hull<\/span>\r\n<span class=\"n\">hull<\/span> <span class=\"o\">=<\/span> <span class=\"n\">ConvexHull<\/span><span class=\"p\">(<\/span><span class=\"n\">player50471<\/span><span class=\"p\">[[<\/span><span class=\"s1\">'x'<\/span><span class=\"p\">,<\/span><span class=\"s1\">'y'<\/span><span class=\"p\">]])<\/span>\r\n\r\n<span class=\"c1\">#Display hull<\/span>\r\n<span class=\"n\">hull<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt output_prompt\">Out[5]:<\/div>\n<div class=\"output_text output_subarea output_execute_result\">\n<pre>&lt;scipy.spatial.qhull.ConvexHull at 0x1faa0c96dd8&gt;<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>See, that is pretty boring. But we can make it so much cooler when we plot the hull onto a chart.<\/p>\n<p>Let&#8217;s start by plotting all 7 event locations as dots on a scatter chart:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In\u00a0[6]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython3\">\n<pre><span class=\"c1\">#Plot the X &amp; Y location with dots<\/span>\r\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">plot<\/span><span class=\"p\">(<\/span><span class=\"n\">player50471<\/span><span class=\"o\">.<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"n\">player50471<\/span><span class=\"o\">.<\/span><span class=\"n\">y<\/span><span class=\"p\">,<\/span> <span class=\"s1\">'o'<\/span><span class=\"p\">)<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt output_prompt\">Out[6]:<\/div>\n<div class=\"output_text output_subarea output_execute_result\">\n<pre>[&lt;matplotlib.lines.Line2D at 0x1faa2d10908&gt;]<\/pre>\n<\/div>\n<\/div>\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"aligncenter wp-image-425\" src=\"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image1.png\" alt=\"Basic Scatter Plot\" width=\"393\" height=\"261\" srcset=\"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image1.png 595w, https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image1-300x199.png 300w, https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image1-272x182.png 272w\" sizes=\"(max-width: 393px) 100vw, 393px\" \/><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Next up, we&#8217;re going to add lines around the most extreme parts of the plot. These most extreme parts are stored in a part of the hull object called simplices. We can just use a for loop to iterate through the simplices and draw lines between them:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In\u00a0[7]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython3\">\n<pre><span class=\"c1\">#Plot the X &amp; Y location with dots<\/span>\r\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">plot<\/span><span class=\"p\">(<\/span><span class=\"n\">player50471<\/span><span class=\"o\">.<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"n\">player50471<\/span><span class=\"o\">.<\/span><span class=\"n\">y<\/span><span class=\"p\">,<\/span> <span class=\"s1\">'o'<\/span><span class=\"p\">)<\/span>\r\n\r\n<span class=\"c1\">#Loop through each of the hull's simplices<\/span>\r\n<span class=\"k\">for<\/span> <span class=\"n\">simplex<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">hull<\/span><span class=\"o\">.<\/span><span class=\"n\">simplices<\/span><span class=\"p\">:<\/span>\r\n    <span class=\"c1\">#Draw a black line between each<\/span>\r\n    <span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">plot<\/span><span class=\"p\">(<\/span><span class=\"n\">defpoints<\/span><span class=\"p\">[<\/span><span class=\"n\">simplex<\/span><span class=\"p\">,<\/span> <span class=\"mi\">0<\/span><span class=\"p\">],<\/span> <span class=\"n\">defpoints<\/span><span class=\"p\">[<\/span><span class=\"n\">simplex<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">],<\/span> <span class=\"s1\">'k-'<\/span><span class=\"p\">)<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"wp-image-426 aligncenter\" src=\"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image2.png\" alt=\"Convex Hull around Plots\" width=\"368\" height=\"241\" srcset=\"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image2.png 600w, https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image2-300x197.png 300w\" sizes=\"(max-width: 368px) 100vw, 368px\" \/><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Looks kind of abstract, but a lot more interesting than the hull object on its own!<\/p>\n<p>Let&#8217;s just add in some shading to make our area even clearer. We&#8217;ll also make it 30% transparent with the alpha argument:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In\u00a0[8]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython3\">\n<pre><span class=\"c1\">#Plot the X &amp; Y location with dots<\/span>\r\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">plot<\/span><span class=\"p\">(<\/span><span class=\"n\">player50471<\/span><span class=\"o\">.<\/span><span class=\"n\">x<\/span><span class=\"p\">,<\/span><span class=\"n\">player50471<\/span><span class=\"o\">.<\/span><span class=\"n\">y<\/span><span class=\"p\">,<\/span> <span class=\"s1\">'o'<\/span><span class=\"p\">)<\/span>\r\n\r\n<span class=\"c1\">#Loop through each of the hull's simplices<\/span>\r\n<span class=\"k\">for<\/span> <span class=\"n\">simplex<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">hull<\/span><span class=\"o\">.<\/span><span class=\"n\">simplices<\/span><span class=\"p\">:<\/span>\r\n    <span class=\"c1\">#Draw a black line between each<\/span>\r\n    <span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">plot<\/span><span class=\"p\">(<\/span><span class=\"n\">defpoints<\/span><span class=\"p\">[<\/span><span class=\"n\">simplex<\/span><span class=\"p\">,<\/span> <span class=\"mi\">0<\/span><span class=\"p\">],<\/span> <span class=\"n\">defpoints<\/span><span class=\"p\">[<\/span><span class=\"n\">simplex<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">],<\/span> <span class=\"s1\">'k-'<\/span><span class=\"p\">)<\/span>\r\n    \r\n<span class=\"c1\">#Fill the area within the lines that we have drawn<\/span>\r\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">fill<\/span><span class=\"p\">(<\/span><span class=\"n\">defpoints<\/span><span class=\"p\">[<\/span><span class=\"n\">hull<\/span><span class=\"o\">.<\/span><span class=\"n\">vertices<\/span><span class=\"p\">,<\/span><span class=\"mi\">0<\/span><span class=\"p\">],<\/span> <span class=\"n\">defpoints<\/span><span class=\"p\">[<\/span><span class=\"n\">hull<\/span><span class=\"o\">.<\/span><span class=\"n\">vertices<\/span><span class=\"p\">,<\/span><span class=\"mi\">1<\/span><span class=\"p\">],<\/span> <span class=\"s1\">'k'<\/span><span class=\"p\">,<\/span> <span class=\"n\">alpha<\/span><span class=\"o\">=<\/span><span class=\"mf\">0.3<\/span><span class=\"p\">)<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt output_prompt\">Out[8]:<\/div>\n<div class=\"output_text output_subarea output_execute_result\">\n<pre>[&lt;matplotlib.patches.Polygon at 0x1faa2f1bb70&gt;]<\/pre>\n<\/div>\n<\/div>\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<p><img decoding=\"async\" loading=\"lazy\" class=\" wp-image-427 aligncenter\" src=\"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image3.png\" alt=\"Shaded Convex Hull\" width=\"390\" height=\"260\" srcset=\"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image3.png 590w, https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image3-300x200.png 300w, https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image3-272x182.png 272w\" sizes=\"(max-width: 390px) 100vw, 390px\" \/><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Perfect, we have one player&#8217;s zone of defensive actions plotted. We don&#8217;t have a pitch or any other players on there yet, but this is great work!<\/p>\n<p>Let&#8217;s work on a bigger project now &#8211; let&#8217;s do all of this over and over for a whole team. We&#8217;ll take a single team out of our dataset, then use for loops\u00a0to create the plot for each player (exactly as above) before plotting them together.<\/p>\n<p>First up, let&#8217;s extract Team B into one dataframe:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In\u00a0[9]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython3\">\n<pre><span class=\"n\">TeamB<\/span> <span class=\"o\">=<\/span> <span class=\"n\">defdata<\/span><span class=\"o\">.<\/span><span class=\"n\">loc<\/span><span class=\"p\">[(<\/span><span class=\"n\">defdata<\/span><span class=\"o\">.<\/span><span class=\"n\">team<\/span> <span class=\"o\">==<\/span> <span class=\"s2\">\"Team B\"<\/span><span class=\"p\">)]<\/span>\r\n<span class=\"n\">TeamB<\/span><span class=\"o\">.<\/span><span class=\"n\">head<\/span><span class=\"p\">()<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt output_prompt\">Out[9]:<\/div>\n<div class=\"output_html rendered_html output_subarea output_execute_result\">\n<div>\n<style>\n    .dataframe thead tr:only-child th {<br \/>        text-align: right;<br \/>    }<\/p>\n<p>    .dataframe thead th {<br \/>        text-align: left;<br \/>    }<\/p>\n<p>    .dataframe tbody tr th {<br \/>        vertical-align: top;<br \/>    }<br \/><\/style>\n<table class=\"dataframe\" border=\"1\">\n<thead>\n<tr style=\"text-align: right;\">\n<th><\/th>\n<th>player<\/th>\n<th>team<\/th>\n<th>minute<\/th>\n<th>x<\/th>\n<th>y<\/th>\n<th>outcome<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<th>2<\/th>\n<td>42593<\/td>\n<td>Team B<\/td>\n<td>6<\/td>\n<td>39.8<\/td>\n<td>83.9<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>5<\/th>\n<td>42593<\/td>\n<td>Team B<\/td>\n<td>8<\/td>\n<td>44.7<\/td>\n<td>91.5<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>6<\/th>\n<td>17476<\/td>\n<td>Team B<\/td>\n<td>12<\/td>\n<td>23.1<\/td>\n<td>1.3<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>8<\/th>\n<td>57112<\/td>\n<td>Team B<\/td>\n<td>17<\/td>\n<td>4.4<\/td>\n<td>57.7<\/td>\n<td>1<\/td>\n<\/tr>\n<tr>\n<th>9<\/th>\n<td>42593<\/td>\n<td>Team B<\/td>\n<td>17<\/td>\n<td>5.8<\/td>\n<td>58.9<\/td>\n<td>1<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Perfect, just as before, but with different players on a single team.<\/p>\n<p>We&#8217;ll now need to go through each player and do exactly what we did to plot just a single player. First up, we need to find out who we are dealing with. We can use .unique() to pool each individual into the variable &#8216;players&#8217;:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In\u00a0[10]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython3\">\n<pre><span class=\"n\">players<\/span> <span class=\"o\">=<\/span> <span class=\"n\">TeamB<\/span><span class=\"p\">[<\/span><span class=\"s2\">\"player\"<\/span><span class=\"p\">]<\/span><span class=\"o\">.<\/span><span class=\"n\">unique<\/span><span class=\"p\">()<\/span>\r\n<span class=\"n\">players<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt output_prompt\">Out[10]:<\/div>\n<div class=\"output_text output_subarea output_execute_result\">\n<pre>array([42593, 17476, 57112, 27789, 14664, 61366, 37748, 57001, 28554,\r\n       17740], dtype=int64)<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Every player now just needs to go into a for loop, where we&#8217;ll do exactly what we did before to get a plot. We&#8217;ll create a temporary dataframe for each player, create a hull from the x\/y coordinates, then plot the lines and fill in the shape with a transparent colour. Let&#8217;s take a look with the help of some comments:<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing code_cell rendered\">\n<div class=\"input\">\n<div class=\"prompt input_prompt\">In\u00a0[11]:<\/div>\n<div class=\"inner_cell\">\n<div class=\"input_area\">\n<div class=\" highlight hl-ipython3\">\n<pre><span class=\"c1\">#For each player in our players variable<\/span>\r\n<span class=\"k\">for<\/span> <span class=\"n\">player<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">players<\/span><span class=\"p\">:<\/span>\r\n    \r\n    <span class=\"c1\">#Create a new dataframe for the player<\/span>\r\n    <span class=\"n\">df<\/span> <span class=\"o\">=<\/span> <span class=\"n\">TeamB<\/span><span class=\"p\">[(<\/span><span class=\"n\">TeamB<\/span><span class=\"o\">.<\/span><span class=\"n\">player<\/span> <span class=\"o\">==<\/span> <span class=\"n\">player<\/span><span class=\"p\">)]<\/span>\r\n    \r\n    <span class=\"c1\">#Create an array of the x\/y coordinate groups<\/span>\r\n    <span class=\"n\">points<\/span> <span class=\"o\">=<\/span> <span class=\"n\">df<\/span><span class=\"p\">[[<\/span><span class=\"s1\">'x'<\/span><span class=\"p\">,<\/span> <span class=\"s1\">'y'<\/span><span class=\"p\">]]<\/span><span class=\"o\">.<\/span><span class=\"n\">values<\/span>\r\n\r\n    <span class=\"c1\">#If there are enough points for a hull, create it. If there's an error, forget about it<\/span>\r\n    <span class=\"k\">try<\/span><span class=\"p\">:<\/span>\r\n        <span class=\"n\">hull<\/span> <span class=\"o\">=<\/span> <span class=\"n\">ConvexHull<\/span><span class=\"p\">(<\/span><span class=\"n\">df<\/span><span class=\"p\">[[<\/span><span class=\"s1\">'x'<\/span><span class=\"p\">,<\/span><span class=\"s1\">'y'<\/span><span class=\"p\">]])<\/span>\r\n        \r\n    <span class=\"k\">except<\/span><span class=\"p\">:<\/span>\r\n        <span class=\"k\">pass<\/span>\r\n    \r\n    <span class=\"c1\">#If we created the hull, draw the lines and fill with 5% transparent red. If there's an error, forget about it<\/span>\r\n    <span class=\"k\">try<\/span><span class=\"p\">:<\/span>     \r\n        <span class=\"k\">for<\/span> <span class=\"n\">simplex<\/span> <span class=\"ow\">in<\/span> <span class=\"n\">hull<\/span><span class=\"o\">.<\/span><span class=\"n\">simplices<\/span><span class=\"p\">:<\/span>\r\n            <span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">plot<\/span><span class=\"p\">(<\/span><span class=\"n\">points<\/span><span class=\"p\">[<\/span><span class=\"n\">simplex<\/span><span class=\"p\">,<\/span> <span class=\"mi\">0<\/span><span class=\"p\">],<\/span> <span class=\"n\">points<\/span><span class=\"p\">[<\/span><span class=\"n\">simplex<\/span><span class=\"p\">,<\/span> <span class=\"mi\">1<\/span><span class=\"p\">],<\/span> <span class=\"s1\">'k-'<\/span><span class=\"p\">)<\/span>\r\n            <span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">fill<\/span><span class=\"p\">(<\/span><span class=\"n\">points<\/span><span class=\"p\">[<\/span><span class=\"n\">hull<\/span><span class=\"o\">.<\/span><span class=\"n\">vertices<\/span><span class=\"p\">,<\/span><span class=\"mi\">0<\/span><span class=\"p\">],<\/span> <span class=\"n\">points<\/span><span class=\"p\">[<\/span><span class=\"n\">hull<\/span><span class=\"o\">.<\/span><span class=\"n\">vertices<\/span><span class=\"p\">,<\/span><span class=\"mi\">1<\/span><span class=\"p\">],<\/span> <span class=\"s1\">'red'<\/span><span class=\"p\">,<\/span> <span class=\"n\">alpha<\/span><span class=\"o\">=<\/span><span class=\"mf\">0.05<\/span><span class=\"p\">)<\/span>\r\n                       \r\n    <span class=\"k\">except<\/span><span class=\"p\">:<\/span>\r\n        <span class=\"k\">pass<\/span>\r\n    \r\n<span class=\"c1\">#Once all of the individual hulls have been created, plot them together<\/span>\r\n<span class=\"n\">plt<\/span><span class=\"o\">.<\/span><span class=\"n\">show<\/span><span class=\"p\">()<\/span>\r\n<\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"output_wrapper\">\n<div class=\"output\">\n<div class=\"output_area\">\n<div class=\"prompt\"><\/div>\n<p><img decoding=\"async\" loading=\"lazy\" class=\" wp-image-428 aligncenter\" src=\"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image4.png\" alt=\"Multiple Shaded Convex Hulls\" width=\"382\" height=\"249\" srcset=\"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image4.png 588w, https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image4-300x195.png 300w\" sizes=\"(max-width: 382px) 100vw, 382px\" \/><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"cell border-box-sizing text_cell rendered\">\n<div class=\"prompt input_prompt\"><\/div>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>Fantastic work! We now have all of the players with enough data points on the chart. The transparency is a nice touch, as we can see any hidden players and where any crossover happens.<\/p>\n<p>Our plot leaves out any players with less than 2 defensive actions in the data, so you may want to plot these as lines or dots. If so, you should be able to figure out how to do this from the code already, or from our other visualisation tutorials.<\/p>\n<p>As for next steps, you might want to plot this on a pitch (pitch drawing tutorial here):<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\" wp-image-429 aligncenter\" src=\"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image5.png\" alt=\"Shaded Hulls on Pitch\" width=\"370\" height=\"262\" srcset=\"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image5.png 593w, https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image5-300x212.png 300w\" sizes=\"(max-width: 370px) 100vw, 370px\" \/><\/p>\n<div class=\"inner_cell\">\n<div class=\"text_cell_render border-box-sizing rendered_html\">\n<p>So now we can see where our team are performing their defensive actions &#8211; although remember a few players are missing. In terms of analysis, does this suggest that this team defends better on the left? Or is it more likely that they faced a team that largely attacked on that side? Visualisation is just one small piece of any analysis!<\/p>\n<h3 id=\"Summary\">Summary<\/h3>\n<p>In this tutorial, we have practiced filtering a dataframe by player or team, then using SciPy&#8217;s convex hull tool to create the data for plotting the smallest area that contains our datapoints.<\/p>\n<p>Some nice extensions to this that you may want to play with include adding some annotations for player names, or changing colours for each player. Of course, these charts aren&#8217;t limited to defensive metrics &#8211; why not take a look at penalty area entry pass zones, or compare goalkeeper distributions? However you build on this work, show us what you&#8217;re achieving on <a href=\"https:\/\/twitter.com\/FC_Python\">Twitter @FC_Python<\/a>!<\/p>\n<p>Find further <a href=\"https:\/\/fcpython.com\/python-data-visualisation\">visualisation tutorials here<\/a>!<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Building on what you can do with event data from the Opta (or any other) event feed, we&#8217;re going to look at one way&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3],"tags":[56,19,54],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v19.13 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Convex Hulls for Football in Python - FC Python<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python\" \/>\n<meta property=\"og:locale\" content=\"en_GB\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Convex Hulls for Football in Python - FC Python\" \/>\n<meta property=\"og:description\" content=\"Building on what you can do with event data from the Opta (or any other) event feed, we&#8217;re going to look at one way&hellip;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python\" \/>\n<meta property=\"og:site_name\" content=\"FC Python\" \/>\n<meta property=\"article:published_time\" content=\"2019-01-31T07:33:29+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2020-12-18T20:08:55+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image1.png\" \/>\n<meta name=\"author\" content=\"FCPythonADMIN\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@FC__Python\" \/>\n<meta name=\"twitter:site\" content=\"@FC__Python\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"FCPythonADMIN\" \/>\n\t<meta name=\"twitter:label2\" content=\"Estimated reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python#article\",\"isPartOf\":{\"@id\":\"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python\"},\"author\":{\"name\":\"FCPythonADMIN\",\"@id\":\"https:\/\/fcpython.com\/#\/schema\/person\/ed81e5728929acd0f3f2d9bf824a0bd0\"},\"headline\":\"Convex Hulls for Football in Python\",\"datePublished\":\"2019-01-31T07:33:29+00:00\",\"dateModified\":\"2020-12-18T20:08:55+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python\"},\"wordCount\":1060,\"publisher\":{\"@id\":\"https:\/\/fcpython.com\/#organization\"},\"keywords\":[\"Convex Hull\",\"matplotlib\",\"Opta\"],\"articleSection\":[\"Visualisation\"],\"inLanguage\":\"en-GB\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python\",\"url\":\"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python\",\"name\":\"Convex Hulls for Football in Python - FC Python\",\"isPartOf\":{\"@id\":\"https:\/\/fcpython.com\/#website\"},\"datePublished\":\"2019-01-31T07:33:29+00:00\",\"dateModified\":\"2020-12-18T20:08:55+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python#breadcrumb\"},\"inLanguage\":\"en-GB\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/fcpython.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Convex Hulls for Football in Python\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/fcpython.com\/#website\",\"url\":\"https:\/\/fcpython.com\/\",\"name\":\"FC Python\",\"description\":\"Learning Python through football\",\"publisher\":{\"@id\":\"https:\/\/fcpython.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/fcpython.com\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-GB\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/fcpython.com\/#organization\",\"name\":\"FC Python\",\"url\":\"https:\/\/fcpython.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-GB\",\"@id\":\"https:\/\/fcpython.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/fcpython.com\/wp-content\/uploads\/2017\/12\/Logocomp9.png\",\"contentUrl\":\"https:\/\/fcpython.com\/wp-content\/uploads\/2017\/12\/Logocomp9.png\",\"width\":981,\"height\":1049,\"caption\":\"FC Python\"},\"image\":{\"@id\":\"https:\/\/fcpython.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/twitter.com\/FC__Python\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/fcpython.com\/#\/schema\/person\/ed81e5728929acd0f3f2d9bf824a0bd0\",\"name\":\"FCPythonADMIN\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-GB\",\"@id\":\"https:\/\/fcpython.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/7a172a6f730270fc0f8bb1a8ff958895?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/7a172a6f730270fc0f8bb1a8ff958895?s=96&d=mm&r=g\",\"caption\":\"FCPythonADMIN\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Convex Hulls for Football in Python - FC Python","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python","og_locale":"en_GB","og_type":"article","og_title":"Convex Hulls for Football in Python - FC Python","og_description":"Building on what you can do with event data from the Opta (or any other) event feed, we&#8217;re going to look at one way&hellip;","og_url":"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python","og_site_name":"FC Python","article_published_time":"2019-01-31T07:33:29+00:00","article_modified_time":"2020-12-18T20:08:55+00:00","og_image":[{"url":"https:\/\/fcpython.com\/wp-content\/uploads\/2019\/01\/Image1.png"}],"author":"FCPythonADMIN","twitter_card":"summary_large_image","twitter_creator":"@FC__Python","twitter_site":"@FC__Python","twitter_misc":{"Written by":"FCPythonADMIN","Estimated reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python#article","isPartOf":{"@id":"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python"},"author":{"name":"FCPythonADMIN","@id":"https:\/\/fcpython.com\/#\/schema\/person\/ed81e5728929acd0f3f2d9bf824a0bd0"},"headline":"Convex Hulls for Football in Python","datePublished":"2019-01-31T07:33:29+00:00","dateModified":"2020-12-18T20:08:55+00:00","mainEntityOfPage":{"@id":"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python"},"wordCount":1060,"publisher":{"@id":"https:\/\/fcpython.com\/#organization"},"keywords":["Convex Hull","matplotlib","Opta"],"articleSection":["Visualisation"],"inLanguage":"en-GB"},{"@type":"WebPage","@id":"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python","url":"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python","name":"Convex Hulls for Football in Python - FC Python","isPartOf":{"@id":"https:\/\/fcpython.com\/#website"},"datePublished":"2019-01-31T07:33:29+00:00","dateModified":"2020-12-18T20:08:55+00:00","breadcrumb":{"@id":"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python#breadcrumb"},"inLanguage":"en-GB","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/fcpython.com\/visualisation\/convex-hulls-football-python#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/fcpython.com\/"},{"@type":"ListItem","position":2,"name":"Convex Hulls for Football in Python"}]},{"@type":"WebSite","@id":"https:\/\/fcpython.com\/#website","url":"https:\/\/fcpython.com\/","name":"FC Python","description":"Learning Python through football","publisher":{"@id":"https:\/\/fcpython.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fcpython.com\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-GB"},{"@type":"Organization","@id":"https:\/\/fcpython.com\/#organization","name":"FC Python","url":"https:\/\/fcpython.com\/","logo":{"@type":"ImageObject","inLanguage":"en-GB","@id":"https:\/\/fcpython.com\/#\/schema\/logo\/image\/","url":"https:\/\/fcpython.com\/wp-content\/uploads\/2017\/12\/Logocomp9.png","contentUrl":"https:\/\/fcpython.com\/wp-content\/uploads\/2017\/12\/Logocomp9.png","width":981,"height":1049,"caption":"FC Python"},"image":{"@id":"https:\/\/fcpython.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/twitter.com\/FC__Python"]},{"@type":"Person","@id":"https:\/\/fcpython.com\/#\/schema\/person\/ed81e5728929acd0f3f2d9bf824a0bd0","name":"FCPythonADMIN","image":{"@type":"ImageObject","inLanguage":"en-GB","@id":"https:\/\/fcpython.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/7a172a6f730270fc0f8bb1a8ff958895?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/7a172a6f730270fc0f8bb1a8ff958895?s=96&d=mm&r=g","caption":"FCPythonADMIN"}}]}},"_links":{"self":[{"href":"https:\/\/fcpython.com\/wp-json\/wp\/v2\/posts\/420"}],"collection":[{"href":"https:\/\/fcpython.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/fcpython.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/fcpython.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/fcpython.com\/wp-json\/wp\/v2\/comments?post=420"}],"version-history":[{"count":11,"href":"https:\/\/fcpython.com\/wp-json\/wp\/v2\/posts\/420\/revisions"}],"predecessor-version":[{"id":441,"href":"https:\/\/fcpython.com\/wp-json\/wp\/v2\/posts\/420\/revisions\/441"}],"wp:attachment":[{"href":"https:\/\/fcpython.com\/wp-json\/wp\/v2\/media?parent=420"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/fcpython.com\/wp-json\/wp\/v2\/categories?post=420"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/fcpython.com\/wp-json\/wp\/v2\/tags?post=420"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}