A library fo visualizing sport season results with interactive standings:
-
Prepare an input file with season results or download one from our examples.
-
Put a
div
withreplayTable
class on your page and supply a link to the input file usingdata-source
attribute:<div class="replayTable" data-source="/path/to/file.csv"> </div>
-
Include D3.js and Replay Table scripts (70+16 KB gzipped) and the stylesheet. Apply some magic to the body:
<head> ... <script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script> <script type="text/javascript" src="https://unpkg.com/replay-table/dist/replay-table.min.js"></script> <link rel="stylesheet" type="text/css" href="https://unpkg.com/replay-table/dist/replay-table.css"> </head> <body> ... <script type="text/javascript">replayTable.magic()</script> </body>
-
Enjoy!
The library is highly customizable via data-
attributes. Check out customization for the details.
Also feel free to embed ready-to-use Replay Tables from our gallery.
npm install -S replay-table
The only dependency is D3.js. D3 is not included with the library so don't forget to plug it up.
The library consists of 5 modules: configure → extract → transform → calculate → visualize.
Take a look at how we use them in the magic
function:
return Array.from(document.getElementsByClassName('replayTable'))
.map(table => {
const config = replayTable.configure(table.id, table.dataset); //build a config from data- attributes
return Promise.resolve(replayTable.extract(config.extract)) //fetch the input
.then(raw => {
const transformed = replayTable.transform(raw, config.transform); //transform into predefined format
const calculated = replayTable.calculate(transformed, config.calculate); //calculate wins, goals, etc.
return replayTable.visualize(calculated, config.visualize); //render interactive standings
})
.catch(error => crash(error));
});
Sometimes you won't need all the modules: for example, feel free to omit configure
if you already have a config.
A Replay Table is returned from the visualize
module. It has methods like play()
and to(roundIndex)
so you can control its behaviour from code.
Makes configs for other modules based on the div data-
attributes. The output looks like this:
extract: {
extractor: csv
},
transform: {
transformer: 'pointsTable',
changeToOutcome: {
25: 'win'
},
insertStartRound: 'Start →'
},
calculate: {
orderBy: ['points', 'wins']
},
visualize: {
columns: ['position', 'item', 'points', 'points.change'],
labels: ['#', 'Driver', 'Points']
}
To save you some time and cognitive effort we've constructed presets
that you can use via data-preset
attribute:
matches,
f1,
winLoss,
chgk.
So this table:
<div class="replayTable"
data-source="/path/to/file.csv">
data-transformer="listOfMatches"
data-change-to-outcome="{ 1: 'win', 0: 'loss' }"
data-order-by="winningPercentage,wins"
data-visualizer="classic"
data-columns="position,item,rounds,wins,losses,winningPercentage,outcome,match"
data-labels="#,Team,G,W,L,Win %"
</div>
is identical to this:
<div class="replayTable"
data-source="/path/to/file.csv">
data-preset="winLoss"
</div>
Fetches the input file, returns a promise.
Parameter | Attribute | Required | Accepts | Default | Examples |
---|---|---|---|---|---|
source | data-source |
yes | string |
null |
/assets/data/football/2016-2017/english-premier-league.json |
extractor | data-extractor |
no | extractor | csv |
csv , json |
If extractor is not defined we try to guess it from the file extension.
Transforms raw data into the predefined format:
[
{
name: 'round name',
results: {
[
{
change: 25,
extras: {
item: {
team: 'Mercedes'
}
},
item: 'Lewis Hamilton',
outcome: 'win'
},
{
...
},
...
]
}
},
{
...
},
...
]
Parameter | Attribute | Accepts | Parses | Default | Examples |
---|---|---|---|---|---|
transformer | data-transformer |
transformer | listOfMatches |
listOfMatches , pointsTable |
|
changeToOutcome | data-change-to-outcome |
object |
JSON object | { 3: 'win', 1: 'draw', 0: 'loss'} |
{ 1: 'win', 0: 'loss'} |
filterItems | data-filter-items |
array of strings |
comma-separated string | [] |
['Golden State Warriors', 'San Antonio Spurs', ...] |
insertStartRound | data-insert-start-round |
string |
0 |
Start |
The structure looks like this:
Round name | First Item | First Item Score | Second Item | Second Item Score |
---|---|---|---|---|
Round | Item | Score | Item | Score |
List should be sorted by round.
Here is an example:
Match Week | Home | Points | Away | Points |
---|---|---|---|---|
1 | Bournemouth | 0 | Aston Villa | 1 |
1 | Chelsea | 2 | Swansea | 2 |
1 | Everton | 2 | Watford | 2 |
... | ... | ... | ... | ... |
Also works with football-data.org fixtures.
Parameter | Attribute | Accepts | Default | Examples |
---|---|---|---|---|
format | data-format |
csv or football-data.org |
csv |
csv , football-data.org |
locationFirst | data-location-first |
home or away |
home |
home , away |
collapseToRounds | data-collapse-to-rounds |
boolean |
false |
true , false |
Use collapseToRounds
when you've got dates instead of match weeks: it groups each team's 1st, 2nd, 3rd, ... games.
The structure looks like this:
Item name | [1st extra column name] | [2nd extra column name] | [...] | 1st round name | 2nd round name | ... | last round name |
---|---|---|---|---|---|---|---|
item | [1st piece of extra info] | [2nd piece of extra info] | [...] | 1st round points | 2nd round points | ... | last round points |
The Formula One example (csv):
Driver | Team | Australia | Bahrain | ... | Abu Dhabi |
---|---|---|---|---|---|
Lewis Hamilton | Mercedes | 18 | 15 | ... | 25 |
Nico Rosberg | Mercedes | 25 | 25 | ... | 18 |
Daniel Ricciardo | Red Bull | 12 | 12 | ... | 10 |
... | ... | .... | ... | ... | ... |
Watch the live demo.
Parameter | Attribute | Accepts | Default | Examples |
---|---|---|---|---|
extraColumnsNumber | data-extra-columns-number |
int |
0 |
1 , 2 |
Calculates wins, goals, points, etc. and adds metadata. The output looks like this:
{
meta: {
lastRound: 38
},
results: {
[
{
meta: {
index: 2,
isLast: false,
items: 20,
name: "2"
},
results: {
[
{
change: 3,
draws: {
change: 0,
total: 0
},
extras: {},
item: 'Leicester',
losses: {
change: 0,
total: 0
},
match: {
location: "away",
opponent: "West Ham",
opponentScore: 1,
score: 2
},
outcome: "win",
points: {
change: 3,
total: 6
},
position: {
highest: 1,
lowest: 4,
strict: 1
},
wins: {
change: 1,
total: 2
}
...//goalsFor, goalsAgainst, goalsDifference, rounds, winningPercentage
},
....
]
}
},
...
]
}
}
See the whole list of calculations in the calculations.js.
Parameter | Attribute | Accepts | Parses | Default | Examples |
---|---|---|---|---|---|
orderBy | data-order-by |
array of calculations |
comma-separated string | ['points'] |
['winningPercentage', 'wins'] |
Renders interactive standings out of calculated data.
Returns a class instance with useful methods:
first()
,last()
,next()
,previous()
andto(roundIndex)
play()
andpause()
preview(roundIndex)
andendPreview()
drillDown(item)
andendDrillDown()
Parameter | Attribute | Accepts | Parses | Default | Examples |
---|---|---|---|---|---|
visualizer | data-visualizer |
visualizer | classic |
classic , sparklines |
|
controls | data-conrols |
array of controls |
comma-separated string | ['play', 'previous', 'next', 'slider'] |
['play', 'slider'] |
startFromRound | data-start-from-round |
int |
null |
0 , 15 |
|
roundsTotalNumber | data-rounds-total-number |
int |
null |
38 , 82 |
|
positionWhenTied | data-position-when-tied |
int |
strict , highest , range or average |
strict , highest |
|
animationSpeed | data-animation-speed |
float |
1.0 |
0.5 , 2.0 |
A simple table with controls on top. Works for any sport and is highly customizable.
Parameter | Attribute | Accepts | Parses | Default | Examples |
---|---|---|---|---|---|
columns | data-columns |
array of columns |
comma-separated string | ['position', 'item', 'points'] |
['position', 'item', 'points', 'outcome', 'points.change] |
labels | data-labels |
array of strings |
comma-separated string | ['#', 'Team', 'Points'] |
['Position', 'Driver', 'Points'] |
colors | data-colors |
object |
JSON object | { 'win': '#ACE680', 'draw': '#B3B3B3', 'loss': '#E68080' } |
{ 'win': 'green', 'draw': 'gray', 'loss': 'red' } |
durations | data-durations |
object |
JSON object | { move: 750, freeze: 750, outcomes: 200} |
{ move: 500, freeze: 400, outcomes: 250} |
A powerful interactive visualization for the sports with matches and points. Might be slow on old devices and in Firefox.
Parameter | Attribute | Accepts | Parses | Default | Examples |
---|---|---|---|---|---|
controls | data-conrols |
array of controls |
comma-separated string | ['play'] |
['play', 'previous', 'next'] |
colors | data-colors |
object |
JSON object | { 'win': '#21c114', 'draw': '#828282', 'loss': '#e63131' } |
{ 'win': 'green', 'draw': 'gray', 'loss': 'red' } |
sparkColors | data-spark-colors |
object |
JSON object | { 'win': '#D7E7C1', 'draw': '#F0F0F0', 'loss': '#EFCEBA' } |
{ 'win': 'green', 'draw': 'gray', 'loss': 'red' } |
currentSparkColors | data-current-spark-colors |
object |
JSON object | { 'win': '#AAD579', 'draw': '#CCCCCC', 'loss': '#E89B77' } |
{ 'win': 'green', 'draw': 'gray', 'loss': 'red' } |
durations | data-durations |
object |
JSON object | { move: 1000, freeze: 500, pre: 750} |
{ move: 750, freeze: 750, pre: 375} |
pointsLabel | data-points-label |
string |
points |
очков |
|
allLabel | data-all-label |
string |
All |
Все |
Please, post your suggestions and bugs via Github issues. PRs are also welcome!
If you own an API or a database with sports results we'd be happy to collaborate.
We'd also be happy to work with sport journalists to leverage Replay Table for a better season recap.
The library was built using the orange time at Targetprocess by Anton Iokov (@antoniokov) and Daria Krupenkina (@dariak).
Sparklines prototype was made by Vitali Yanusheuski.
We're open to your ideas and are ready to help with integrating Replay Table into your website.
Please, write us an email to [email protected] or ping on Twitter at @antoniokov.