LT Panel
RT Panel

Creating Models on League of Legends Using Competitive Data

Ace 2018-11-07 01:42:48
  Hi All! Welcome to my Kernel. In this kernel we are looking at how to create a model in League of Legends that will assist in predicting various events in the game, such as a win, an inhibitor take, a baron take, and more. The aim of this project is simple; can we calculate the next best event given what has occurred previously in the game so that the likelihood of eventually leading to an objective or win increases based on real match statistics? The answer of course, is YES. We will be looking at competitive League of Legends data in order to create a model that will predict and correlate the various objectives and objective combinations into the dragon objective capture. Before we dive into the more granular data, let's look at the number of neutral objectives taken at different points (time) in the game.
Click to expand code! library(plyr) library(RColorBrewer)library(dplyr) library(tidyverse) library(grid) library(gridExtra) library(magrittr) library(tidyr) library(purrr) library(stringr) library(ggplot2) library(shiny)

Time to import the League of Legends competitive files:

Click to expand code! bans <- read.csv("../input/bans.csv", stringsAsFactors = FALSE),gold <- read.csv("../input/gold.csv", stringsAsFactors = FALSE) names(gold) = sub('min_', '', colnames(gold)) gp1 <- subset(gold, select = -c(55:97)) ks <- read.csv("../input/kills.csv", stringsAsFactors = FALSE) lol <- read.csv("../input/LeagueofLegends.csv", stringsAsFactors = FALSE) %>% filter(Year == 2017 | Year == 2018) minfo <- read.csv("../input/matchinfo.csv", stringsAsFactors = FALSE) mons <- read.csv("../input/monsters.csv", stringsAsFactors = FALSE) struc <- read.csv("../input/structures.csv", stringsAsFactors = FALSE) mgp <- minfo%>% subset(select = -c(Season, blueTop, redTop, blueJungle, redJungle,blueMiddle, redMiddle, blueADC, redADC, blueSupport, redSupport))%>%filter( Year == 2017 | Year == 2018) %>% left_join(gp1, by = "Address") structures <- minfo%>% filter( Year == 2017 | Year == 2018) %>% left_join(struc, by = "Address") kills <- minfo%>% subset(select = -c(Season, blueTop, redTop, blueJungle, redJungle,blueMiddle, redMiddle, blueADC, redADC, blueSupport, redSupport))%>%filter( Year == 2017 | Year == 2018) %>% left_join(ks, by = "Address") mg <- minfo%>% subset(select = -c(Season, blueTop, redTop, blueJungle, redJungle,blueMiddle, redMiddle, blueADC, redADC, blueSupport, redSupport))%>%filter( Year == 2017 | Year == 2018) %>% left_join(mons, by = "Address") mg %>% ggplot(aes(x = Type.y, y = Time, fill = Type.y))+geom_violin()+coord_flip()+labs(title = "Neutral Objectives Taken over Time", y ="Time (mins)", x="Objective Type")+theme(legend.position = "top", legend.text = element_text(size= 8))
Click to expand code!mg %>% ggplot(aes(x = Type.y, y = Time, fill = Type.y))+geom_violin()+coord_flip()+labs(title = "Neutral Objectives Taken over Time", y ="Time (mins)", x="Objective Type")+theme(legend.position = "top", legend.text = element_text(size= 8))
Click to expand code! struc1 <- struc %>% arrange(Address, Time)mons1 <- mons %>% arrange(Address, Time) most1 <- rbind.fill(mons1, struc1) most2 <- most1 %>% arrange(Address, Time) ylol <- lol%>% select(League, Year, Season, bResult, rResult, Address, gamelength) most3 <- most2 %>% left_join(ylol, by = "Address") %>% filter(Year==2017|Year==2018) gp <- read_csv("../input/gold.csv") gpy <- lol %>% select(Year, Address) %>%left_join(gold, by = "Address") %>% filter(Year==2017|Year ==2018) names(gpy)[4:98] gpy2<-gpy gpy3 <- gather(gpy2,min, value, -Year, -Address, -Type) gpy4 <- gpy3%>% na.omit%>%arrange(Address, min) gpy4$min <- as.numeric(as.character(gpy4$min)) gpy4$value <- as.numeric(as.character(gpy4$value)) gpy5 <- gpy4 %>% arrange(Address, min) gpyt <- gpy5 %>% filter(Type == "golddiff") glimpse(gpyt) tst <- most3 %>% arrange(Address, Time) tst1 <- tst %>% filter(Team == "bDragons"|Team =="bTowers"|Team == "bBarons"|Team =="bHeralds"|Team =="bInhibs") tst2 <- tst %>% filter(Team == "rDragons"|Team =="rTowers"|Team == "rBarons"|Team =="rHeralds"|Team =="rInhibs")
Before we go into the math, let's go through some of the various factors that typically affect another aspect of the game such as a dragon being taken are as follows: Jungler Positioning: warding, camp timings, tracking movement, death timer Champions: damage output (health % dmg), dueling potential, priority, health % damage, overall laning potential, team composition. Summoner spells: which are available. Key target or initiate flashes up or down Minion Wave Management: lanes pushing, big wave accumulating, cannon waves. Item Spikes: Early spiking champions have a greater chance of securing objectives due to them being more impactful at, of course, an earlier point in the game. Scaling champs require more time and gold to provide a bigger impact. Major Objectives: Has a tower been taken bot? Has top tower fallen? How many towers up is team A over team B? One can get more granular as well and ask some of the following questions: As a result are the minions at inner tower making it harder for the laner to respond to a dragon take? Is baron up? Is Rift Herald up? Is mid tower down, limiting vision from the enemy? Does one team have deeper jungle wards? I will be focusing on the major objectives and gold differentials which will reflect item advantages and therefore, additional team fight advantages.  
Click to expand code! tst5 <- tst1 %>% rbind(tst2) tst4 <- tst5 %>% mutate(bvr = substr(Team,1,1))noo <- tst4%>% group_by(Address) %>% arrange(Time)%>% dplyr::mutate( #Type = factor(Type) dragonID = cumsum(dplyr::lag(Team == 'bDragons'|Team == 'rDragons', default = 1))) %>% filter(bvr == "b")%>% dplyr::group_by(Address, League, bvr, dragonID) %>% dplyr::summarize( dragonType = last(Type), Time = last(Time), tmp = list(as.data.frame(table(Type)))) noo1 <- tst4%>% group_by(Address) %>% arrange(Time)%>% dplyr::mutate( #Type = factor(Type) dragonID = cumsum(dplyr::lag(Team == 'bDragons'|Team == 'rDragons', default = 1))) %>% filter(bvr == "r")%>% dplyr::group_by(Address, League, bvr, dragonID) %>% dplyr::summarize( dragonType = last(Type), Time = last(Time), tmp = list(as.data.frame(-table(Type)))) haha <- noo%>%rbind(na.omit(noo1)) glimpse(haha) brdrag<- haha%>% unnest() %>% spread(Type, Freq, fill = 0) %>% #select(-ends_with("DRAGON")) %>% group_by(Address) %>% arrange(Time)%>% mutate_at(vars(AIR_DRAGON:OUTER_TURRET), cumsum) %>% filter(str_detect(dragonType, "DRAGON")) brdgp <- brdrag %>% mutate(min = ceiling(Time)) %>%inner_join(gpyt, by = c("Address", "min")) brdgp%>%ungroup()%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, OUTER_TURRET)) brdgp%>%ungroup()%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, BARON_NASHOR)) brdgp%>%ungroup()%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, INNER_TURRET)) brdgp%>%ungroup()%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, (OUTER_TURRET +INNER_TURRET))) bvg <- brdgp %>% filter(bvr == "b")%>% ungroup() rvg <- brdgp %>% filter(bvr == "r")%>% ungroup() bvg%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, OUTER_TURRET)) bvg%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, BARON_NASHOR)) bvg%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, INNER_TURRET)) bvg%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, (OUTER_TURRET +INNER_TURRET))) rvg%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, OUTER_TURRET)) rvg%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, BARON_NASHOR)) rvg%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, INNER_TURRET)) rvg%>%group_by(dragonID) %>% dplyr::summarize(N = n(),Cor = cor(value, (OUTER_TURRET +INNER_TURRET))) bvo1 <- lm(dragonID ~ + OUTER_TURRET + value, bvg) bvo1 bvi1 <- lm(dragonID ~ INNER_TURRET + value, bvg)#%>% summary() bvi1 boi1 <- lm(dragonID ~ (OUTER_TURRET+INNER_TURRET), bvg)#%>% summary() boi1 bvoi1 <- lm(dragonID ~ OUTER_TURRET+INNER_TURRET+value, bvg)#%>% summary() bvoi1 bb1 <- lm(dragonID ~ BARON_NASHOR, bvg)#%>% summary() bb1 bvb1 <- lm(dragonID ~ BARON_NASHOR + value, bvg)#%>% summary() bvb1 bvboi1 <- lm(dragonID ~ BARON_NASHOR+OUTER_TURRET+INNER_TURRET+value, bvg)#%>% summary() bvboi1 bvo1%>%summary() bvi1%>%summary() bvoi1%>%summary() boi1%>%summary() bb1%>%summary() bvb1%>%summary() bvboi1%>%summary() rvo1 <- lm(dragonID ~ OUTER_TURRET + value, rvg) rvo1 rvi1 <- lm(dragonID ~ INNER_TURRET + value, rvg)#%>% summary() rvi1 roi1 <- lm(dragonID ~ (OUTER_TURRET+INNER_TURRET), rvg)#%>% summary() roi1 rvoi1 <- lm(dragonID ~ OUTER_TURRET+INNER_TURRET+value, rvg)#%>% summary() rvoi1 rb1 <- lm(dragonID ~ BARON_NASHOR, rvg)#%>% summary() rb1 rvb1 <- lm(dragonID ~ BARON_NASHOR + value, rvg)#%>% summary() rvb1 rvboi1 <- lm(dragonID ~ BARON_NASHOR+OUTER_TURRET+INNER_TURRET+value, rvg)#%>% summary() rvboi1 rvo1%>%summary() rvi1%>%summary() rvoi1%>%summary() roi1%>%summary() rb1%>%summary() rvb1%>%summary() rvboi1%>%summary() bvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(OUTER_TURRET, value, col = 'darkblue'))+geom_point()+geom_jitter()+geom_smooth(method = "lm",colour="blue", se = FALSE)+facet_grid(~League) + ggtitle("Blue Side Dragon's taken by Outer Turret and GP Deltas") +xlab("Outer Turret (Delta)") + ylab("Gold Differential")+theme(legend.position = "none") rvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(OUTER_TURRET, value, col = "red"))+geom_point()+geom_jitter()+geom_smooth(method = "lm", colour="blue", se = FALSE)+facet_grid(~League) + ggtitle("Red Side Dragon's taken by Outer Turret and GP Deltas") +xlab("Outer Turret (Delta)") + ylab("Gold Differential")+theme(legend.position = "none") bvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(INNER_TURRET, value, col = 'darkblue'))+geom_point()+geom_jitter()+geom_smooth(method = "lm",colour="blue", se = FALSE)+facet_grid(~League) + ggtitle("Blue Side Dragon's taken by Inner Turret and GP Deltas") +xlab("Outer Turret (Delta)") + ylab("Gold Differential")+theme(legend.position = "none") rvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(INNER_TURRET, value, col = "red"))+geom_point()+geom_jitter()+geom_smooth(method = "lm",colour="blue", se = FALSE)+facet_grid(~League) + ggtitle("Red Side Dragon's Dragon's taken by Inner Turret and GP Deltas") +xlab("Outer Turret (Delta)") + ylab("Gold Differential") +theme(legend.position = "none") bvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(INNER_TURRET + OUTER_TURRET, value, col = 'darkblue'))+geom_point()+geom_jitter()+geom_smooth(method = "lm",colour="blue", se = FALSE)+facet_grid(~League)+ ggtitle("Blue Side Dragon's Dragon's taken by Inner + Outer Turrets and GP Deltas") +xlab("Outer and Inner Turret (Delta)") + ylab("Gold Differential")+theme(legend.position = "none") rvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(INNER_TURRET + OUTER_TURRET, value, col = "red"))+geom_point()+geom_jitter()+geom_smooth(method = "lm", colour="blue", se = FALSE)+facet_grid(~League)+ ggtitle("Red Side Dragon's Dragon's taken by Inner + Outer Turrets and GP Deltas") +xlab("Outer and Inner Turret (Delta)") + ylab("Gold Differential")+theme(legend.position = "none")

A sample of one of the many models one can create using the code above.

Now that we cleaned our data files, calculated and measured the correlations and formulas for the various objectives and their effects on securing a major objective such as a dragon, we can even measure regressions on gold differences and turret takes effects on the dragon secure. We will measure the different combinations, by region and by major tournament. Have you noticed any interesting trends?
Click to expand code!bvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(OUTER_TURRET, value, col = 'darkblue'))+geom_point()+geom_jitter()+geom_smooth(method = "lm",colour="blue", se = FALSE)+facet_grid(~League) + ggtitle("Blue Side Dragon's taken by Outer Turret and GP Deltas") +xlab("Outer Turret (Delta)") + ylab("Gold Differential")+theme(legend.position = "none")rvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(OUTER_TURRET, value, col = "red"))+geom_point()+geom_jitter()+geom_smooth(method = "lm", colour="blue", se = FALSE)+facet_grid(~League) + ggtitle("Red Side Dragon's taken by Outer Turret and GP Deltas") +xlab("Outer Turret (Delta)") + ylab("Gold Differential")+theme(legend.position = "none") bvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(INNER_TURRET, value, col = 'darkblue'))+geom_point()+geom_jitter()+geom_smooth(method = "lm",colour="blue", se = FALSE)+facet_grid(~League) + ggtitle("Blue Side Dragon's taken by Inner Turret and GP Deltas") +xlab("Outer Turret (Delta)") + ylab("Gold Differential")+theme(legend.position = "none") rvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(INNER_TURRET, value, col = "red"))+geom_point()+geom_jitter()+geom_smooth(method = "lm",colour="blue", se = FALSE)+facet_grid(~League) + ggtitle("Red Side Dragon's Dragon's taken by Inner Turret and GP Deltas") +xlab("Outer Turret (Delta)") + ylab("Gold Differential") +theme(legend.position = "none") bvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(INNER_TURRET + OUTER_TURRET, value, col = 'darkblue'))+geom_point()+geom_jitter()+geom_smooth(method = "lm",colour="blue", se = FALSE)+facet_grid(~League)+ ggtitle("Blue Side Dragon's Dragon's taken by Inner + Outer Turrets and GP Deltas") +xlab("Outer and Inner Turret (Delta)") + ylab("Gold Differential")+theme(legend.position = "none") rvg %>% filter(League %in% c("EULCS", "LCK", "LMS","MSI","NALCS", "RR", "WC")) %>%ggplot(aes(INNER_TURRET + OUTER_TURRET, value, col = "red"))+geom_point()+geom_jitter()+geom_smooth(method = "lm", colour="blue", se = FALSE)+facet_grid(~League)+ ggtitle("Red Side Dragon's Dragon's taken by Inner + Outer Turrets and GP Deltas") +xlab("Outer and Inner Turret (Delta)") + ylab("Gold Differential")+theme(legend.position = "none")
     Find anything interesting? It is clear that gold has a massive impact on whether dragons are taken, but there are some interesting notes such as the correlation differences between having Inner Turrets on Red and Blue Side. Red Side will take Dragons at a deficit much more often than Blue Side would. Are there positional advantages for Red Side in competitive?  Which region tends to go for dragons at a gold deficit? Which region might overprioritize Dragons with a large gold lead? Why is this a trend that exists? With all these questions, you can get answers that lead to further questions, which can solve the varying problems within a team's or region's focus. This is useful for dragons, but how would we switch to another major objective? In order to count the effects on other objectives, all you would have to do is change the original function's primary variables (dragonID, dragonType, filter) for the correct objective.

