Project 1

Introduction

The goal of this project is to perform manipulations from a given text file and create a CSV file that has a players name, state, total points, initial rating, and the average rating of their opponents. To start I will be pulling the text file and read it while cleaning the dataframe

library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.1     ✔ readr     2.2.0
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.3     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(gt)

elo <- read.delim(text = grep("^-+\\s*$", readLines("ELO.txt"), value = TRUE, invert = TRUE), sep = "|", header = TRUE, strip.white = TRUE)
Warning in readLines("ELO.txt"): incomplete final line found on 'ELO.txt'
elo <- elo |> 
  slice(-1) |>
  select(-X)

Splitting the data

at this point I have two rows dedicated to information for each player. I will break these rows up into two different dataframes. In addition, I need the starting elo of each player added to one of the dataframes. To achieve this, I used string manipulation to split the string containing the player’s id number, starting elo, and ending elo. There was also a provisional rating that we are choosing to ignore that had to be removed from some lines.

odd_rows <- elo |> 
  filter( row_number() %% 2 == 1)

even_rows <- elo |> 
  filter(row_number() %% 2 == 0)

eloSplit <- data.frame( 
  
  str_split_fixed(
    
    str_split_fixed( even_rows[,2],"[R:](.)", 2)[,2] , 
    
    "->", 2 )
) 

names(eloSplit) <- c("starting_elo", "ending_elo")

eloSplit <- eloSplit |> 
  mutate(
    across( starting_elo:ending_elo, 
            \(x) str_remove(x, "P.*")
    )
  )

even_rows <- even_rows |> 
  mutate( eloSplit ) 

Obtaining the Mean elo

Now that we have moved onto the odd rows, we will need to remove the win/lose/draw indicators in the round elements. Once that is achieved with more string manipulations we can go on to replace the indexes the elements represent with the starting elo of each of the player’s opponents. once that is in place we are able to find the rounded mean elo of their opponents and add a column to our odd row dataframe.

odd_rows <- odd_rows |> 
  mutate( across( Round:Round.6, \(x) gsub("\\D", "", x) ) ) |> 
  mutate(start_elo = even_rows$starting_elo) |> 
  mutate(start_elo = as.numeric(str_trim(start_elo)))

elo_lookup <- set_names(odd_rows$start_elo, as.character(odd_rows$Pair))

odd_rows <- odd_rows |>
  mutate(across(Round:Round.6, ~ elo_lookup[as.character(.)]))

odd_rows <- odd_rows |>
  rowwise() |> 
  mutate(row_mean = round( mean(as.numeric(c_across(Round:Round.6)), na.rm = TRUE) ) )|> 
  ungroup()

Putting it All Together

Now that we have successfully broken down the data we are able to create a new dataframe using information from both dataframes we have created. This will ultimately produce a dataset that has all the relevant player information for each player

player_data <- data.frame( 
  player_name = odd_rows$Player.Name,
  player_state = even_rows$Pair,
  total_points = odd_rows$Total,
  pre_rating = even_rows$starting_elo,
  average_opponent_rating = odd_rows$row_mean
)

write.csv(player_data, "player_data.csv")

player_data |> 
  gt() |>
  tab_header(title = "Player Data") |>
  cols_align(align = "left" )
