Introduction

Permainan “League of Legends” adalah salah satu game online paling populer di seluruh dunia, dengan jutaan pemain aktif setiap harinya. Permainan ini menggabungkan elemen-elemen MOBA (Multiplayer Online Battle Arena) dan RPG (Role-Playing Game), di mana pemain berperan sebagai karakter atau hero dan bertarung dalam tim untuk mengalahkan tim lawan dan meraih kemenangan.

Dalam permainan ini, setiap karakter atau hero memiliki kemampuan yang unik dan berbeda, serta strategi yang dapat digunakan untuk memenangkan permainan. Oleh karena itu, memahami karakter dan strategi yang paling efektif sangat penting bagi pemain untuk menjadi sukses dalam permainan ini.

Dataset “League of Legends Challenger Ranked Games 2020” yang tersedia di Kaggle berisi data permainan dari wilayah Korea Selatan dengan pemain peringkat “Challenger”, yang dianggap sebagai salah satu wilayah pemain paling kompetitif di dunia. Data ini mencakup berbagai informasi, seperti hasil akhir permainan, strategi yang digunakan oleh pemain, statistik pemain, serta penggunaan item atau perlengkapan yang berbeda dalam setiap permainan.

Dataset ini akan digunakan untuk mengembangkan model prediksi untuk hasil permainan, serta meningkatkan pemahaman tentang strategi yang paling efektif dalam permainan ini. Ada tiga jenis model machine learning yang akan digunakan: Naive Bayes, Decision Tree, dan Random Forest. Ketiga model ini sangat populer dan sering digunakan dalam berbagai bidang analisis data karena memiliki kemampuan untuk menghasilkan prediksi yang akurat.

Dengan memanfaatkan dataset ini, pengguna dapat memperoleh wawasan yang berharga tentang dinamika permainan “League of Legends” dan cara-cara untuk mencapai kemenangan dalam permainan yang paling kompetitif di dunia.

Data Preperation

Import Library

Pertama kita akan memanggil library yang dibutuhkan, karena kita akan melakukan sedikit visualisasi data dan membuat model logistic regression, maka kita akan menggunkan library sebagai berikut.

library(dplyr)
library(gtools)
library(car)
library(GGally)
library(caret)
library(class)
library(corrplot)
library(DMwR)
library(mice)
library(ROSE)
library(ggplot2)
library(inspectdf)
library(e1071)
library(ROCR)
library(partykit)
library(randomForest)

Read data / importing data

Sebelum menganalisa dataset, kita akan memanggil dataset “League Of Legends High elo Ranked Games(2020)” dari CHallenger_Ranked_Games.csv dan disimpan kedalam object bernama lol, kemudian akan dilakukan Exploratory Data Analyst terhadap dataset tersebut.

lol <- read.csv("Challenger_Ranked_Games.csv")

Inspeksi Data

Selanjutnya kita akan melihat dataset yang sudah kita panggil, gunakan head untuk melihat 6 baris pertama.

head(lol)

Selanjutnya melihat isi dataset dengan menggunakan glimpse

glimpse(lol)
#> Rows: 26,904
#> Columns: 50
#> $ gameId                  <dbl> 4247263043, 4247155821, 4243963257, 4241678498…
#> $ gameDuraton             <int> 1323, 1317, 932, 2098, 2344, 1567, 1686, 1588,…
#> $ blueWins                <int> 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0…
#> $ blueFirstBlood          <int> 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0…
#> $ blueFirstTower          <int> 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0…
#> $ blueFirstBaron          <int> 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0…
#> $ blueFirstDragon         <int> 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0…
#> $ blueFirstInhibitor      <int> 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0…
#> $ blueDragonKills         <int> 0, 2, 0, 1, 2, 2, 0, 2, 3, 1, 0, 2, 3, 0, 3, 0…
#> $ blueBaronKills          <int> 0, 0, 0, 1, 2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0…
#> $ blueTowerKills          <int> 0, 4, 0, 2, 11, 8, 2, 10, 8, 4, 0, 9, 7, 4, 10…
#> $ blueInhibitorKills      <int> 0, 0, 0, 0, 3, 1, 0, 2, 1, 0, 0, 2, 1, 0, 1, 0…
#> $ blueWardPlaced          <int> 38, 57, 28, 129, 114, 65, 72, 79, 89, 43, 3, 4…
#> $ blueWardkills           <int> 13, 18, 7, 39, 35, 23, 26, 39, 57, 20, 0, 14, …
#> $ blueKills               <int> 15, 19, 5, 26, 27, 26, 16, 36, 26, 20, 1, 25, …
#> $ blueDeath               <int> 31, 8, 20, 36, 40, 18, 31, 20, 25, 11, 0, 9, 7…
#> $ blueAssist              <int> 22, 31, 8, 45, 47, 43, 30, 88, 44, 34, 1, 24, …
#> $ blueChampionDamageDealt <int> 56039, 60243, 24014, 101607, 134826, 59839, 70…
#> $ blueTotalGold           <int> 37001, 41072, 22929, 63447, 74955, 52221, 4710…
#> $ blueTotalMinionKills    <int> 440, 531, 306, 774, 831, 576, 601, 613, 704, 4…
#> $ blueTotalLevel          <int> 56, 63, 42, 80, 83, 66, 65, 71, 71, 57, 12, 61…
#> $ blueAvgLevel            <dbl> 11.2, 12.6, 8.4, 16.0, 16.6, 13.2, 13.0, 14.2,…
#> $ blueJungleMinionKills   <int> 105, 111, 64, 226, 252, 171, 136, 157, 144, 11…
#> $ blueKillingSpree        <int> 4, 3, 0, 3, 5, 9, 5, 7, 5, 5, 0, 4, 4, 2, 5, 4…
#> $ blueTotalHeal           <int> 31035, 26397, 6567, 24718, 37161, 40925, 20503…
#> $ blueObjectDamageDealt   <int> 5675, 45303, 5651, 31019, 95287, 51855, 31788,…
#> $ redWins                 <int> 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1…
#> $ redFirstBlood           <int> 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1…
#> $ redFirstTower           <int> 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1…
#> $ redFirstBaron           <int> 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1…
#> $ redFirstDragon          <int> 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1…
#> $ redFirstInhibitor       <int> 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1…
#> $ redDragonKills          <int> 3, 0, 2, 3, 3, 1, 4, 2, 1, 0, 0, 0, 0, 3, 1, 4…
#> $ redBaronKills           <int> 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1…
#> $ redTowerKills           <int> 9, 2, 5, 6, 6, 2, 7, 1, 2, 1, 0, 0, 3, 2, 3, 8…
#> $ redInhibitorKills       <int> 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1…
#> $ redWardPlaced           <int> 50, 46, 40, 96, 100, 58, 72, 76, 96, 47, 3, 28…
#> $ redWardkills            <int> 17, 18, 9, 56, 43, 18, 29, 28, 33, 19, 1, 10, …
#> $ redKills                <int> 31, 8, 20, 36, 40, 18, 31, 20, 25, 11, 0, 9, 7…
#> $ redDeath                <int> 15, 19, 5, 26, 27, 26, 16, 36, 26, 20, 1, 25, …
#> $ redAssist               <int> 68, 8, 20, 72, 83, 34, 59, 30, 34, 13, 0, 8, 5…
#> $ redChampionDamageDealt  <int> 73016, 44218, 30797, 125866, 131536, 64242, 78…
#> $ redTotalGold            <int> 49832, 37136, 35364, 70528, 74596, 44280, 5803…
#> $ redTotalMinionKills     <int> 533, 571, 400, 832, 794, 555, 640, 608, 646, 4…
#> $ redTotalLevel           <int> 64, 59, 50, 84, 85, 61, 73, 63, 69, 53, 13, 52…
#> $ redAvgLevel             <dbl> 12.8, 11.8, 10.0, 16.8, 17.0, 12.2, 14.6, 12.6…
#> $ redJungleMinionKills    <int> 138, 134, 89, 205, 227, 140, 214, 133, 159, 92…
#> $ redKillingSpree         <int> 8, 3, 5, 9, 7, 3, 5, 5, 6, 3, 0, 4, 2, 4, 4, 9…
#> $ redTotalHeal            <int> 38711, 24829, 12589, 65312, 86431, 23741, 3141…
#> $ redObjectDamageDealt    <int> 60463, 14105, 45201, 88202, 64141, 20132, 6104…