Champions and their effect on Dragon Secures

We looked into the varying objective advantages and disadvantages on a dragon secure. Let's focus on another variable that could very heavily influence a dragon secure, champions.
Click to expand code!chimps <- lol %>% select(Address, redSupportChamp, redADCChamp, redMiddleChamp, redJungleChamp, redTopChamp, blueTopChamp, blueJungleChamp, blueMiddleChamp, blueADCChamp, blueSupportChamp) chimps1 <- chimps %>% gather(role, champname, -Address)chimps2 <- chimps1 %>% mutate(bvr = substr(role, 1,1)) rekt <- mons1 %>% dplyr::group_by(Address, Team) %>% dplyr::filter(Team == "bDragons" | Team == "rDragons" ) %>% dplyr::count(Team == "bDragons", Team == "rDragons") rekttl <- rekt %>% dplyr::group_by(Address, Team) %>% dplyr::mutate(total = sum(n)) rekttl <- rekt %>% dplyr::group_by(Address) %>% dplyr::mutate(total = sum(n)) rekt1 <- rekt %>% mutate(bvr = substr(Team, 1,1)) rekttl1 <- rekttl %>% select(Address, Team, total) rekttl1 <- rekttl %>% select(Address, total) rko <- chimps2 %>% left_join(rekttl1, by = c("Address")) rekt2 <- rekt1 %>% select(Address, Team, n, bvr) rko1 <- rko %>% left_join(rekt2, by = c("Address", "bvr"), fill = 0) rko2 <- rko1 rko2[is.na(rko2)]<-0 drgchmpsum<- rko2 %>% dplyr::group_by(champname) %>% dplyr::summarize(game_count =n(), drag_avg_game = sum(n)/n(), drag_percent_secured = (sum(n))/(sum(total))) head(drgchmpsum) drgchmpsum %>% arrange(desc(game_count)) %>% ggplot(aes(drag_percent_secured))+facet_wrap(~champname)+geom_histogram()+theme(legend.position = "none")+ggtitle("Comparing ratios of Dragon's secured per champion") drgchmpsum1 <- drgchmpsum drgchmpsum1$avg_type <- ifelse(drgchmpsum1$drag_percent_secured < 0.5, "below", "above") # above / below avg flag drgchmpsum1 <- drgchmpsum1[order(drgchmpsum1$drag_percent_secured), ] # sort drgchmpsum1$champname <- factor(drgchmpsum1$champname, levels = drgchmpsum1$champname) drgchmpsum1 %>% filter(game_count >40)%>% ggplot(aes(x=champname, y=drag_percent_secured, label=(drag_percent_secured))) + geom_bar(stat='identity', aes(fill=(avg_type)), width=.5) + scale_fill_manual(name="Dragon Average", labels = c("Above Average", "Below Average"), values = c("above"="#00ba38", "below"="#f8766d")) + labs(subtitle="Average Dragon Secures per Champion in Competitive", title= "Dragon Averages") + coord_flip()
champname game_count drag_avg_game drag_percent_secured
Aatrox 6 1 .25
Ahri 446 1.896861 0.5032719
Akali 17 1.941176 0.5892857
Alistar 490 1.695918 0.4856809
Amumu 1 0 0
Anivia 45 1.644444 0.4713376
 