Player Data
player_name player_state total_points pre_rating average_opponent_rating
GARY HUA ON 6.0 1794 1605
DAKSHESH DARURI MI 6.0 1553 1469
ADITYA BAJAJ MI 6.0 1384 1564
PATRICK H SCHILLING MI 5.5 1716 1574
HANSHI ZUO MI 5.5 1655 1501
HANSEN SONG OH 5.0 1686 1519
GARY DEE SWATHELL MI 5.0 1649 1372
EZEKIEL HOUGHTON MI 5.0 1641 1468
STEFANO LEE ON 5.0 1411 1523
ANVIT RAO MI 5.0 1365 1554
CAMERON WILLIAM MC LEMAN MI 4.5 1712 1468
KENNETH J TACK MI 4.5 1663 1506
TORRANCE HENRY JR MI 4.5 1666 1498
BRADLEY SHAW MI 4.5 1610 1515
ZACHARY JAMES HOUGHTON MI 4.5 1220 1484
MIKE NIKITIN MI 4.0 1604 1386
RONALD GRZEGORCZYK MI 4.0 1629 1499
DAVID SUNDEEN MI 4.0 1600 1480
DIPANKAR ROY MI 4.0 1564 1426
JASON ZHENG MI 4.0 1595 1411
DINH DANG BUI ON 4.0 1563 1470
EUGENE L MCCLURE MI 4.0 1555 1300
ALAN BUI ON 4.0 1363 1214
MICHAEL R ALDRICH MI 4.0 1229 1357
LOREN SCHWIEBERT MI 3.5 1745 1363
MAX ZHU ON 3.5 1579 1507
GAURAV GIDWANI MI 3.5 1552 1222
SOFIA ADINA STANESCU-BELLU MI 3.5 1507 1522
CHIEDOZIE OKORIE MI 3.5 1602 1314
GEORGE AVERY JONES ON 3.5 1522 1144
RISHI SHETTY MI 3.5 1494 1260
JOSHUA PHILIP MATHEWS ON 3.5 1441 1379
JADE GE MI 3.5 1449 1277
MICHAEL JEFFERY THOMAS MI 3.5 1399 1375
JOSHUA DAVID LEE MI 3.5 1438 1150
SIDDHARTH JHA MI 3.5 1355 1388
AMIYATOSH PWNANANDAM MI 3.5 980 1385
BRIAN LIU MI 3.0 1423 1539
JOEL R HENDON MI 3.0 1436 1430
FOREST ZHANG MI 3.0 1348 1391
KYLE WILLIAM MURPHY MI 3.0 1403 1248
JARED GE MI 3.0 1332 1150
ROBERT GLEN VASEY MI 3.0 1283 1107
JUSTIN D SCHILLING MI 3.0 1199 1327
DEREK YAN MI 3.0 1242 1152
JACOB ALEXANDER LAVALLEY MI 3.0 377 1358
ERIC WRIGHT MI 2.5 1362 1392
DANIEL KHAIN MI 2.5 1382 1356
MICHAEL J MARTIN MI 2.5 1291 1286
SHIVAM JHA MI 2.5 1056 1296
TEJAS AYYAGARI MI 2.5 1011 1356
ETHAN GUO MI 2.5 935 1495
JOSE C YBARRA MI 2.0 1393 1345
LARRY HODGE MI 2.0 1270 1206
ALEX KONG MI 2.0 1186 1406
MARISA RICCI MI 2.0 1153 1414
MICHAEL LU MI 2.0 1092 1363
VIRAJ MOHILE MI 2.0 917 1391
SEAN M MC CORMICK MI 2.0 853 1319
JULIA SHEN MI 1.5 967 1330
JEZZEL FARKAS ON 1.5 955 1327
ASHWIN BALAJI MI 1.0 1530 1186
THOMAS JOSEPH HOSMER MI 1.0 1175 1350
BEN LI MI 1.0 1163 1263

Analysis Data

analysis_df <- player_data |> 
  mutate(differential = as.numeric(pre_rating) - as.numeric(average_opponent_rating) )

mean_mean <- mean(player_data$average_opponent_rating)
mean_differential <- mean(analysis_df$differential)
 

expected_outcome_low = 1 / ( 1 + 10^( (min(as.numeric(analysis_df$pre_rating)) - mean_mean)  /400) )

expected_outcome_high = 1 / ( 1 + 10^( (max(as.numeric(analysis_df$pre_rating)) - mean_mean)  /400) )

analysis_df |> 
  gt() |>
  tab_header("Player Data with Average Differential")
