<p><div><div><div><div data-show-faces="false" data-height="21" data-width="100" data-layout="button_count" data-send="true" data-href="https://www.r-bloggers.com/another-prediction-for-the-fifa-world-cup-2018/"></div></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://www.mango-solutions.com/blog/another-prediction-of-fifa-world-cup-2018"> Mango Solutions</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<p> <img sizes="100vw" src="https://i1.wp.com/www.mango-solutions.com/blog/images/2/c/3/a/4/2c3a4c02aafb7249a204c2db97052a8677cc9e96-fifa-wc-blog.png?w=456&ssl=1" data-recalc-dims="1"></p>
<h5>Ava Yang, Data Scientist</h5>
<p>Given that the UEFA Champion League final a few weeks ago between Real Madrid and Liverpool is the only match I’ve watched properly in over ten years, how dare I presume I can guess that Brazil is going to lift the trophy in the 2018 FIFA World Cup? Well, here goes…</p>
<p>By the way, if you find the below dry to read, it is because of my limited natural language on the subject matter…data science tricks to the rescue!</p>
<blockquote>
<p>This blogpost is largely based on the prediction framework from an eRum 2018 talk by Claus Thorn Ekstrøm. For first hand materials please take a look at the <a rel="nofollow" href="http://www.biostatistics.dk/talks/eRum2018/#1" target="_blank">slides</a>, <a rel="nofollow" href="https://www.youtube.com/watch?v=urJ1obHPsV8" target="_blank">video</a> and <a rel="nofollow" href="https://www.mango-solutions.com/blog/another-prediction-of-fifa-world-cup-2018/github.com/ekstroem/socceR2018" target="_blank">code</a>.</p>
</blockquote>
<p>The idea is that in each simulation run of a tournament, we find team winner, runners-up, third and fourth etc. N times of simulation runs e.g. 10k returns a list of winners with highest probability to be ranked top.</p>
<p>Apart from the winner question, this post seeks to answer which team will be top scorer and how many goals will they score. After following Claus’s analysis rmarkdown file, I collected new data, put functions in a package and tried another modelling approach. Whilst the model is too simplistic to be correct, it captures the trend and is a fair starting point to add complex layers on top.</p>
<h3>Initialization</h3>
<p>To begin with, we load packages including accompnying R package <code>worldcup</code> where my utility functions reside. Package is a convenient way to share code, seal utility functions and speed up iteration. Global parameters <strong>normalgoals</strong> (the average number of goals scored in a world cup match) and <strong>nsim</strong> (number of simulations) are declared in the YAML section at the top of the RMarkdown document.</p>
<p>Next we load three datasets that have been tidied up from open source resource or updated from original version. Plenty of time was spent on gathering data, aligning team names and cleaning up features.</p>
<ul>
<li><code>team_data</code> contains features associated with team</li>
<li><code>group_match_data</code> is match schedule, public</li>
<li><code>wcmatches_train</code> is a match dataset available on <a rel="nofollow" href="https://www.kaggle.com/abecklas/fifa-world-cup/data" target="_blank">this Kaggle competition</a> and can be used as training set to estimate parameter lamda i.e. the average goals scored in a match for a single team. Records from 1994 up to 2014 are kept in the training set.</li>
</ul>
<pre><code>library(tidyverse)
library(magrittr)
devtools::load_all(&quot;worldcup&quot;)

normalgoals &lt;- params$normalgoals 
nsim &lt;- params$nsim

data(team_data) 
data(group_match_data) 
data(wcmatches_train)</code></pre>
<h3>Play game</h3>
<p>Claus proposed three working models to calculate single match outcome. The first is based on two independent poisson distributions, where two teams are treated equal and so the result is random regardless of their actual skills and talent. The second assumes the scoring event in a match are two possion events, the difference of two poisson events believed to have skellam distribution. The result turns out to be much more reliable as the parameters are estimated from actual bettings. The third one is based on <a rel="nofollow" href="https://www.eloratings.net/about" target="_blank">World Football ELO Ratings</a> rules. From current ELO ratings, we calculate expected result of one side in a match. It can be seen as the probability of success in a binomial distribution. It seems that this approach overlooked draw due to the nature of binomial distribution i.e. binary.</p>
<p>The fourth model presented here is my first attempt. To spell out: we assumed two independent poisson events, with lambdas predicted from a trained poisson model. Then predicted goal is simulated by <code>rpois</code>.</p>
<p>Model candidate each has its own function, and it is specified by the <strong>play_fun</strong> parameter and provided to higher level wrapper function <code>play_game</code>.</p>
<pre><code># Specify team Spain and Portugal
play_game(play_fun = &quot;play_fun_simplest&quot;, 
          team1 = 7, team2 = 8, 
          musthavewinner=FALSE, normalgoals = normalgoals)</code></pre>
