This assignment extends the chess tournament dataset from Project 1 by calculating each player’s expected score using the Elo rating system. The expected score represents the probability of a player winning or drawing against opponents, based on rating differences.
Since my Project 1 table only contains each player’s average opponent rating, I approximate their Elo expectation by treating all seven rounds as if they were played against an opponent of that average strength.
I then compare each player’s expected score to their actual score, identifying the five players who most overperformed and the five who most underperformed relative to expectations.
The Elo formula I use is taken from Wikipedia — Elo rating system (Mathematical details).
The dataset from Project 1 is loaded below, showing the first few rows of player information.
chess_players <- read_csv("https://raw.githubusercontent.com/egabrielvice/assignments/main/chess_players.csv")
head(chess_players)
## # A tibble: 6 × 5
## Name State Points PreRating AvgOpponent
## <chr> <chr> <dbl> <dbl> <dbl>
## 1 GARY HUA ON 6 1794 1635
## 2 DAKSHESH DARURI MI 6 1553 1503
## 3 ADITYA BAJAJ MI 6 1384 1557
## 4 PATRICK H SCHILLING MI 5.5 1716 1604
## 5 HANSHI ZUO MI 5.5 1655 1547
## 6 HANSEN SONG OH 5 1686 1552
The Elo expected score per game is defined as:
\[ E = \frac{1}{1 + 10^{(R_{opp} - R_{player})/400}} \]
where:
- \(R_{player}\) = the player’s
rating,
- \(R_{opp}\) = the opponent’s
rating.
Since the tournament had 7 rounds, I approximate each player’s total expected score as:
\[ E_{total} = 7 \times \frac{1}{1 + 10^{(\text{AvgOpponent} - \text{PreRating})/400}} \]
The R code below applies this formula to compute each player’s expected score and the difference between actual and expected results.
expected_prob <- function(Rp, Ro) {
1 / (1 + 10 ^ ((Ro - Rp) / 400))
}
elo_results <- chess_players %>%
mutate(
ExpScore = 7 * expected_prob(PreRating, AvgOpponent),
Diff = Points - ExpScore
)
The table below shows the first ten players with their actual points, expected score, and difference.
elo_results %>%
select(Name, State, PreRating, Points, AvgOpponent, ExpScore, Diff) %>%
head(10) %>%
kable(caption = "Preview of Elo Results (first 10 players)")
| Name | State | PreRating | Points | AvgOpponent | ExpScore | Diff |
|---|---|---|---|---|---|---|
| GARY HUA | ON | 1794 | 6.0 | 1635 | 4.998552 | 1.0014477 |
| DAKSHESH DARURI | MI | 1553 | 6.0 | 1503 | 4.000242 | 1.9997582 |
| ADITYA BAJAJ | MI | 1384 | 6.0 | 1557 | 1.888282 | 4.1117179 |
| PATRICK H SCHILLING | MI | 1716 | 5.5 | 1604 | 4.590744 | 0.9092565 |
| HANSHI ZUO | MI | 1655 | 5.5 | 1547 | 4.554232 | 0.9457676 |
| HANSEN SONG | OH | 1686 | 5.0 | 1552 | 4.786714 | 0.2132858 |
| GARY DEE SWATHELL | MI | 1649 | 5.0 | 1434 | 5.426070 | -0.4260701 |
| EZEKIEL HOUGHTON | MI | 1641 | 5.0 | 1508 | 4.777993 | 0.2220073 |
| STEFANO LEE | ON | 1411 | 5.0 | 1525 | 2.391098 | 2.6089016 |
| ANVIT RAO | MI | 1365 | 5.0 | 1544 | 1.841037 | 3.1589633 |
Next, I identify the five players who most overperformed relative to their expected score.
top_over <- elo_results %>%
arrange(desc(Diff)) %>%
select(Name, PreRating, Points, ExpScore, Diff) %>%
mutate(ExpScore = round(ExpScore, 2), Diff = round(Diff, 2)) %>%
head(5)
kable(top_over, caption = "Top 5 Overperformers (Actual − Expected)")
| Name | PreRating | Points | ExpScore | Diff |
|---|---|---|---|---|
| ADITYA BAJAJ | 1384 | 6.0 | 1.89 | 4.11 |
| ZACHARY JAMES HOUGHTON | 1220 | 4.5 | 1.18 | 3.32 |
| ANVIT RAO | 1365 | 5.0 | 1.84 | 3.16 |
| JACOB ALEXANDER LAVALLEY | 377 | 3.0 | 0.05 | 2.95 |
| AMIYATOSH PWNANANDAM | 980 | 3.5 | 0.68 | 2.82 |
Finally, I identify the five players who most underperformed relative to their expected score.
top_under <- elo_results %>%
arrange(Diff) %>%
select(Name, PreRating, Points, ExpScore, Diff) %>%
mutate(ExpScore = round(ExpScore, 2), Diff = round(Diff, 2)) %>%
head(5)
kable(top_under, caption = "Top 5 Underperformers (Actual − Expected)")
| Name | PreRating | Points | ExpScore | Diff |
|---|---|---|---|---|
| ASHWIN BALAJI | 1530 | 1.0 | 3.77 | -2.77 |
| LOREN SCHWIEBERT | 1745 | 3.5 | 6.00 | -2.50 |
| GEORGE AVERY JONES | 1522 | 3.5 | 5.75 | -2.25 |
| GAURAV GIDWANI | 1552 | 3.5 | 5.51 | -2.01 |
| CHIEDOZIE OKORIE | 1602 | 3.5 | 5.40 | -1.90 |
Using the Elo expected-score formula, I estimated each player’s expected points based on their pre-rating compared to the average rating of their opponents. By comparing expected scores to actual tournament results, I identified the five players who most overperformed and the five who most underperformed relative to expectations.
This analysis illustrates how Elo ratings can be applied not only for ranking players but also for evaluating tournament performance. As described in Wikipedia’s Elo rating system, expected scores reflect the probability of winning or drawing against an opponent, and differences between actual and expected outcomes highlight players who performed above or below their rating-based predictions.
While minor differences exist across Elo implementations, the results consistently reveal which players exceeded expectations and which underperformed. Overall, this confirms the usefulness of the Elo system for benchmarking tournament performance.