Dari hasil tinjauan dataset di atas, ternyata dataset terdiri dari 26,904 baris dan 50 kolom. Beberapa kolom ada yang bertipe integer dan double.

Deskripsi Data:

Berikut adalah beberapa deskripsi dari kolom-kolom pada dataset kali ini:

  • gameId: Nomor unik untuk setiap pertandingan.
  • gameDuration: Durasi (dalam detik) dari pertandingan.
  • blueWins: Target variabel, apakah tim biru memenangkan pertandingan atau tidak (1 untuk kemenangan tim biru, 0 untuk kemenangan tim merah).
  • blueFirstBlood: Indikator apakah tim biru membunuh peserta pertama atau tidak. (1 = ya, 0 = tidak)
  • blueFirstTower: Indikator apakah tim biru berhasil menghancurkan menara pertama atau tidak. (1 = ya, 0 = tidak)
  • blueFirstBaron: Indikator apakah tim biru berhasil membunuh Baron Nashor pertama atau tidak. (1 = ya, 0 = tidak)
  • blueFirstDragon: Indikator apakah tim biru berhasil membunuh naga pertama atau tidak. (1 = ya, 0 = tidak)
  • blueFirstInhibitor: Indikator apakah tim biru berhasil menghancurkan inhibitor pertama atau tidak. (1 = ya, 0 = tidak)
  • blueDragonKills: Jumlah naga yang berhasil dibunuh oleh tim biru.
  • blueBaronKills: Jumlah Baron Nashor yang berhasil dibunuh oleh tim biru.
  • blueTowerKills: Jumlah menara yang berhasil dihancurkan oleh tim biru.
  • blueInhibitorKills: Jumlah inhibitor yang berhasil dihancurkan oleh tim biru.
  • blueWardPlaced: Jumlah ward yang ditempatkan oleh tim biru.
  • blueWardKills: Jumlah ward yang berhasil dibunuh oleh tim biru.
  • blueKills: Jumlah peserta yang berhasil dibunuh oleh tim biru.
  • blueDeath: Jumlah peserta tim biru yang tewas dalam pertandingan.
  • blueAssist: Jumlah assist yang berhasil dilakukan oleh tim biru.
  • blueChampionDamageDealt: Jumlah kerusakan yang dilakukan oleh para peserta tim biru pada peserta lawan.
  • blueTotalGold: Jumlah emas yang dikumpulkan oleh tim biru selama pertandingan.
  • blueTotalMinionKills: Jumlah minion yang berhasil dibunuh oleh tim biru.
  • blueTotalLevel: Total level dari semua peserta tim biru selama pertandingan.
  • blueAvgLevel: Rata-rata level dari semua peserta tim biru selama pertandingan.
  • blueJungleMinionKills: Jumlah minion hutan yang berhasil dibunuh oleh tim biru.
  • blueKillingSpree: Jumlah peserta tim biru yang berhasil membunuh peserta lawan secara beruntun tanpa mati.
  • blueTotalHeal: Total jumlah penyembuhan yang dilakukan oleh tim biru selama pertandingan.
  • blueObjectDamageDealt: Jumlah kerusakan yang dilakukan oleh tim biru pada struktur seperti menara dan inhibitor.
  • redWins: Hasil pertandingan dari tim merah (0 = kalah, 1 = menang)
  • redFirstBlood: Indikator apakah tim merah mendapat first blood (0 = tidak, 1 = ya)
  • redFirstTower: Indikator apakah tim merah menghancurkan tower pertama (0 = tidak, 1 = ya)
  • redFirstBaron: Indikator apakah tim merah membunuh baron pertama (0 = tidak, 1 = ya)
  • redFirstDragon: Indikator apakah tim merah membunuh naga pertama (0 = tidak, 1 = ya)
  • redFirstInhibitor: Indikator apakah tim merah menghancurkan inhibitor pertama (0 = tidak, 1 = ya)
  • redDragonKills: Jumlah naga yang dibunuh oleh tim merah
  • redBaronKills: Jumlah baron yang dibunuh oleh tim merah
  • redTowerKills: Jumlah tower yang dihancurkan oleh tim merah
  • redInhibitorKills: Jumlah inhibitor yang dihancurkan oleh tim merah
  • redWardPlaced: Jumlah ward yang diletakkan oleh tim merah
  • redWardkills: Jumlah ward yang dihancurkan oleh tim merah
  • redKills: Jumlah kill yang didapat oleh tim merah
  • redDeath: Jumlah death yang didapat oleh tim merah
  • redAssist: Jumlah assist yang didapat oleh tim merah
  • redChampionDamageDealt: Total kerusakan yang diberikan oleh tim merah kepada champion lawan
  • redTotalGold: Total gold yang didapatkan oleh tim merah
  • redTotalMinionKills: Jumlah minion yang dibunuh oleh tim merah
  • redTotalLevel: Total level dari semua anggota tim merah
  • redAvgLevel: Rata-rata level dari anggota tim merah
  • redJungleMinionKills: Jumlah minion hutan yang berhasil dibunuh oleh tim merah.
  • redKillingSpree: Jumlah peserta tim merah yang berhasil membunuh peserta lawan secara beruntun tanpa mati.
  • redTotalHeal: Total jumlah penyembuhan yang dilakukan oleh tim merah selama pertandingan.
  • redObjectDamageDealt: Jumlah kerusakan yang dilakukan oleh tim merah pada struktur seperti menara dan inhibitor.