<pre><code>##      Agoals Bgoals
## [1,]      0      1</code></pre>
<pre><code>play_game(team_data = team_data, play_fun = &quot;play_fun_skellam&quot;, 
          team1 = 7, team2 = 8, 
          musthavewinner=FALSE, normalgoals = normalgoals)</code></pre>
<pre><code>##      Agoals Bgoals
## [1,]      1      4</code></pre>
<pre><code>play_game(team_data = team_data, play_fun = &quot;play_fun_elo&quot;, 
          team1 = 7, team2 = 8)</code></pre>
<pre><code>##      Agoals Bgoals
## [1,]      0      1</code></pre>
<pre><code>play_game(team_data = team_data, train_data = wcmatches_train, 
          play_fun = &quot;play_fun_double_poisson&quot;, 
          team1 = 7, team2 = 8)</code></pre>
<pre><code>##      Agoals Bgoals
## [1,]      2      2</code></pre>
<h3>Estimate poisson mean from training</h3>
<p>Let’s have a quick look at the core of my training function. Target variable in the <code>glm</code> function is the number of goals a team obtained in a match. Predictors are FIFA and ELO ratings at a point before the 2014 tournament started. Both are popular ranking systems – the difference being that the FIFA rating is official and the latter is in the wild, adapted from chess ranking methodology.</p>
<pre><code>mod &lt;- glm(goals ~ elo + fifa_start, family = poisson(link = log), data = wcmatches_train)
broom::tidy(mod)</code></pre>
<pre><code>##          term      estimate    std.error  statistic      p.value
## 1 (Intercept) -3.5673415298 0.7934373236 -4.4960596 6.922433e-06
## 2         elo  0.0021479463 0.0005609247  3.8292949 1.285109e-04
## 3  fifa_start -0.0002296051 0.0003288228 -0.6982638 4.850123e-01</code></pre>
<p>From the model summary, the ELO rating is statistically significant whereas the FIFA rating is not. More interesting is that the estimate for the FIFA ratings variable is negative, inferring the effect is 0.9997704 relative to average. Overall, FIFA rating appears to be less predictive to the goals one may score than ELO rating. One possible reason is that ratings in 2014 alone are collected, and it may be worth future effort to go into history. Challenge to FIFA ratings’ predictive power is not <a rel="nofollow" href="https://www.sbnation.com/soccer/2017/11/16/16666012/world-cup-2018-draw-elo-rankings-fifa" target="_blank">new</a> after all.</p>
<p>Training set <strong>wcmatches_train</strong> has a <strong>home</strong> column, representing whether team X in match Y is the home team. However, it’s hard to say in a third country whether a team/away position makes much difference comparing to league competetions. Also, I didn’t find an explicit home/away split for the Russian World Cup. We could derive a similar feature – host advantage, indicating host nation or continent in future model interation. Home advantage stands no better chance for the time being.</p>
<h3>Group and kickout stages</h3>
<p>Presented below are examples showing how to find winners at various stages – from group to round 16, quarter-finals, semi-finals and final.</p>
<pre><code>find_group_winners(team_data = team_data, 
                   group_match_data = group_match_data, 
                   play_fun = &quot;play_fun_double_poisson&quot;,
                   train_data = wcmatches_train)$goals %>% 
  filter(groupRank %in% c(1,2)) %>% collect()</code></pre>
<pre><code>## Warning: package 'bindrcpp' was built under R version 3.4.4

## # A tibble: 16 x 11
##    number name         group  rating   elo fifa_start points goalsFore
##     </code></pre>