Elo is a system of ratings/rankings (named after its creator, Arpad Elo) for pairwise matchups. In short, pairs of “teams” (“A” and “B”) begin a match with rankings \(R_A\) and \(R_B\). The result (“score”) of the game is coded as 0/0.5/1 for loss/tie/win, respectively. The prior expectation of this result can be expressed as \[P_A = \frac{1}{1 + 10^{(R_B - R_A) / 400}}\] \[P_B = \frac{1}{1 + 10^{(R_A - R_B) / 400}} = 1 - P_A\] where \[P_i\] is the prior probability that team \(i\) wins the match.

After each match, ratings are updated as follows: \[R^{new}_A = R_A + K(S_A - P_A)\] \[R^{new}_B = R_B + K(S_B - P_B) = R_B + K(1 - S_A - (1 - P_A)) = R_B - K(S_A - P_A)\] where \(S_i\) is the score of team \(i\) (0/0.5/1) and \(K\) is an update weight (commonly called the “k-factor”).

Therefore, we see that the system as a whole (all teams) retains (“conserves”) its total sum of Elo ratings; for every rating point team A gains/loses, team B loses/gains the same amount.

`elo`

PackageThe `elo`

package includes functions to address all kinds of Elo calculations.

`library(elo)`

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

Vectors or scalars of Elo scores are denoted

`elo.A`

or`elo.B`

.Vectors or scalars of wins by team A are denoted by

`wins.A`

.Vectors or scalars of win probabilities are denoted by

`p.A`

.Vectors of team names are denoted

`team.A`

or`team.B`

.

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

:

```
<- c(1500, 1500)
elo.A <- c(1500, 1600)
elo.B elo.prob(elo.A, elo.B)
```

`## [1] 0.500000 0.359935`

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

:

```
<- c(1, 0)
wins.A elo.update(wins.A, elo.A, elo.B, k = 20)
```

`## [1] 10.0000 -7.1987`

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

:

`elo.calc(wins.A, elo.A, elo.B, k = 20)`

```
## elo.A elo.B
## 1 1510.000 1490.000
## 2 1492.801 1607.199
```

It may be helpful to calculate `wins.A`

from raw scores:

```
<- c(4, 1)
points.A <- c(3, 3)
points.B elo.calc(score(points.A, points.B), elo.A, elo.B, k = 20)
```

```
## elo.A elo.B
## 1 1510.000 1490.000
## 2 1492.801 1607.199
```

All of the “basic” functions accept formulas as input:

```
<- data.frame(elo.A = c(1500, 1500), elo.B = c(1500, 1600),
dat wins.A = c(1, 0), k = 20)
<- wins.A ~ elo.A + elo.B + k(k)
form elo.prob(form, data = dat)
```

`## [1] 0.500000 0.359935`

`elo.update(form, data = dat)`

`## [1] 10.0000 -7.1987`

`elo.calc(form, data = dat)`

```
## elo.A elo.B
## 1 1510.000 1490.000
## 2 1492.801 1607.199
```

Note that for `elo.prob()`

, `formula =`

can be more succinct:

`elo.prob(~ elo.A + elo.B, data = dat)`

`## [1] 0.500000 0.359935`

We can even adjust the Elos, for, e.g., home-field advantage.

`elo.calc(wins.A ~ adjust(elo.A, 10) + elo.B + k(k), data = dat)`

```
## elo.A elo.B
## 1 1509.712 1490.288
## 2 1492.534 1607.466
```

All of these functions assume that Elo scores are constant. The next vignette explores calculating “running” Elos.