Data Wrangling

Cek Nilai Unique

Mengecek nilai unik di setiap kolom untuk membantu dalam penentuan tipe data terutama tipe data kategorik/factor. Karena ingin mengecek semua kolomnya sekaligus maka digunakan perintah lapply().

lengths(lapply(lol, unique))
#>                  gameId             gameDuraton                blueWins 
#>                   26848                    2110                       2 
#>          blueFirstBlood          blueFirstTower          blueFirstBaron 
#>                       2                       2                       2 
#>         blueFirstDragon      blueFirstInhibitor         blueDragonKills 
#>                       2                       2                       8 
#>          blueBaronKills          blueTowerKills      blueInhibitorKills 
#>                       5                      12                       9 
#>          blueWardPlaced           blueWardkills               blueKills 
#>                     192                      98                      92 
#>               blueDeath              blueAssist blueChampionDamageDealt 
#>                      97                     203                   23857 
#>           blueTotalGold    blueTotalMinionKills          blueTotalLevel 
#>                   21250                    1016                     116 
#>            blueAvgLevel   blueJungleMinionKills        blueKillingSpree 
#>                     116                     348                      27 
#>           blueTotalHeal   blueObjectDamageDealt                 redWins 
#>                   20364                   22888                       2 
#>           redFirstBlood           redFirstTower           redFirstBaron 
#>                       2                       2                       2 
#>          redFirstDragon       redFirstInhibitor          redDragonKills 
#>                       2                       2                       7 
#>           redBaronKills           redTowerKills       redInhibitorKills 
#>                       5                      12                       8 
#>           redWardPlaced            redWardkills                redKills 
#>                     188                      98                      95 
#>                redDeath               redAssist  redChampionDamageDealt 
#>                      94                     200                   23909 
#>            redTotalGold     redTotalMinionKills           redTotalLevel 
#>                   21339                    1037                     114 
#>             redAvgLevel    redJungleMinionKills         redKillingSpree 
#>                     114                     343                      28 
#>            redTotalHeal    redObjectDamageDealt 
#>                   20481                   22860

Selanjutnya kita bisa mengubah tipe data di setiap kolom berdasarkan informasi di atas

Cek Tipe Data

Melakukan inspeksi tipe data untuk memastikan tipe data dari setiap kolomnya sudah sesuai.

glimpse(lol)
#> Rows: 26,904
#> Columns: 50
#> $ gameId                  <dbl> 4247263043, 4247155821, 4243963257, 4241678498…
#> $ gameDuraton             <int> 1323, 1317, 932, 2098, 2344, 1567, 1686, 1588,…
#> $ blueWins                <int> 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0…
#> $ blueFirstBlood          <int> 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0…
#> $ blueFirstTower          <int> 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0…
#> $ blueFirstBaron          <int> 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0…
#> $ blueFirstDragon         <int> 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0…
#> $ blueFirstInhibitor      <int> 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0…
#> $ blueDragonKills         <int> 0, 2, 0, 1, 2, 2, 0, 2, 3, 1, 0, 2, 3, 0, 3, 0…
#> $ blueBaronKills          <int> 0, 0, 0, 1, 2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0…
#> $ blueTowerKills          <int> 0, 4, 0, 2, 11, 8, 2, 10, 8, 4, 0, 9, 7, 4, 10…
#> $ blueInhibitorKills      <int> 0, 0, 0, 0, 3, 1, 0, 2, 1, 0, 0, 2, 1, 0, 1, 0…
#> $ blueWardPlaced          <int> 38, 57, 28, 129, 114, 65, 72, 79, 89, 43, 3, 4…
#> $ blueWardkills           <int> 13, 18, 7, 39, 35, 23, 26, 39, 57, 20, 0, 14, …
#> $ blueKills               <int> 15, 19, 5, 26, 27, 26, 16, 36, 26, 20, 1, 25, …
#> $ blueDeath               <int> 31, 8, 20, 36, 40, 18, 31, 20, 25, 11, 0, 9, 7…
#> $ blueAssist              <int> 22, 31, 8, 45, 47, 43, 30, 88, 44, 34, 1, 24, …
#> $ blueChampionDamageDealt <int> 56039, 60243, 24014, 101607, 134826, 59839, 70…
#> $ blueTotalGold           <int> 37001, 41072, 22929, 63447, 74955, 52221, 4710…
#> $ blueTotalMinionKills    <int> 440, 531, 306, 774, 831, 576, 601, 613, 704, 4…
#> $ blueTotalLevel          <int> 56, 63, 42, 80, 83, 66, 65, 71, 71, 57, 12, 61…
#> $ blueAvgLevel            <dbl> 11.2, 12.6, 8.4, 16.0, 16.6, 13.2, 13.0, 14.2,…
#> $ blueJungleMinionKills   <int> 105, 111, 64, 226, 252, 171, 136, 157, 144, 11…
#> $ blueKillingSpree        <int> 4, 3, 0, 3, 5, 9, 5, 7, 5, 5, 0, 4, 4, 2, 5, 4…
#> $ blueTotalHeal           <int> 31035, 26397, 6567, 24718, 37161, 40925, 20503…
#> $ blueObjectDamageDealt   <int> 5675, 45303, 5651, 31019, 95287, 51855, 31788,…
#> $ redWins                 <int> 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1…
#> $ redFirstBlood           <int> 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1…
#> $ redFirstTower           <int> 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1…
#> $ redFirstBaron           <int> 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1…
#> $ redFirstDragon          <int> 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1…
#> $ redFirstInhibitor       <int> 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1…
#> $ redDragonKills          <int> 3, 0, 2, 3, 3, 1, 4, 2, 1, 0, 0, 0, 0, 3, 1, 4…
#> $ redBaronKills           <int> 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1…
#> $ redTowerKills           <int> 9, 2, 5, 6, 6, 2, 7, 1, 2, 1, 0, 0, 3, 2, 3, 8…
#> $ redInhibitorKills       <int> 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1…
#> $ redWardPlaced           <int> 50, 46, 40, 96, 100, 58, 72, 76, 96, 47, 3, 28…
#> $ redWardkills            <int> 17, 18, 9, 56, 43, 18, 29, 28, 33, 19, 1, 10, …
#> $ redKills                <int> 31, 8, 20, 36, 40, 18, 31, 20, 25, 11, 0, 9, 7…
#> $ redDeath                <int> 15, 19, 5, 26, 27, 26, 16, 36, 26, 20, 1, 25, …
#> $ redAssist               <int> 68, 8, 20, 72, 83, 34, 59, 30, 34, 13, 0, 8, 5…
#> $ redChampionDamageDealt  <int> 73016, 44218, 30797, 125866, 131536, 64242, 78…
#> $ redTotalGold            <int> 49832, 37136, 35364, 70528, 74596, 44280, 5803…
#> $ redTotalMinionKills     <int> 533, 571, 400, 832, 794, 555, 640, 608, 646, 4…
#> $ redTotalLevel           <int> 64, 59, 50, 84, 85, 61, 73, 63, 69, 53, 13, 52…
#> $ redAvgLevel             <dbl> 12.8, 11.8, 10.0, 16.8, 17.0, 12.2, 14.6, 12.6…
#> $ redJungleMinionKills    <int> 138, 134, 89, 205, 227, 140, 214, 133, 159, 92…
#> $ redKillingSpree         <int> 8, 3, 5, 9, 7, 3, 5, 5, 6, 3, 0, 4, 2, 4, 4, 9…
#> $ redTotalHeal            <int> 38711, 24829, 12589, 65312, 86431, 23741, 3141…
#> $ redObjectDamageDealt    <int> 60463, 14105, 45201, 88202, 64141, 20132, 6104…

