Let’s try to replicate the results shown in the tweet below.
Top 10 NA players at RIFLE vs. RIFLE fights in 2022 (phantom/vandal):
1. @Cryocells_ (58%)
2. @NiSMO_VAL (58%)
3. @trentFPS (58%)
4. @DerrekOW (55%)
5. @diceyzx (54%)
6. @yay (54%)
7. @valyngod (53%)
8. @TenZOfficial (53%)
9. @777eeiu (53%)
10. @s0mcs (53%)
What surprises you?— Weltis (@WeltisGames) October 9, 2022
We start by identifying the events that he mentioned. (“Data using Group Stages and Playoffs for VCT Stage 1, 2 and LCQ. International LANs not used (due to likely average tougher competition.”)
events <- load_valorant("events")
events_na2022 <- events |>
inner_join(get_all_region_names(), by = "regionId") |>
filter(regionName == "North America" & grepl("2022", name)) |>
distinct(event_id = id, event_name = name)
events_na2022
#> event_id event_name
#> 1 1740 VCT 2022 - North America - Last Chance Qualifier
#> 2 1490 VCT North America 2022 - Stage 2 Challengers - Playoffs
#> 3 1461 VCT North America 2022 - Stage 2 Challengers - Group Stage
#> 4 1286 VCT North America 2022 - Stage 1 Challengers - Main Event
#> 5 1199 VCT North America 2022 - Stage 1 Challengers - Group Stage
And then we grab all series played at those events.
series <- load_valorant("series")
series_na2022 <- series |>
rename(event_id = eventId, start_date = startDate) |>
inner_join(
events_na2022,
by = "event_id"
) |>
unnest_wider(c(team1, team2), names_sep = "_") |>
select(
series_id = id,
event_id,
team1_id,
team1_name,
team2_id,
team2_name
)
series_na2022
#> # A tibble: 100 × 6
#> series_id event_id team1_id team1_name team2_id team2_name
#> <int> <int> <int> <chr> <int> <chr>
#> 1 34293 1740 5201 The Guard 305 100 Thieves
#> 2 34294 1740 617 FaZe Clan 305 100 Thieves
#> 3 34269 1740 305 100 Thieves 141 Cloud9
#> 4 34132 1740 5201 The Guard 617 FaZe Clan
#> 5 34080 1740 141 Cloud9 116 NRG Esports
#> 6 34079 1740 305 100 Thieves 388 Sentinels
#> 7 34032 1740 1448 Evil Geniuses 116 NRG Esports
#> 8 34030 1740 617 FaZe Clan 305 100 Thieves
#> 9 34031 1740 388 Sentinels 7510 Shopify Rebellion
#> 10 34029 1740 5201 The Guard 141 Cloud9
#> # ℹ 90 more rows
And all matches in those series.
set_names_from_id_element <- function(x) {
match_ids <- x |> map_int(~pluck(.x, "id"))
x |> set_names(match_ids)
}
matches <- load_valorant("matches") |> set_names_from_id_element()
matches_na2022 <- matches[names(matches) %in% as.character(series_na2022$series_id)]
Before we move on, we should stop and make some helpful “mappings” for IDs for series, matches, players, and teams.
series_player_mapping_na2022 <- matches_na2022 |>
map_dfr(
~pluck(.x, "playerStats"),
.id = "series_id"
) |>
distinct(series_id, team_number = teamNumber, player_id = playerId) |>
mutate(across(series_id, as.integer))
map_pluck_matches <- function(matches, element) {
matches |> map_int(~pluck(.x, element))
}
series_team_mapping_na2022 <- tibble(
series_id = matches_na2022 |> map_pluck_matches("id"),
`1` = matches_na2022 |> map_pluck_matches("team1Id"),
`2` = matches_na2022 |> map_pluck_matches("team2Id")
) |>
pivot_longer(
-series_id,
names_to = "team_number",
values_to = "team_id"
) |>
mutate(
across(team_number, as.integer)
)
series_team_player_mapping_na2022 <- series_player_mapping_na2022 |>
inner_join(
series_team_mapping_na2022 |>
transmute(series_id, team_number, team_id),
by = c("series_id", "team_number")
) |>
select(-team_number)
series_match_mapping_na2022 <- matches_na2022 |>
map_dfr(
~tibble(match_id = pluck(.x, "matches", "id")),
.id = "series_id"
) |>
mutate(
across(series_id, as.integer)
)
Next, we grab the round details of all the matches, including round events and load-outs.
## the names for matches are series ids
## the names for match details are match ids (individual maps between two teams)
match_details <- load_valorant("match_details") |> set_names_from_id_element()
match_details_na2022 <- match_details[names(match_details) %in% as.character(series_match_mapping_na2022$match_id)]
cleanly_pluck_from_match_details <- function(match_details, element) {
match_details |>
map_dfr(\(.x) pluck(.x, element), .id = "match_id") |>
as_tibble() |>
mutate(
across(match_id, as.integer)
)
}
match_events_na2022 <- cleanly_pluck_from_match_details(
match_details_na2022,
"events"
)
econ_na2022 <- cleanly_pluck_from_match_details(
match_details_na2022,
"economies"
)
Then, we create a dataframe of weapon kills, combining the match details with the various mappings that we created before.
players <- load_valorant("players")
teams <- get_all_team_names()
weapon_ids <- get_all_weapon_names() |>
select(
weapon_id = weaponId,
weapon = weaponName,
weapon_category = weaponCategory
)
weapon_kills_na2022 <- match_events_na2022 |>
filter(!is.na(killId), damageType == "weapon") |>
select(
match_id,
round_id = roundId,
round_number = roundNumber,
kill_id = killId,
killer_id = playerId,
victim_id = referencePlayerId,
killer_weapon_id = weaponId
) |>
## data doesn"t list victim"s actual weapon, so we use what they bought in their loadout
left_join(
econ_na2022 |>
select(
match_id,
round_id = roundId,
victim_id = playerId,
victim_weapon_id = weaponId
),
by = c("match_id", "round_id", "victim_id")
) |>
left_join(
series_match_mapping_na2022,
by = "match_id"
) |>
left_join(
series_team_player_mapping_na2022 |>
select(
series_id,
killer_id = player_id,
killer_team_id = team_id
),
by = c("series_id", "killer_id")
) |>
left_join(
series_team_player_mapping_na2022 |>
select(
series_id,
victim_id = player_id,
victim_team_id = team_id
),
by = c("series_id", "victim_id")
) |>
left_join(
players |> select(killer_id = id, killer_ign = ign),
by = "killer_id"
) |>
left_join(
players |> select(victim_id = id, victim_ign = ign),
by = "victim_id"
) |>
left_join(
teams |> select(killer_team_id = id, killer_team_name = name),
by = "killer_team_id"
) |>
left_join(
teams |> select(victim_team_id = id, victim_team_name = name),
by = "victim_team_id"
) |>
left_join(
weapon_ids |> rename_all(~sprintf("killer_%s", .x)),
by = "killer_weapon_id"
) |>
left_join(
weapon_ids |> rename_all(~sprintf("victim_%s", .x)),
by = "victim_weapon_id"
)
glimpse(weapon_kills_na2022)
#> Rows: 34,556
#> Columns: 19
#> $ match_id <int> 76753, 76753, 76753, 76753, 76753, 76753, 76753…
#> $ round_id <int> 1196159, 1196159, 1196159, 1196159, 1196159, 11…
#> $ round_number <int> 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3,…
#> $ kill_id <int> 9730768, 9730772, 9730771, 9730769, 9730770, 97…
#> $ killer_id <int> 1012, 269, 763, 1012, 1012, 122, 4141, 4141, 26…
#> $ victim_id <int> 4141, 2551, 1091, 11412, 1804, 1804, 1012, 763,…
#> $ killer_weapon_id <dbl> 11, 12, 12, 11, 11, 18, 18, 18, 18, 19, 19, 18,…
#> $ victim_weapon_id <dbl> 12, 11, 11, 12, 12, 18, 18, 18, 18, 18, 18, 11,…
#> $ series_id <int> 34293, 34293, 34293, 34293, 34293, 34293, 34293…
#> $ killer_team_id <int> 305, 305, 305, 305, 305, 305, 5201, 5201, 305, …
#> $ victim_team_id <int> 5201, 5201, 5201, 5201, 5201, 5201, 305, 305, 5…
#> $ killer_ign <chr> "bang", "Derrek", "Asuna", "bang", "bang", "ste…
#> $ victim_ign <chr> "neT", "Sayaplayer", "JonahP", "trent", "valyn"…
#> $ killer_team_name <chr> "100 Thieves", "100 Thieves", "100 Thieves", "1…
#> $ victim_team_name <chr> "The Guard", "The Guard", "The Guard", "The Gua…
#> $ killer_weapon <chr> "classic", "ghost", "ghost", "classic", "classi…
#> $ killer_weapon_category <chr> "sidearm", "sidearm", "sidearm", "sidearm", "si…
#> $ victim_weapon <chr> "ghost", "classic", "classic", "ghost", "ghost"…
#> $ victim_weapon_category <chr> "sidearm", "sidearm", "sidearm", "sidearm", "si…
Next, we count up how many kills and deaths individual players had in their Phantom/Vandal vs. Phantom/Vandal scenarios.
rifle_rifle_win_rates_by_player_na2022 <- bind_rows(
weapon_kills_na2022 |>
mutate(
ign = killer_ign,
player_id = killer_id,
is_killer = TRUE
),
weapon_kills_na2022 |>
mutate(
ign = victim_ign,
player_id = victim_id,
is_killer = FALSE
)
) |>
## could due ALL rifles for real for real
# filter(
# killer_weapon_category == "rifle" &
# victim_weapon_category == "rifle"
# ) |>
filter(
(killer_weapon %in% c("phantom", "vandal")) &
(victim_weapon %in% c("phantom", "vandal"))
) |>
count(player_id, ign, is_killer, sort = TRUE) |>
group_by(player_id, ign) |>
mutate(total = sum(n), prop = n / total) |>
ungroup() |>
filter(is_killer) |>
select(-is_killer) |>
arrange(desc(prop))
rifle_rifle_win_rates_by_player_na2022
#> # A tibble: 85 × 5
#> player_id ign n total prop
#> <int> <chr> <int> <int> <dbl>
#> 1 394 Poach 12 20 0.6
#> 2 3016 koalanoob 70 122 0.574
#> 3 1802 Cryocells 209 367 0.569
#> 4 11412 trent 344 614 0.560
#> 5 1780 NiSMO 122 218 0.560
#> 6 789 dicey 199 361 0.551
#> 7 3798 Zander 149 272 0.548
#> 8 1804 valyn 336 616 0.545
#> 9 3352 penny 99 182 0.544
#> 10 7025 wippie 135 250 0.54
#> # ℹ 75 more rows
Before we look at the results, let’s make one final mapping to link players to the last teams that they played with. This adds a bit of extra information for our final results.
last_player_team_mapping_na2022 <- series_team_player_mapping_na2022 |>
group_by(player_id) |>
slice_max(series_id, n = 1, with_ties = FALSE) |>
ungroup() |>
select(player_id, team_id) |>
left_join(
players |> select(player_id = id, ign),
by = "player_id"
) |>
left_join(
teams |>
select(team_id = id, last_team = name),
by = "team_id"
)
Finally, the results. (We use a minimum of 200 fights like Weltis.) The top 10 are very close to those that were posted.
top_rifle_rifle_win_rates_na2022 <- rifle_rifle_win_rates_by_player_na2022 |>
filter(total >= 200) |>
inner_join(
last_player_team_mapping_na2022 |> select(player_id, last_team),
by = "player_id"
) |>
transmute(
rank = row_number(desc(prop)),
ign,
last_team,
n,
total,
prop
)
head(top_rifle_rifle_win_rates_na2022, 20)
#> # A tibble: 20 × 6
#> rank ign last_team n total prop
#> <int> <chr> <chr> <int> <int> <dbl>
#> 1 1 Cryocells XSET (Inactive) 209 367 0.569
#> 2 2 trent The Guard 344 614 0.560
#> 3 3 NiSMO Ghost Gaming 122 218 0.560
#> 4 4 dicey FaZe Clan 199 361 0.551
#> 5 5 Zander Version1 (Inactive) 149 272 0.548
#> 6 6 valyn The Guard 336 616 0.545
#> 7 7 wippie Version1 (Inactive) 135 250 0.54
#> 8 8 Derrek 100 Thieves 287 535 0.536
#> 9 9 BcJ XSET (Inactive) 279 522 0.534
#> 10 10 yay OpTic Gaming 156 294 0.531
#> 11 11 supamen FaZe Clan 360 679 0.530
#> 12 12 crashies OpTic Gaming 266 502 0.530
#> 13 13 SicK Sentinels 169 319 0.530
#> 14 14 TenZ Sentinels 195 371 0.526
#> 15 15 neT The Guard 281 538 0.522
#> 16 16 Moose Shopify Rebellion 272 523 0.520
#> 17 17 Marved OpTic Gaming 307 591 0.519
#> 18 18 ShahZaM Sentinels 267 515 0.518
#> 19 19 bang 100 Thieves 298 575 0.518
#> 20 20 skuba Knights 115 222 0.518
Here’s a direct comparison of our lists.
Player | Weltis Rank | Weltis % | valorantr Rank | valorantr % |
---|---|---|---|---|
Cryo | 1 | 58 | 1 | 57 |
NiSMO | 2 | 58 | 3 | 56 |
trent | 3 | 58 | 2 | 56 |
Derrek | 4 | 55 | 8 | 54 |
dicey | 5 | 54 | 4 | 55 |
yay | 6 | 54 | 10 | 53 |
valyn | 7 | 53 | 6 | 55 |
TenZ | 8 | 53 | 14 | 53 |
eeiu | 9 | 53 | 27 | 51 |
s0ms | 10 | 53 | 21 | 51 |