53Devon/Dota_2_Machine_Learning
0
1# import library
2import streamlit as st
3import pickle
4import json
5import pandas as pd
6import numpy as np
7
8with open("preprocessor.pkl", "rb") as file:
9 preprocessor = pickle.load(file)
10
11with open("model.pkl", "rb") as file:
12 model = pickle.load(file)
13
14with open("heroes.txt", "r") as file:
15 heroes = json.load(file)
16
17with open("team.txt", "r") as file:
18 team = json.load(file)
19
20
21def cosine_sim(vect1, vect2):
22 norm_1 = np.linalg.norm(vect1)
23 norm_2 = np.linalg.norm(vect2)
24
25 cos_sim = (vect1 @ vect2) / (norm_1 * norm_2)
26 return cos_sim
27
28
29def recsys(df, index_data_inf, top_N):
30 index_rec = []
31 cossim = pd.Series(
32 [cosine_sim(df.loc[index_data_inf], x) for x in df.values], index=df.index
33 ).drop(index=index_data_inf)
34 for data_idx in cossim.sort_values(ascending=False)[:top_N].index:
35 index_rec.append(data_idx)
36 return index_rec
37
38
39def run():
40 st.title("Halaman Prediksi & Rekomendasi")
41 st.image("prediction_pic.jpg")
42
43 st.markdown(
44 '<div style="text-align: justify;; text-indent: 40px;">Halaman ini bertujuan untuk memprediksi kemenangan tim berdasarkan informasi komposisi, detail tim dan lain-lain. Diharapkan dari prediksi ini pihak manajemen dan pelatih dapat membuat strategi dan <i>threshold</i> batas agar potensi kemenangan tim semakin besar.<br><br>',
45 unsafe_allow_html=True,
46 )
47
48 tab_1, tab_2 = st.tabs(
49 [
50 "Prediksi Kemenangan Tim Lawan",
51 "Rekomendasi Lineup Terhadap Musuh",
52 ]
53 )
54 with tab_1:
55 with st.form("Form Result Prediction 1"):
56 st.subheader("Prediksi Kemenangan Tim Musuh")
57
58 st.markdown(
59 "Model prediksi yang digunakan adalah Support Vector Machine (SVM) untuk menghasilkan prediksi akan kemenangan dari tim lawan berdasarkan pada _feature_ yang telah diolah."
60 )
61 input_tournament = st.text_input(
62 "Tournament Name",
63 help="Field ini memungkinkan pengguna untuk memasukkan nama turnamen yang diinginkan.",
64 )
65
66 input_team = st.selectbox(
67 "Nama Tim Lawan",
68 sorted(team),
69 help="Field ini memungkinkan pengguna untuk mengisi nama tim yang akan diprediksi.",
70 )
71
72 input_side = st.selectbox(
73 "Sisi tim Lawan",
74 ("dire", "radiant"),
75 index=0,
76 help="Field ini memungkinkan pengguna untuk mengisi bagian sisi yang dipilih oleh pengguna berupa dire atau radiant.",
77 )
78
79 input_score = st.slider("Target Skor Akhir Lawan", min_value=0, value=0)
80
81 input_duration = st.slider(
82 "Durasi dari Pertandingan (Menit)", min_value=0, value=0
83 )
84
85 input_hero = st.multiselect(
86 "Komposisi Karakter dalam Tim (hero lineup)",
87 heroes,
88 help="Field ini berisi nama karakter yang dipilih oleh tim dimulai dari nama karakter pertama sampai terakhir dipilih.",
89 max_selections=5,
90 )
91
92 name_changes = {
93 "Anti-Mage": "antimage",
94 "Centaur Warrunner": "centaur",
95 "Clockwerk": "rattletrap",
96 "Doom": "doom_bringer",
97 "Io": "wisp",
98 "Lifestealer": "life_stealer",
99 "Magnus": "magnataur",
100 "Nature's Prohpet": "furion",
101 "Necrophos": "necrolyte",
102 "Outworld Devourer": "obsidian_destroyer",
103 "Queen of Pain": "queenofpain",
104 "Shadow Fiend": "nevermore",
105 "Treant Protector": "treant",
106 "Underlord": "abyssal_underlord",
107 "Vengeful Spirit": "vengefulspirit",
108 "Windranger": "windrunner",
109 "Wraith King": "skeleton_king",
110 "Zeus": "zuus",
111 }
112
113 org_heroes = {name_changes.get(hero, hero) for hero in heroes}
114 hero_images = {
115 hero: f"https://cdn.akamai.steamstatic.com/apps/dota2/images/dota_react/heroes/{hero.lower().replace(' ', '_')}.png"
116 for hero in org_heroes
117 }
118
119 hero_con = [name_changes.get(hero,hero) for hero in input_hero]
120
121 col1, col2, col3, col4, col5 = st.columns(5)
122
123 col1.markdown('<div style="text-align: center;margin-bottom: 10px">Karakter Pertama</div>', unsafe_allow_html=True)
124 col2.markdown('<div style="text-align: center;margin-bottom: 10px">Karakter Kedua</div>', unsafe_allow_html=True)
125 col3.markdown('<div style="text-align: center;margin-bottom: 10px">Karakter Ketiga</div>', unsafe_allow_html=True)
126 col4.markdown('<div style="text-align: center;margin-bottom: 10px">Karakter Keempat</div>', unsafe_allow_html=True)
127 col5.markdown('<div style="text-align: center;margin-bottom: 10px">Karakter Kelima</div>', unsafe_allow_html=True)
128
129 cols = st.columns(5)
130 for i in range(5):
131 col = cols[i]
132 if i < len(input_hero):
133 pic = col.image(hero_images[hero_con[i]], width=120)
134 else:
135 pic = col.image('block.png', width=120)
136
137 st.markdown("---")
138
139 col1, col2, coldiv = st.columns((3, 3, 3))
140 how_to = col2.popover("Cara Kerja Model Prediksi")
141 submitted = col1.form_submit_button("Prediksi Hasil Pertandingan")
142
143 if submitted:
144 if len(input_hero) < 5:
145 st.subheader("Kekurangan Komposisi Karakter")
146 else:
147 data_inf = {
148 "TOURNAMENT": input_tournament,
149 "TEAM": input_team,
150 "SIDE": input_side,
151 "HERO_1": input_hero[4],
152 "HERO_2": input_hero[3],
153 "HERO_3": input_hero[2],
154 "HERO_4": input_hero[1],
155 "HERO_5": input_hero[0],
156 "SCORE": input_score,
157 }
158
159 data_inf = pd.DataFrame([data_inf])
160
161 data_final = preprocessor.transform(data_inf)
162
163 pred = model.predict(data_final)
164 if pred == 1:
165 st.markdown(
166 f'<h4>Ketika pertandingan berakhir dengan skor lawan sebesar {input_score} dan komposisi tertera, {input_team} diprediksi <span style="color: green;">Menang</span></h4>',
167 unsafe_allow_html=True,
168 )
169 else:
170 st.markdown(
171 f'<h4>Ketika pertandingan berakhir dengan skor lawan sebesar {input_score} dan komposisi tertera, {input_team} diprediksi <span style="color: red;">Kalah</span></h4>',
172 unsafe_allow_html=True,
173 )
174
175 df = pd.read_csv("P1M2_Devon.csv")
176 df_bar_1 = (
177 df[["HERO_1", "HERO_2", "HERO_3", "HERO_4", "HERO_5"]]
178 .stack()
179 .value_counts()
180 .reset_index()
181 )
182 df_bar_1.columns = ["HERO", "Global Hero Pick"]
183 df_bar_1["WIN COUNT"] = 0
184 for index, row in df_bar_1.iterrows():
185 hero = row["HERO"]
186 win_count = 0
187 for col in ["HERO_1", "HERO_2", "HERO_3", "HERO_4", "HERO_5"]:
188 win_count += (
189 df[df["RESULT"] == "WIN"][col]
190 .value_counts()
191 .get(hero, 0)
192 )
193 df_bar_1.at[index, "WIN COUNT"] = win_count
194 df_bar_1["Global Winrate (%)"] = round(
195 (df_bar_1["WIN COUNT"] / df_bar_1["Global Hero Pick"] * 100), 2
196 )
197
198 df_stat = df_bar_1[
199 (df_bar_1["HERO"] == input_hero[0])
200 | (df_bar_1["HERO"] == input_hero[1])
201 | (df_bar_1["HERO"] == input_hero[2])
202 | (df_bar_1["HERO"] == input_hero[3])
203 | (df_bar_1["HERO"] == input_hero[4])
204 ].sort_values(by="HERO")
205
206 all_heroes = df[df["TEAM"] == input_team][
207 ["HERO_1", "HERO_2", "HERO_3", "HERO_4", "HERO_5"]
208 ]
209 for hero in heroes:
210 all_heroes[hero] = all_heroes.apply(
211 lambda row: int(hero in row.values), axis=1
212 )
213 df_stat["Team Hero Pick"] = None
214 df_stat["Team Hero Winrate (%)"] = None
215 for hero in df_stat["HERO"]:
216 pick_count = all_heroes.apply(
217 lambda row: hero in row.values, axis=1
218 ).sum()
219 win_count_team = all_heroes[
220 all_heroes.apply(
221 lambda row: hero in row.values
222 and df.loc[row.name, "RESULT"] == "WIN",
223 axis=1,
224 )
225 ].shape[0]
226 df_stat.loc[df_stat["HERO"] == hero, "Team Hero Pick"] = (
227 pick_count
228 )
229 df_stat.loc[
230 df_stat["HERO"] == hero, "Team Hero Winrate (%)"
231 ] = (
232 round(win_count_team / pick_count * 100, 2)
233 if pick_count > 0
234 else 0
235 )
236
237 df_stat.index = np.arange(1, len(df_stat) + 1)
238 df_stat.rename(columns={"HERO": "Nama Karakter"}, inplace=True)
239
240 st.markdown(
241 "<h5>Informasi statistik komposisi karakter tim lawan:</h5>",
242 unsafe_allow_html=True,
243 )
244 st.dataframe(df_stat.drop(columns=["WIN COUNT"]))
245
246 if len(input_hero) == 5:
247 with how_to:
248 st.markdown("##### Langkah-langkah:")
249 st.markdown("1. Mengubah format data input menjadi dataframe")
250 data_inf = {
251 "TOURNAMENT": input_tournament,
252 "TEAM": input_team,
253 "SIDE": input_side,
254 "HERO_1": input_hero[4],
255 "HERO_2": input_hero[3],
256 "HERO_3": input_hero[2],
257 "HERO_4": input_hero[1],
258 "HERO_5": input_hero[0],
259 "SCORE": input_score,
260 "DURATION": input_duration,
261 }
262
263 st.dataframe(data_inf)
264
265 st.markdown(
266 "2. Melakukan preprocessing data kolom yang dianggap berpengaruh secara statistik menggunakan pipeline yang telah diolah yakni scaling pada data numerikal dan encoding pada data kategorikal"
267 )
268 st.markdown(
269 "3. Pengabunggan hasil pengolahan menjadi sebuah dataframe hasil preprocessing"
270 )
271 st.markdown(
272 "4. Melakukan model prediksi berdasarkan hasil model yang telah dibuat menggunakan pelatihan dataset."
273 )
274 st.markdown("5. Menampilkan hasil prediksi")
275
276 with tab_2:
277 with st.form("Form Result Prediction"):
278 st.subheader("Rekomendasi Karakter kepada Tim Aliansi")
279 st.markdown(
280 "Proses rekomendasi ini menggunakan data historis kemenangan lawan tim musuh yang menang dengan memanfaatkan _cosine similarity_ sebagai indeks kemiripan komposisi aliansi agar dapat meningkatkan kemungkinan menang dalam pertandingan."
281 )
282
283 input_team = st.selectbox(
284 "Nama Tim Lawan",
285 sorted(team),
286 help="Field ini memungkinkan pengguna untuk mengisi nama tim lawan.",
287 )
288
289 input_hero = st.multiselect(
290 "Komposisi Karakter dalam Tim Aliansi (hero lineup)",
291 heroes,
292 help="Field ini berisi nama karakter yang dipilih oleh tim dimulai dari nama karakter pertama sampai terakhir dipilih.",
293 max_selections=4,
294 )
295
296 name_changes = {
297 "Anti-Mage": "antimage",
298 "Centaur Warrunner": "centaur",
299 "Clockwerk": "rattletrap",
300 "Doom": "doom_bringer",
301 "Io": "wisp",
302 "Lifestealer": "life_stealer",
303 "Magnus": "magnataur",
304 "Nature's Prohpet": "furion",
305 "Necrophos": "necrolyte",
306 "Outworld Devourer": "obsidian_destroyer",
307 "Queen of Pain": "queenofpain",
308 "Shadow Fiend": "nevermore",
309 "Treant Protector": "treant",
310 "Underlord": "abyssal_underlord",
311 "Vengeful Spirit": "vengefulspirit",
312 "Windranger": "windrunner",
313 "Wraith King": "skeleton_king",
314 "Zeus": "zuus",
315 }
316
317 org_heroes = {name_changes.get(hero, hero) for hero in heroes}
318 hero_images = {
319 hero: f"https://cdn.akamai.steamstatic.com/apps/dota2/images/dota_react/heroes/{hero.lower().replace(' ', '_')}.png"
320 for hero in org_heroes
321 }
322
323 hero_con = [name_changes.get(hero,hero) for hero in input_hero]
324
325 col1, col2, col3, col4, col5 = st.columns(5)
326
327 col1.markdown('<div style="text-align: center;margin-bottom: 10px">Karakter Pertama</div>', unsafe_allow_html=True)
328 col2.markdown('<div style="text-align: center;margin-bottom: 10px">Karakter Kedua</div>', unsafe_allow_html=True)
329 col3.markdown('<div style="text-align: center;margin-bottom: 10px">Karakter Ketiga</div>', unsafe_allow_html=True)
330 col4.markdown('<div style="text-align: center;margin-bottom: 10px">Karakter Keempat</div>', unsafe_allow_html=True)
331 col5.markdown('<div style="text-align: center;margin-bottom: 10px">Karakter Kelima</div>', unsafe_allow_html=True)
332
333 cols = st.columns(5)
334 for i in range(5):
335 col = cols[i]
336 if i < len(input_hero):
337 pic = col.image(hero_images[hero_con[i]], width=120)
338 else:
339 pic = col.image('block.png', width=120)
340
341 st.write('')
342
343 col1, col2, coldiv = st.columns((9.5, 14, 9.3))
344 how_to = col2.popover("Cara Kerja Model Rekomendaasi")
345 submitted2 = col1.form_submit_button("Membuat Rekomendasi")
346 detail_rekomendasi_dataframe = coldiv.popover('Detail Rekomendasi')
347
348 with how_to:
349 st.markdown("##### Langkah-langkah:")
350 st.markdown('1. Melakukan Filter data terkait dengan semua lawan dari tim musuh yang berakhir dalam kondisi menang. ')
351 st.markdown('2. Melakukan input komposisi karakter ke dalam dataframe.')
352 st.markdown('3. Melakukan perhitungan cosine similarity terhadap data input.')
353 st.markdown('4. Mengumpulkan keseluruhan karakter pada 5 data paling mirip.')
354 st.markdown('5. Melakukan pengurutan data berdasarkan jumlah pertemuan karakter pada 5 data tersebut dan persortir berdasarkan global winrate')
355 st.markdown('5. Menampilkan hasil rekomendasi.')
356
357 if submitted2:
358 if len(input_hero) == 0:
359 st.markdown(
360 "<h4>Dibutuhkan minimal 1 pemilihan karakter untuk melihat data historis, rekomendasi berdasarkan statistik sebagai berikut:</h4>",
361 unsafe_allow_html=True,
362 )
363 df = pd.read_csv("P1M2_Devon.csv")
364 df = df[
365 (df["MATCH_ID"].isin(df[df["TEAM"] == input_team]["MATCH_ID"]))
366 ]
367 df = df[(df["TEAM"] != input_team)]
368
369 df_bar_1 = (
370 df[["HERO_1", "HERO_2", "HERO_3", "HERO_4", "HERO_5"]]
371 .stack()
372 .value_counts()
373 .reset_index()
374 )
375 df_bar_1.columns = ["Nama Karakter", "Hero Pick"]
376 df_bar_1["Win Count"] = 0
377 for index, row in df_bar_1.iterrows():
378 hero = row["Nama Karakter"]
379 win_count = 0
380 for col in ["HERO_1", "HERO_2", "HERO_3", "HERO_4", "HERO_5"]:
381 win_count += (
382 df[df["RESULT"] == "WIN"][col]
383 .value_counts()
384 .get(hero, 0)
385 )
386 df_bar_1.at[index, "Win Count"] = win_count
387 df_bar_1["Winrate (%)"] = round(
388 df_bar_1["Win Count"] / df_bar_1["Hero Pick"] * 100, 2
389 )
390 df_bar_1.sort_values(
391 by="Winrate (%)", ascending=False, inplace=True
392 )
393 df_bar_1.reset_index(drop=True)
394 df_bar_1.index = np.arange(1, len(df_bar_1) + 1)
395 st.dataframe(df_bar_1)
396 else:
397 df = pd.read_csv("P1M2_Devon.csv")
398 df_bar_1 = (
399 df[["HERO_1", "HERO_2", "HERO_3", "HERO_4", "HERO_5"]]
400 .stack()
401 .value_counts()
402 .reset_index()
403 )
404 df_bar_1.columns = ["HERO", "Global Hero Pick"]
405 df_bar_1["WIN COUNT"] = 0
406 for index, row in df_bar_1.iterrows():
407 hero = row["HERO"]
408 win_count = 0
409 for col in ["HERO_1", "HERO_2", "HERO_3", "HERO_4", "HERO_5"]:
410 win_count += (
411 df[df["RESULT"] == "WIN"][col]
412 .value_counts()
413 .get(hero, 0)
414 )
415 df_bar_1.at[index, "WIN COUNT"] = win_count
416 df_bar_1["Global Winrate (%)"] = round(
417 df_bar_1["WIN COUNT"] / df_bar_1["Global Hero Pick"] * 100, 2
418 )
419
420 all_heroes = df[
421 (df["MATCH_ID"].isin(df[df["TEAM"] == input_team]["MATCH_ID"]))
422 & (df["RESULT"] == "WIN")
423 ]
424 all_heroes = all_heroes[(df["TEAM"] != input_team)][
425 ["HERO_1", "HERO_2", "HERO_3", "HERO_4", "HERO_5"]
426 ]
427 for hero in heroes:
428 all_heroes[hero] = all_heroes.apply(
429 lambda row: int(hero in row.values), axis=1
430 )
431 all_heroes.drop(
432 columns=["HERO_1", "HERO_2", "HERO_3", "HERO_4", "HERO_5"],
433 inplace=True,
434 )
435 match len(input_hero):
436 case 4:
437 data_inf = {
438 "HERO_2": input_hero[3],
439 "HERO_3": input_hero[2],
440 "HERO_4": input_hero[1],
441 "HERO_5": input_hero[0],
442 }
443 data_inf = pd.DataFrame([data_inf])
444 for hero in heroes:
445 data_inf[hero] = data_inf.apply(
446 lambda row: int(hero in row.values), axis=1
447 )
448 data_inf.drop(
449 columns=["HERO_2", "HERO_3", "HERO_4", "HERO_5"],
450 inplace=True,
451 )
452
453 case 3:
454 data_inf = {
455 "HERO_3": input_hero[2],
456 "HERO_4": input_hero[1],
457 "HERO_5": input_hero[0],
458 }
459 data_inf = pd.DataFrame([data_inf])
460 for hero in heroes:
461 data_inf[hero] = data_inf.apply(
462 lambda row: int(hero in row.values), axis=1
463 )
464 data_inf.drop(
465 columns=["HERO_3", "HERO_4", "HERO_5"], inplace=True
466 )
467
468 case 2:
469 data_inf = {
470 "HERO_4": input_hero[1],
471 "HERO_5": input_hero[0],
472 }
473 data_inf = pd.DataFrame([data_inf])
474 for hero in heroes:
475 data_inf[hero] = data_inf.apply(
476 lambda row: int(hero in row.values), axis=1
477 )
478 data_inf.drop(columns=["HERO_4", "HERO_5"], inplace=True)
479
480 case 1:
481 data_inf = {
482 "HERO_5": input_hero[0],
483 }
484 data_inf = pd.DataFrame([data_inf])
485 for hero in heroes:
486 data_inf[hero] = data_inf.apply(
487 lambda row: int(hero in row.values), axis=1
488 )
489 data_inf.drop(columns=["HERO_5"], inplace=True)
490
491 data_inf.index = pd.RangeIndex(
492 start=99999, stop=99999 + len(data_inf)
493 )
494 df_combine = pd.concat([all_heroes, data_inf], axis=0)
495 result = recsys(df_combine, 99999, 5)
496 filtered_df = df.loc[df.index.isin(result)]
497 filtered_df_stack = (
498 filtered_df[["HERO_1", "HERO_2", "HERO_3", "HERO_4", "HERO_5"]]
499 .stack()
500 .value_counts()
501 .reset_index()
502 )
503 filtered_df_stack = pd.DataFrame(filtered_df_stack)
504 for hero in filtered_df_stack["index"]:
505 if any(hero == hero_list for hero_list in input_hero):
506 filtered_df_stack = filtered_df_stack[
507 filtered_df_stack["index"] != hero
508 ]
509 filtered_df_stack.rename(
510 columns={"index": "HERO", "count": "Encounter"}, inplace=True
511 )
512
513 filtered_df_stack["Global Winrate (%)"] = None
514 for hero in filtered_df_stack["HERO"]:
515 filtered_df_stack.loc[
516 filtered_df_stack["HERO"] == hero, "Global Winrate (%)"
517 ] = df_bar_1.loc[
518 df_bar_1["HERO"] == hero, "Global Winrate (%)"
519 ].values[0]
520
521 filtered_df_stack.sort_values(
522 by=["Encounter", "Global Winrate (%)"],
523 ascending=False,
524 inplace=True,
525 )
526 filtered_df_stack.index = np.arange(1, len(filtered_df_stack) + 1)
527 filtered_df_stack.rename(
528 columns={"HERO": "Nama Karakter"}, inplace=True
529 )
530
531 col1, col2 = st.columns((6,3))
532 col1.markdown(
533 "<h4>Rekomendasi Pemilihan Karakter:</h4>",
534 unsafe_allow_html=True,
535 )
536 col1.markdown(f' 1. {filtered_df_stack["Nama Karakter"].iloc[0]}')
537 col1.markdown(f' 2. {filtered_df_stack["Nama Karakter"].iloc[1]}')
538 col1.markdown(f' 3. {filtered_df_stack["Nama Karakter"].iloc[2]}')
539
540 st.markdown('<br>', unsafe_allow_html=True)
541
542 with detail_rekomendasi_dataframe:
543 st.markdown(
544 '<h4>Detail Informasi Statistik</h4>', unsafe_allow_html=True
545 )
546 st.dataframe(filtered_df_stack)
547
548
549
550if __name__ == "__main__":
551 run()
552 