Kolom yang tipe datanya akan diubah: - blueWins, blueFirstBlood, blueFirstTower, blueFirstBaron, blueFirstDragon, blueFirstInhibitor -> Factor

Kolom yang dibuang: - gameId - blueTotalLevel karena nilainya sudah terwakili di blueAvgLevel - Semua kolom yang di tim Red karena kita fokus menganalisa Tim Blue.

Mengubah sekaligus beberapa tipe data dengan menggunakan perintah mutate_at

lol1 <- lol %>% 
  select(-c(1,20,27:50)) %>% 
  mutate_at(vars(blueWins, blueFirstBlood, blueFirstTower, blueFirstBaron, blueFirstDragon, blueFirstInhibitor), as.factor)

Mengecek kembali tipe data:

# Cek kembali struktur data
glimpse(lol1)
#> Rows: 26,904
#> Columns: 24
#> $ gameDuraton             <int> 1323, 1317, 932, 2098, 2344, 1567, 1686, 1588,…
#> $ blueWins                <fct> 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0…
#> $ blueFirstBlood          <fct> 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0…
#> $ blueFirstTower          <fct> 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0…
#> $ blueFirstBaron          <fct> 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0…
#> $ blueFirstDragon         <fct> 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0…
#> $ blueFirstInhibitor      <fct> 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0…
#> $ blueDragonKills         <int> 0, 2, 0, 1, 2, 2, 0, 2, 3, 1, 0, 2, 3, 0, 3, 0…
#> $ blueBaronKills          <int> 0, 0, 0, 1, 2, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0…
#> $ blueTowerKills          <int> 0, 4, 0, 2, 11, 8, 2, 10, 8, 4, 0, 9, 7, 4, 10…
#> $ blueInhibitorKills      <int> 0, 0, 0, 0, 3, 1, 0, 2, 1, 0, 0, 2, 1, 0, 1, 0…
#> $ blueWardPlaced          <int> 38, 57, 28, 129, 114, 65, 72, 79, 89, 43, 3, 4…
#> $ blueWardkills           <int> 13, 18, 7, 39, 35, 23, 26, 39, 57, 20, 0, 14, …
#> $ blueKills               <int> 15, 19, 5, 26, 27, 26, 16, 36, 26, 20, 1, 25, …
#> $ blueDeath               <int> 31, 8, 20, 36, 40, 18, 31, 20, 25, 11, 0, 9, 7…
#> $ blueAssist              <int> 22, 31, 8, 45, 47, 43, 30, 88, 44, 34, 1, 24, …
#> $ blueChampionDamageDealt <int> 56039, 60243, 24014, 101607, 134826, 59839, 70…
#> $ blueTotalGold           <int> 37001, 41072, 22929, 63447, 74955, 52221, 4710…
#> $ blueTotalLevel          <int> 56, 63, 42, 80, 83, 66, 65, 71, 71, 57, 12, 61…
#> $ blueAvgLevel            <dbl> 11.2, 12.6, 8.4, 16.0, 16.6, 13.2, 13.0, 14.2,…
#> $ blueJungleMinionKills   <int> 105, 111, 64, 226, 252, 171, 136, 157, 144, 11…
#> $ blueKillingSpree        <int> 4, 3, 0, 3, 5, 9, 5, 7, 5, 5, 0, 4, 4, 2, 5, 4…
#> $ blueTotalHeal           <int> 31035, 26397, 6567, 24718, 37161, 40925, 20503…
#> $ blueObjectDamageDealt   <int> 5675, 45303, 5651, 31019, 95287, 51855, 31788,…

Hasilnya tipe data setiap kolomnya sudah sesuai.

Cek Duplikat

Mengecek apakah terdapat data yang memiliki kesamaan nilai atau duplikat.

#cek duplicate
sum(duplicated(lol1))
#> [1] 56

Ternyata ada 56 data yang berisi duplikat value, kita bisa menghilangkan dengan fungsi distinct

lol1 <- distinct(lol1)

Selanjutnya cek apakah terdapat missing values.

Cek Missing Values

colSums(is.na(lol1))
#>             gameDuraton                blueWins          blueFirstBlood 
#>                       0                       0                       0 
#>          blueFirstTower          blueFirstBaron         blueFirstDragon 
#>                       0                       0                       0 
#>      blueFirstInhibitor         blueDragonKills          blueBaronKills 
#>                       0                       0                       0 
#>          blueTowerKills      blueInhibitorKills          blueWardPlaced 
#>                       0                       0                       0 
#>           blueWardkills               blueKills               blueDeath 
#>                       0                       0                       0 
#>              blueAssist blueChampionDamageDealt           blueTotalGold 
#>                       0                       0                       0 
#>          blueTotalLevel            blueAvgLevel   blueJungleMinionKills 
#>                       0                       0                       0 
#>        blueKillingSpree           blueTotalHeal   blueObjectDamageDealt 
#>                       0                       0                       0

Hasilnya tidak terdapat missing values, karena data kita sudah bersih kita bisa lanjut ke proses EDA.

Exploratory Data Analysis

Mendefinisikan Business Problem

Dalam kasus kali ini, kita akan memprediksi kolom target outcome berdasarkan beberapa variabel prediktor yang dipilih. Kita akan menentukan:

  • variable target (y): blueWins.
  • variable prediktor (x): semua variabel prediktor selain variable blueWins.

Selanjutnya kita akan cek apakah ada ketidaknormalan pada setiap kolom dari dataframe heart_new. Kita bisa mengeceknya dengan menggunakan summary.

