The ‘elo’ Package

Ethan Heinzen

2017-08-16

The elo Package

The elo package includes functions to address all kinds of Elo calculations.

library(elo)

Naming Schema

Most functions begin with the prefix “elo.”, for easy autocompletion.

Basic Functions

To calculate the probability team.A beats team.B, use elo.prob():

elo.A <- c(1500, 1500)
elo.B <- c(1500, 1600)
elo.prob(elo.A, elo.B)
## [1] 0.500000 0.359935

To calculate the score update after the two teams play, use elo.update():

wins.A <- c(1, 0)
elo.update(elo.A, elo.B, wins.A, k = 20)
## [1] 10.0000 -7.1987

To calculate the new Elo scores after the update, use elo.calc():

elo.calc(elo.A, elo.B, wins.A, k = 20)
##      elo.A    elo.B
## 1 1510.000 1490.000
## 2 1492.801 1607.199

The elo.run function

With two variable Elos

To calculate a series of Elo updates, use elo.run(). This function has a formula = and data = interface. We first load the dataset tournament.

data(tournament)
str(tournament)
## 'data.frame':    56 obs. of  4 variables:
##  $ team.Home     : Factor w/ 8 levels "Athletic Armadillos",..: 2 3 4 5 6 7 8 1 3 4 ...
##  $ team.Visitor  : Factor w/ 8 levels "Athletic Armadillos",..: 1 1 1 1 1 1 1 2 2 2 ...
##  $ points.Home   : int  8 7 10 9 8 6 7 12 9 11 ...
##  $ points.Visitor: num  12 6 13 8 9 8 6 4 2 12 ...

formula = should be in the format of wins.A ~ team.A + team.B. The score() function will help to calculate winners on the fly (1 = win, 0.5 = tie, 0 = loss).

tournament$wins.A <- tournament$points.Home > tournament$points.Visitor
elo.run(wins.A ~ team.Home + team.Visitor, data = tournament, k = 20)
## 
## An object of class 'elo.run', containing information on 8 teams and 56 matches.
elo.run(score(points.Home, points.Visitor) ~ team.Home + team.Visitor, data = tournament, k = 20)
## 
## An object of class 'elo.run', containing information on 8 teams and 56 matches.

For more complicated Elo updates, you can include the special function k() in the formula = argument. Here we’re taking the log of the win margin as part of our update.

elo.run(score(points.Home, points.Visitor) ~ team.Home + team.Visitor +
        k(20*log(abs(points.Home - points.Visitor) + 1)), data = tournament)
## 
## An object of class 'elo.run', containing information on 8 teams and 56 matches.

It’s also possible to adjust one team’s Elo for a variety of factors (e.g., home-field advantage). The adjust() special function will take as its second argument a vector or a constant.

elo.run(score(points.Home, points.Visitor) ~ adjust(team.Home, 10) + team.Visitor,
        data = tournament, k = 20)
## 
## An object of class 'elo.run', containing information on 8 teams and 56 matches.

With a fixed-Elo opponent

elo.run() also recognizes if one of the columns is numeric, and interprets that as a fixed-Elo opponent.

tournament$elo.Visitor <- 1500
elo.run(score(points.Home, points.Visitor) ~ team.Home + elo.Visitor,
        data = tournament, k = 20)
## 
## An object of class 'elo.run', containing information on 8 teams and 56 matches.

Helper functions

There are several helper functions that are useful to use when interacting with objects of class "elo.run".

as.matrix.elo.run() creates a matrix of running Elos.

e <- elo.run(score(points.Home, points.Visitor) ~ team.Home + team.Visitor,
             data = tournament, k = 20)
head(as.matrix(e))
##      Athletic Armadillos Blundering Baboons Cunning Cats Defense-less Dogs
## [1,]            1500.000               1500     1500.000          1500.000
## [2,]            1510.000               1490     1500.000          1500.000
## [3,]            1499.712               1490     1510.288          1500.000
## [4,]            1509.721               1490     1510.288          1489.992
## [5,]            1499.441               1490     1510.288          1489.992
## [6,]            1509.457               1490     1510.288          1489.992
##      Elegant Emus Fabulous Frogs Gallivanting Gorillas Helpless Hyenas
## [1,]      1500.00       1500.000                  1500            1500
## [2,]      1500.00       1500.000                  1500            1500
## [3,]      1500.00       1500.000                  1500            1500
## [4,]      1500.00       1500.000                  1500            1500
## [5,]      1510.28       1500.000                  1500            1500
## [6,]      1510.28       1489.984                  1500            1500

as.data.frame.elo.run() gives the long version (perfect, for, e.g., ggplot2).

str(as.data.frame(e))
## 'data.frame':    120 obs. of  3 variables:
##  $ game: num  0 0 0 0 0 0 0 0 1 1 ...
##  $ team: Factor w/ 8 levels "Athletic Armadillos",..: 1 2 3 4 5 6 7 8 2 1 ...
##  $ elo : num  1500 1500 1500 1500 1500 1500 1500 1500 1490 1510 ...

Finally, last() will extract the final Elos per team.

last(e)
##   Athletic Armadillos    Blundering Baboons          Cunning Cats 
##              1573.246              1418.969              1554.024 
##     Defense-less Dogs          Elegant Emus        Fabulous Frogs 
##              1452.811              1508.891              1535.245 
## Gallivanting Gorillas       Helpless Hyenas 
##              1483.299              1473.516