1. Introduction

This report applies Market Basket Analysis (MBA), a data mining technique traditionally used in retail to study product co-occurrence in shopping carts, to uncover hidden patterns within successful team compositions in League of Legends Season 11.

The core analytical concept treats each winning team composition as a single transaction: the five champions played by the winning team in a match form a “basket,” and individual champions are the “items” inside it. The goal is to identify sets of champions that statistically appear together most frequently in matches resulting in a victory.

The analysis is based on the League of Legends High Elo Ranked Team Comp S11 dataset sourced from Kaggle (leetolo, available at kaggle.com/datasets/leetolo/league-of-legend-high-elo-ranked-team-comp-s11). Focusing on High Elo data is methodologically important, it filters out the noise of casual play and captures synergies that are deliberately constructed by the top-tier player base, making the co-occurrence patterns more likely to reflect intentional strategic decisions rather than coincidence.

To provide a comprehensive view of team composition structure, three complementary techniques are applied. Apriori association rules extract directional synergies between champions (e.g., teams that pick champion A are significantly more likely to also pick champion B). A pair synergy network provides a graph-based visualization of the strongest and most stable pairwise relationships across all winning teams. Class Association Rules (CAR) identify champion combinations where the consequent is always Victory, highlighting the picks that most consistently anchor a winning composition.

Throughout the analysis, it is important to note that MBA is a descriptive method, it discovers co-occurrence patterns and correlations, but does not imply direct causality. The synergies identified reflect the Season 11 meta: the strategies and champion combinations that top players converged on as most effective during that period.