summary(lol1)
#>   gameDuraton   blueWins  blueFirstBlood blueFirstTower blueFirstBaron
#>  Min.   : 190   0:13425   0:13329        0:12731        0:20297       
#>  1st Qu.:1153   1:13423   1:13519        1:14117        1: 6551       
#>  Median :1435                                                         
#>  Mean   :1449                                                         
#>  3rd Qu.:1738                                                         
#>  Max.   :3301                                                         
#>  blueFirstDragon blueFirstInhibitor blueDragonKills blueBaronKills 
#>  0:15937         0:16994            Min.   :0.00    Min.   :0.000  
#>  1:10911         1: 9854            1st Qu.:0.00    1st Qu.:0.000  
#>                                     Median :1.00    Median :0.000  
#>                                     Mean   :1.39    Mean   :0.307  
#>                                     3rd Qu.:2.00    3rd Qu.:1.000  
#>                                     Max.   :7.00    Max.   :4.000  
#>  blueTowerKills   blueInhibitorKills blueWardPlaced   blueWardkills   
#>  Min.   : 0.000   Min.   :0.0000     Min.   :  0.00   Min.   :  0.00  
#>  1st Qu.: 1.000   1st Qu.:0.0000     1st Qu.: 38.00   1st Qu.: 11.00  
#>  Median : 4.000   Median :0.0000     Median : 58.00   Median : 21.00  
#>  Mean   : 4.396   Mean   :0.6189     Mean   : 58.67   Mean   : 22.34  
#>  3rd Qu.: 7.000   3rd Qu.:1.0000     3rd Qu.: 79.00   3rd Qu.: 32.00  
#>  Max.   :11.000   Max.   :9.0000     Max.   :230.00   Max.   :109.00  
#>    blueKills       blueDeath        blueAssist     blueChampionDamageDealt
#>  Min.   : 0.00   Min.   :  0.00   Min.   :  0.00   Min.   :     0         
#>  1st Qu.:15.00   1st Qu.: 15.00   1st Qu.: 20.00   1st Qu.: 43178         
#>  Median :23.00   Median : 23.00   Median : 36.00   Median : 64951         
#>  Mean   :24.17   Mean   : 24.16   Mean   : 39.94   Mean   : 69696         
#>  3rd Qu.:32.00   3rd Qu.: 32.00   3rd Qu.: 53.00   3rd Qu.: 90112         
#>  Max.   :99.00   Max.   :102.00   Max.   :238.00   Max.   :333957         
#>  blueTotalGold    blueTotalLevel    blueAvgLevel  blueJungleMinionKills
#>  Min.   :  3704   Min.   :  5.00   Min.   : 1.0   Min.   :  0.0        
#>  1st Qu.: 37175   1st Qu.: 56.00   1st Qu.:11.2   1st Qu.: 90.0        
#>  Median : 48080   Median : 66.00   Median :13.2   Median :131.0        
#>  Mean   : 48152   Mean   : 65.01   Mean   :13.0   Mean   :129.6        
#>  3rd Qu.: 58635   3rd Qu.: 75.00   3rd Qu.:15.0   3rd Qu.:173.0        
#>  Max.   :121920   Max.   :132.00   Max.   :26.4   Max.   :402.0        
#>  blueKillingSpree blueTotalHeal    blueObjectDamageDealt
#>  Min.   : 0.000   Min.   :     0   Min.   :     0       
#>  1st Qu.: 3.000   1st Qu.: 14097   1st Qu.: 15625       
#>  Median : 5.000   Median : 21978   Median : 34937       
#>  Mean   : 5.549   Mean   : 25043   Mean   : 38398       
#>  3rd Qu.: 7.000   3rd Qu.: 32708   3rd Qu.: 57822       
#>  Max.   :29.000   Max.   :162442   Max.   :167692

Tampak ada ketidaknormalan pada kolom-kolom dengan Q3 yang bernilai 1 tapi nilai max-nya lebih dari Q3, ini mengindikasikan adanya outlier, akan tetapi di dalam sebuah permainan, nilai yang terpaut jauh dari datanya itu disebabkan karen playernya memang memiliki kemampuan diatas rata-rata player lain. Jadi kita tidak akan membuang outlier semacam ini karena ini juga penting dimasukkan sebagai salah satu indikator dalam sebuah strategi di game nantinya.

Cek class-imbalance

Kita akan mengecek keseimbangan data dari variabel target

prop.table(table(lol1$blueWins))
#> 
#>         0         1 
#> 0.5000372 0.4999628

Hasilnya kelas target kita seimbang.

Cross validation

RNGkind(sample.kind = "Rounding") # kalo Rnya beda versi
set.seed(100) # mengunci seed agar hasil split sama di tiaip komputer

index <- sample(nrow(lol1), nrow(lol1)*0.80)
lol_train <- lol1[index, ] 
lol_test <- lol1[-index, ] 

Naive Bayes

Naive Bayes merupakan salah satu metode klasifikasi yang menggunakan teorema Bayes. Pola pikir bayesian adalah selalu mengubah keyakinan kita berdasarkan informasi baru yang diterima. Naive Bayes melakukan klasifikasi berdasarkan peluang dependent antara prediktor dengan target variable (Bayes Theorem)

Kita akan membuat model naive bayes menggunakan data train dengan menggunakan semua predictor dan laplace = 1

lol_bayes <- naiveBayes(formula = blueWins ~ . ,
                          data = lol_train,
                          laplace = 1)

lakukan predict pada data test

lol_pred <- predict(lol_bayes, #model yang sudah dibuat
                      newdata = lol_test, #menguji di data yang belum dilihat
                      type = "class") #menghasilkan kelas tiap observasinya

Model Evaluation

confusionMatrix(lol_pred, #hasil predict
                lol_test$blueWins, #data aktual dari votes test
                positive = "1")
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction    0    1
#>          0 2276  537
#>          1  438 2119
#>                                                
#>                Accuracy : 0.8184               
#>                  95% CI : (0.8079, 0.8287)     
#>     No Information Rate : 0.5054               
#>     P-Value [Acc > NIR] : < 0.00000000000000022
#>                                                
#>                   Kappa : 0.6367               
#>                                                
#>  Mcnemar's Test P-Value : 0.001698             
#>                                                
#>             Sensitivity : 0.7978               
#>             Specificity : 0.8386               
#>          Pos Pred Value : 0.8287               
#>          Neg Pred Value : 0.8091               
#>              Prevalence : 0.4946               
#>          Detection Rate : 0.3946               
#>    Detection Prevalence : 0.4762               
#>       Balanced Accuracy : 0.8182               
#>                                                
#>        'Positive' Class : 1                    
#> 

Positive = menang FN = diprediksi kalah ternyata menang FP = diprediksi menang ternyata kalah

Meminimalisir: FP turun -> Precision, Karena kita mau meminimalisir kesalahan diprediksi menang ternyata kalah. Sehingga fokus kita di nilai Precision.

  • Accuracy : 0.9497
  • Sensitivity : 0.9420
  • Pos Pred Value : 0.9557

Terlihat Akurasi yang didapatkan sangat tinggi yakni 94.97%, dengan recall dan precision juga tinggi. Tapi disini kita hanay fokus pada nilai Pos Pred Value

ROC dan AUC Test

Selain menggunakan Confusion Matrix, kita bisa menggunakan metric lain untuk membandingkan antara model 1 dengan lainnya. Salah satunya adalah dengan menggunakan ROC Curve dan nilai Area Under the Curve (AUC).

