Marvel Rivals Performance Analysis

Author

Marco Hernandez

Overview

Marvel Rivals is a objective-based competitive hero shooter, where one team of 6 battles to win an objective over the opposing team. Rivals features three roles that each have different characters to choose from: Strategist, Duelist, and Vanguard. The game offers casual game play as well as competitive, with new season and half-season updates every couple of months.

My dataset includes over 500 of my own matches and tracks a wide variety of variables through each, including: competitive or casual match status, win/loss, points gained/lost (competitive), total points (competitive), current rank (competitive), rank up games (competitive), KDA (kills/deaths/assists) ratio, mvp or svp earned, hero/heroes played, main role played, type of match, map, duration in minutes, bots, and platform.

Using this data, I will explore the relationship between my KDA ratio and win rate to determine the importance of KDA ratio in winning competitive games. I will also analyze my overall win rate by season, with a visual distinction between competitive games and non-competitive games. Together, these analyses provide insight into how individual KDA performance relates to match outcome, and how overall success has varied across seasons. This seasonal comparison can help identify periods of stronger or weaker performance, which may aid further analysis oh how to optimize gameplay.

Data Wrangling

The dataset was compiled from personal gameplay records tracked over multiple game seasons through the site tracker.gg/marvel-rivals. Each row represents a single completed match, distinguished by their specific date and time stamp.

Variables

datetime - Date and time the match was played

season - Current game season during the match

competitive - Indicates whether the match was competitive (TRUE/FALSE)

won - Indicates whether the match was won (TRUE/FALSE)

points_change - Change in rank points after the match

total_points - Total rank points after the match

current_rank - Player’s rank at the end of the match

rank_up - Indicates whether the match resulted in a rank increase (TRUE/FALSE)

kda_ratio - Kill/Death/Assist ratio recorded for the match

mvp_or_svp - Indicates whether the player received MVP or SVP recognition (TRUE/FALSE)

heroes_played - Hero or heroes used during the match

main_role - Role played during the match (DPS = Duelist, Tank = Vanguard, Support = Strategist)

match_type - Type of match played

map - Map where the match took place

match_duration - Length of the match in minutes

bots - Indicates whether bots were present in the match (TRUE/FALSE)

xbox_or_pc - Platform used to play the match

# Setup
library(tidyverse)
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.2.0     ✔ readr     2.1.6
✔ forcats   1.0.1     ✔ stringr   1.6.0
✔ ggplot2   4.0.2     ✔ tibble    3.3.1
✔ lubridate 1.9.5     ✔ tidyr     1.3.2
✔ purrr     1.2.1     
── 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(ggplot2)
library(gt)
rivals_data <- read_csv("rivals.csv", na = c("", "null"))
Rows: 516 Columns: 17
── Column specification ────────────────────────────────────────────────────────
Delimiter: ","
chr (8): datetime, current_rank, heroes_played, main_role, match_type, map, ...
dbl (4): season, points_change, total_points, kda_ratio
lgl (5): competitive, won, rank_up, mvp_or_svp, bots

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Visualization

# Filter dataset to only Competitive games
rivals_comp_data <-
  filter(rivals_data, competitive, TRUE)

#Create KDA vs Match Outcome box plot
kda_win_plot <-
  rivals_comp_data |> 
  select(kda_ratio, won) |> 
  ggplot(aes(x = kda_ratio, y = won, fill = won)) +
  geom_boxplot() +
  labs(
    title = "KDA Ratio vs. Competitive Match Outcome",
    x = "KDA Ratio",
    y = "Match Won"
  ) 

kda_win_plot

# Create Season vs Win Percentage bar chart
season_win_plot <-
  rivals_data |> 
  group_by(season, competitive) |> 
  select(won, season) |> 
  summarise(win_percentage = mean(won)* 100) |> 
  ggplot(aes(x = season, y = win_percentage, fill = competitive)) +
  geom_col(position = "dodge") +
  labs(
    title = "Win Percentage by Season",
    x = "Season",
    y = "Win Percentage"
  ) +
  scale_x_continuous(
    limits = c(2, 7),
    breaks = seq(2.5, 6.5, .5)
  )
Adding missing grouping variables: `competitive`
`summarise()` has regrouped the output.
season_win_plot

Tables

# Create KDA Ratio vs. Match Outcome table
kda_win_table <-
  rivals_comp_data |> 
  select(kda_ratio, won) |> 
  group_by(won) |> 
  summarise(
    avg_kda = mean(kda_ratio),
    median_kda = median(kda_ratio)
  ) |> 
  gt() |> 
    cols_label(
    won = "Match Won",
    avg_kda = "Average KDA Ratio",
    median_kda = "Median KDA Ratio"
  )

kda_win_table
Match Won Average KDA Ratio Median KDA Ratio
FALSE 3.105691 2.50
TRUE 13.602717 11.79
# Create Win Percentage by Season display table
season_win_table <-
  rivals_data |> 
  group_by(season, competitive) |> 
  select(won, season) |> 
  summarise(win_percentage = round(mean(won)* 100, 1)) |> 
  gt() |> 
  cols_label(
    competitive = "Competitive",
    win_percentage = "Win Percentage"
  )
Adding missing grouping variables: `competitive`
`summarise()` has regrouped the output.
season_win_table
Competitive Win Percentage
2.5
FALSE 54.9
TRUE 52.0
3
FALSE 59.8
TRUE 42.6
3.5
FALSE 70.0
TRUE 45.7
4
FALSE 66.7
TRUE 37.1
4.5
FALSE 56.0
TRUE 29.2
5
FALSE 50.0
TRUE 80.0
5.5
FALSE 70.0
TRUE 43.8
6
TRUE 50.0
6.5
TRUE 33.3

Conclusions

The analysis shows a clear relationship between KDA ratio and match outcome. Matches that were won generally had markedly higher KDA ratios than matches that were lost, suggesting that stronger individual performance plays an important role in winning competitive Marvel Rivals games. The seasonal analysis shows that win percentage varies across seasons and between competitive and non-competitive matches. These differences highlight how overall performance can vary over time and between competitive and casual game modes, and open the door for further analysis toward succeeding in the game. Overall, this dataset demonstrates how gameplay statistics can be used to gain a better understanding of patterns that may help optimize match success.