{"id":18,"date":"2012-07-18T16:21:59","date_gmt":"2012-07-18T16:21:59","guid":{"rendered":"http:\/\/opisthokonta.net\/?p=18"},"modified":"2012-07-18T17:55:12","modified_gmt":"2012-07-18T17:55:12","slug":"r-functions-for-soccer-league-tables-and-result-matrix","status":"publish","type":"post","link":"https:\/\/opisthokonta.net\/?p=18","title":{"rendered":"R functions for soccer league tables and result matrix"},"content":{"rendered":"<p>Here are three R functions i wrote to calculate ranking tables in soccer leagues based on the result of played matches. The functions are made for ordinary leagues where each team play every other team twice, one time at the home field, the other at the opposing teams home field, but the match.result() and league.table() function can be used on more general data.  <\/p>\n<p>The first function, match.results() just computes the outcome of a match (Home, Draw or Away, i.e &#8220;H&#8221;, &#8220;D&#8221; or &#8220;A&#8221;) based on number of goals scored, and is used by the other two functions. <\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n&gt; res &lt;- match.results(c(1,2,1,2,3,1,0,5), c(0,1,2,0,3,0,4,0))\r\n&gt; res\r\n[1] &quot;H&quot; &quot;H&quot; &quot;A&quot; &quot;H&quot; &quot;D&quot; &quot;H&quot; &quot;A&quot; &quot;H&quot;\r\n<\/pre>\n<p>The league.table() function returns a data.frame with some statistics for each team, such as number of wins, draws, loss (for both home and away games), goals, goal difference etc. As input it takes vectors with the name of the home team, away team, goals score by the home team and goals scored by the away team. Three points are given for a win, one point for a draw, and zero points for a loss, as is used in most leagues. If you want to compute an alternative table with a different point scheme you can just change the three variables first in the function body. The teams are ranked by the number of points awarded, but if two or more teams have the same numbero of points, they are ranked by goal difference. If the goal difference is also equal, number of goals scored is used.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n#load data from football-data.co.uk\r\nmatchdata &lt;- read.csv(&quot;premierLeague2011-11.csv&quot;)\r\nattach(matchdata)\r\nleague.table(HomeTeam, AwayTeam, FTHG, FTAG)\r\n\r\n            PLD HW HD HL AW AD AL GF GA  GD PTS\r\nMan United   38 18  1  0  5 10  4 78 37  41  80\r\nChelsea      38 14  3  2  7  5  7 69 33  36  71\r\nMan City     38 13  4  2  8  4  7 60 33  27  71\r\nArsenal      38 11  4  4  8  7  4 72 43  29  68\r\nTottenham    38  9  9  1  7  5  7 55 46   9  62\r\nLiverpool    38 12  4  3  5  3 11 59 44  15  58\r\nEverton      38  9  7  3  4  8  7 51 45   6  54\r\nFulham       38  8  7  4  3  9  7 49 43   6  49\r\nAston Villa  38  8  7  4  4  5 10 48 59 -11  48\r\nSunderland   38  7  5  7  5  6  8 45 56 -11  47\r\nWest Brom    38  8  6  5  4  5 10 56 71 -15  47\r\nNewcastle    38  6  8  5  5  5  9 56 57  -1  46\r\nStoke        38 10  4  5  3  3 13 46 48  -2  46\r\nBolton       38 10  5  4  2  5 12 52 56  -4  46\r\nBlackburn    38  7  7  5  4  3 12 46 59 -13  43\r\nWigan        38  5  8  6  4  7  8 40 61 -21  42\r\nWolves       38  8  4  7  3  3 13 46 66 -20  40\r\nBirmingham   38  6  8  5  2  7 10 37 58 -21  39\r\nBlackpool    38  5  5  9  5  4 10 55 78 -23  39\r\nWest Ham     38  5  5  9  2  7 10 43 70 -27  33\r\n\r\n<\/pre>\n<p>The last function is result.matrix(), which returns a matrix with the match results. with home teams on the rows, and away teams on the columns. The cell contents can be formated in three different ways using the format argument. By default this is set to &#8220;score&#8221; which gives the output like &#8220;2 &#8211; 1&#8221;. &#8220;HDA&#8221; gives either &#8220;A&#8221;, &#8220;D&#8221; or &#8220;H&#8221;. &#8220;difference&#8221; gives the goal difference. The diagonal consists of &#8220;NA&#8221;s.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\n#only the five first rows and columns to save space\r\nresult.matrix(m$HomeTeam, m$AwayTeam, m$FTHG, m$FTAG, format=&quot;score&quot;)[1:5,1:5]\r\n\r\n            Arsenal Aston Villa Birmingham Blackburn Blackpool\r\nArsenal     NA      &quot;1 - 2&quot;     &quot;2 - 1&quot;    &quot;0 - 0&quot;   &quot;6 - 0&quot;  \r\nAston Villa &quot;2 - 4&quot; NA          &quot;0 - 0&quot;    &quot;4 - 1&quot;   &quot;3 - 2&quot;  \r\nBirmingham  &quot;0 - 3&quot; &quot;1 - 1&quot;     NA         &quot;2 - 1&quot;   &quot;2 - 0&quot;  \r\nBlackburn   &quot;1 - 2&quot; &quot;2 - 0&quot;     &quot;1 - 1&quot;    NA        &quot;2 - 2&quot;  \r\nBlackpool   &quot;1 - 3&quot; &quot;1 - 1&quot;     &quot;1 - 2&quot;    &quot;1 - 2&quot;   NA       \r\n\r\n<\/pre>\n<p>And here is the code for the three functions.<\/p>\n<pre class=\"brush: r; title: ; notranslate\" title=\"\">\r\nmatch.results &lt;- function(homeGoals, awayGoals){\r\n  #Determines the match outcome (H, D or A) based on goals scored by home and away teams.\r\n  \r\n  home &lt;- homeGoals &gt; awayGoals\r\n  away &lt;- awayGoals &gt; homeGoals\r\n  draws &lt;- homeGoals == awayGoals\r\n    \r\n  results &lt;- character(length(homeGoals))\r\n  results[draws] &lt;- &quot;D&quot;\r\n  results[home] &lt;- &quot;H&quot;\r\n  results[away] &lt;- &quot;A&quot;\r\n\r\n  return(results)\r\n}\r\n\r\nleague.table &lt;- function(homeTeam, awayTeam, homeGoals, awayGoals){\r\n                         \r\n  #points awarded for a match outcome  \r\n  winPts &lt;- 3\r\n  drawPts &lt;- 1\r\n  loosePts &lt;- 0\r\n  \r\n  if (length(unique(sapply(list(homeTeam, awayTeam, homeGoals, awayGoals), length))) != 1 ){\r\n    warning(&quot;input vectors not of same length.&quot;)\r\n  }\r\n  \r\n  numMatches &lt;- length(homeTeam)\r\n  \r\n  teams &lt;- levels(factor(c(as.character(homeTeam), as.character(awayTeam))))\r\n  numTeams &lt;- length(teams)\r\n  \r\n  #vector with outcome of a match (H, D or A)\r\n  results &lt;- match.results(homeGoals, awayGoals)\r\n  \r\n  #for output\r\n  homeWins &lt;- numeric(numTeams)\r\n  homeDraws &lt;- numeric(numTeams)\r\n  homeLoss &lt;- numeric(numTeams)\r\n  awayWins &lt;- numeric(numTeams)\r\n  awayDraws &lt;- numeric(numTeams)\r\n  awayLoss &lt;- numeric(numTeams)\r\n  goalsFor &lt;- numeric(numTeams)\r\n  goalsAgainst &lt;- numeric(numTeams)\r\n  goalsDifference &lt;- numeric(numTeams)\r\n  playedMatches &lt;- numeric(numTeams)\r\n  pts &lt;- numeric(numTeams)\r\n\r\n  for (t in 1:numTeams) {\r\n    #mathc results for a given team\r\n    homeResults &lt;- results[homeTeam == teams[t]]\r\n    awayResults &lt;- results[awayTeam == teams[t]]\r\n\r\n    playedMatches[t] &lt;- length(homeResults) + length(awayResults)\r\n    \r\n    goalsForH &lt;- sum(homeGoals[homeTeam == teams[t]])\r\n    goalsForA &lt;- sum(awayGoals[awayTeam == teams[t]])\r\n    goalsFor[t] &lt;- goalsForA + goalsForH\r\n    goalsAgainstH &lt;- sum(awayGoals[homeTeam == teams[t]])\r\n    goalsAgainstA &lt;- sum(homeGoals[awayTeam == teams[t]])\r\n    goalsAgainst[t] &lt;- goalsAgainstA + goalsAgainstH\r\n    goalsDifference[t] &lt;- goalsFor[t] - goalsAgainst[t]\r\n    \r\n    homeWins[t] &lt;- sum(homeResults == &quot;H&quot;)\r\n    homeDraws[t] &lt;- sum(homeResults == &quot;D&quot;)\r\n    homeLoss[t] &lt;- sum(homeResults == &quot;A&quot;)\r\n    awayWins[t] &lt;- sum(awayResults == &quot;A&quot;)\r\n    awayDraws[t] &lt;- sum(awayResults == &quot;D&quot;)\r\n    awayLoss[t] &lt;- sum(awayResults == &quot;H&quot;)\r\n      \r\n    totWins &lt;- homeWins[t] + awayWins[t]\r\n    totDraws &lt;- homeDraws[t] + awayDraws[t]\r\n    totLoss &lt;- homeLoss[t] + awayLoss[t]\r\n    \r\n    pts[t] &lt;- (winPts * totWins) + (drawPts * totDraws) + (loosePts * totLoss)\r\n    \r\n    }\r\n\r\n  table &lt;- data.frame(cbind(playedMatches, homeWins, homeDraws, \r\n                            homeLoss, awayWins, awayDraws, awayLoss, \r\n                            goalsFor, goalsAgainst, goalsDifference, pts),\r\n                      row.names=teams)\r\n\r\n    \r\n  names(table) &lt;- c(&quot;PLD&quot;, &quot;HW&quot;, &quot;HD&quot;, &quot;HL&quot;, &quot;AW&quot;, &quot;AD&quot;, &quot;AL&quot;, &quot;GF&quot;, &quot;GA&quot;, &quot;GD&quot;, &quot;PTS&quot;)\r\n  ord &lt;- order(-table$PTS, -table$GD, -table$GF)\r\n  table &lt;- table[ord, ]\r\n\r\n  return(table)\r\n\r\n  }\r\n  \r\nresult.matrix &lt;- function(homeTeam, awayTeam, homeGoals, awayGoals, format=&quot;score&quot;){\r\n  \r\n  if (length(unique(sapply(list(homeTeam, awayTeam, homeGoals, awayGoals), length))) != 1 ){\r\n    warning(&quot;input vectors not of same length.&quot;)\r\n  }\r\n  \r\n  teams &lt;- levels(factor(c(as.character(homeTeam), as.character(awayTeam))))\r\n  numTeams &lt;- length(teams)\r\n  numMatches &lt;- length(homeTeam)\r\n  \r\n  if (format == &quot;HDA&quot;){\r\n    results &lt;- match.results(homeGoals, awayGoals)\r\n  }\r\n  \r\n  resultMatrix &lt;- matrix(nrow=numTeams, ncol=numTeams, dimnames=list(teams, teams))\r\n  \r\n  for (m in 1:numMatches){\r\n    \r\n    if (format == &quot;score&quot;){\r\n      cell &lt;- paste(homeGoals[m], &quot;-&quot;, awayGoals[m])\r\n      }\r\n    else if (format == &quot;HDA&quot;){\r\n      cell &lt;- results[m]\r\n    }\r\n    else if (format == &quot;difference&quot;){\r\n      cell &lt;- homeGoals[m] - awayGoals[m]\r\n    }\r\n    \r\n    resultMatrix[homeTeam[m], awayTeam[m]] &lt;- cell\r\n  }\r\n    \r\n  return(resultMatrix)\r\n  \r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Here are three R functions i wrote to calculate ranking tables in soccer leagues based on the result of played matches. The functions are made for ordinary leagues where each team play every other team twice, one time at the &hellip; <a href=\"https:\/\/opisthokonta.net\/?p=18\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5,6],"tags":[50,7,51],"class_list":["post-18","post","type-post","status-publish","format-standard","hentry","category-r","category-soccer","tag-r","tag-result-matrix","tag-soccer"],"_links":{"self":[{"href":"https:\/\/opisthokonta.net\/index.php?rest_route=\/wp\/v2\/posts\/18","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/opisthokonta.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/opisthokonta.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/opisthokonta.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/opisthokonta.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=18"}],"version-history":[{"count":11,"href":"https:\/\/opisthokonta.net\/index.php?rest_route=\/wp\/v2\/posts\/18\/revisions"}],"predecessor-version":[{"id":30,"href":"https:\/\/opisthokonta.net\/index.php?rest_route=\/wp\/v2\/posts\/18\/revisions\/30"}],"wp:attachment":[{"href":"https:\/\/opisthokonta.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=18"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/opisthokonta.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=18"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/opisthokonta.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=18"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}