ROC adalah kurva yang menggambarkan hubungan antara True Positive Rate (Sensitivity atau Recall) dengan False Positive Rate (1-Specificity) pada setiap threshold. Model yang baik idealnya memiliki True Positive Rate yang tinggi dan False Positive Rate yang rendah. Note: Specificity adalah True Negative Rate.

lol_prob <- predict(lol_bayes, lol_test, type = "raw")
lol_prob <- lol_prob[,2]
naive_roc <- prediction(lol_prob, 
                        lol_test$blueWins)
# performance
naive_roc_vec <- performance(naive_roc, 
                             "tpr", # True Positive Rate (Recall)
                             "fpr" # False Positive Rate (1 - Specificity)
                             )
# buat plot
plot(naive_roc_vec)

abline(0,1 , lty = 2) # buat garis lurus

auc.perf <- performance(naive_roc, measure ="auc")
auc.perf@y.values
#> [[1]]
#> [1] 0.898096

AUC = 0.898096, maka dapat disimpulkan bahwa model sangat baik dalam memisahkan kelas menang dan kalah.

Decision Tree

Decision Tree merupakan tree-based model yang cukup sederhana dengan performa yang robust/powerful untuk prediksi. Decision Tree menghasilkan visualisasi berupa pohon keputusan yang dapat diinterpretasi dengan mudah.

lol_tree <- ctree(formula = blueWins ~ ., 
                   data = lol_train)

Visualisasi:

# visualisasi plot decision tree
plot(lol_tree, type="simple")

Karena hasil plot di atas sangat bertumpuk, kita akan membuat plot decision tree dengan menggunakan fancyRpartPlot dari library rpart dan rattle

library(rpart)
library(rattle)

tree_model <- rpart(blueWins ~ ., 
                   data = lol_train)

fancyRpartPlot(tree_model, caption = NULL)

Dari hasil decision tree sederhana di atas, kita bisa membuat strategi dalam game LOL kedepannya, seperti jika ingin meningkatkan peluang tim akan menang maka harus menggunakan strategi (lihat leaf node dengan error terkecil) :

  1. Menghancurkan tower lawan kurang dari 3.5 tower, kematian player >=13 kali, membunuh tim lawan <9.5 kali.
  2. Menghancurkan tower lebih dari 3.5 tower lawan, jumlah kematian tim <25 kali, membunuh inhibitor >0.5 kali, membunuh tim lawan <26 kali.

Model Evaluation

Test

# prediksi kelas di data test
lol_tree_pred <- predict(object = lol_tree, 
                          newdata = lol_test) 
# confusion matrix
confusionMatrix(data = lol_tree_pred,
                reference = lol_test$blueWins,
                positive = "1")
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction    0    1
#>          0 2579   78
#>          1  135 2578
#>                                                
#>                Accuracy : 0.9603               
#>                  95% CI : (0.9548, 0.9654)     
#>     No Information Rate : 0.5054               
#>     P-Value [Acc > NIR] : < 0.00000000000000022
#>                                                
#>                   Kappa : 0.9207               
#>                                                
#>  Mcnemar's Test P-Value : 0.0001245            
#>                                                
#>             Sensitivity : 0.9706               
#>             Specificity : 0.9503               
#>          Pos Pred Value : 0.9502               
#>          Neg Pred Value : 0.9706               
#>              Prevalence : 0.4946               
#>          Detection Rate : 0.4801               
#>    Detection Prevalence : 0.5052               
#>       Balanced Accuracy : 0.9604               
#>                                                
#>        'Positive' Class : 1                    
#> 

Positive = menang FN = diprediksi kalah ternyata menang FP = diprediksi menang ternyata kalah

Meminimalisir: FP turun -> Precision, Karena kita mau meminimalisir kesalahan diprediksi menang ternyata kalah. Sehingga fokus kita di nilai Precision.

Train

# prediksi kelas di data train
pred_lol_train <- predict(object = lol_tree,
                           newdata = lol_train,
                           type = "response")

# confusion matrix data train
confusionMatrix(pred_lol_train,
                lol_train$blueWins,
                positive = "1")
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction     0     1
#>          0 10279   220
#>          1   432 10547
#>                                                
#>                Accuracy : 0.9696               
#>                  95% CI : (0.9673, 0.9719)     
#>     No Information Rate : 0.5013               
#>     P-Value [Acc > NIR] : < 0.00000000000000022
#>                                                
#>                   Kappa : 0.9393               
#>                                                
#>  Mcnemar's Test P-Value : < 0.00000000000000022
#>                                                
#>             Sensitivity : 0.9796               
#>             Specificity : 0.9597               
#>          Pos Pred Value : 0.9607               
#>          Neg Pred Value : 0.9790               
#>              Prevalence : 0.5013               
#>          Detection Rate : 0.4911               
#>    Detection Prevalence : 0.5112               
#>       Balanced Accuracy : 0.9696               
#>                                                
#>        'Positive' Class : 1                    
#> 

Test :

  • Accuracy : 0.9603
  • Sensitivity : 0.9706
  • Pos Pred Value : 0.9502

Train :

  • Accuracy : 0.9696
  • Sensitivity : 0.9796
  • Pos Pred Value : 0.9607

Accuracy turun sebesar 1% tidak masalah, bukan dikategorikan overfitting

ROC dan AUC Test

lol_tree_prob <- predict(object = lol_tree, newdata = lol_test, type = "prob") 
lol_tree_prob <- lol_tree_prob[,2]
tree_roc <- prediction(lol_tree_prob, 
                        lol_test$blueWins)
# performance
tree_roc_vec <- performance(tree_roc, 
                             "tpr", # True Positive Rate (Recall)
                             "fpr" # False Positive Rate (1 - Specificity)
                             )
# buat plot
plot(tree_roc_vec)

abline(0,1 , lty = 2) # buat garis lurus

auc.perf <- performance(tree_roc, measure ="auc")
auc.perf@y.values
#> [[1]]
#> [1] 0.9895049

AUC = 0.9895049, maka dapat disimpulkan bahwa model sangat baik dalam memisahkan kelas menang dan kalah.

Karena kita

Pruning and Tree Size

lol_tree_tuned <- ctree(formula = blueWins ~ .,
                             data = lol_train,
                             control = ctree_control(mincriterion = 0.05,
                                                     minsplit = 20,
                                                     minbucket = 10))

Model Evaluation

Test

# prediksi kelas di data test
pred_lol_test_tuned <- predict(lol_tree_tuned, 
                                lol_test)