Although the charts aren't very clear yet, one can easily arrange by descending order of percentages of dragon secures, clip out the best and worst performers, then go into why it might be that way. This can be changed by the red side or blue side as well, and all of these are manipulatable. For example, see below:
drgchmpsum %>% filter(game_count >20) %>%arrange(desc(drag_percent_secured))%>%head(n=10)drgchmpsum %>% filter(game_count >20) %>%arrange(drag_percent_secured)%>%head(n=10)
champname game_count drag_avg_game drag_percent_secured
Leona 23 2.652174 0.7011494
Aurelion Sol 76 2.223684 0.5971731
Brand 59 2.338983 0.5922747
Kayle 21 1.904762 0.5714286
Blitzcrank 266 1.932331 0.5667034
Sion 53 2.283019 0.5654206
Singed 40 1.925 0.557971
Ornn 108 2.12037 0.5478469
Swain 40 2.15 0.5443038
Fizz 138 2.036232 0.5435203
champname game_count drag_avg_game drag_percent_secured
Irelia 32 1.15625 0.3592233
Nocturne 24 1.333333 0.3595506
Xerath 24 1.666667 0.4123711
Ziggs 118 1.627119 0.4155844
Sivir 374 1.671123 0.4352368
KogMaw 466 1.607296 0.4431953
Soraka 41 1.585366 0.4452055
Katarina 72 1.583333 0.4453125
Zilean 114 1.701754 0.448037
Illaoi 24 1.458333 0.4487179
At first glance, it seems like champions with strong early-mid game initiation and roaming power seem to secure dragon more often than the others. These types of information are all available to manipulate and create models which can help any individual looking to gain an edge in the draft or in game. Which champions should we be playing? What objectives will increase our chances of taking a first Baron?
  Feel free to download this kernel here and edit the code as you see fit! Follow me on Twitter at @AlbertPCoh if you have any questions or just want to see new content!
 

Latest Poll

first poll

Who will win the League of Legends World Championship?