This report is an independent analysis that complements the companion clustering project, offering an alternative, rule-based view of champion synergies. (Champion Clustering and Dimension Reduction, available at https://rpubs.com/Marta_B/Lol_Champion_ClustersiDimension), which identified four stable champion archetypes based on base statistics.

The clustering project asks what the archetypes look like in terms of champions and indicators; this report asks how champions interact—i.e., which combinations recur in winning teams and whether they map onto the same archetype structure.


2. Packages

library(readr)
library(dplyr)
library(tidyr)
library(stringr)
library(knitr)

library(ggplot2)
library(ggraph)
library(tidygraph)
library(viridis)

library(arules)
library(arulesViz)

3. Data and preprocessing (winner definition + transaction building)

The dataset contains 2,901 winning team transactions built from a total of 2,901 matches (T1 won 630, T2 won 2,271). Every transaction consists of exactly 5 champions, confirmed by the basket size diagnostic, covering 153 unique champions across the entire pool. The imbalance between T1 and T2 wins is expected in this data format and does not affect the analysis, since we extract the winning team regardless of which side they played on.

3.1 Load the dataset and define the winner

df <- read_delim("s11.csv", delim = ";", show_col_types = FALSE)

t1_cols <- paste0("team_1__", str_pad(1:5, 3, pad = "0"))
t2_cols <- paste0("team_2__", str_pad(1:5, 3, pad = "0"))
all_champ_cols <- c(t1_cols, t2_cols)

stopifnot(all(t1_cols %in% names(df)), all(t2_cols %in% names(df)))
stopifnot("result" %in% names(df))

df <- df %>%
  mutate(
    match_id = row_number(),
    result = str_to_lower(str_trim(result)),
    winner_team = case_when(
      result == "victory" ~ "T1",
      result == "defeat"  ~ "T2",
      TRUE ~ NA_character_
    )
  )

kable(
  as.data.frame(table(df$winner_team, useNA = "ifany")),
  col.names = c("winner_team", "n_matches")
)
winner_team n_matches
T1 630
T2 2271

3.2 Build winner transactions (MBA input)

long <- df %>%
  select(match_id, winner_team, all_of(all_champ_cols)) %>%
  pivot_longer(cols = all_of(all_champ_cols), names_to = "slot", values_to = "champion") %>%
  mutate(
    team = if_else(str_detect(slot, "^team_1__"), "T1", "T2"),
    champion = str_squish(champion)
  ) %>%
  filter(!is.na(champion), champion != "") %>%
  distinct(match_id, winner_team, team, champion)

winners_long <- long %>%
  filter(team == winner_team) %>%
  select(match_id, champion) %>%
  distinct()

trans_list <- split(winners_long$champion, winners_long$match_id)
transactions <- as(trans_list, "transactions")

transactions
## transactions in sparse format with
##  2901 transactions (rows) and
##  153 items (columns)

3.3 Quick diagnostics (sanity checks)

kable(data.frame(
  n_transactions = length(transactions),
  n_unique_champions = n_distinct(winners_long$champion)
))
n_transactions n_unique_champions
2901 153
basket_sizes <- winners_long %>%
  count(match_id, name = "basket_size") %>%
  count(basket_size, name = "n_transactions")

kable(basket_sizes, col.names = c("basket_size", "n_transactions"))
basket_size n_transactions
5 2901

A note on team imbalance The dataset shows a significant disproportion between T1 wins (630) and T2 wins (2,271). This likely reflects the data collection perspective — matches are recorded from the viewpoint of a single player, where “result” indicates whether that player’s team won, and team assignment is not randomized. Since both T1 and T2 winners are pooled into the same transaction set regardless of side, this imbalance does not affect the MBA results — every winning composition contributes equally to the analysis irrespective of which column it was stored in.

A note on support levels With 153 unique champions and fixed basket sizes of exactly 5, the combinatorial space is extremely large. Even the strongest synergy in the dataset (Aphelios–Thresh, support ≈ 2%) appears in only 58 of 2,901 matches. This is expected given the champion pool size and the diversity of high-elo play, but it means all rules in this analysis describe relatively rare patterns. Lift and confidence are therefore more informative than raw support for evaluating synergy strength.


4. Apriori association rules (interpretable synergy rules)

4.1 Why these thresholds?

We aim for a readable set of rules: - support = 0.01 ensures a rule appears in at least ~1% of winner baskets (stability), - confidence = 0.2 reduces noise and keeps the rule set compact.

rules <- apriori(
  transactions,
  parameter = list(supp = 0.01, conf = 0.2, minlen = 2, maxlen = 3)
)
## Apriori
## 
## Parameter specification:
##  confidence minval smax arem  aval originalSupport maxtime support minlen
##         0.2    0.1    1 none FALSE            TRUE       5    0.01      2
##  maxlen target  ext
##       3  rules TRUE
## 
## Algorithmic control:
##  filter tree heap memopt load sort verbose
##     0.1 TRUE TRUE  FALSE TRUE    2    TRUE
## 
## Absolute minimum support count: 29 
## 
## set item appearances ...[0 item(s)] done [0.00s].
## set transactions ...[153 item(s), 2901 transaction(s)] done [0.00s].
## sorting and recoding items ... [97 item(s)] done [0.00s].
## creating transaction tree ... done [0.00s].
## checking subsets of size 1 2 3
##  done [0.00s].
## writing ... [61 rule(s)] done [0.00s].
## creating S4 object  ... done [0.00s].
rules <- rules[!is.redundant(rules)]
rules_sorted <- sort(rules, by = "lift", decreasing = TRUE)

# keep only "positive synergy" rules
rules_sorted <- subset(rules_sorted, subset = lift > 1)

kable(data.frame(n_rules = length(rules_sorted)))
n_rules
60
inspect(head(rules_sorted, 20))
##      lhs           rhs        support    confidence coverage   lift     count
## [1]  {Aphelios} => {Thresh}   0.01999311 0.3815789  0.05239573 4.358112 58   
## [2]  {Thresh}   => {Aphelios} 0.01999311 0.2283465  0.08755602 4.358112 58   
## [3]  {Yone}     => {Taliyah}  0.02068252 0.2343750  0.08824543 2.394091 60   
## [4]  {Taliyah}  => {Yone}     0.02068252 0.2112676  0.09789728 2.394091 60   
## [5]  {Renekton} => {Taliyah}  0.01482248 0.2216495  0.06687349 2.264103 43   
## [6]  {Ornn}     => {Graves}   0.01103068 0.3764706  0.02930024 2.056763 32   
## [7]  {Lee Sin}  => {Akali}    0.01413306 0.2369942  0.05963461 2.016188 41   
## [8]  {Syndra}   => {Graves}   0.02171665 0.3559322  0.06101344 1.944556 63   
## [9]  {Samira}   => {Alistar}  0.01758014 0.2161017  0.08135126 1.746270 51   
## [10] {Lee Sin}  => {Gragas}   0.01206481 0.2023121  0.05963461 1.741565 35   
## [11] {Viktor}   => {Graves}   0.01344364 0.2977099  0.04515684 1.626472 39   
## [12] {Lillia}   => {Lucian}   0.02309548 0.2392857  0.09651844 1.581248 67   
## [13] {Malphite} => {Graves}   0.01482248 0.2828947  0.05239573 1.545532 43   
## [14] {Taliyah}  => {Lucian}   0.02275078 0.2323944  0.09789728 1.535709 66   
## [15] {Nidalee}  => {Lucian}   0.01689073 0.2158590  0.07824888 1.426440 49   
## [16] {Sett}     => {Graves}   0.02826612 0.2538700  0.11134092 1.386962 82   
## [17] {Pantheon} => {Kai'Sa}   0.02688728 0.2689655  0.09996553 1.383456 78   
## [18] {Viktor}   => {Kai'Sa}   0.01206481 0.2671756  0.04515684 1.374249 35   
## [19] {Malphite} => {Jhin}     0.01447777 0.2763158  0.05239573 1.344953 42   
## [20] {Ekko}     => {Lucian}   0.01344364 0.2031250  0.06618407 1.342291 39

4.2 How to read a rule

Each association rule takes the form {LHS} ⇒ {RHS} and is described by three measures:

  • Support: how often the entire rule pattern occurs in winner baskets.
  • Confidence: the probability of RHS given LHS — out of all winning teams that included the LHS champion(s), what fraction also included the RHS champion.
  • Lift: synergy beyond champion popularity — how much more often LHS and RHS appear together than would be expected if their picks were statistically independent. Lift > 1 indicates a genuine synergy; lift = 1 means the co-occurrence is purely random.

Worked example: Rule {Aphelios} ⇒ {Thresh} has support = 0.020, confidence = 0.382, and lift = 4.36. This means: this pair appeared in 2% of all winning baskets (58 out of 2,901 matches); in 38% of matches where Aphelios was on the winning team, Thresh was also present; and this co-occurrence is 4.36 times more frequent than would be expected if the two champions were picked independently of each other. The high lift confirms this is a genuine strategic synergy, not a coincidence driven by the individual popularity of either champion.

4.3 Diagnostic: rule length distribution (pairs vs longer patterns)

rule_sizes <- table(size(lhs(rules_sorted)) + size(rhs(rules_sorted)))
kable(as.data.frame(rule_sizes), col.names = c("rule_size_total_items", "n_rules"))
rule_size_total_items n_rules
2 60

The Apriori algorithm produced 60 non-redundant rules with positive lift (lift > 1), all of them pair rules (rule size = 2), meaning no three-champion combinations met the minimum support threshold of 1%. This is consistent with the combinatorial difficulty of the problem, with 153 champions and 5-champion baskets, three-way co-occurrences are naturally rare even in a dataset of nearly 3,000 matches.

The top rules by lift are dominated by the Aphelios-Thresh pairing (lift = 4.36, count = 58), which is by far the strongest synergy in the dataset. This duo is well-known in Season 11 as a bot lane combination, Aphelios is a high-damage but immobile marksman who benefits enormously from Thresh’s ability to peel and engage, making their co-occurrence in winning teams far higher than their individual popularity would predict. The second strongest pattern is Yone–Taliyah (lift = 2.39, count = 60), a mid-lane combination where Taliyah’s Weaver’s Wall trap enables Yone’s engage, representing a clear strategic synergy rather than coincidence. Renekton–Taliyah (lift = 2.26) extends this pattern further, suggesting a lane-dominant early-game composition built around Taliyah as a playmaking anchor. Lower in the list, Graves appears multiple times as an RHS (paired with Ornn, Syndra, Malphite, Sett, Viktor), suggesting it functions as a flexible jungle pick that complements a variety of team compositions.


5. Visual validation (are the top rules stable or just rare?)

plot(head(rules_sorted, 20), method = "grouped")

The grouped plot confirms the structure of the rule set visually. The Aphelios-Thresh pair stands out clearly in the top-left as the largest and darkest circle, highest support and highest lift simultaneously, confirming it is both frequent and genuinely synergistic rather than merely popular. The remaining rules scatter across lower lift values with smaller dot sizes, indicating that most synergies in the dataset are moderate in strength and relatively rare in absolute frequency. The absence of high-support rules beyond the Aphelios–Thresh pair suggests that Season 11 winner compositions were diverse rather than dominated by a single recurring team structure.


6. Pair synergy network (global structure of winner synergies)

This network is not directional. It shows which pairs co-occur in winner baskets more than expected.

6.1 Why a custom synergy score?

We rank pairs with:

\[ score = lift \cdot \log(1+n) \]

  • lift captures “beyond popularity” synergy,
  • log(1+n) rewards pairs that appear more often (reliability).
M <- n_distinct(winners_long$match_id)

supp_1 <- winners_long %>%
  distinct(match_id, champion) %>%
  count(champion, name = "nA") %>%
  mutate(suppA = nA / M)

winner_pairs <- winners_long %>%
  inner_join(winners_long, by = "match_id", relationship = "many-to-many") %>%
  filter(champion.x < champion.y) %>%
  transmute(a = champion.x, b = champion.y) %>%
  count(a, b, name = "n", sort = TRUE)

pairs_metrics <- winner_pairs %>%
  mutate(suppAB = n / M) %>%
  left_join(supp_1 %>% select(a = champion, nA, suppA), by = "a") %>%
  left_join(supp_1 %>% select(b = champion, nB = nA, suppB = suppA), by = "b") %>%
  mutate(
    lift = suppAB / (suppA * suppB),
    score = lift * log1p(n)
  )

# stability filters
min_n_pair <- 30
min_n_single <- 50

pairs_metrics_stable <- pairs_metrics %>%
  filter(n >= min_n_pair, nA >= min_n_single, nB >= min_n_single) %>%
  arrange(desc(score))

kable(head(pairs_metrics_stable, 20))
a b n suppAB nA suppA nB suppB lift score
Aphelios Thresh 58 0.0199931 152 0.0523957 254 0.0875560 4.358112 17.770366
Taliyah Yone 60 0.0206825 284 0.0978973 256 0.0882454 2.394091 9.841807
Renekton Taliyah 43 0.0148225 194 0.0668735 284 0.0978973 2.264103 8.567794
Graves Syndra 63 0.0217166 531 0.1830403 177 0.0610134 1.944556 8.087182
Nidalee Renekton 33 0.0113754 227 0.0782489 194 0.0668735 2.173873 7.665858
Akali Lee Sin 41 0.0141331 341 0.1175457 173 0.0596346 2.016188 7.535846
Graves Ornn 32 0.0110307 531 0.1830403 85 0.0293002 2.056763 7.191488
Alistar Samira 51 0.0175801 359 0.1237504 236 0.0813513 1.746270 6.899939
Maokai Nidalee 34 0.0117201 230 0.0792830 227 0.0782489 1.889178 6.716686
Lillia Lucian 67 0.0230955 280 0.0965184 439 0.1513271 1.581248 6.672088
Lucian Taliyah 66 0.0227508 439 0.1513271 284 0.0978973 1.535709 6.457182
Gragas Yone 48 0.0165460 337 0.1161668 256 0.0882454 1.614058 6.281623
Gragas Lee Sin 35 0.0120648 337 0.1161668 173 0.0596346 1.741565 6.240932
Lillia Yone 41 0.0141331 280 0.0965184 256 0.0882454 1.659333 6.202039
Graves Sett 82 0.0282661 531 0.1830403 323 0.1113409 1.386962 6.128764
Kai’Sa Pantheon 78 0.0268873 564 0.1944157 290 0.0999655 1.383456 6.044937
Graves Viktor 39 0.0134436 531 0.1830403 131 0.0451568 1.626472 5.999858
Kai’Sa Leona 93 0.0320579 564 0.1944157 370 0.1275422 1.292855 5.873822
Graves Malphite 43 0.0148225 531 0.1830403 152 0.0523957 1.545532 5.848587
Samira Taliyah 37 0.0127542 236 0.0813513 284 0.0978973 1.601471 5.825489

6.2 Network plot (reproducible layout)

set.seed(1)

top_edges <- pairs_metrics_stable %>%
  slice_head(n = 50) %>%
  transmute(from = a, to = b, weight = lift)

g <- as_tbl_graph(top_edges, directed = FALSE) %>%
  mutate(
    community = as.factor(group_louvain()),
    importance = centrality_degree()
  )

ggraph(g, layout = "fr") +
  geom_edge_link(aes(width = weight), alpha = 0.25, color = "gray50") +
  geom_node_point(aes(color = community, size = importance)) +
  geom_node_text(aes(label = name), repel = TRUE, size = 4) +
  scale_edge_width(range = c(0.5, 3)) +
  theme_void(base_size = 12) +
  labs(
    title = "Winner synergy network (pair lift)",
    subtitle = paste0("Filters: n >= ", min_n_pair, ", nA/nB >= ", min_n_single,
                      " | edges: top 50 by score")
  )

The network reveals six communities of co-occurring champions in winning teams. The most central node by degree is Kai’Sa (green community, largest circle), connected to Pantheon, Zoe, Leona, Viktor, and Sett, suggesting she functioned as a highly adaptable ADC that paired well with diverse supports and solo laners. Graves (pink/red community) is the second most connected node, linking to Syndra, Ornn, Sett, Viktor, Malphite, and others consistent with the Apriori rules finding Graves as a recurring RHS. The Aphelios-Thresh pair appears as a separate isolated community (pink, far right) with only a single edge between them and no connections to the main graph, which reflects their exceptionally high mutual lift but relative independence from the broader synergy network. The gold community (Taliyah, Yone, Renekton, Lucian, Nidalee, Lillia) represents a cluster of mobile, map-controlling champions that frequently co-occurred, a pattern consistent with the Season 11 meta favoring fast-paced, objective-focused compositions.


7. CAR (Class Association Rules) for Victory (descriptive; fixed)

Fix applied: the class label must be an item without ‘=’ (arulesCBA/CAR parsing is strict). So we use CLASS_Victory and CLASS_Defeat instead of Result=Victory.

all_matches_car <- df %>%
  select(match_id, winner_team, all_of(all_champ_cols)) %>%
  pivot_longer(cols = all_of(all_champ_cols), names_to = "slot", values_to = "champion") %>%
  mutate(
    team_label = if_else(str_detect(slot, "^team_1__"), "T1", "T2"),
    class_item = if_else(team_label == winner_team, "CLASS_Victory", "CLASS_Defeat"),
    champion = str_squish(champion)
  ) %>%
  filter(!is.na(champion), champion != "") %>%
  distinct(match_id, team_label, champion, class_item)

basket_id <- paste(all_matches_car$match_id, all_matches_car$team_label, sep = "__")

trans_list_all <- split(all_matches_car$champion, basket_id)
class_list_all <- split(all_matches_car$class_item, basket_id)

final_list_all <- mapply(
  c,
  trans_list_all,
  lapply(class_list_all, function(x) unique(x)[1]),
  SIMPLIFY = FALSE
)

transactions_all <- as(final_list_all, "transactions")

rules_victory <- apriori(
  transactions_all,
  parameter = list(supp = 0.005, conf = 0.6, minlen = 2, maxlen = 3),
  appearance = list(rhs = "CLASS_Victory", default = "lhs")
)
## Apriori
## 
## Parameter specification:
##  confidence minval smax arem  aval originalSupport maxtime support minlen
##         0.6    0.1    1 none FALSE            TRUE       5   0.005      2
##  maxlen target  ext
##       3  rules TRUE
## 
## Algorithmic control:
##  filter tree heap memopt load sort verbose
##     0.1 TRUE TRUE  FALSE TRUE    2    TRUE
## 
## Absolute minimum support count: 29 
## 
## set item appearances ...[1 item(s)] done [0.00s].
## set transactions ...[155 item(s), 5802 transaction(s)] done [0.00s].
## sorting and recoding items ... [121 item(s)] done [0.00s].
## creating transaction tree ... done [0.00s].
## checking subsets of size 1 2 3
##  done [0.00s].
## writing ... [18 rule(s)] done [0.00s].
## creating S4 object  ... done [0.00s].
rules_victory <- rules_victory[!is.redundant(rules_victory)]
rules_victory_sorted <- sort(rules_victory, by = "lift", decreasing = TRUE)

kable(data.frame(n_rules_victory = length(rules_victory_sorted)))
n_rules_victory
18
inspect(head(rules_victory_sorted, 15))
##      lhs                        rhs             support     confidence
## [1]  {Sett, Zoe}             => {CLASS_Victory} 0.005342985 0.7209302 
## [2]  {Jhin, Malphite}        => {CLASS_Victory} 0.007238883 0.6774194 
## [3]  {Akali, Hecarim}        => {CLASS_Victory} 0.005170631 0.6521739 
## [4]  {Kayle}                 => {CLASS_Victory} 0.007066529 0.6507937 
## [5]  {Alistar, Miss Fortune} => {CLASS_Victory} 0.007411238 0.6417910 
## [6]  {Alistar, Zoe}          => {CLASS_Victory} 0.005860048 0.6415094 
## [7]  {Maokai, Nidalee}       => {CLASS_Victory} 0.005860048 0.6415094 
## [8]  {Rek'Sai}               => {CLASS_Victory} 0.011892451 0.6388889 
## [9]  {Kai'Sa, Zoe}           => {CLASS_Victory} 0.007755946 0.6338028 
## [10] {Graves, Sett}          => {CLASS_Victory} 0.014133058 0.6307692 
## [11] {Kai'Sa, Viktor}        => {CLASS_Victory} 0.006032403 0.6250000 
## [12] {Swain}                 => {CLASS_Victory} 0.006204757 0.6206897 
## [13] {Irelia, Jhin}          => {CLASS_Victory} 0.008445364 0.6202532 
## [14] {Jhin, Sett}            => {CLASS_Victory} 0.012409514 0.6153846 
## [15] {Ezreal, Lucian}        => {CLASS_Victory} 0.006204757 0.6101695 
##      coverage    lift     count
## [1]  0.007411238 1.441860 31   
## [2]  0.010685970 1.354839 42   
## [3]  0.007928301 1.304348 30   
## [4]  0.010858325 1.301587 41   
## [5]  0.011547742 1.283582 43   
## [6]  0.009134781 1.283019 34   
## [7]  0.009134781 1.283019 34   
## [8]  0.018614271 1.277778 69   
## [9]  0.012237160 1.267606 45   
## [10] 0.022406067 1.261538 82   
## [11] 0.009651844 1.250000 35   
## [12] 0.009996553 1.241379 36   
## [13] 0.013615994 1.240506 49   
## [14] 0.020165460 1.230769 72   
## [15] 0.010168907 1.220339 36
top_k <- min(12, length(rules_victory_sorted))
if (top_k > 0) {
  plot(head(rules_victory_sorted, top_k), method = "graph", engine = "htmlwidget")
}

The CAR analysis produced 18 rules pointing to CLASS_Victory with confidence ≥ 0.60. The strongest rule is {Sett, Zoe} ⇒ CLASS_Victory (confidence = 0.72, lift = 1.44), meaning that in 72% of all matches where this combination appeared on a team, that team won. {Jhin, Malphite} (confidence = 0.68) and {Akali, Hecarim} (0.65) follow closely. Notably, Kayle appears as a solo single-champion rule (confidence = 0.65), suggesting she was a strong pick in this patch independently of her teammates. Several Zoe rules appear (with Alistar, Kai’Sa, Sett), pointing to Zoe as a particularly win-predictive champion when paired correctly. Rek’Sai also appears as a solo rule (confidence = 0.64), reinforcing the delta support finding in Section 8. All rules have lift modestly above 1 (range 1.22–1.44), confirming these are genuine win associations beyond the baseline 50% win rate, but also reminding us this is a descriptive, not causal, analysis.


9. Conclusion

This analysis applied three complementary Market Basket Analysis techniques to 2,901 winning team compositions from Season 11 High Elo ranked play, and the results converge on a consistent and interpretable picture of how successful teams were built during that period.

The Apriori rules identify the Aphelios–Thresh bot lane as the single strongest statistical synergy in the dataset (lift = 4.36), a pairing whose mechanical interdependence — an immobile hypercarry paired with a peeling, engaging support, translates directly into co-occurrence patterns in winning teams. The Yone–Taliyah and Renekton–Taliyah combinations form a second tier of intentional synergies built around Taliyah as a mid-lane playmaker, reflecting a Season 11 meta trend toward coordinated, map-controlling compositions.

The pair synergy network adds structural context: Kai’Sa and Graves emerge as the most central champions in the winner graph, functioning as flexible picks that connected across multiple team archetypes rather than being tied to a single composition style. Their high degree centrality suggests they were “glue” champions, picks that worked well regardless of what the rest of the team was doing.

The CAR and delta support analyses together reveal an important distinction between popular and predictive champions. High-pick-rate champions like Jhin and Kai’Sa appear frequently in both winning and losing teams, making their raw frequency uninformative about outcome. The truly win-predictive picks in Season 11 were a mix of popular flexible carries and lower-frequency specialists - Sona (rr = 2.45), Tryndamere (rr = 2.15), Kayle (rr = 1.86), and Rek’Sai (rr = 1.77) appeared rarely but at a disproportionately high rate on the winning side, suggesting that correctly identifying and executing niche picks may have been as valuable as mastering the most popular meta champions.

The cross-method validation synthesizes all three perspectives into a single hierarchy of evidence. Six champions - Alistar, Jhin, Kai’Sa, Lucian, Nidalee, and Sett - are confirmed by all three methods simultaneously, representing the most robustly supported picks of the season. A secondary tier of composition-dependent picks (Akali, Graves, Malphite, Viktor) and individually strong picks (Hecarim, Irelia, Rek’Sai) completes the picture of how different champions contributed to winning in different ways.

As with all Market Basket Analysis, these findings are descriptive and should be interpreted as correlates rather than causes of winning. No patch or meta-shift control was applied, roles and lane assignments were not modeled, and the dataset reflects a single season snapshot. Future work could extend this analysis by incorporating patch version as a temporal variable, modeling lane-specific synergies to distinguish bot lane duos from cross-lane compositions, or applying the same framework to other seasons to track how the meta evolved over time.


10. Connecting the analyses: do synergistic champions share the same statistical archetype?

The clustering analysis performed in the companion report identified four stable champion archetypes based on base statistics, with a fundamental division between Ranged/Mana-like and Melee/Tanky-like champions along PC1. This section asks whether the synergies discovered by MBA reflect that underlying structure, specifically, whether winning champion pairs tend to come from the same statistical archetype or from complementary ones.

Note: Dataset used for MBA is 5 years old and cluster dataset was updated 2 years ago. Through this time some champions could have been changed or added.

# load clustering results from companion project
lol_clusters <- read.csv("lol_clusters.csv")

cluster_info <- lol_clusters %>%
  select(Name, cluster, fuzzy_cluster_label)

pairs_with_clusters <- pairs_metrics_stable %>%
  left_join(cluster_info, by = c("a" = "Name")) %>%
  left_join(cluster_info, by = c("b" = "Name"),
            suffix = c("_a", "_b")) %>%
  filter(!is.na(cluster_a), !is.na(cluster_b)) %>%
  mutate(
    same_cluster = cluster_a == cluster_b,
    same_fuzzy   = fuzzy_cluster_label_a == fuzzy_cluster_label_b,
    cluster_pair = paste0("KM", pmin(as.integer(cluster_a),
                                     as.integer(cluster_b)),
                          "-KM", pmax(as.integer(cluster_a),
                                      as.integer(cluster_b)))
  )

# how many pairs matched
cat("Total stable pairs:", nrow(pairs_metrics_stable), "\n")
## Total stable pairs: 161
cat("Pairs matched to cluster data:", nrow(pairs_with_clusters), "\n\n")
## Pairs matched to cluster data: 161
# Q1: same vs different fuzzy archetype
q1 <- pairs_with_clusters %>%
  group_by(same_fuzzy) %>%
  summarise(
    n_pairs  = n(),
    avg_lift = round(mean(lift), 3),
    avg_n    = round(mean(n), 1),
    .groups  = "drop"
  ) %>%
  mutate(same_fuzzy = ifelse(same_fuzzy,
                             "Same archetype",
                             "Different archetype"))

knitr::kable(q1,
  col.names = c("Pair type", "N pairs", "Avg lift", "Avg co-occurrences"),
  caption = "Synergy strength by archetype similarity (fuzzy K=2)")
Synergy strength by archetype similarity (fuzzy K=2)
Pair type N pairs Avg lift Avg co-occurrences
Different archetype 96 1.181 46.1
Same archetype 65 1.180 43.9
# Q2: which cluster combinations produce strongest synergies
q2 <- pairs_with_clusters %>%
  group_by(cluster_pair) %>%
  summarise(
    n_pairs  = n(),
    avg_lift = round(mean(lift), 3),
    avg_n    = round(mean(n), 1),
    .groups  = "drop"
  ) %>%
  arrange(desc(avg_lift))

knitr::kable(q2,
  col.names = c("Cluster pair", "N pairs", "Avg lift", "Avg co-occurrences"),
  caption = "Synergy strength by K-means cluster combination")
Synergy strength by K-means cluster combination
Cluster pair N pairs Avg lift Avg co-occurrences
KM4-KM4 1 2.016 41.0
KM2-KM3 19 1.392 44.8
KM1-KM4 6 1.231 43.5
KM3-KM3 31 1.185 44.8
KM1-KM2 8 1.152 41.8
KM1-KM3 67 1.139 47.1
KM1-KM1 18 1.131 44.1
KM3-KM4 10 1.061 42.1
KM2-KM4 1 0.975 37.0
# Q3: boxplot
ggplot(pairs_with_clusters,
       aes(x = fuzzy_cluster_label_a,
           y = lift,
           fill = fuzzy_cluster_label_b)) +
  geom_boxplot(alpha = 0.7, outlier.shape = 21) +
  theme_minimal() +
  labs(
    title = "Lift distribution by archetype combination",
    subtitle = "Does synergy strength depend on whether champions share the same archetype?",
    x = "Champion A archetype",
    y = "Lift",
    fill = "Champion B archetype"
  ) +
  scale_fill_manual(values = c("Melee/Tanky-like" = "steelblue",
                                "Ranged/Mana-like" = "coral"))

The results reveal a striking and counterintuitive finding: synergy strength in winning teams is almost completely independent of whether two champions share the same statistical archetype. The average lift for same-archetype pairs (1.180) and different-archetype pairs (1.181) is virtually identical, and the boxplot confirms that all four archetype combinations have nearly overlapping lift distributions with similar medians around 1.1.

This means that the clustering structure discovered in the companion report, the fundamental ranged vs. melee divide encoded in PC1, does not predict which champions will synergize in winning compositions. High Elo players do not preferentially build teams of statistically similar champions; instead, synergies emerge equally across archetype boundaries.

The K-means cluster breakdown adds nuance to this picture. The strongest average lift (2.016) belongs to the KM4-KM4 pair, but this is based on only a single pair and should be interpreted cautiously. The most reliable finding is the KM2-KM3 combination (19 pairs, avg lift = 1.392), representing synergies between the two melee sub-archetypes identified in the clustering project, this is the only cluster combination with both a meaningful sample size and consistently elevated lift. The dominant combination by volume is KM1-KM3 (67 pairs), which crosses the melee/ranged boundary and produces moderate lift (1.139), confirming that cross-archetype pairings are not only common but genuinely synergistic.

Taken together, these results suggest that champion design archetypes and in-game team synergies are two largely orthogonal dimensions of the League of Legends champion space, a finding that connects both projects and reveals the limits of using base statistics alone to predict strategic compatibility.


Session Info

sessionInfo()
## R version 4.5.2 (2025-10-31)
## Platform: aarch64-apple-darwin20
## Running under: macOS Sequoia 15.5
## 
## Matrix products: default
## BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## time zone: Europe/Warsaw
## tzcode source: internal
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] arulesViz_1.5.4   arules_1.7.13     Matrix_1.7-4      viridis_0.6.5    
##  [5] viridisLite_0.4.2 tidygraph_1.3.1   ggraph_2.2.2      ggplot2_4.0.1    
##  [9] knitr_1.51        stringr_1.6.0     tidyr_1.3.2       dplyr_1.1.4      
## [13] readr_2.1.6      
## 
## loaded via a namespace (and not attached):
##  [1] gtable_0.3.6       xfun_0.55          bslib_0.9.0        htmlwidgets_1.6.4 
##  [5] visNetwork_2.1.4   ggrepel_0.9.6      lattice_0.22-7     tzdb_0.5.0        
##  [9] vctrs_0.6.5        tools_4.5.2        generics_0.1.4     parallel_4.5.2    
## [13] ca_0.71.1          tibble_3.3.0       pkgconfig_2.0.3    RColorBrewer_1.1-3
## [17] S7_0.2.1           lifecycle_1.0.4    compiler_4.5.2     farver_2.1.2      
## [21] ggforce_0.5.0      codetools_0.2-20   graphlayouts_1.2.3 seriation_1.5.8   
## [25] htmltools_0.5.9    sass_0.4.10        yaml_2.3.12        pillar_1.11.1     
## [29] crayon_1.5.3       jquerylib_0.1.4    MASS_7.3-65        cachem_1.1.0      
## [33] iterators_1.0.14   foreach_1.5.2      TSP_1.2.6          tidyselect_1.2.1  
## [37] digest_0.6.39      stringi_1.8.7      purrr_1.2.1        forcats_1.0.1     
## [41] labeling_0.4.3     polyclip_1.10-7    fastmap_1.2.0      grid_4.5.2        
## [45] cli_3.6.5          magrittr_2.0.4     withr_3.0.2        scales_1.4.0      
## [49] bit64_4.6.0-1      registry_0.5-1     rmarkdown_2.30     igraph_2.2.2      
## [53] bit_4.6.0          otel_0.2.0         gridExtra_2.3      hms_1.1.4         
## [57] memoise_2.0.1      evaluate_1.0.5     rlang_1.1.6        Rcpp_1.1.0        
## [61] glue_1.8.0         tweenr_2.0.3       rstudioapi_0.17.1  vroom_1.6.7       
## [65] jsonlite_2.0.0     R6_2.6.1