# confusion matrix data test
confusionMatrix(pred_lol_test_tuned, 
               lol_test$blueWins, 
                positive = "1")
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction    0    1
#>          0 2600  102
#>          1  114 2554
#>                                              
#>                Accuracy : 0.9598             
#>                  95% CI : (0.9542, 0.9649)   
#>     No Information Rate : 0.5054             
#>     P-Value [Acc > NIR] : <0.0000000000000002
#>                                              
#>                   Kappa : 0.9195             
#>                                              
#>  Mcnemar's Test P-Value : 0.4542             
#>                                              
#>             Sensitivity : 0.9616             
#>             Specificity : 0.9580             
#>          Pos Pred Value : 0.9573             
#>          Neg Pred Value : 0.9623             
#>              Prevalence : 0.4946             
#>          Detection Rate : 0.4756             
#>    Detection Prevalence : 0.4968             
#>       Balanced Accuracy : 0.9598             
#>                                              
#>        'Positive' Class : 1                  
#> 

Train

# prediksi kelas di data train
pred_lol_train_tuned <- predict(object = lol_tree_tuned, 
                                 newdata = lol_train)

# confusion matrix data train
confusionMatrix(data = pred_lol_train_tuned,
                reference = lol_train$blueWins,
                positive = "1")
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction     0     1
#>          0 10405   274
#>          1   306 10493
#>                                              
#>                Accuracy : 0.973              
#>                  95% CI : (0.9707, 0.9751)   
#>     No Information Rate : 0.5013             
#>     P-Value [Acc > NIR] : <0.0000000000000002
#>                                              
#>                   Kappa : 0.946              
#>                                              
#>  Mcnemar's Test P-Value : 0.198              
#>                                              
#>             Sensitivity : 0.9746             
#>             Specificity : 0.9714             
#>          Pos Pred Value : 0.9717             
#>          Neg Pred Value : 0.9743             
#>              Prevalence : 0.5013             
#>          Detection Rate : 0.4885             
#>    Detection Prevalence : 0.5028             
#>       Balanced Accuracy : 0.9730             
#>                                              
#>        'Positive' Class : 1                  
#> 

Hasil dari confusion matrix:

Test :

  • Accuracy : 0.9603
  • Sensitivity : 0.9680
  • Pos Pred Value : 0.9526

Train :

  • Accuracy : 0.9709
  • Sensitivity : 0.9787
  • Pos Pred Value : 0.9637

Tampak penurunan akurasi sebesar 1%, tidak masalah, bukan dikategorikan overfitting.

ROC dan AUC Test

prob_lol_test_tuned <- predict(lol_tree_tuned, lol_test, type = "prob")
prob_lol_test_tuned <- prob_lol_test_tuned[,2]
tree_tuned_roc <- prediction(prob_lol_test_tuned, 
                        lol_test$blueWins)
# performance
tree_tuned_roc_vec <- performance(tree_tuned_roc, 
                             "tpr", # True Positive Rate (Recall)
                             "fpr" # False Positive Rate (1 - Specificity)
                             )
# buat plot
plot(tree_tuned_roc_vec)

abline(0,1 , lty = 2) # buat garis lurus

auc.perf <- performance(tree_tuned_roc, measure ="auc")
auc.perf@y.values
#> [[1]]
#> [1] 0.9854001

AUC = 0.9861413, maka dapat disimpulkan bahwa model sangat baik dalam memisahkan kelas menang dan kalah.

Random Forest

Random Forest adalah salah satu jenis Ensemble Method yang terdiri dari banyak Decision Tree. Masing-masing Decision Tree memiliki karakteristik masing-masing dan tidak saling berhubungan. Random Forest memanfaatkan konsep Bagging (Bootstrap and Aggregation) dalam pembuatannya. Berikut adalah prosesnya:

  1. Bootstrap sampling: Membuat data dengan random sampling (with replacement) dari data keseluruhan dan mengizinkan adanya baris yang terduplikat.
  2. Dibuat 1 decision tree untuk masing-masing data hasil bootstrap. Digunakan parameter mtry untuk memilih banyaknya calon prediktor secara random (Automatic Feature Selection)
  3. Melakukan prediksi terhadap observasi yang baru untuk setiap Decision Tree.
  4. Aggregation: Menghasilkan satu prediksi tunggal untuk memprediksi.
  • Kasus klasifikasi: majority voting
  • Kasus regresi: rata-rata nilai target

Kekurangan dari Random Forest adalah membutuhkan waktu komputasi yang cukup lama. Hal ini dapat diatasi dengan membuang predictor yang variansinya mendekati nol (dianggap kurang informatif). Prediktor yang memiliki nilai seragam (low variance) bisa diremove sebelum dimasukkan kedalam pemodelan. karena kolom tersebut tidak memberikan informasi yang berarti.

Untuk mengetahui kolom mana saja yang bervariansi bisa menggunakan function nearZeroVar() dari caret:

nearZeroVar(lol1)
#> integer(0)

Ternyata tidak ada kolom yang bervariansi mendekati nol (dianggap kurang informatif)

Model Fitting

# set.seed(123)
# 
# ctrl <- trainControl(method = "repeatedcv",
#                      number = 5, # k-fold, perulangan cross validation sebanyak 5 kali
#                      repeats = 3) # repetisi, proses k fold di ulang lagi sebanyak 3 kali
# 
# lol_forest <- train(blueWins ~ .,  # train di caret itu mempermudah mengevaluasi bermacam model
#                    data = lol_train,
#                    method = "rf", # random forest, ini bisa diubah tergantung modelnya
#                    trControl = ctrl) # k-fold cross validation
# 
# saveRDS(lol_forest, "lol_forest_2.RDS") # simpan model

Salah satu kelemahan Random Forest adalah pembuatan model yang membutuhkan waktu yang cukup lama. Practice yang baik saat selesai melakukan training adalah menyimpan model tersebut ke dalam bentuk file RDS dengan function saveRDS() agar model dapat langsung digunakan tanpa harus training dari awal.

# read model
lol_forest <- readRDS("lol_forest_2.RDS")
lol_forest
#> Random Forest 
#> 
#> 21478 samples
#>    23 predictor
#>     2 classes: '0', '1' 
#> 
#> No pre-processing
#> Resampling: Cross-Validated (5 fold, repeated 3 times) 
#> Summary of sample sizes: 17182, 17182, 17183, 17182, 17183, 17182, ... 
#> Resampling results across tuning parameters:
#> 
#>   mtry  Accuracy   Kappa    
#>    2    0.9679827  0.9359615
#>   12    0.9718006  0.9435987
#>   23    0.9698917  0.9397808
#> 
#> Accuracy was used to select the optimal model using the largest value.
#> The final value used for the model was mtry = 12.

Pada summary model di atas, dilakukan beberapa kali percobaan mtry (jumlah predictor random yang digunakan saat splitting node). Secara default akan dicoba sebanyak 3 nilai mtrydan maksimal mtry sebanyak jumlah predictor (numerik + dummy variable). Model yang dipilih adalah mtry = 12 dengan nilai Accuracy tertinggi ketika diujikan ke data hasil bootstrap sampling (atau data in-sample, bisa dianggap sebagai data train seperti pada pembuatan model).

Out-of-Bag (OOB) Error

