Football demo tck tests and tech docs

Summary: Heading fix in examples.md

Reviewers: teon.banek, buda, mtomic

Reviewed By: teon.banek, buda, mtomic

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D1290
This commit is contained in:
Ivan Paljak 2018-04-13 15:09:17 +02:00
parent 6e849d212a
commit 92613fcab8
4 changed files with 469296 additions and 26 deletions

View File

@ -1,12 +1,12 @@
## Examples
This chapter shows you how to use Memgraph on real-world data and how to get interesting
and useful information out of it.
This chapter shows you how to use Memgraph on real-world data and how to get
interesting and useful information out of it.
### TED Talks Example
[TED](https://www.ted.com/) is a nonprofit organization devoted to spreading ideas,
usually in the form of short, powerful talks.
[TED](https://www.ted.com/) is a nonprofit organization devoted to spreading
ideas, usually in the form of short, powerful talks.
Today, TED talks are influential videos from expert speakers on almost all
topics — from science to business to global issues.
Here we present a small dataset which consists of 97 talks. We'll show you how
@ -15,24 +15,24 @@ to model this data as a graph and demonstrate a few example queries.
#### Data Model
Each TED talk has a main speaker, so we
identify two types of nodes — `Talk` and `Speaker`. Also, we will add
an edge of type `Gave` pointing to a `Talk` from its main `Speaker`. Each speaker has a name
so we can add property `name` to `Speaker` node. Likewise, we'll add properties
`name`, `title` and `description` to node `Talk`.
Furthermore, each talk is given in a specific TED event, so
we can create node `Event` with property `name` and relationship `InEvent`
between talk and event.
an edge of type `Gave` pointing to a `Talk` from its main `Speaker`.
Each speaker has a name so we can add property `name` to `Speaker` node.
Likewise, we'll add properties `name`, `title` and `description` to node
`Talk`. Furthermore, each talk is given in a specific TED event, so we can
create node `Event` with property `name` and relationship `InEvent` between
talk and event.
Talks are tagged with keywords to facilitate searching, hence we
add node `Tag` with property `name` and relationship `HasTag` between talk and
tag. Moreover, users give ratings to each talk
by selecting up to three predefined string values.
Therefore we add node `Rating` with these values as property `name` and relationship
`HasRating` with property `user_count` between talk and rating nodes.
tag. Moreover, users give ratings to each talk by selecting up to three
predefined string values. Therefore we add node `Rating` with these values as
property `name` and relationship`HasRating` with property `user_count` between
talk and rating nodes.
#### Example Queries
We have prepared a database snapshot for this example, so you can easily import it
when starting Memgraph using the `--durability-directory` option.
We have prepared a database snapshot for this example, so you can easily import
it when starting Memgraph using the `--durability-directory` option.
```
/usr/lib/memgraph/memgraph --durability-directory /usr/share/memgraph/examples/TEDTalk \
@ -92,9 +92,9 @@ ORDER BY TalksCount DESC, Tag LIMIT 20;
```
5) Find 20 talks most rated as "Funny". If you want to query by other ratings,
possible values are: Obnoxious, Jaw-dropping, OK, Persuasive, Beautiful, Confusing,
Longwinded, Unconvincing, Fascinating, Ingenious, Courageous, Funny, Informative and
Inspiring.
possible values are: Obnoxious, Jaw-dropping, OK, Persuasive, Beautiful,
Confusing, Longwinded, Unconvincing, Fascinating, Ingenious, Courageous, Funny,
Informative and Inspiring.
```
MATCH (r:Rating{name:"Funny"})<-[e:HasRating]-(m:Talk)
RETURN m.name, e.user_count ORDER BY e.user_count DESC LIMIT 20;
@ -109,9 +109,10 @@ WHERE r.user_count > 1000
RETURN n.title, s.name, r.user_count ORDER BY r.user_count DESC;
```
7) Now let's see one real-world example &mdash; how to make a real-time recommendation.
If you've just watched a talk from a certain speaker(e.g. Hans Rosling) you might be
interested in finding more talks from the same speaker on a similar topic:
7) Now let's see one real-world example &mdash; how to make a real-time
recommendation. If you've just watched a talk from a certain
speaker (e.g. Hans Rosling) you might be interested in finding more talks from
the same speaker on a similar topic:
```
MATCH (n:Speaker {name: "Hans Rosling"})-[:Gave]->(m:Talk)
@ -121,7 +122,8 @@ RETURN m.title as Title, COLLECT(tag.name), COUNT(tag) as TagCount
ORDER BY TagCount DESC, Title;
```
The following few queries are focused on extracting information about TED events.
The following few queries are focused on extracting information about
TED events.
8) Find how many talks were given per event:
```
@ -147,7 +149,8 @@ RETURN n.name as Speaker, EventsCount
ORDER BY EventsCount DESC, Speaker;
```
11) For each speaker search for other speakers that participated in same events:
11) For each speaker search for other speakers that participated in same
events:
```
MATCH (n:Speaker)-[:Gave]->()-[:InEvent]->(e:Event)<-[:InEvent]-()<-[:Gave]-(m:Speaker)
WHERE n.name != m.name
@ -156,6 +159,178 @@ RETURN n.name AS Speaker, COLLECT(m.name) AS Others
ORDER BY Speaker;
```
### Football Example
[Football](https://en.wikipedia.org/wiki/Association_football)
(soccer for the heathens) is a team sport played between two teams of eleven
players with a spherical ball. The game is played on a rectangular pitch with
a goal at each and. The object of the game is to score by moving the ball
beyond the goal line into the opposing goal. The game is played by more than
250 million players in over 200 countries, making it the world's most
popular sport.
In this example, we will present a graph model of a reasonably sized dataset
of football matches across world's most popular leagues.
#### Data Model
In essence, we are trying to model a set of football matches. All information
about a single match is going to be contained in three nodes and two edges.
Two of the nodes will represent the teams that have played the match, while the
third node will represent the game itself. Both edges are directed from the
team nodes to the game node and are labeled as `:Played`.
Let us consider a real life example of this model&mdash;Arsene Wenger's 1000th.
game in charge of Arsenal. This was a regular fixture of a 2013/2014
English Premier League, yet it was written in the stars that this historic
moment would be a big London derby against Chelsea on Stanford Bridge. The
sketch below shows how this game is being modeled in our database.
```
+---------------+ +-----------------------------+
|n: Team | |w: Game |
| |-[:Played {side: "home", outcome: "won"}]-->| |
|name: "Chelsea"| |HT_home_score: 4 |
+---------------+ |HT_away_score: 0 |
|HT_result: "H" |
|FT_home_score: 6 |
|FT_away_score: 0 |
|FT_result: "H" |
+---------------+ |date: "2014-03-22" |
|m: Team | |league: "ENG-Premier League" |
| |-[:Played {side: "away", outcome: "lost"}]->|season: 2013 |
|name: "Arsenal"| |referee: "Andre Marriner" |
+---------------+ +-----------------------------+
```
#### Example Queries
We have prepared a database snapshot for this example, so you can easily import
it when starting Memgraph using the `--durability-directory` option.
```
/usr/lib/memgraph/memgraph --durability-directory /usr/share/memgraph/examples/football \
--durability-enabled=false --snapshot-on-exit=false
```
When using Docker, you can import the example with the following command:
```
docker run -p 7687:7687 \
-v mg_lib:/var/lib/memgraph -v mg_log:/var/log/memgraph -v mg_etc:/etc/memgraph \
memgraph --durability-directory /usr/share/memgraph/examples/football \
--durability-enabled=false --snapshot-on-exit=false
```
Now you're ready to try out some of the following queries.
NOTE: If you modify the dataset, the changes will stay only during this run of
Memgraph.
1) You might wonder, what leagues are supported?
```
MATCH (n:Game)
RETURN DISTINCT n.league AS League
ORDER BY League;
```
2) We have stored a certain number of seasons for each league. What is the
oldest/newest season we have included?
```
MATCH (n:Game)
RETURN DISTINCT n.league AS League, MIN(n.season) AS Oldest, MAX(n.season) AS Newest
ORDER BY League;
```
3) You have already seen one game between Chelsea and Arsenal, let's list all of
them in chronological order.
```
MATCH (n:Team {name: "Chelsea"})-[e:Played]->(w:Game)<-[f:Played]-(m:Team {name: "Arsenal"})
RETURN w.date AS Date, e.side AS Chelsea, f.side AS Arsenal,
w.FT_home_score AS home_score, w.FT_away_score AS away_score
ORDER BY Date;
```
4) How about filtering games in which Chelsea won?
```
MATCH (n:Team {name: "Chelsea"})-[e:Played {outcome: "won"}]->
(w:Game)<-[f:Played]-(m:Team {name: "Arsenal"})
RETURN w.date AS Date, e.side AS Chelsea, f.side AS Arsenal,
w.FT_home_score AS home_score, w.FT_away_score AS away_score
ORDER BY Date;
```
5) Home field advantage is a thing in football. Let's list the number of home
defeats for each Premier League team in the 2016/2017 season.
```
MATCH (n:Team)-[:Played {side: "home", outcome: "lost"}]->
(w:Game {league: "ENG-Premier League", season: 2016})
RETURN n.name AS Team, count(w) AS home_defeats
ORDER BY home_defeats, Team;
```
6) At the end of the season the team with the most points wins the league. For
each victory, a team is awarded 3 points and for each draw it is awarded
1 point. Let's find out how many points did reigning champions (Chelsea) have
at the end of 2016/2017 season.
```
MATCH (n:Team {name: "Chelsea"})-[:Played {outcome: "drew"}]->(w:Game {season: 2016})
WITH n, COUNT(w) AS draw_points
MATCH (n)-[:Played {outcome: "won"}]->(w:Game {season: 2016})
RETURN draw_points + 3 * COUNT(w) AS total_points;
```
7) In fact, why not retrieve the whole table?
```
MATCH (n)-[:Played {outcome: "drew"}]->(w:Game {league: "ENG-Premier League", season: 2016})
WITH n, COUNT(w) AS draw_points
MATCH (n)-[:Played {outcome: "won"}]->(w:Game {league: "ENG-Premier League", season: 2016})
RETURN n.name AS Team, draw_points + 3 * COUNT(w) AS total_points
ORDER BY total_points DESC;
```
8) People have always debated which of the major leagues is the most exciting.
One basic metric is the average number of goals per game. Let's see the results
at the end of the 2016/2017 season. WARNING: This might shock you.
```
MATCH (w:Game {season: 2016})
RETURN w.league, AVG(w.FT_home_score) + AVG(w.FT_away_score) AS avg_goals_per_game
ORDER BY avg_goals_per_game DESC;
```
9) Another metric might be the number of comebacks&mdash;games where one side
was winning at half time but were overthrown by the other side by the end
of the match. Let's count such occurrences during all supported seasons across
all supported leagues.
```
MATCH (g:Game) WHERE
(g.HT_result = "H" AND g.FT_result = "A") OR
(g.HT_result = "A" AND g.FT_result = "H")
RETURN g.league AS League, count(g) AS Comebacks
ORDER BY Comebacks DESC;
```
10) Exciting leagues also tend to be very unpredictable. On that note, let's list
all triplets of teams where, during the course of one season, team A won against
team B, team B won against team C and team C won against team A.
```
MATCH (a)-[:Played {outcome: "won"}]->(p:Game {league: "ENG-Premier League", season: 2016})<--
(b)-[:Played {outcome: "won"}]->(q:Game {league: "ENG-Premier League", season: 2016})<--
(c)-[:Played {outcome: "won"}]->(r:Game {league: "ENG-Premier League", season: 2016})<--(a)
WHERE p.date < q.date AND q.date < r.date
RETURN a.name AS Team1, b.name AS Team2, c.name AS Team3;
```
Now you're ready to explore the world of graph databases with Memgraph
by yourself and try it on many more examples and datasets.
@ -169,5 +344,3 @@ examples, execute the query:
```
MATCH (n) DETACH DELETE n;
```

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,613 @@
Feature: New Football Example
Scenario: Find all supported leagues
Given graph "football"
When executing query:
"""
MATCH (n:Game)
RETURN DISTINCT n.league AS League
ORDER BY League;
"""
Then the result should be:
| League |
| 'BEL-Jupiler League' |
| 'ENG-Championship' |
| 'ENG-Conference' |
| 'ENG-League 1' |
| 'ENG-League 2' |
| 'ENG-Premier League' |
| 'ESP-La Liga' |
| 'ESP-La Liga 2' |
| 'FRA-Ligue 1' |
| 'FRA-Ligue 2' |
| 'GER-Bundesliga' |
| 'GER-Bundesliga 2' |
| 'GRE-Ethniki Katigoria' |
| 'ITA-Serie A' |
| 'ITA-Serie B' |
| 'NED-Eredivisie' |
| 'POR-Liga 1' |
| 'SCO-Division 1' |
| 'SCO-Division 2' |
| 'SCO-Division 3' |
| 'SCO-Premiership' |
| 'TUR-Ligi 1' |
Scenario: Season interval for supported leagues
Given graph "football"
When executing query:
"""
MATCH (n:Game)
RETURN DISTINCT n.league AS League, MIN(n.season) AS Oldest, MAX(n.season) AS Newest
ORDER BY League;
"""
Then the result should be:
| League | Oldest | Newest |
| 'BEL-Jupiler League' | 2007 | 2016 |
| 'ENG-Championship' | 2007 | 2016 |
| 'ENG-Conference' | 2007 | 2016 |
| 'ENG-League 1' | 2007 | 2016 |
| 'ENG-League 2' | 2007 | 2016 |
| 'ENG-Premier League' | 2007 | 2016 |
| 'ESP-La Liga' | 2007 | 2016 |
| 'ESP-La Liga 2' | 2007 | 2016 |
| 'FRA-Ligue 1' | 2007 | 2016 |
| 'FRA-Ligue 2' | 2007 | 2016 |
| 'GER-Bundesliga' | 2007 | 2016 |
| 'GER-Bundesliga 2' | 2007 | 2016 |
| 'GRE-Ethniki Katigoria' | 2007 | 2016 |
| 'ITA-Serie A' | 2007 | 2016 |
| 'ITA-Serie B' | 2007 | 2016 |
| 'NED-Eredivisie' | 2007 | 2016 |
| 'POR-Liga 1' | 2007 | 2016 |
| 'SCO-Division 1' | 2007 | 2016 |
| 'SCO-Division 2' | 2007 | 2016 |
| 'SCO-Division 3' | 2007 | 2016 |
| 'SCO-Premiership' | 2007 | 2016 |
| 'TUR-Ligi 1' | 2007 | 2016 |
Scenario: Chronological order of Chelsea and Arsenal games.
Given graph "football"
When executing query:
"""
MATCH (n:Team {name: "Chelsea"})-[e:Played]->(w:Game)<-[f:Played]-(m:Team {name: "Arsenal"})
RETURN w.date AS Date, e.side AS Chelsea, f.side AS Arsenal,
w.FT_home_score AS home_score, w.FT_away_score AS away_score
ORDER BY Date;
"""
Then the result should be:
| Date | Chelsea | Arsenal | home_score | away_score |
| '2007-12-16' | 'away' | 'home' | 1 | 0 |
| '2008-03-23' | 'home' | 'away' | 2 | 1 |
| '2008-11-30' | 'home' | 'away' | 1 | 2 |
| '2009-05-10' | 'away' | 'home' | 1 | 4 |
| '2009-11-29' | 'away' | 'home' | 0 | 3 |
| '2010-02-07' | 'home' | 'away' | 2 | 0 |
| '2010-10-03' | 'home' | 'away' | 2 | 0 |
| '2010-12-27' | 'away' | 'home' | 3 | 1 |
| '2011-10-29' | 'home' | 'away' | 3 | 5 |
| '2012-04-21' | 'away' | 'home' | 0 | 0 |
| '2012-09-29' | 'away' | 'home' | 1 | 2 |
| '2013-01-20' | 'home' | 'away' | 2 | 1 |
| '2013-12-23' | 'away' | 'home' | 0 | 0 |
| '2014-03-22' | 'home' | 'away' | 6 | 0 |
| '2014-10-05' | 'home' | 'away' | 2 | 0 |
| '2015-04-26' | 'away' | 'home' | 0 | 0 |
| '2015-09-19' | 'home' | 'away' | 2 | 0 |
| '2016-01-24' | 'away' | 'home' | 0 | 1 |
| '2016-09-24' | 'away' | 'home' | 3 | 0 |
| '2017-02-04' | 'home' | 'away' | 3 | 1 |
Scenario: Chronological order of Chelsea and Arsenal games which Chelsea won.
Given graph "football"
When executing query:
"""
MATCH (n:Team {name: "Chelsea"})-[e:Played {outcome: "won"}]->
(w:Game)<-[f:Played]-(m:Team {name: "Arsenal"})
RETURN w.date AS Date, e.side AS Chelsea, f.side AS Arsenal,
w.FT_home_score AS home_score, w.FT_away_score AS away_score
ORDER BY Date;
"""
Then the result should be:
| Date | Chelsea | Arsenal | home_score | away_score |
| '2008-03-23' | 'home' | 'away' | 2 | 1 |
| '2009-05-10' | 'away' | 'home' | 1 | 4 |
| '2009-11-29' | 'away' | 'home' | 0 | 3 |
| '2010-02-07' | 'home' | 'away' | 2 | 0 |
| '2010-10-03' | 'home' | 'away' | 2 | 0 |
| '2012-09-29' | 'away' | 'home' | 1 | 2 |
| '2013-01-20' | 'home' | 'away' | 2 | 1 |
| '2014-03-22' | 'home' | 'away' | 6 | 0 |
| '2014-10-05' | 'home' | 'away' | 2 | 0 |
| '2015-09-19' | 'home' | 'away' | 2 | 0 |
| '2016-01-24' | 'away' | 'home' | 0 | 1 |
| '2017-02-04' | 'home' | 'away' | 3 | 1 |
Scenario: Number of home defeats during 2016/17 EPL season per team.
Given graph "football"
When executing query:
"""
MATCH (n:Team)-[:Played {side: "home", outcome: "lost"}]->
(w:Game {league: "ENG-Premier League", season: 2016})
RETURN n.name AS Team, count(w) AS home_defeats
ORDER BY home_defeats, Team;
"""
Then the result should be:
| Team | home_defeats |
| 'Manchester City' | 1 |
| 'Manchester United' | 1 |
| 'Arsenal' | 2 |
| 'Chelsea' | 2 |
| 'Everton' | 2 |
| 'Liverpool' | 2 |
| 'Leicester City' | 5 |
| 'AFC Bournemouth' | 6 |
| 'Burnley' | 6 |
| 'Stoke City' | 6 |
| 'Hull City' | 7 |
| 'Southampton' | 7 |
| 'Watford' | 7 |
| 'Swansea City' | 8 |
| 'West Bromwich Albion' | 8 |
| 'West Ham United' | 8 |
| 'Middlesbrough' | 9 |
| 'Crystal Palace' | 11 |
| 'Sunderland' | 11 |
Scenario: Number of points Chelsea had at the end of 2016/17 EPL season.
Given graph "football"
When executing query:
"""
MATCH (n:Team {name: "Chelsea"})-[:Played {outcome: "drew"}]->(w:Game {season: 2016})
WITH n, COUNT(w) AS draw_points
MATCH (n)-[:Played {outcome: "won"}]->(w:Game {season: 2016})
RETURN draw_points + 3 * COUNT(w) AS total_points;
"""
Then the result should be:
| total_points |
| 93 |
Scenario: EPL 2016/17 table
Given graph "football"
When executing query:
"""
MATCH (n)-[:Played {outcome: "drew"}]->(w:Game {league: "ENG-Premier League", season: 2016})
WITH n, COUNT(w) AS draw_points
MATCH (n)-[:Played {outcome: "won"}]->(w:Game {league: "ENG-Premier League", season: 2016})
RETURN n.name AS Team, draw_points + 3 * COUNT(w) AS total_points
ORDER BY total_points DESC;
"""
Then the result should be:
| Team | total_points |
| 'Chelsea' | 93 |
| 'Tottenham Hotspur' | 86 |
| 'Manchester City' | 78 |
| 'Liverpool' | 76 |
| 'Arsenal' | 75 |
| 'Manchester United' | 69 |
| 'Everton' | 61 |
| 'AFC Bournemouth' | 46 |
| 'Southampton' | 46 |
| 'West Bromwich Albion' | 45 |
| 'West Ham United' | 45 |
| 'Leicester City' | 44 |
| 'Stoke City' | 44 |
| 'Crystal Palace' | 41 |
| 'Swansea City' | 41 |
| 'Burnley' | 40 |
| 'Watford' | 40 |
| 'Hull City' | 34 |
| 'Middlesbrough' | 28 |
| 'Sunderland' | 24 |
Scenario: Number of goals per league (not AVG due to precision issues with tck_engine]
Given graph "football"
When executing query:
"""
MATCH (w:Game {season: 2016})
RETURN w.league AS League, SUM(w.FT_home_score) + SUM(w.FT_away_score) AS Goals, COUNT(w) AS Games
ORDER BY Goals DESC;
"""
Then the result should be:
| League | Goals | Games |
| 'ENG-Conference' | 1504 | 552 |
| 'ENG-League 2' | 1465 | 552 |
| 'ENG-Championship' | 1441 | 552 |
| 'ENG-League 1' | 1417 | 552 |
| 'ITA-Serie A' | 1123 | 380 |
| 'ESP-La Liga' | 1118 | 380 |
| 'ENG-Premier League' | 1064 | 380 |
| 'ESP-La Liga 2' | 1041 | 462 |
| 'ITA-Serie B' | 1021 | 462 |
| 'FRA-Ligue 1' | 994 | 380 |
| 'FRA-Ligue 2' | 943 | 380 |
| 'NED-Eredivisie' | 884 | 306 |
| 'GER-Bundesliga' | 877 | 306 |
| 'TUR-Ligi 1' | 828 | 306 |
| 'GER-Bundesliga 2' | 761 | 306 |
| 'POR-Liga 1' | 728 | 306 |
| 'BEL-Jupiler League' | 658 | 240 |
| 'SCO-Premiership' | 628 | 228 |
| 'GRE-Ethniki Katigoria' | 556 | 240 |
| 'SCO-Division 3' | 531 | 180 |
| 'SCO-Division 2' | 507 | 180 |
| 'SCO-Division 1' | 469 | 180 |
Scenario: Number of comebacks during 2016/17 season across all leagues
Given graph "football"
When executing query:
"""
MATCH (g:Game) WHERE
(g.HT_result = "H" AND g.FT_result = "A") OR
(g.HT_result = "A" AND g.FT_result = "H")
RETURN g.league AS League, count(g) AS Comebacks
ORDER BY Comebacks DESC;
"""
Then the result should be:
| League | Comebacks |
| 'ENG-League 1' | 267 |
| 'ENG-Conference' | 264 |
| 'ENG-League 2' | 257 |
| 'ENG-Championship' | 250 |
| 'ITA-Serie B' | 179 |
| 'ESP-La Liga 2' | 172 |
| 'ITA-Serie A' | 166 |
| 'ESP-La Liga' | 164 |
| 'FRA-Ligue 2' | 157 |
| 'NED-Eredivisie' | 155 |
| 'ENG-Premier League' | 151 |
| 'FRA-Ligue 1' | 151 |
| 'TUR-Ligi 1' | 149 |
| 'GER-Bundesliga' | 136 |
| 'GER-Bundesliga 2' | 136 |
| 'BEL-Jupiler League' | 128 |
| 'SCO-Division 3' | 110 |
| 'POR-Liga 1' | 107 |
| 'SCO-Division 2' | 92 |
| 'SCO-Premiership' | 88 |
| 'SCO-Division 1' | 84 |
| 'GRE-Ethniki Katigoria' | 83 |
Scenario: Cyclic victories in EPL 2016/2017 (A beats B beats C beats A).
Given graph "football"
When executing query:
"""
MATCH (a)-[:Played {outcome: "won"}]->(p:Game {league: "ENG-Premier League", season: 2016})<--
(b)-[:Played {outcome: "won"}]->(q:Game {league: "ENG-Premier League", season: 2016})<--
(c)-[:Played {outcome: "won"}]->(r:Game {league: "ENG-Premier League", season: 2016})<--(a)
WHERE p.date < q.date AND q.date < r.date
RETURN a.name AS Team1, b.name AS Team2, c.name AS Team3;
"""
Then the result should be:
| Team1 | Team2 | Team3 |
| 'Burnley' | 'Liverpool' | 'Leicester City' |
| 'West Ham United' | 'AFC Bournemouth' | 'West Bromwich Albion' |
| 'Swansea City' | 'Burnley' | 'Liverpool' |
| 'West Ham United' | 'AFC Bournemouth' | 'Everton' |
| 'Stoke City' | 'Sunderland' | 'AFC Bournemouth' |
| 'Leicester City' | 'Burnley' | 'Watford' |
| 'Burnley' | 'Liverpool' | 'West Bromwich Albion' |
| 'Crystal Palace' | 'Stoke City' | 'Swansea City' |
| 'Southampton' | 'West Ham United' | 'Crystal Palace' |
| 'Southampton' | 'Burnley' | 'Crystal Palace' |
| 'Southampton' | 'Swansea City' | 'Crystal Palace' |
| 'Liverpool' | 'Arsenal' | 'AFC Bournemouth' |
| 'AFC Bournemouth' | 'West Bromwich Albion' | 'Burnley' |
| 'AFC Bournemouth' | 'Stoke City' | 'Burnley' |
| 'Manchester City' | 'Manchester United' | 'Leicester City' |
| 'Manchester City' | 'West Bromwich Albion' | 'Leicester City' |
| 'Manchester City' | 'Sunderland' | 'Leicester City' |
| 'Everton' | 'Stoke City' | 'Watford' |
| 'Everton' | 'West Bromwich Albion' | 'Watford' |
| 'Leicester City' | 'Burnley' | 'AFC Bournemouth' |
| 'Arsenal' | 'Burnley' | 'Everton' |
| 'Arsenal' | 'Chelsea' | 'Everton' |
| 'Arsenal' | 'Southampton' | 'Everton' |
| 'Arsenal' | 'Watford' | 'Everton' |
| 'Watford' | 'Leicester City' | 'Manchester City' |
| 'Middlesbrough' | 'AFC Bournemouth' | 'Liverpool' |
| 'Burnley' | 'Everton' | 'West Ham United' |
| 'Watford' | 'West Ham United' | 'Sunderland' |
| 'AFC Bournemouth' | 'Hull City' | 'Southampton' |
| 'Arsenal' | 'Chelsea' | 'Manchester City' |
| 'Hull City' | 'Leicester City' | 'Manchester City' |
| 'Leicester City' | 'Burnley' | 'Everton' |
| 'West Ham United' | 'Sunderland' | 'Leicester City' |
| 'West Ham United' | 'AFC Bournemouth' | 'Leicester City' |
| 'Manchester City' | 'AFC Bournemouth' | 'Liverpool' |
| 'Swansea City' | 'Burnley' | 'AFC Bournemouth' |
| 'Watford' | 'Manchester United' | 'Tottenham Hotspur' |
| 'Southampton' | 'Burnley' | 'Everton' |
| 'Burnley' | 'Liverpool' | 'Manchester City' |
| 'Crystal Palace' | 'Stoke City' | 'Swansea City' |
| 'Crystal Palace' | 'Middlesbrough' | 'Swansea City' |
| 'Chelsea' | 'Manchester United' | 'Tottenham Hotspur' |
| 'Sunderland' | 'AFC Bournemouth' | 'Stoke City' |
| 'Southampton' | 'West Ham United' | 'Burnley' |
| 'AFC Bournemouth' | 'West Bromwich Albion' | 'Hull City' |
| 'Manchester City' | 'AFC Bournemouth' | 'Everton' |
| 'Liverpool' | 'Arsenal' | 'Swansea City' |
| 'Liverpool' | 'West Bromwich Albion' | 'Swansea City' |
| 'Liverpool' | 'Middlesbrough' | 'Swansea City' |
| 'Liverpool' | 'Arsenal' | 'Swansea City' |
| 'Burnley' | 'Everton' | 'Arsenal' |
| 'Leicester City' | 'Crystal Palace' | 'Southampton' |
| 'Leicester City' | 'Burnley' | 'Southampton' |
| 'AFC Bournemouth' | 'Swansea City' | 'Crystal Palace' |
| 'AFC Bournemouth' | 'Everton' | 'Crystal Palace' |
| 'Arsenal' | 'Sunderland' | 'Watford' |
| 'Arsenal' | 'Stoke City' | 'Watford' |
| 'Leicester City' | 'Manchester City' | 'Burnley' |
| 'Southampton' | 'Middlesbrough' | 'Swansea City' |
| 'Southampton' | 'West Ham United' | 'Swansea City' |
| 'Southampton' | 'AFC Bournemouth' | 'Swansea City' |
| 'Crystal Palace' | 'Stoke City' | 'Sunderland' |
| 'Crystal Palace' | 'Stoke City' | 'Sunderland' |
| 'AFC Bournemouth' | 'Liverpool' | 'Everton' |
| 'Liverpool' | 'Arsenal' | 'Hull City' |
| 'Liverpool' | 'Chelsea' | 'Hull City' |
| 'Liverpool' | 'West Bromwich Albion' | 'Hull City' |
| 'Liverpool' | 'Chelsea' | 'Hull City' |
| 'Burnley' | 'Liverpool' | 'Watford' |
| 'Tottenham Hotspur' | 'Swansea City' | 'Liverpool' |
| 'Tottenham Hotspur' | 'Hull City' | 'Liverpool' |
| 'Leicester City' | 'Manchester City' | 'Swansea City' |
| 'AFC Bournemouth' | 'Liverpool' | 'Manchester City' |
| 'AFC Bournemouth' | 'Everton' | 'Manchester City' |
| 'Middlesbrough' | 'Swansea City' | 'Crystal Palace' |
| 'Middlesbrough' | 'Sunderland' | 'Crystal Palace' |
| 'Liverpool' | 'Chelsea' | 'Leicester City' |
| 'Liverpool' | 'West Bromwich Albion' | 'Leicester City' |
| 'Liverpool' | 'Watford' | 'Leicester City' |
| 'Liverpool' | 'Sunderland' | 'Leicester City' |
| 'Liverpool' | 'Everton' | 'Leicester City' |
| 'Liverpool' | 'Chelsea' | 'Leicester City' |
| 'Liverpool' | 'Swansea City' | 'Leicester City' |
| 'Hull City' | 'Southampton' | 'Leicester City' |
| 'Hull City' | 'Swansea City' | 'Leicester City' |
| 'Arsenal' | 'AFC Bournemouth' | 'Liverpool' |
| 'Arsenal' | 'Swansea City' | 'Liverpool' |
| 'Arsenal' | 'Swansea City' | 'Liverpool' |
| 'Arsenal' | 'Hull City' | 'Liverpool' |
| 'Middlesbrough' | 'AFC Bournemouth' | 'Stoke City' |
| 'Burnley' | 'Liverpool' | 'Swansea City' |
| 'Burnley' | 'AFC Bournemouth' | 'Swansea City' |
| 'Watford' | 'Hull City' | 'Southampton' |
| 'Watford' | 'Everton' | 'Southampton' |
| 'Watford' | 'West Ham United' | 'Southampton' |
| 'West Bromwich Albion' | 'West Ham United' | 'Crystal Palace' |
| 'West Bromwich Albion' | 'Swansea City' | 'Crystal Palace' |
| 'West Bromwich Albion' | 'West Ham United' | 'Crystal Palace' |
| 'West Bromwich Albion' | 'Sunderland' | 'Crystal Palace' |
| 'West Bromwich Albion' | 'Stoke City' | 'Crystal Palace' |
| 'Sunderland' | 'Leicester City' | 'Manchester City' |
| 'Watford' | 'West Ham United' | 'Crystal Palace' |
| 'Watford' | 'Manchester United' | 'Crystal Palace' |
| 'Watford' | 'West Ham United' | 'Crystal Palace' |
| 'Watford' | 'Everton' | 'Crystal Palace' |
| 'Swansea City' | 'Leicester City' | 'Hull City' |
| 'Burnley' | 'Leicester City' | 'Liverpool' |
| 'Swansea City' | 'Burnley' | 'AFC Bournemouth' |
| 'Swansea City' | 'Crystal Palace' | 'AFC Bournemouth' |
| 'Swansea City' | 'Crystal Palace' | 'AFC Bournemouth' |
| 'West Bromwich Albion' | 'Watford' | 'Everton' |
| 'West Ham United' | 'Sunderland' | 'AFC Bournemouth' |
| 'West Ham United' | 'Hull City' | 'AFC Bournemouth' |
| 'West Ham United' | 'Crystal Palace' | 'AFC Bournemouth' |
| 'West Ham United' | 'Crystal Palace' | 'AFC Bournemouth' |
| 'Hull City' | 'Southampton' | 'Everton' |
| 'Arsenal' | 'Chelsea' | 'West Bromwich Albion' |
| 'Arsenal' | 'Crystal Palace' | 'West Bromwich Albion' |
| 'West Ham United' | 'Sunderland' | 'Leicester City' |
| 'West Ham United' | 'AFC Bournemouth' | 'Leicester City' |
| 'West Ham United' | 'Burnley' | 'Leicester City' |
| 'West Ham United' | 'Swansea City' | 'Leicester City' |
| 'Burnley' | 'Liverpool' | 'Tottenham Hotspur' |
| 'Chelsea' | 'West Ham United' | 'Crystal Palace' |
| 'Chelsea' | 'Leicester City' | 'Crystal Palace' |
| 'Chelsea' | 'Burnley' | 'Crystal Palace' |
| 'Chelsea' | 'Manchester United' | 'Crystal Palace' |
| 'Chelsea' | 'West Ham United' | 'Crystal Palace' |
| 'Chelsea' | 'Everton' | 'Crystal Palace' |
| 'Chelsea' | 'Sunderland' | 'Crystal Palace' |
| 'Chelsea' | 'Stoke City' | 'Crystal Palace' |
| 'Stoke City' | 'Sunderland' | 'Leicester City' |
| 'Stoke City' | 'Burnley' | 'Leicester City' |
| 'Stoke City' | 'Swansea City' | 'Leicester City' |
| 'West Ham United' | 'AFC Bournemouth' | 'Hull City' |
| 'West Ham United' | 'Sunderland' | 'Hull City' |
| 'Everton' | 'Leicester City' | 'Liverpool' |
| 'Sunderland' | 'Crystal Palace' | 'Watford' |
| 'Stoke City' | 'Watford' | 'Burnley' |
| 'Stoke City' | 'Watford' | 'Burnley' |
| 'Stoke City' | 'Swansea City' | 'Burnley' |
| 'Sunderland' | 'AFC Bournemouth' | 'Leicester City' |
| 'West Bromwich Albion' | 'Southampton' | 'Watford' |
| 'West Bromwich Albion' | 'Crystal Palace' | 'Watford' |
| 'Manchester City' | 'Crystal Palace' | 'Chelsea' |
| 'Middlesbrough' | 'Sunderland' | 'Hull City' |
| 'Watford' | 'Manchester United' | 'Tottenham Hotspur' |
| 'Hull City' | 'Leicester City' | 'Manchester City' |
| 'Stoke City' | 'Swansea City' | 'Liverpool' |
| 'Stoke City' | 'Hull City' | 'Liverpool' |
| 'Swansea City' | 'Liverpool' | 'Tottenham Hotspur' |
| 'West Bromwich Albion' | 'Crystal Palace' | 'Southampton' |
| 'West Bromwich Albion' | 'Burnley' | 'Southampton' |
| 'West Bromwich Albion' | 'Swansea City' | 'Southampton' |
| 'West Bromwich Albion' | 'West Ham United' | 'Southampton' |
| 'Swansea City' | 'Leicester City' | 'West Ham United' |
| 'Leicester City' | 'Burnley' | 'Everton' |
| 'Leicester City' | 'Liverpool' | 'Everton' |
| 'Arsenal' | 'Burnley' | 'Crystal Palace' |
| 'Arsenal' | 'Swansea City' | 'Crystal Palace' |
| 'Arsenal' | 'Chelsea' | 'Crystal Palace' |
| 'Arsenal' | 'Swansea City' | 'Crystal Palace' |
| 'Arsenal' | 'West Ham United' | 'Crystal Palace' |
| 'Arsenal' | 'Sunderland' | 'Crystal Palace' |
| 'Arsenal' | 'Stoke City' | 'Crystal Palace' |
| 'Arsenal' | 'Southampton' | 'Crystal Palace' |
| 'Burnley' | 'Watford' | 'Everton' |
| 'Burnley' | 'Liverpool' | 'Everton' |
| 'Burnley' | 'Liverpool' | 'Everton' |
| 'Southampton' | 'Everton' | 'Manchester City' |
| 'Hull City' | 'Leicester City' | 'Stoke City' |
| 'Hull City' | 'Liverpool' | 'Stoke City' |
| 'AFC Bournemouth' | 'Liverpool' | 'Tottenham Hotspur' |
| 'Swansea City' | 'Burnley' | 'Watford' |
| 'Swansea City' | 'Sunderland' | 'Watford' |
| 'Swansea City' | 'Southampton' | 'Watford' |
| 'Swansea City' | 'Crystal Palace' | 'Watford' |
| 'Swansea City' | 'Crystal Palace' | 'Watford' |
| 'Chelsea' | 'Watford' | 'Manchester United' |
| 'West Bromwich Albion' | 'Swansea City' | 'Liverpool' |
| 'West Bromwich Albion' | 'Hull City' | 'Liverpool' |
| 'West Bromwich Albion' | 'Leicester City' | 'Liverpool' |
| 'Middlesbrough' | 'Sunderland' | 'AFC Bournemouth' |
| 'Middlesbrough' | 'Hull City' | 'AFC Bournemouth' |
| 'Watford' | 'Middlesbrough' | 'Hull City' |
| 'Watford' | 'West Ham United' | 'Hull City' |
| 'Watford' | 'Arsenal' | 'Hull City' |
| 'Watford' | 'Leicester City' | 'Hull City' |
| 'Watford' | 'Everton' | 'Hull City' |
| 'Stoke City' | 'Hull City' | 'Swansea City' |
| 'Stoke City' | 'Watford' | 'Swansea City' |
| 'Stoke City' | 'Watford' | 'Swansea City' |
| 'Liverpool' | 'Leicester City' | 'Crystal Palace' |
| 'Liverpool' | 'Swansea City' | 'Crystal Palace' |
| 'Liverpool' | 'Chelsea' | 'Crystal Palace' |
| 'Liverpool' | 'Arsenal' | 'Crystal Palace' |
| 'Liverpool' | 'Swansea City' | 'Crystal Palace' |
| 'Liverpool' | 'Everton' | 'Crystal Palace' |
| 'Liverpool' | 'Sunderland' | 'Crystal Palace' |
| 'Liverpool' | 'Stoke City' | 'Crystal Palace' |
| 'Leicester City' | 'Manchester City' | 'Arsenal' |
| 'Leicester City' | 'Liverpool' | 'Arsenal' |
| 'Leicester City' | 'Crystal Palace' | 'Arsenal' |
| 'Sunderland' | 'Crystal Palace' | 'Middlesbrough' |
| 'Sunderland' | 'Hull City' | 'Middlesbrough' |
| 'Sunderland' | 'AFC Bournemouth' | 'Middlesbrough' |
| 'Crystal Palace' | 'Stoke City' | 'Burnley' |
| 'West Bromwich Albion' | 'Southampton' | 'Leicester City' |
| 'West Bromwich Albion' | 'Burnley' | 'Leicester City' |
| 'West Bromwich Albion' | 'Swansea City' | 'Leicester City' |
| 'West Bromwich Albion' | 'Arsenal' | 'Leicester City' |
| 'Everton' | 'Crystal Palace' | 'Chelsea' |
| 'Sunderland' | 'Hull City' | 'AFC Bournemouth' |
| 'Arsenal' | 'Chelsea' | 'Tottenham Hotspur' |
| 'Watford' | 'Hull City' | 'Liverpool' |
| 'Watford' | 'Leicester City' | 'Liverpool' |
| 'Tottenham Hotspur' | 'Manchester City' | 'West Ham United' |
| 'Tottenham Hotspur' | 'Chelsea' | 'West Ham United' |
| 'Tottenham Hotspur' | 'Hull City' | 'West Ham United' |
| 'Hull City' | 'Swansea City' | 'Sunderland' |
| 'Hull City' | 'Southampton' | 'Sunderland' |
| 'Hull City' | 'Leicester City' | 'Sunderland' |
| 'Hull City' | 'Middlesbrough' | 'Sunderland' |
| 'Hull City' | 'AFC Bournemouth' | 'Sunderland' |
| 'Watford' | 'Manchester United' | 'Leicester City' |
| 'Watford' | 'Everton' | 'Leicester City' |
| 'Watford' | 'Manchester United' | 'Leicester City' |
| 'Watford' | 'Everton' | 'Leicester City' |
| 'Watford' | 'Arsenal' | 'Leicester City' |
| 'Crystal Palace' | 'Chelsea' | 'Manchester City' |
| 'Everton' | 'Stoke City' | 'Swansea City' |
| 'Everton' | 'West Bromwich Albion' | 'Swansea City' |
| 'Everton' | 'Middlesbrough' | 'Swansea City' |
| 'Everton' | 'West Ham United' | 'Swansea City' |
| 'Everton' | 'Arsenal' | 'Swansea City' |
| 'Everton' | 'Manchester City' | 'Swansea City' |
| 'Everton' | 'AFC Bournemouth' | 'Swansea City' |
| 'Everton' | 'West Ham United' | 'Swansea City' |
| 'Manchester United' | 'West Bromwich Albion' | 'Arsenal' |
| 'Manchester United' | 'Crystal Palace' | 'Arsenal' |
| 'Manchester United' | 'Tottenham Hotspur' | 'Arsenal' |
| 'Southampton' | 'Everton' | 'Arsenal' |
| 'Southampton' | 'Crystal Palace' | 'Arsenal' |
| 'Watford' | 'Swansea City' | 'Everton' |
| 'West Bromwich Albion' | 'Crystal Palace' | 'Chelsea' |
| 'Burnley' | 'Crystal Palace' | 'AFC Bournemouth' |
| 'Burnley' | 'Everton' | 'AFC Bournemouth' |
| 'Middlesbrough' | 'Swansea City' | 'Southampton' |
| 'Stoke City' | 'Watford' | 'Arsenal' |
| 'Stoke City' | 'Watford' | 'Arsenal' |
| 'Stoke City' | 'Crystal Palace' | 'Arsenal' |
| 'Sunderland' | 'AFC Bournemouth' | 'Swansea City' |
| 'Sunderland' | 'Hull City' | 'Swansea City' |
| 'Sunderland' | 'AFC Bournemouth' | 'Swansea City' |
| 'Sunderland' | 'Watford' | 'Swansea City' |
| 'Hull City' | 'Leicester City' | 'Crystal Palace' |
| 'Hull City' | 'Swansea City' | 'Crystal Palace' |
| 'Hull City' | 'Swansea City' | 'Crystal Palace' |
| 'Hull City' | 'Southampton' | 'Crystal Palace' |
| 'Manchester United' | 'West Ham United' | 'Tottenham Hotspur' |
| 'West Ham United' | 'AFC Bournemouth' | 'Liverpool' |
| 'West Ham United' | 'Swansea City' | 'Liverpool' |
| 'West Ham United' | 'Hull City' | 'Liverpool' |
| 'West Ham United' | 'Crystal Palace' | 'Liverpool' |
| 'West Ham United' | 'Crystal Palace' | 'Liverpool' |
| 'Watford' | 'Manchester United' | 'Chelsea' |
| 'Sunderland' | 'Watford' | 'Arsenal' |
| 'Sunderland' | 'Crystal Palace' | 'Arsenal' |
| 'West Bromwich Albion' | 'Leicester City' | 'Manchester City' |
| 'Middlesbrough' | 'AFC Bournemouth' | 'Liverpool' |
| 'Middlesbrough' | 'Swansea City' | 'Liverpool' |
| 'Middlesbrough' | 'Hull City' | 'Liverpool' |
| 'Sunderland' | 'Crystal Palace' | 'Chelsea' |
| 'Leicester City' | 'West Ham United' | 'Tottenham Hotspur' |
| 'Leicester City' | 'West Ham United' | 'Tottenham Hotspur' |
| 'Hull City' | 'Liverpool' | 'Tottenham Hotspur' |
| 'Hull City' | 'West Ham United' | 'Tottenham Hotspur' |
| 'Burnley' | 'Everton' | 'West Ham United' |
| 'Burnley' | 'AFC Bournemouth' | 'West Ham United' |
| 'Burnley' | 'Leicester City' | 'West Ham United' |
| 'Burnley' | 'Liverpool' | 'West Ham United' |
| 'Everton' | 'West Bromwich Albion' | 'Arsenal' |
| 'Everton' | 'West Bromwich Albion' | 'Arsenal' |
| 'Everton' | 'Crystal Palace' | 'Arsenal' |
| 'Crystal Palace' | 'Arsenal' | 'Manchester United' |
| 'Southampton' | 'Leicester City' | 'Stoke City' |
| 'Southampton' | 'Burnley' | 'Stoke City' |
| 'Southampton' | 'Swansea City' | 'Stoke City' |
| 'West Bromwich Albion' | 'West Ham United' | 'Swansea City' |
| 'West Bromwich Albion' | 'Hull City' | 'Swansea City' |
| 'West Bromwich Albion' | 'AFC Bournemouth' | 'Swansea City' |
| 'West Bromwich Albion' | 'West Ham United' | 'Swansea City' |
| 'West Bromwich Albion' | 'Watford' | 'Swansea City' |
| 'Watford' | 'Leicester City' | 'Manchester City' |
| 'Watford' | 'Everton' | 'Manchester City' |

File diff suppressed because it is too large Load Diff