Player Data with Average Differential
player_name player_state total_points pre_rating average_opponent_rating differential
GARY HUA ON 6.0 1794 1605 189
DAKSHESH DARURI MI 6.0 1553 1469 84
ADITYA BAJAJ MI 6.0 1384 1564 -180
PATRICK H SCHILLING MI 5.5 1716 1574 142
HANSHI ZUO MI 5.5 1655 1501 154
HANSEN SONG OH 5.0 1686 1519 167
GARY DEE SWATHELL MI 5.0 1649 1372 277
EZEKIEL HOUGHTON MI 5.0 1641 1468 173
STEFANO LEE ON 5.0 1411 1523 -112
ANVIT RAO MI 5.0 1365 1554 -189
CAMERON WILLIAM MC LEMAN MI 4.5 1712 1468 244
KENNETH J TACK MI 4.5 1663 1506 157
TORRANCE HENRY JR MI 4.5 1666 1498 168
BRADLEY SHAW MI 4.5 1610 1515 95
ZACHARY JAMES HOUGHTON MI 4.5 1220 1484 -264
MIKE NIKITIN MI 4.0 1604 1386 218
RONALD GRZEGORCZYK MI 4.0 1629 1499 130
DAVID SUNDEEN MI 4.0 1600 1480 120
DIPANKAR ROY MI 4.0 1564 1426 138
JASON ZHENG MI 4.0 1595 1411 184
DINH DANG BUI ON 4.0 1563 1470 93
EUGENE L MCCLURE MI 4.0 1555 1300 255
ALAN BUI ON 4.0 1363 1214 149
MICHAEL R ALDRICH MI 4.0 1229 1357 -128
LOREN SCHWIEBERT MI 3.5 1745 1363 382
MAX ZHU ON 3.5 1579 1507 72
GAURAV GIDWANI MI 3.5 1552 1222 330
SOFIA ADINA STANESCU-BELLU MI 3.5 1507 1522 -15
CHIEDOZIE OKORIE MI 3.5 1602 1314 288
GEORGE AVERY JONES ON 3.5 1522 1144 378
RISHI SHETTY MI 3.5 1494 1260 234
JOSHUA PHILIP MATHEWS ON 3.5 1441 1379 62
JADE GE MI 3.5 1449 1277 172
MICHAEL JEFFERY THOMAS MI 3.5 1399 1375 24
JOSHUA DAVID LEE MI 3.5 1438 1150 288
SIDDHARTH JHA MI 3.5 1355 1388 -33
AMIYATOSH PWNANANDAM MI 3.5 980 1385 -405
BRIAN LIU MI 3.0 1423 1539 -116
JOEL R HENDON MI 3.0 1436 1430 6
FOREST ZHANG MI 3.0 1348 1391 -43
KYLE WILLIAM MURPHY MI 3.0 1403 1248 155
JARED GE MI 3.0 1332 1150 182
ROBERT GLEN VASEY MI 3.0 1283 1107 176
JUSTIN D SCHILLING MI 3.0 1199 1327 -128
DEREK YAN MI 3.0 1242 1152 90
JACOB ALEXANDER LAVALLEY MI 3.0 377 1358 -981
ERIC WRIGHT MI 2.5 1362 1392 -30
DANIEL KHAIN MI 2.5 1382 1356 26
MICHAEL J MARTIN MI 2.5 1291 1286 5
SHIVAM JHA MI 2.5 1056 1296 -240
TEJAS AYYAGARI MI 2.5 1011 1356 -345
ETHAN GUO MI 2.5 935 1495 -560
JOSE C YBARRA MI 2.0 1393 1345 48
LARRY HODGE MI 2.0 1270 1206 64
ALEX KONG MI 2.0 1186 1406 -220
MARISA RICCI MI 2.0 1153 1414 -261
MICHAEL LU MI 2.0 1092 1363 -271
VIRAJ MOHILE MI 2.0 917 1391 -474
SEAN M MC CORMICK MI 2.0 853 1319 -466
JULIA SHEN MI 1.5 967 1330 -363
JEZZEL FARKAS ON 1.5 955 1327 -372
ASHWIN BALAJI MI 1.0 1530 1186 344
THOMAS JOSEPH HOSMER MI 1.0 1175 1350 -175
BEN LI MI 1.0 1163 1263 -100
library(scales)

Attaching package: 'scales'
The following object is masked from 'package:purrr':

    discard
The following object is masked from 'package:readr':

    col_factor

Findings and Conclusion

Looking at the data gathered, we can see that the average player rating is 1378.625. The average difference between the mean player elo and the elo of any given player is -0.125. This may seem like a fair enough differential, it does not take into account the high variance in the ratings. When taking this into account the percent chance for the average player to beat the worst player is 99.7% and the chance for the average player to beat the best player is 8.4% the skill discrepancy is very high in this dataset and it could offer a better experience to break this into two different tournaments. The loss of elo may hurt lower rated players less, but it would not offer them the same improvement opportunities as facing similarly skilled opponents.