Pada tahap Bootstrap sampling, terdapat data yang tidak digunakan dalam pembuatan model, ini yang disebut sebagai data Out-of-Bag (OOB). Model Random Forest akan menggunakan data OOB sebagai data test untuk melakukan evaluasi dengan cara menghitung error. Error inilah yang disebut sebagai OOB Error. Dalam kasus klasifikasi, OOB error merupakan persentase data OOB yang misklasifikasi.

# lihat finalModel dari fb_forest
lol_forest$finalModel
#> 
#> Call:
#>  randomForest(x = x, y = y, mtry = param$mtry) 
#>                Type of random forest: classification
#>                      Number of trees: 500
#> No. of variables tried at each split: 12
#> 
#>         OOB estimate of  error rate: 2.8%
#> Confusion matrix:
#>       0     1 class.error
#> 0 10334   377  0.03519746
#> 1   225 10542  0.02089719

No. of variables tried at each split: 12 -> mtry terbaik

Nilai OOB Error pada model lol_forest sebesar 2.8% di unseen data. Dengan kata lain, akurasi model pada data OOB adalah 97.2%.

plot(lol_forest$finalModel)
legend("topright", colnames(lol_forest$finalModel$err.rate),col = 1:6, cex = 0.8, fill = 1:6)

Tampak garis hitamnya(OOB) berada hampir persis di tengah garis merah(0) dan hijau(1). Artinya kejadian misklasifikasi pada model ini sangat kecil atau model dapat membagi kelas menang dan kalah pada target dengan sangat baik.

Model Evaluation

lol_forest_pred <- predict(lol_forest,
                           lol_test)

confusionMatrix(data = lol_forest_pred,
                reference = lol_test$blueWins,
                positive = "1")
#> Confusion Matrix and Statistics
#> 
#>           Reference
#> Prediction    0    1
#>          0 2618   59
#>          1   96 2597
#>                                                
#>                Accuracy : 0.9711               
#>                  95% CI : (0.9663, 0.9754)     
#>     No Information Rate : 0.5054               
#>     P-Value [Acc > NIR] : < 0.00000000000000022
#>                                                
#>                   Kappa : 0.9423               
#>                                                
#>  Mcnemar's Test P-Value : 0.003833             
#>                                                
#>             Sensitivity : 0.9778               
#>             Specificity : 0.9646               
#>          Pos Pred Value : 0.9644               
#>          Neg Pred Value : 0.9780               
#>              Prevalence : 0.4946               
#>          Detection Rate : 0.4836               
#>    Detection Prevalence : 0.5015               
#>       Balanced Accuracy : 0.9712               
#>                                                
#>        'Positive' Class : 1                    
#> 
  • Accuracy : 0.9711
  • Sensitivity : 0.9774
  • Pos Pred Value : 0.9647

Tampak semua metriknya sangat tinggi, hal ini mengindikasikan model kita sangat baik.

Interpretation

Pada machine learning model, terdapat trade-off antara sisi interpretability dan performance. Performance Random Forest dapat diunggulkan dibandingkan model yang lain, namun tidak terlalu dapat diinterpretasi karena banyak faktor random yang terlibat. Namun setidaknya kita dapat melihat predictor apa saja yang paling penting dalam pembuatan Random Forest melalui variable importancenya:

varImp(lol_forest)
#> rf variable importance
#> 
#>   only 20 most important variables shown (out of 23)
#> 
#>                          Overall
#> blueTowerKills          100.0000
#> blueDeath                81.0005
#> blueInhibitorKills       49.4202
#> blueFirstInhibitor1      22.3885
#> blueKills                20.0873
#> gameDuraton              13.3761
#> blueAssist               10.7625
#> blueObjectDamageDealt     7.3553
#> blueFirstTower1           5.4420
#> blueTotalGold             5.1288
#> blueKillingSpree          4.0864
#> blueChampionDamageDealt   3.5704
#> blueWardPlaced            3.2580
#> blueAvgLevel              2.9530
#> blueTotalLevel            2.8753
#> blueTotalHeal             2.3624
#> blueJungleMinionKills     1.8807
#> blueDragonKills           1.4538
#> blueWardkills             1.4404
#> blueFirstBaron1           0.5139
plot(varImp(lol_forest))

Di plot, sumbu x diwakili oleh nilai importance, nilai importance ini berasal dari perhitungan seberapa sering suatu variable digunakan ketika membuat tree.

Ternyata variable yang mempengaruhi dalam pembuatan model random forest ini adalah blueTowerKills, blueDeath, dan blueInhibitorKills.

ROC dan AUC Test

lol_forest_prob <- predict(lol_forest, lol_test, type = "prob")
lol_forest_prob <- lol_forest_prob[,2]
forest_tuned_roc <- prediction(lol_forest_prob, 
                        lol_test$blueWins)
# performance
forest_roc_vec <- performance(forest_tuned_roc, 
                             "tpr", # True Positive Rate (Recall)
                             "fpr" # False Positive Rate (1 - Specificity)
                             )
# buat plot
plot(forest_roc_vec)

abline(0,1 , lty = 2) # buat garis lurus

auc.perf <- performance(forest_tuned_roc, measure ="auc")
auc.perf@y.values
#> [[1]]
#> [1] 0.996296

AUC = 0.996296, maka dapat disimpulkan bahwa model sangat baik dalam memisahkan kelas menang dan kalah.

Kesimpulan

Dari hasil decision tree sederhana, kita bisa membuat strategi dalam game LOL kedepannya, seperti jika ingin meningkatkan peluang tim akan menang maka harus menggunakan strategi:

  1. Menghancurkan tower lawan kurang dari 3.5 tower, kematian player >=13 kali, membunuh tim lawan <9.5 kali.
  2. Menghancurkan tower lebih dari 3.5 tower lawan, jumlah kematian tim <25 kali, membunuh inhibitor >0.5 kali, membunuh tim lawan <26 kali.

Dari Ketiga model yang dibuat, diperoleh

  • Model Naive Bayes:

    • Accuracy : 0.9497
    • Sensitivity : 0.9420
    • Pos Pred Value : 0.9557
    • AUC = 0.898096
  • Model Decision Tree :

    • Accuracy : 0.9603
    • Sensitivity : 0.9706
    • Neg Pred Value : 0.9502
    • AUC = 0.9895049
  • Model Decision Tree Pruning :

    • Accuracy : 0.9603
    • Sensitivity : 0.9680
    • Pos Pred Value : 0.9526
    • AUC = 0.9861413
  • Model Random Forest :

    • Accuracy : 0.9711
    • Sensitivity : 0.9774
    • Pos Pred Value : 0.9647
    • AUC = 0.996296

Dari hasil keempat model yang telah dibuat, ternyata model Random Forest merupakan model terbaik, dilihat dari tingkat Accuracy, Sensitivity, Precision dan bahkan AUCnya, semuanya tinggi. Sehingga kita bisa menggunakan model Random Forest untuk melakukan prediksi dalam permainan League of Legends kedepannya.