Initial commit: добавление проекта predictV1

Включает модели ML для предсказаний, API маршруты, скрипты обучения и данные.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-21 17:22:58 +03:00
commit 8a134239d7
42 changed files with 12831 additions and 0 deletions

19
.gitignore vendored Normal file
View File

@@ -0,0 +1,19 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
.venv/
venv/
ENV/
# macOS
.DS_Store
# CatBoost
catboost_info/
# IDE
.vscode/
.idea/

34
BD.txt Normal file
View File

@@ -0,0 +1,34 @@
CREATE TABLE hero (
id INTEGER PRIMARY KEY,
name VARCHAR(255)
);
CREATE TABLE matches (
id BIGINT PRIMARY KEY,
start_time INTEGER,
leagueid INTEGER,
radiant_team_id INTEGER,
dire_team_id INTEGER,
radiant_win BOOLEAN,
source VARCHAR(255)
);
CREATE TABLE details_match (
match_id BIGINT,
hero_id INTEGER,
team INTEGER,
"order" INTEGER,
players_id BIGINT,
pos INTEGER
);
CREATE TABLE teams (
id BIGINT PRIMARY KEY,
name VARCHAR(255)
);
CREATE TABLE pro_players (
id BIGINT PRIMARY KEY,
name VARCHAR(255),
team_id BIGINT
);

0
CLAUDE.md Normal file
View File

View File

@@ -0,0 +1,226 @@
feature,importance
radiant_hero_96,3.5448903148379154
is_first_pick_radiant,3.1565223041927504
radiant_hero_137,2.0235517900167266
radiant_hero_106,1.987790361290638
dire_hero_96,1.960286880219167
dire_hero_95,1.9082867265736683
dire_hero_64,1.7996110650970016
radiant_hero_29,1.7315926094694511
radiant_hero_72,1.6448044545454794
radiant_hero_114,1.6441341241899101
dire_hero_13,1.6043480443067073
dire_hero_29,1.5796733982550784
dire_hero_100,1.5595810413341753
radiant_hero_120,1.5349678998581766
radiant_hero_126,1.4997652826627643
dire_hero_10,1.4639340774176453
dire_hero_137,1.4356855498450338
dire_hero_37,1.3886017253491347
dire_hero_49,1.374665187198288
radiant_hero_102,1.3283477995280788
dire_hero_45,1.3278670924698959
dire_hero_17,1.2812245277976781
dire_hero_79,1.2784221281359065
dire_hero_39,1.2667408091038088
radiant_hero_49,1.2632189131127254
radiant_hero_38,1.1962602198205863
dire_hero_123,1.1872109503677573
dire_hero_102,1.1228931434997156
radiant_hero_18,1.1174737519883087
dire_hero_87,1.077588910539817
radiant_hero_129,1.0219559481460052
radiant_hero_46,1.0018953069589642
dire_hero_38,0.9996211369063055
radiant_hero_37,0.993539916392573
dire_hero_120,0.9862796010369532
dire_hero_66,0.9376575493895805
dire_hero_3,0.9028065601906793
dire_hero_2,0.901682474329439
dire_hero_28,0.8948171866399827
radiant_hero_89,0.89147406950863
radiant_hero_53,0.8648249886966961
radiant_hero_98,0.8585308463944029
radiant_hero_87,0.8381995066812421
radiant_hero_66,0.8232327944369873
radiant_hero_39,0.8207115488272696
dire_hero_94,0.8003485263994297
dire_hero_129,0.7788770107522424
radiant_hero_28,0.7776717128986811
dire_hero_8,0.7691400847093839
dire_hero_62,0.7635947687074511
dire_hero_16,0.7634034659406227
radiant_hero_8,0.7619724662859222
dire_hero_77,0.7528422859580692
dire_hero_59,0.7508683533785772
radiant_hero_17,0.7476607306553408
radiant_hero_77,0.7441942841775127
dire_hero_53,0.7276366474281736
dire_hero_99,0.7233076956342283
radiant_hero_13,0.7193942058596804
radiant_hero_128,0.7001897029702516
radiant_hero_70,0.6938372976032963
radiant_hero_64,0.6691934104513008
radiant_hero_123,0.638529772522028
dire_hero_98,0.6319093349534167
radiant_hero_45,0.6062424683655009
radiant_hero_79,0.5868955332730199
radiant_hero_52,0.5840206533460277
dire_hero_72,0.5806209726259308
dire_hero_89,0.5790685908445176
dire_hero_18,0.5722360992240108
radiant_hero_95,0.5635903439598497
dire_hero_70,0.5583839849822786
dire_hero_69,0.5520070584047531
dire_hero_46,0.5396131611619958
dire_hero_119,0.5358381979706183
radiant_hero_65,0.5192645247152368
radiant_hero_62,0.5183279865244088
radiant_hero_135,0.49936800424506134
radiant_hero_2,0.4817157203218963
radiant_hero_69,0.4674047119278839
dire_hero_105,0.4672531552610654
dire_hero_128,0.46490506129257847
dire_hero_52,0.45906683460354497
dire_hero_131,0.4519549389897154
radiant_hero_100,0.4237007080091267
radiant_hero_3,0.4200207882294507
dire_hero_58,0.41845651287518343
dire_hero_101,0.39098204345118254
radiant_hero_58,0.36615994337973956
radiant_hero_90,0.3654215922377207
radiant_hero_109,0.36114422894241377
radiant_hero_85,0.3397063517524785
dire_hero_47,0.33120483123859173
dire_hero_65,0.3201442668327861
radiant_hero_16,0.3054077635508628
dire_hero_51,0.27585362626776144
dire_hero_126,0.273936700171308
radiant_hero_145,0.27080666451804547
dire_hero_50,0.24193256479700528
dire_hero_14,0.2307593677866238
dire_hero_138,0.22995826671565825
dire_hero_108,0.22299160580230648
radiant_hero_50,0.22224984591119692
radiant_hero_55,0.22024426849261636
radiant_hero_110,0.21815464793464948
dire_hero_145,0.21662542865055226
dire_hero_109,0.21105314668783304
dire_hero_71,0.2037500212802944
radiant_hero_51,0.20131521608992764
radiant_hero_99,0.1878406955271859
dire_hero_114,0.18771311441859298
radiant_hero_61,0.18042090494382437
radiant_hero_19,0.17972494076335502
dire_hero_135,0.17674176940995884
radiant_hero_138,0.17638797830930697
radiant_hero_11,0.17173288969835926
radiant_hero_131,0.17163285185364796
radiant_hero_71,0.1690626448094322
dire_hero_63,0.1677136535489791
dire_hero_136,0.1665628310620548
radiant_hero_59,0.15482478477426076
dire_hero_1,0.14761511544285089
radiant_hero_47,0.14432777647536155
dire_hero_74,0.14328897339865496
radiant_hero_60,0.1350544787435433
dire_hero_54,0.13444883015330528
radiant_hero_33,0.13418407698461643
radiant_hero_14,0.13062864684037695
dire_hero_55,0.11982716113335097
dire_hero_41,0.11590519381635243
radiant_hero_101,0.11145841473118147
radiant_hero_26,0.11042331242123556
radiant_hero_121,0.10624052682211534
dire_hero_73,0.10523838204127042
dire_hero_85,0.10116628070719334
radiant_hero_75,0.0990778076023039
radiant_hero_136,0.0971404322283737
dire_hero_121,0.0882738489185766
radiant_hero_48,0.08644988028482627
radiant_hero_105,0.08636504455479098
dire_hero_78,0.086083302792892
dire_hero_112,0.08186185440747837
radiant_hero_21,0.08031441854242871
dire_hero_11,0.08022938628242476
radiant_hero_1,0.07993183126994755
radiant_hero_73,0.07987048197448883
dire_hero_26,0.0751263061140198
radiant_hero_40,0.07500706396983621
radiant_hero_88,0.06448627461148718
radiant_hero_103,0.0596421467981599
radiant_hero_35,0.05811051984217868
radiant_hero_108,0.05783500222406305
radiant_hero_10,0.048789752327982654
dire_hero_21,0.04778443130941926
dire_hero_6,0.04540743981832734
dire_hero_106,0.04419874118360171
radiant_hero_9,0.04183971731090627
dire_hero_7,0.04151823003037786
radiant_hero_36,0.04071609662932216
dire_hero_93,0.03917124022761937
radiant_hero_57,0.03746648579498447
dire_hero_32,0.035168701377993536
radiant_hero_54,0.03379848987272846
radiant_hero_83,0.031345340329813724
dire_hero_103,0.03072106078174553
dire_hero_60,0.030582548339197258
radiant_hero_32,0.029014217937254724
radiant_hero_80,0.026474334315182813
dire_hero_33,0.02517097042722223
radiant_hero_78,0.024575425448720087
radiant_hero_82,0.022026769618172896
dire_hero_86,0.021481951040091762
dire_hero_88,0.020529621747134498
radiant_hero_68,0.019783709474760987
dire_hero_75,0.019335981384286435
radiant_hero_63,0.018498391135059748
radiant_hero_5,0.018167936059773637
dire_hero_40,0.01747482989495453
radiant_hero_41,0.01742109465745823
radiant_hero_23,0.015723888573406297
radiant_hero_97,0.014281791505775597
dire_hero_68,0.012826265095086503
radiant_hero_27,0.012675770453317775
dire_hero_110,0.012543695625851782
radiant_hero_15,0.011406703339127508
dire_hero_19,0.010305059042443747
dire_hero_97,0.010205238293995356
dire_hero_104,0.009982129745724043
radiant_hero_74,0.009456035513835673
dire_hero_91,0.007828627023014963
dire_hero_35,0.007655784085914658
radiant_hero_104,0.0072801341665292155
dire_hero_82,0.0066323538462641
dire_hero_23,0.0060004954896882255
radiant_hero_86,0.005622298563036045
radiant_hero_25,0.005493482170013952
radiant_hero_22,0.0054192470856789625
dire_hero_48,0.0051407952840448615
radiant_hero_7,0.005138603464622757
dire_hero_90,0.005123300607378032
radiant_hero_67,0.004597631094333278
dire_hero_36,0.002401848761390748
dire_hero_61,0.00032280295629982147
dire_hero_43,0.0
radiant_hero_56,0.0
dire_hero_67,0.0
dire_hero_56,0.0
dire_hero_9,0.0
dire_hero_5,0.0
dire_hero_57,0.0
radiant_hero_6,0.0
dire_hero_80,0.0
radiant_hero_112,0.0
dire_hero_22,0.0
dire_hero_25,0.0
radiant_hero_43,0.0
dire_hero_15,0.0
radiant_hero_91,0.0
radiant_hero_93,0.0
radiant_hero_94,0.0
dire_hero_34,0.0
radiant_hero_34,0.0
radiant_hero_119,0.0
dire_hero_27,0.0
dire_hero_83,0.0
1 feature importance
2 radiant_hero_96 3.5448903148379154
3 is_first_pick_radiant 3.1565223041927504
4 radiant_hero_137 2.0235517900167266
5 radiant_hero_106 1.987790361290638
6 dire_hero_96 1.960286880219167
7 dire_hero_95 1.9082867265736683
8 dire_hero_64 1.7996110650970016
9 radiant_hero_29 1.7315926094694511
10 radiant_hero_72 1.6448044545454794
11 radiant_hero_114 1.6441341241899101
12 dire_hero_13 1.6043480443067073
13 dire_hero_29 1.5796733982550784
14 dire_hero_100 1.5595810413341753
15 radiant_hero_120 1.5349678998581766
16 radiant_hero_126 1.4997652826627643
17 dire_hero_10 1.4639340774176453
18 dire_hero_137 1.4356855498450338
19 dire_hero_37 1.3886017253491347
20 dire_hero_49 1.374665187198288
21 radiant_hero_102 1.3283477995280788
22 dire_hero_45 1.3278670924698959
23 dire_hero_17 1.2812245277976781
24 dire_hero_79 1.2784221281359065
25 dire_hero_39 1.2667408091038088
26 radiant_hero_49 1.2632189131127254
27 radiant_hero_38 1.1962602198205863
28 dire_hero_123 1.1872109503677573
29 dire_hero_102 1.1228931434997156
30 radiant_hero_18 1.1174737519883087
31 dire_hero_87 1.077588910539817
32 radiant_hero_129 1.0219559481460052
33 radiant_hero_46 1.0018953069589642
34 dire_hero_38 0.9996211369063055
35 radiant_hero_37 0.993539916392573
36 dire_hero_120 0.9862796010369532
37 dire_hero_66 0.9376575493895805
38 dire_hero_3 0.9028065601906793
39 dire_hero_2 0.901682474329439
40 dire_hero_28 0.8948171866399827
41 radiant_hero_89 0.89147406950863
42 radiant_hero_53 0.8648249886966961
43 radiant_hero_98 0.8585308463944029
44 radiant_hero_87 0.8381995066812421
45 radiant_hero_66 0.8232327944369873
46 radiant_hero_39 0.8207115488272696
47 dire_hero_94 0.8003485263994297
48 dire_hero_129 0.7788770107522424
49 radiant_hero_28 0.7776717128986811
50 dire_hero_8 0.7691400847093839
51 dire_hero_62 0.7635947687074511
52 dire_hero_16 0.7634034659406227
53 radiant_hero_8 0.7619724662859222
54 dire_hero_77 0.7528422859580692
55 dire_hero_59 0.7508683533785772
56 radiant_hero_17 0.7476607306553408
57 radiant_hero_77 0.7441942841775127
58 dire_hero_53 0.7276366474281736
59 dire_hero_99 0.7233076956342283
60 radiant_hero_13 0.7193942058596804
61 radiant_hero_128 0.7001897029702516
62 radiant_hero_70 0.6938372976032963
63 radiant_hero_64 0.6691934104513008
64 radiant_hero_123 0.638529772522028
65 dire_hero_98 0.6319093349534167
66 radiant_hero_45 0.6062424683655009
67 radiant_hero_79 0.5868955332730199
68 radiant_hero_52 0.5840206533460277
69 dire_hero_72 0.5806209726259308
70 dire_hero_89 0.5790685908445176
71 dire_hero_18 0.5722360992240108
72 radiant_hero_95 0.5635903439598497
73 dire_hero_70 0.5583839849822786
74 dire_hero_69 0.5520070584047531
75 dire_hero_46 0.5396131611619958
76 dire_hero_119 0.5358381979706183
77 radiant_hero_65 0.5192645247152368
78 radiant_hero_62 0.5183279865244088
79 radiant_hero_135 0.49936800424506134
80 radiant_hero_2 0.4817157203218963
81 radiant_hero_69 0.4674047119278839
82 dire_hero_105 0.4672531552610654
83 dire_hero_128 0.46490506129257847
84 dire_hero_52 0.45906683460354497
85 dire_hero_131 0.4519549389897154
86 radiant_hero_100 0.4237007080091267
87 radiant_hero_3 0.4200207882294507
88 dire_hero_58 0.41845651287518343
89 dire_hero_101 0.39098204345118254
90 radiant_hero_58 0.36615994337973956
91 radiant_hero_90 0.3654215922377207
92 radiant_hero_109 0.36114422894241377
93 radiant_hero_85 0.3397063517524785
94 dire_hero_47 0.33120483123859173
95 dire_hero_65 0.3201442668327861
96 radiant_hero_16 0.3054077635508628
97 dire_hero_51 0.27585362626776144
98 dire_hero_126 0.273936700171308
99 radiant_hero_145 0.27080666451804547
100 dire_hero_50 0.24193256479700528
101 dire_hero_14 0.2307593677866238
102 dire_hero_138 0.22995826671565825
103 dire_hero_108 0.22299160580230648
104 radiant_hero_50 0.22224984591119692
105 radiant_hero_55 0.22024426849261636
106 radiant_hero_110 0.21815464793464948
107 dire_hero_145 0.21662542865055226
108 dire_hero_109 0.21105314668783304
109 dire_hero_71 0.2037500212802944
110 radiant_hero_51 0.20131521608992764
111 radiant_hero_99 0.1878406955271859
112 dire_hero_114 0.18771311441859298
113 radiant_hero_61 0.18042090494382437
114 radiant_hero_19 0.17972494076335502
115 dire_hero_135 0.17674176940995884
116 radiant_hero_138 0.17638797830930697
117 radiant_hero_11 0.17173288969835926
118 radiant_hero_131 0.17163285185364796
119 radiant_hero_71 0.1690626448094322
120 dire_hero_63 0.1677136535489791
121 dire_hero_136 0.1665628310620548
122 radiant_hero_59 0.15482478477426076
123 dire_hero_1 0.14761511544285089
124 radiant_hero_47 0.14432777647536155
125 dire_hero_74 0.14328897339865496
126 radiant_hero_60 0.1350544787435433
127 dire_hero_54 0.13444883015330528
128 radiant_hero_33 0.13418407698461643
129 radiant_hero_14 0.13062864684037695
130 dire_hero_55 0.11982716113335097
131 dire_hero_41 0.11590519381635243
132 radiant_hero_101 0.11145841473118147
133 radiant_hero_26 0.11042331242123556
134 radiant_hero_121 0.10624052682211534
135 dire_hero_73 0.10523838204127042
136 dire_hero_85 0.10116628070719334
137 radiant_hero_75 0.0990778076023039
138 radiant_hero_136 0.0971404322283737
139 dire_hero_121 0.0882738489185766
140 radiant_hero_48 0.08644988028482627
141 radiant_hero_105 0.08636504455479098
142 dire_hero_78 0.086083302792892
143 dire_hero_112 0.08186185440747837
144 radiant_hero_21 0.08031441854242871
145 dire_hero_11 0.08022938628242476
146 radiant_hero_1 0.07993183126994755
147 radiant_hero_73 0.07987048197448883
148 dire_hero_26 0.0751263061140198
149 radiant_hero_40 0.07500706396983621
150 radiant_hero_88 0.06448627461148718
151 radiant_hero_103 0.0596421467981599
152 radiant_hero_35 0.05811051984217868
153 radiant_hero_108 0.05783500222406305
154 radiant_hero_10 0.048789752327982654
155 dire_hero_21 0.04778443130941926
156 dire_hero_6 0.04540743981832734
157 dire_hero_106 0.04419874118360171
158 radiant_hero_9 0.04183971731090627
159 dire_hero_7 0.04151823003037786
160 radiant_hero_36 0.04071609662932216
161 dire_hero_93 0.03917124022761937
162 radiant_hero_57 0.03746648579498447
163 dire_hero_32 0.035168701377993536
164 radiant_hero_54 0.03379848987272846
165 radiant_hero_83 0.031345340329813724
166 dire_hero_103 0.03072106078174553
167 dire_hero_60 0.030582548339197258
168 radiant_hero_32 0.029014217937254724
169 radiant_hero_80 0.026474334315182813
170 dire_hero_33 0.02517097042722223
171 radiant_hero_78 0.024575425448720087
172 radiant_hero_82 0.022026769618172896
173 dire_hero_86 0.021481951040091762
174 dire_hero_88 0.020529621747134498
175 radiant_hero_68 0.019783709474760987
176 dire_hero_75 0.019335981384286435
177 radiant_hero_63 0.018498391135059748
178 radiant_hero_5 0.018167936059773637
179 dire_hero_40 0.01747482989495453
180 radiant_hero_41 0.01742109465745823
181 radiant_hero_23 0.015723888573406297
182 radiant_hero_97 0.014281791505775597
183 dire_hero_68 0.012826265095086503
184 radiant_hero_27 0.012675770453317775
185 dire_hero_110 0.012543695625851782
186 radiant_hero_15 0.011406703339127508
187 dire_hero_19 0.010305059042443747
188 dire_hero_97 0.010205238293995356
189 dire_hero_104 0.009982129745724043
190 radiant_hero_74 0.009456035513835673
191 dire_hero_91 0.007828627023014963
192 dire_hero_35 0.007655784085914658
193 radiant_hero_104 0.0072801341665292155
194 dire_hero_82 0.0066323538462641
195 dire_hero_23 0.0060004954896882255
196 radiant_hero_86 0.005622298563036045
197 radiant_hero_25 0.005493482170013952
198 radiant_hero_22 0.0054192470856789625
199 dire_hero_48 0.0051407952840448615
200 radiant_hero_7 0.005138603464622757
201 dire_hero_90 0.005123300607378032
202 radiant_hero_67 0.004597631094333278
203 dire_hero_36 0.002401848761390748
204 dire_hero_61 0.00032280295629982147
205 dire_hero_43 0.0
206 radiant_hero_56 0.0
207 dire_hero_67 0.0
208 dire_hero_56 0.0
209 dire_hero_9 0.0
210 dire_hero_5 0.0
211 dire_hero_57 0.0
212 radiant_hero_6 0.0
213 dire_hero_80 0.0
214 radiant_hero_112 0.0
215 dire_hero_22 0.0
216 dire_hero_25 0.0
217 radiant_hero_43 0.0
218 dire_hero_15 0.0
219 radiant_hero_91 0.0
220 radiant_hero_93 0.0
221 radiant_hero_94 0.0
222 dire_hero_34 0.0
223 radiant_hero_34 0.0
224 radiant_hero_119 0.0
225 dire_hero_27 0.0
226 dire_hero_83 0.0

View File

@@ -0,0 +1,411 @@
feature,importance
radiant_h126_p2,5.8458473601590715
radiant_h96_p4,5.2413063229870165
dire_h10_p3,4.904683311960459
dire_h37_p3,4.8751699328103335
radiant_h89_p1,2.4715406190858067
radiant_h46_p1,2.455964779498448
radiant_h29_p4,2.3728880092419993
dire_h95_p3,2.230103174869408
radiant_h100_p1,2.055912748373654
radiant_h137_p2,2.011249388205107
radiant_h114_p1,1.8834632265713043
radiant_h106_p2,1.862418726366778
radiant_h18_p1,1.859907271551847
radiant_h120_p4,1.768662877638573
radiant_h37_p1,1.7297278884453906
radiant_h8_p1,1.4434684244563847
dire_h94_p3,1.394179590286315
radiant_h110_p1,1.3797194949204172
dire_h13_p2,1.364320358951324
dire_h137_p2,1.3423520562699351
radiant_h128_p1,1.2200491444875372
radiant_h38_p2,1.1861633538551983
dire_h29_p5,1.1845192629048282
radiant_h95_p1,1.1685338110028223
radiant_h114_p2,1.108551650488793
dire_h45_p3,1.0881588369614426
radiant_h16_p4,1.0870253279315796
dire_h105_p5,1.026037232399492
dire_h64_p3,0.9866609384774393
dire_h50_p3,0.9397591580025132
dire_h126_p2,0.9309916370809915
dire_h100_p3,0.8936596185436807
radiant_h128_p4,0.8909609351058465
dire_h69_p5,0.7665391285229455
dire_h62_p5,0.7160026508265112
radiant_h135_p4,0.6826178347246731
dire_h129_p5,0.6618225408354624
radiant_h13_p2,0.6453650965826967
radiant_h100_p2,0.643789764434119
dire_h101_p3,0.6268127211790587
dire_h2_p5,0.6256596657614748
radiant_h79_p1,0.6062709463538
radiant_h48_p1,0.5665592628123931
radiant_h72_p1,0.5643459637965885
dire_h59_p2,0.5592053371112652
dire_h123_p3,0.538750259730039
radiant_h49_p1,0.5323370766514985
radiant_h96_p2,0.5268237229744013
radiant_h16_p2,0.5227450878750749
dire_h96_p5,0.5144175057861532
dire_h79_p3,0.5140812953951107
dire_h102_p3,0.5001100296143036
dire_h58_p5,0.49248173684856916
dire_h96_p2,0.49046939907184245
dire_h38_p5,0.4900988922655234
radiant_h62_p4,0.4693824339639839
dire_h108_p5,0.4481346895143821
dire_h51_p3,0.42574007658374796
radiant_h101_p4,0.41274612052046733
dire_h16_p5,0.3991129414642077
dire_h98_p2,0.38203893761032565
radiant_h102_p4,0.3789026178513027
dire_h33_p5,0.37858029577187385
radiant_h66_p4,0.36920914966145385
dire_h79_p5,0.368997590165472
dire_h14_p2,0.36729790467873835
dire_h53_p2,0.3575502549625211
dire_h14_p5,0.3489320324776503
dire_h131_p5,0.33238827327668086
radiant_h64_p1,0.31654185831002823
dire_h145_p3,0.31636605491217396
dire_h66_p5,0.3056930294825178
dire_h49_p3,0.30216040809135813
radiant_h98_p2,0.2983369585155709
dire_h119_p5,0.2969651388369031
radiant_h51_p4,0.29503345298908507
dire_h123_p5,0.29457863284774666
radiant_h138_p4,0.28462730610184617
dire_h38_p3,0.28411522961712354
radiant_h129_p4,0.28071616499432145
radiant_h87_p1,0.2772353555717822
dire_h71_p2,0.2771697129061259
dire_h65_p2,0.2671044075454367
radiant_h28_p4,0.26180741762762
dire_h3_p3,0.24847447988978347
dire_h66_p3,0.24572704845069399
dire_h98_p5,0.23450022414707072
radiant_h121_p4,0.23225208934071856
dire_h41_p3,0.22719989028850499
dire_h128_p3,0.22553985774297788
dire_h123_p2,0.22489449839123
radiant_h85_p4,0.22098025932507387
radiant_h100_p4,0.21314814210252667
dire_h64_p5,0.2124310360629601
radiant_h137_p4,0.21077351042091994
dire_h54_p3,0.20960551422486035
dire_h77_p5,0.20917567201529455
dire_h74_p2,0.20900629651347968
radiant_h62_p2,0.20047283058947185
dire_h72_p3,0.19963348230418612
radiant_h17_p2,0.19943046071083703
dire_h1_p3,0.1989514989016405
dire_h112_p3,0.189525683304801
dire_h135_p5,0.18660153542829755
radiant_h51_p1,0.1839451724510298
dire_h17_p2,0.18144460466176354
radiant_h39_p2,0.1773343712477707
dire_h128_p5,0.1716387627657839
radiant_h59_p2,0.1658113264628484
radiant_h123_p4,0.16524458827289465
dire_h136_p3,0.16428663110142036
dire_h99_p3,0.15563937327452676
radiant_h28_p2,0.1552433245712145
radiant_h110_p4,0.1481849002793656
radiant_h109_p1,0.1480295895745146
dire_h114_p3,0.14642637091481026
radiant_h136_p1,0.14475648830556773
dire_h6_p3,0.14266759350968217
radiant_h10_p1,0.1407809735639152
dire_h21_p5,0.14009499849996965
radiant_h19_p4,0.13554800050635046
radiant_h45_p1,0.1260353455334229
dire_h62_p2,0.12444567821891489
dire_h87_p3,0.12400854793721895
dire_h47_p2,0.12322736048740845
radiant_h64_p4,0.12025364829699399
dire_h28_p2,0.1167194303132419
radiant_h79_p4,0.11126994656278297
dire_h65_p3,0.11001179790040895
radiant_h55_p4,0.109830151464457
radiant_h123_p1,0.10923279779321164
radiant_h5_p1,0.10825087911784789
dire_h52_p2,0.10721595385915003
radiant_h47_p4,0.10657272981209304
dire_h99_p5,0.10521485722571794
dire_h58_p3,0.10339880809387055
radiant_h80_p1,0.10303018459499493
radiant_h3_p1,0.10205005418627881
radiant_h77_p4,0.10129398823464544
dire_h55_p5,0.10110820227360126
dire_h120_p2,0.09822231013289165
radiant_h58_p1,0.09653167031005891
radiant_h65_p2,0.09466007371296598
dire_h26_p3,0.09409797989341304
radiant_h49_p2,0.09291996782584751
dire_h89_p3,0.09133770378009658
radiant_h65_p4,0.08943794758237843
radiant_h99_p1,0.08531614935843866
radiant_h119_p4,0.08499065485609215
dire_h128_p2,0.08468309192671122
dire_h103_p3,0.08321942302625805
dire_h131_p3,0.07824810644437837
dire_h3_p5,0.07781846172892126
radiant_h21_p1,0.07714277136775362
dire_h17_p3,0.07665703405232827
dire_h79_p2,0.07223633538998198
radiant_h128_p2,0.07148459937706637
dire_h11_p2,0.0699511983979549
dire_h138_p3,0.06729258112518181
radiant_h53_p2,0.06426211048703748
dire_h38_p2,0.06328898002223096
radiant_h87_p4,0.06045458721975193
dire_h8_p3,0.06008848291210074
dire_h85_p5,0.05701646690525239
dire_h59_p3,0.05695578045236214
dire_h33_p2,0.05685990074342323
dire_h145_p2,0.05685848500238464
radiant_h35_p2,0.0563485612274038
dire_h80_p3,0.054475049923284107
dire_h37_p5,0.05412797726459125
dire_h51_p2,0.05238656751072729
radiant_h21_p2,0.04891215667599588
radiant_h70_p1,0.048388185285702684
radiant_h131_p4,0.04632085263177447
radiant_h7_p4,0.045736550588018905
dire_h50_p2,0.04486684683506996
dire_h32_p2,0.044500529959637526
dire_h109_p3,0.04413961848243405
radiant_h131_p1,0.043885261597800616
radiant_h2_p1,0.04229473461558145
radiant_h59_p1,0.04001087584966364
dire_h58_p2,0.03949191457469012
dire_h137_p5,0.0391094560324883
dire_h136_p5,0.03900219076238762
radiant_h129_p2,0.03894637522695674
dire_h51_p5,0.03759830461523884
radiant_h50_p1,0.03759053956597159
dire_h72_p5,0.03489297482691151
dire_h29_p3,0.03282918656004574
dire_h69_p2,0.032820814888430985
dire_h40_p5,0.03222312506053804
dire_h126_p3,0.030721958885042398
dire_h7_p2,0.030039091015776988
radiant_h99_p4,0.029512814453624206
radiant_h90_p4,0.028939315633782634
radiant_h38_p4,0.028884251904941007
radiant_h145_p4,0.02867246389735757
radiant_h52_p2,0.028327909211325466
radiant_h41_p1,0.02830912056061567
dire_h65_p5,0.02654669563264836
radiant_h3_p2,0.02638330196290669
radiant_h101_p2,0.02629175112197391
dire_h16_p2,0.025772625978061615
dire_h89_p5,0.025423167507288597
dire_h56_p3,0.023965420375433373
dire_h27_p5,0.023630615883621444
radiant_h14_p4,0.023577743244984795
dire_h46_p2,0.02351381664660187
radiant_h145_p1,0.022954662063548625
radiant_h105_p1,0.02172386381775776
dire_h101_p2,0.021665429499901225
dire_h138_p2,0.020603105835900505
radiant_h80_p2,0.020205886463181916
radiant_h45_p4,0.019873648320156683
dire_h75_p3,0.019095868018218366
dire_h71_p5,0.01883594340373369
radiant_h50_p4,0.01851801278627182
dire_h82_p2,0.016021411378539022
radiant_h14_p1,0.015638780734061875
radiant_h7_p2,0.015228489858375386
dire_h35_p2,0.014581160993008303
dire_h37_p2,0.014149570144541398
dire_h19_p5,0.014128813027374842
radiant_h68_p4,0.013874112600904527
dire_h73_p5,0.01370357622392973
dire_h45_p2,0.013429139431625712
dire_h121_p2,0.012720482369728212
dire_h103_p2,0.012580245658732822
dire_h78_p5,0.011479650208403117
radiant_h75_p1,0.011279211658043697
radiant_h87_p2,0.011009288964859922
radiant_h70_p4,0.010970134670089926
radiant_h15_p4,0.010315240629483772
dire_h82_p3,0.010238889513433087
dire_h86_p3,0.010192656397498409
radiant_h71_p2,0.01016778152865779
radiant_h83_p1,0.01012520567813156
dire_h48_p3,0.009918647322710925
radiant_h6_p1,0.009671953743806175
dire_h100_p2,0.009571320930816713
radiant_h90_p1,0.009527065031710249
dire_h119_p3,0.009483840085729776
dire_h11_p3,0.009196223596199492
radiant_h3_p4,0.008785437579069688
radiant_h126_p4,0.008492948766508739
dire_h90_p3,0.007462288997548156
dire_h36_p2,0.007168244918332031
radiant_h65_p1,0.007075073353615007
dire_h110_p2,0.007058771939753352
radiant_h88_p1,0.006970413712592924
dire_h3_p2,0.006770288324985224
radiant_h75_p4,0.005605529479664017
dire_h49_p2,0.005416375206431143
dire_h18_p3,0.004093083977963181
radiant_h54_p1,0.0037737028436135673
dire_h46_p3,0.0035428141984589754
dire_h63_p3,0.0024149663125720977
radiant_h67_p1,0.0023868366959004647
radiant_h33_p4,0.000814854247060661
radiant_h62_p1,0.00025903618440001206
dire_h88_p5,0.0002260965151758602
radiant_h98_p1,0.00013342577498281341
dire_h26_p5,9.687953196849277e-05
radiant_h94_p1,3.0479330214625887e-05
radiant_h138_p2,1.2930866595732473e-06
dire_h23_p5,0.0
radiant_h53_p1,0.0
dire_h91_p3,0.0
dire_h14_p3,0.0
dire_h68_p3,0.0
dire_h120_p5,0.0
dire_h53_p5,0.0
dire_h85_p3,0.0
radiant_h71_p4,0.0
radiant_h27_p1,0.0
radiant_h23_p4,0.0
dire_h97_p5,0.0
dire_h70_p3,0.0
dire_h13_p5,0.0
radiant_h73_p4,0.0
dire_h110_p3,0.0
dire_h39_p2,0.0
dire_h28_p5,0.0
dire_h131_p2,0.0
radiant_h71_p1,0.0
dire_h21_p2,0.0
radiant_h102_p1,0.0
radiant_h40_p1,0.0
radiant_h32_p1,0.0
dire_h40_p2,0.0
radiant_h66_p1,0.0
dire_h70_p5,0.0
radiant_h51_p2,0.0
dire_h27_p3,0.0
dire_h19_p3,0.0
dire_h129_p2,0.0
radiant_h11_p2,0.0
radiant_h79_p2,0.0
radiant_h88_p2,0.0
dire_h18_p5,0.0
dire_h104_p5,0.0
dire_h77_p2,0.0
radiant_h89_p4,0.0
dire_h138_p5,0.0
radiant_h73_p1,0.0
radiant_h19_p2,0.0
dire_h105_p3,0.0
radiant_h17_p1,0.0
dire_h68_p2,0.0
dire_h19_p2,0.0
dire_h39_p3,0.0
radiant_h47_p2,0.0
radiant_h9_p2,0.0
dire_h47_p5,0.0
dire_h114_p5,0.0
dire_h88_p2,0.0
dire_h49_p5,0.0
radiant_h58_p2,0.0
radiant_h61_p1,0.0
dire_h23_p2,0.0
radiant_h64_p2,0.0
dire_h100_p5,0.0
radiant_h73_p2,0.0
radiant_h78_p4,0.0
radiant_h129_p1,0.0
dire_h64_p2,0.0
radiant_h1_p1,0.0
radiant_h32_p4,0.0
radiant_h120_p2,0.0
radiant_h22_p2,0.0
dire_h60_p5,0.0
dire_h69_p3,0.0
radiant_h66_p5,0.0
radiant_h61_p2,0.0
radiant_h103_p4,0.0
radiant_h108_p4,0.0
radiant_h82_p1,0.0
dire_h61_p3,0.0
radiant_h138_p1,0.0
radiant_h9_p1,0.0
dire_h121_p5,0.0
dire_h89_p2,0.0
radiant_h36_p1,0.0
dire_h103_p5,0.0
radiant_h97_p4,0.0
radiant_h114_p4,0.0
radiant_h36_p2,0.0
radiant_h85_p1,0.0
dire_h43_p2,0.0
radiant_h58_p4,0.0
radiant_h69_p4,0.0
radiant_h39_p1,0.0
radiant_h74_p2,0.0
dire_h102_p5,0.0
radiant_h123_p2,0.0
radiant_h2_p4,0.0
radiant_h57_p1,0.0
radiant_h98_p4,0.0
radiant_h119_p1,0.0
dire_h86_p5,0.0
radiant_h61_p4,0.0
radiant_h14_p2,0.0
radiant_h60_p4,0.0
radiant_h103_p1,0.0
radiant_h104_p4,0.0
dire_h53_p3,0.0
radiant_h136_p4,0.0
dire_h106_p2,0.0
radiant_h68_p1,0.0
radiant_h63_p1,0.0
radiant_h105_p4,0.0
dire_h34_p2,0.0
radiant_h72_p4,0.0
dire_h101_p5,0.0
radiant_h121_p1,0.0
dire_h93_p5,0.0
dire_h10_p2,0.0
radiant_h21_p4,0.0
radiant_h101_p1,0.0
radiant_h126_p1,0.0
radiant_h37_p2,0.0
radiant_h131_p2,0.0
radiant_h11_p1,0.0
radiant_h66_p2,0.0
dire_h62_p3,0.0
radiant_h60_p1,0.0
radiant_h52_p4,0.0
dire_h95_p5,0.0
radiant_h90_p2,0.0
radiant_h88_p4,0.0
dire_h114_p2,0.0
radiant_h33_p2,0.0
radiant_h26_p4,0.0
radiant_h39_p4,0.0
dire_h9_p3,0.0
radiant_h13_p4,0.0
dire_h126_p5,0.0
dire_h7_p5,0.0
radiant_h40_p2,0.0
dire_h87_p5,0.0
dire_h21_p3,0.0
radiant_h86_p4,0.0
dire_h85_p2,0.0
radiant_h49_p4,0.0
dire_h41_p5,0.0
dire_h98_p3,0.0
dire_h73_p3,0.0
dire_h45_p5,0.0
radiant_h69_p1,0.0
radiant_h25_p2,0.0
1 feature importance
2 radiant_h126_p2 5.8458473601590715
3 radiant_h96_p4 5.2413063229870165
4 dire_h10_p3 4.904683311960459
5 dire_h37_p3 4.8751699328103335
6 radiant_h89_p1 2.4715406190858067
7 radiant_h46_p1 2.455964779498448
8 radiant_h29_p4 2.3728880092419993
9 dire_h95_p3 2.230103174869408
10 radiant_h100_p1 2.055912748373654
11 radiant_h137_p2 2.011249388205107
12 radiant_h114_p1 1.8834632265713043
13 radiant_h106_p2 1.862418726366778
14 radiant_h18_p1 1.859907271551847
15 radiant_h120_p4 1.768662877638573
16 radiant_h37_p1 1.7297278884453906
17 radiant_h8_p1 1.4434684244563847
18 dire_h94_p3 1.394179590286315
19 radiant_h110_p1 1.3797194949204172
20 dire_h13_p2 1.364320358951324
21 dire_h137_p2 1.3423520562699351
22 radiant_h128_p1 1.2200491444875372
23 radiant_h38_p2 1.1861633538551983
24 dire_h29_p5 1.1845192629048282
25 radiant_h95_p1 1.1685338110028223
26 radiant_h114_p2 1.108551650488793
27 dire_h45_p3 1.0881588369614426
28 radiant_h16_p4 1.0870253279315796
29 dire_h105_p5 1.026037232399492
30 dire_h64_p3 0.9866609384774393
31 dire_h50_p3 0.9397591580025132
32 dire_h126_p2 0.9309916370809915
33 dire_h100_p3 0.8936596185436807
34 radiant_h128_p4 0.8909609351058465
35 dire_h69_p5 0.7665391285229455
36 dire_h62_p5 0.7160026508265112
37 radiant_h135_p4 0.6826178347246731
38 dire_h129_p5 0.6618225408354624
39 radiant_h13_p2 0.6453650965826967
40 radiant_h100_p2 0.643789764434119
41 dire_h101_p3 0.6268127211790587
42 dire_h2_p5 0.6256596657614748
43 radiant_h79_p1 0.6062709463538
44 radiant_h48_p1 0.5665592628123931
45 radiant_h72_p1 0.5643459637965885
46 dire_h59_p2 0.5592053371112652
47 dire_h123_p3 0.538750259730039
48 radiant_h49_p1 0.5323370766514985
49 radiant_h96_p2 0.5268237229744013
50 radiant_h16_p2 0.5227450878750749
51 dire_h96_p5 0.5144175057861532
52 dire_h79_p3 0.5140812953951107
53 dire_h102_p3 0.5001100296143036
54 dire_h58_p5 0.49248173684856916
55 dire_h96_p2 0.49046939907184245
56 dire_h38_p5 0.4900988922655234
57 radiant_h62_p4 0.4693824339639839
58 dire_h108_p5 0.4481346895143821
59 dire_h51_p3 0.42574007658374796
60 radiant_h101_p4 0.41274612052046733
61 dire_h16_p5 0.3991129414642077
62 dire_h98_p2 0.38203893761032565
63 radiant_h102_p4 0.3789026178513027
64 dire_h33_p5 0.37858029577187385
65 radiant_h66_p4 0.36920914966145385
66 dire_h79_p5 0.368997590165472
67 dire_h14_p2 0.36729790467873835
68 dire_h53_p2 0.3575502549625211
69 dire_h14_p5 0.3489320324776503
70 dire_h131_p5 0.33238827327668086
71 radiant_h64_p1 0.31654185831002823
72 dire_h145_p3 0.31636605491217396
73 dire_h66_p5 0.3056930294825178
74 dire_h49_p3 0.30216040809135813
75 radiant_h98_p2 0.2983369585155709
76 dire_h119_p5 0.2969651388369031
77 radiant_h51_p4 0.29503345298908507
78 dire_h123_p5 0.29457863284774666
79 radiant_h138_p4 0.28462730610184617
80 dire_h38_p3 0.28411522961712354
81 radiant_h129_p4 0.28071616499432145
82 radiant_h87_p1 0.2772353555717822
83 dire_h71_p2 0.2771697129061259
84 dire_h65_p2 0.2671044075454367
85 radiant_h28_p4 0.26180741762762
86 dire_h3_p3 0.24847447988978347
87 dire_h66_p3 0.24572704845069399
88 dire_h98_p5 0.23450022414707072
89 radiant_h121_p4 0.23225208934071856
90 dire_h41_p3 0.22719989028850499
91 dire_h128_p3 0.22553985774297788
92 dire_h123_p2 0.22489449839123
93 radiant_h85_p4 0.22098025932507387
94 radiant_h100_p4 0.21314814210252667
95 dire_h64_p5 0.2124310360629601
96 radiant_h137_p4 0.21077351042091994
97 dire_h54_p3 0.20960551422486035
98 dire_h77_p5 0.20917567201529455
99 dire_h74_p2 0.20900629651347968
100 radiant_h62_p2 0.20047283058947185
101 dire_h72_p3 0.19963348230418612
102 radiant_h17_p2 0.19943046071083703
103 dire_h1_p3 0.1989514989016405
104 dire_h112_p3 0.189525683304801
105 dire_h135_p5 0.18660153542829755
106 radiant_h51_p1 0.1839451724510298
107 dire_h17_p2 0.18144460466176354
108 radiant_h39_p2 0.1773343712477707
109 dire_h128_p5 0.1716387627657839
110 radiant_h59_p2 0.1658113264628484
111 radiant_h123_p4 0.16524458827289465
112 dire_h136_p3 0.16428663110142036
113 dire_h99_p3 0.15563937327452676
114 radiant_h28_p2 0.1552433245712145
115 radiant_h110_p4 0.1481849002793656
116 radiant_h109_p1 0.1480295895745146
117 dire_h114_p3 0.14642637091481026
118 radiant_h136_p1 0.14475648830556773
119 dire_h6_p3 0.14266759350968217
120 radiant_h10_p1 0.1407809735639152
121 dire_h21_p5 0.14009499849996965
122 radiant_h19_p4 0.13554800050635046
123 radiant_h45_p1 0.1260353455334229
124 dire_h62_p2 0.12444567821891489
125 dire_h87_p3 0.12400854793721895
126 dire_h47_p2 0.12322736048740845
127 radiant_h64_p4 0.12025364829699399
128 dire_h28_p2 0.1167194303132419
129 radiant_h79_p4 0.11126994656278297
130 dire_h65_p3 0.11001179790040895
131 radiant_h55_p4 0.109830151464457
132 radiant_h123_p1 0.10923279779321164
133 radiant_h5_p1 0.10825087911784789
134 dire_h52_p2 0.10721595385915003
135 radiant_h47_p4 0.10657272981209304
136 dire_h99_p5 0.10521485722571794
137 dire_h58_p3 0.10339880809387055
138 radiant_h80_p1 0.10303018459499493
139 radiant_h3_p1 0.10205005418627881
140 radiant_h77_p4 0.10129398823464544
141 dire_h55_p5 0.10110820227360126
142 dire_h120_p2 0.09822231013289165
143 radiant_h58_p1 0.09653167031005891
144 radiant_h65_p2 0.09466007371296598
145 dire_h26_p3 0.09409797989341304
146 radiant_h49_p2 0.09291996782584751
147 dire_h89_p3 0.09133770378009658
148 radiant_h65_p4 0.08943794758237843
149 radiant_h99_p1 0.08531614935843866
150 radiant_h119_p4 0.08499065485609215
151 dire_h128_p2 0.08468309192671122
152 dire_h103_p3 0.08321942302625805
153 dire_h131_p3 0.07824810644437837
154 dire_h3_p5 0.07781846172892126
155 radiant_h21_p1 0.07714277136775362
156 dire_h17_p3 0.07665703405232827
157 dire_h79_p2 0.07223633538998198
158 radiant_h128_p2 0.07148459937706637
159 dire_h11_p2 0.0699511983979549
160 dire_h138_p3 0.06729258112518181
161 radiant_h53_p2 0.06426211048703748
162 dire_h38_p2 0.06328898002223096
163 radiant_h87_p4 0.06045458721975193
164 dire_h8_p3 0.06008848291210074
165 dire_h85_p5 0.05701646690525239
166 dire_h59_p3 0.05695578045236214
167 dire_h33_p2 0.05685990074342323
168 dire_h145_p2 0.05685848500238464
169 radiant_h35_p2 0.0563485612274038
170 dire_h80_p3 0.054475049923284107
171 dire_h37_p5 0.05412797726459125
172 dire_h51_p2 0.05238656751072729
173 radiant_h21_p2 0.04891215667599588
174 radiant_h70_p1 0.048388185285702684
175 radiant_h131_p4 0.04632085263177447
176 radiant_h7_p4 0.045736550588018905
177 dire_h50_p2 0.04486684683506996
178 dire_h32_p2 0.044500529959637526
179 dire_h109_p3 0.04413961848243405
180 radiant_h131_p1 0.043885261597800616
181 radiant_h2_p1 0.04229473461558145
182 radiant_h59_p1 0.04001087584966364
183 dire_h58_p2 0.03949191457469012
184 dire_h137_p5 0.0391094560324883
185 dire_h136_p5 0.03900219076238762
186 radiant_h129_p2 0.03894637522695674
187 dire_h51_p5 0.03759830461523884
188 radiant_h50_p1 0.03759053956597159
189 dire_h72_p5 0.03489297482691151
190 dire_h29_p3 0.03282918656004574
191 dire_h69_p2 0.032820814888430985
192 dire_h40_p5 0.03222312506053804
193 dire_h126_p3 0.030721958885042398
194 dire_h7_p2 0.030039091015776988
195 radiant_h99_p4 0.029512814453624206
196 radiant_h90_p4 0.028939315633782634
197 radiant_h38_p4 0.028884251904941007
198 radiant_h145_p4 0.02867246389735757
199 radiant_h52_p2 0.028327909211325466
200 radiant_h41_p1 0.02830912056061567
201 dire_h65_p5 0.02654669563264836
202 radiant_h3_p2 0.02638330196290669
203 radiant_h101_p2 0.02629175112197391
204 dire_h16_p2 0.025772625978061615
205 dire_h89_p5 0.025423167507288597
206 dire_h56_p3 0.023965420375433373
207 dire_h27_p5 0.023630615883621444
208 radiant_h14_p4 0.023577743244984795
209 dire_h46_p2 0.02351381664660187
210 radiant_h145_p1 0.022954662063548625
211 radiant_h105_p1 0.02172386381775776
212 dire_h101_p2 0.021665429499901225
213 dire_h138_p2 0.020603105835900505
214 radiant_h80_p2 0.020205886463181916
215 radiant_h45_p4 0.019873648320156683
216 dire_h75_p3 0.019095868018218366
217 dire_h71_p5 0.01883594340373369
218 radiant_h50_p4 0.01851801278627182
219 dire_h82_p2 0.016021411378539022
220 radiant_h14_p1 0.015638780734061875
221 radiant_h7_p2 0.015228489858375386
222 dire_h35_p2 0.014581160993008303
223 dire_h37_p2 0.014149570144541398
224 dire_h19_p5 0.014128813027374842
225 radiant_h68_p4 0.013874112600904527
226 dire_h73_p5 0.01370357622392973
227 dire_h45_p2 0.013429139431625712
228 dire_h121_p2 0.012720482369728212
229 dire_h103_p2 0.012580245658732822
230 dire_h78_p5 0.011479650208403117
231 radiant_h75_p1 0.011279211658043697
232 radiant_h87_p2 0.011009288964859922
233 radiant_h70_p4 0.010970134670089926
234 radiant_h15_p4 0.010315240629483772
235 dire_h82_p3 0.010238889513433087
236 dire_h86_p3 0.010192656397498409
237 radiant_h71_p2 0.01016778152865779
238 radiant_h83_p1 0.01012520567813156
239 dire_h48_p3 0.009918647322710925
240 radiant_h6_p1 0.009671953743806175
241 dire_h100_p2 0.009571320930816713
242 radiant_h90_p1 0.009527065031710249
243 dire_h119_p3 0.009483840085729776
244 dire_h11_p3 0.009196223596199492
245 radiant_h3_p4 0.008785437579069688
246 radiant_h126_p4 0.008492948766508739
247 dire_h90_p3 0.007462288997548156
248 dire_h36_p2 0.007168244918332031
249 radiant_h65_p1 0.007075073353615007
250 dire_h110_p2 0.007058771939753352
251 radiant_h88_p1 0.006970413712592924
252 dire_h3_p2 0.006770288324985224
253 radiant_h75_p4 0.005605529479664017
254 dire_h49_p2 0.005416375206431143
255 dire_h18_p3 0.004093083977963181
256 radiant_h54_p1 0.0037737028436135673
257 dire_h46_p3 0.0035428141984589754
258 dire_h63_p3 0.0024149663125720977
259 radiant_h67_p1 0.0023868366959004647
260 radiant_h33_p4 0.000814854247060661
261 radiant_h62_p1 0.00025903618440001206
262 dire_h88_p5 0.0002260965151758602
263 radiant_h98_p1 0.00013342577498281341
264 dire_h26_p5 9.687953196849277e-05
265 radiant_h94_p1 3.0479330214625887e-05
266 radiant_h138_p2 1.2930866595732473e-06
267 dire_h23_p5 0.0
268 radiant_h53_p1 0.0
269 dire_h91_p3 0.0
270 dire_h14_p3 0.0
271 dire_h68_p3 0.0
272 dire_h120_p5 0.0
273 dire_h53_p5 0.0
274 dire_h85_p3 0.0
275 radiant_h71_p4 0.0
276 radiant_h27_p1 0.0
277 radiant_h23_p4 0.0
278 dire_h97_p5 0.0
279 dire_h70_p3 0.0
280 dire_h13_p5 0.0
281 radiant_h73_p4 0.0
282 dire_h110_p3 0.0
283 dire_h39_p2 0.0
284 dire_h28_p5 0.0
285 dire_h131_p2 0.0
286 radiant_h71_p1 0.0
287 dire_h21_p2 0.0
288 radiant_h102_p1 0.0
289 radiant_h40_p1 0.0
290 radiant_h32_p1 0.0
291 dire_h40_p2 0.0
292 radiant_h66_p1 0.0
293 dire_h70_p5 0.0
294 radiant_h51_p2 0.0
295 dire_h27_p3 0.0
296 dire_h19_p3 0.0
297 dire_h129_p2 0.0
298 radiant_h11_p2 0.0
299 radiant_h79_p2 0.0
300 radiant_h88_p2 0.0
301 dire_h18_p5 0.0
302 dire_h104_p5 0.0
303 dire_h77_p2 0.0
304 radiant_h89_p4 0.0
305 dire_h138_p5 0.0
306 radiant_h73_p1 0.0
307 radiant_h19_p2 0.0
308 dire_h105_p3 0.0
309 radiant_h17_p1 0.0
310 dire_h68_p2 0.0
311 dire_h19_p2 0.0
312 dire_h39_p3 0.0
313 radiant_h47_p2 0.0
314 radiant_h9_p2 0.0
315 dire_h47_p5 0.0
316 dire_h114_p5 0.0
317 dire_h88_p2 0.0
318 dire_h49_p5 0.0
319 radiant_h58_p2 0.0
320 radiant_h61_p1 0.0
321 dire_h23_p2 0.0
322 radiant_h64_p2 0.0
323 dire_h100_p5 0.0
324 radiant_h73_p2 0.0
325 radiant_h78_p4 0.0
326 radiant_h129_p1 0.0
327 dire_h64_p2 0.0
328 radiant_h1_p1 0.0
329 radiant_h32_p4 0.0
330 radiant_h120_p2 0.0
331 radiant_h22_p2 0.0
332 dire_h60_p5 0.0
333 dire_h69_p3 0.0
334 radiant_h66_p5 0.0
335 radiant_h61_p2 0.0
336 radiant_h103_p4 0.0
337 radiant_h108_p4 0.0
338 radiant_h82_p1 0.0
339 dire_h61_p3 0.0
340 radiant_h138_p1 0.0
341 radiant_h9_p1 0.0
342 dire_h121_p5 0.0
343 dire_h89_p2 0.0
344 radiant_h36_p1 0.0
345 dire_h103_p5 0.0
346 radiant_h97_p4 0.0
347 radiant_h114_p4 0.0
348 radiant_h36_p2 0.0
349 radiant_h85_p1 0.0
350 dire_h43_p2 0.0
351 radiant_h58_p4 0.0
352 radiant_h69_p4 0.0
353 radiant_h39_p1 0.0
354 radiant_h74_p2 0.0
355 dire_h102_p5 0.0
356 radiant_h123_p2 0.0
357 radiant_h2_p4 0.0
358 radiant_h57_p1 0.0
359 radiant_h98_p4 0.0
360 radiant_h119_p1 0.0
361 dire_h86_p5 0.0
362 radiant_h61_p4 0.0
363 radiant_h14_p2 0.0
364 radiant_h60_p4 0.0
365 radiant_h103_p1 0.0
366 radiant_h104_p4 0.0
367 dire_h53_p3 0.0
368 radiant_h136_p4 0.0
369 dire_h106_p2 0.0
370 radiant_h68_p1 0.0
371 radiant_h63_p1 0.0
372 radiant_h105_p4 0.0
373 dire_h34_p2 0.0
374 radiant_h72_p4 0.0
375 dire_h101_p5 0.0
376 radiant_h121_p1 0.0
377 dire_h93_p5 0.0
378 dire_h10_p2 0.0
379 radiant_h21_p4 0.0
380 radiant_h101_p1 0.0
381 radiant_h126_p1 0.0
382 radiant_h37_p2 0.0
383 radiant_h131_p2 0.0
384 radiant_h11_p1 0.0
385 radiant_h66_p2 0.0
386 dire_h62_p3 0.0
387 radiant_h60_p1 0.0
388 radiant_h52_p4 0.0
389 dire_h95_p5 0.0
390 radiant_h90_p2 0.0
391 radiant_h88_p4 0.0
392 dire_h114_p2 0.0
393 radiant_h33_p2 0.0
394 radiant_h26_p4 0.0
395 radiant_h39_p4 0.0
396 dire_h9_p3 0.0
397 radiant_h13_p4 0.0
398 dire_h126_p5 0.0
399 dire_h7_p5 0.0
400 radiant_h40_p2 0.0
401 dire_h87_p5 0.0
402 dire_h21_p3 0.0
403 radiant_h86_p4 0.0
404 dire_h85_p2 0.0
405 radiant_h49_p4 0.0
406 dire_h41_p5 0.0
407 dire_h98_p3 0.0
408 dire_h73_p3 0.0
409 dire_h45_p5 0.0
410 radiant_h69_p1 0.0
411 radiant_h25_p2 0.0

View File

@@ -0,0 +1,4 @@
feature,importance
pred_with_positions,2.8814398908281142
pred_bag_of_heroes,6.656601650820854
pred_with_players,0.3906490762854252
1 feature importance
2 pred_with_positions 2.8814398908281142
3 pred_bag_of_heroes 6.656601650820854
4 pred_with_players 0.3906490762854252

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,226 @@
feature
is_first_pick_radiant
radiant_hero_1
dire_hero_1
radiant_hero_2
dire_hero_2
radiant_hero_3
dire_hero_3
radiant_hero_5
dire_hero_5
radiant_hero_6
dire_hero_6
radiant_hero_7
dire_hero_7
radiant_hero_8
dire_hero_8
radiant_hero_9
dire_hero_9
radiant_hero_10
dire_hero_10
radiant_hero_11
dire_hero_11
radiant_hero_13
dire_hero_13
radiant_hero_14
dire_hero_14
radiant_hero_15
dire_hero_15
radiant_hero_16
dire_hero_16
radiant_hero_17
dire_hero_17
radiant_hero_18
dire_hero_18
radiant_hero_19
dire_hero_19
radiant_hero_21
dire_hero_21
radiant_hero_22
dire_hero_22
radiant_hero_23
dire_hero_23
radiant_hero_25
dire_hero_25
radiant_hero_26
dire_hero_26
radiant_hero_27
dire_hero_27
radiant_hero_28
dire_hero_28
radiant_hero_29
dire_hero_29
radiant_hero_32
dire_hero_32
radiant_hero_33
dire_hero_33
radiant_hero_34
dire_hero_34
radiant_hero_35
dire_hero_35
radiant_hero_36
dire_hero_36
radiant_hero_37
dire_hero_37
radiant_hero_38
dire_hero_38
radiant_hero_39
dire_hero_39
radiant_hero_40
dire_hero_40
radiant_hero_41
dire_hero_41
radiant_hero_43
dire_hero_43
radiant_hero_45
dire_hero_45
radiant_hero_46
dire_hero_46
radiant_hero_47
dire_hero_47
radiant_hero_48
dire_hero_48
radiant_hero_49
dire_hero_49
radiant_hero_50
dire_hero_50
radiant_hero_51
dire_hero_51
radiant_hero_52
dire_hero_52
radiant_hero_53
dire_hero_53
radiant_hero_54
dire_hero_54
radiant_hero_55
dire_hero_55
radiant_hero_56
dire_hero_56
radiant_hero_57
dire_hero_57
radiant_hero_58
dire_hero_58
radiant_hero_59
dire_hero_59
radiant_hero_60
dire_hero_60
radiant_hero_61
dire_hero_61
radiant_hero_62
dire_hero_62
radiant_hero_63
dire_hero_63
radiant_hero_64
dire_hero_64
radiant_hero_65
dire_hero_65
radiant_hero_66
dire_hero_66
radiant_hero_67
dire_hero_67
radiant_hero_68
dire_hero_68
radiant_hero_69
dire_hero_69
radiant_hero_70
dire_hero_70
radiant_hero_71
dire_hero_71
radiant_hero_72
dire_hero_72
radiant_hero_73
dire_hero_73
radiant_hero_74
dire_hero_74
radiant_hero_75
dire_hero_75
radiant_hero_77
dire_hero_77
radiant_hero_78
dire_hero_78
radiant_hero_79
dire_hero_79
radiant_hero_80
dire_hero_80
radiant_hero_82
dire_hero_82
radiant_hero_83
dire_hero_83
radiant_hero_85
dire_hero_85
radiant_hero_86
dire_hero_86
radiant_hero_87
dire_hero_87
radiant_hero_88
dire_hero_88
radiant_hero_89
dire_hero_89
radiant_hero_90
dire_hero_90
radiant_hero_91
dire_hero_91
radiant_hero_93
dire_hero_93
radiant_hero_94
dire_hero_94
radiant_hero_95
dire_hero_95
radiant_hero_96
dire_hero_96
radiant_hero_97
dire_hero_97
radiant_hero_98
dire_hero_98
radiant_hero_99
dire_hero_99
radiant_hero_100
dire_hero_100
radiant_hero_101
dire_hero_101
radiant_hero_102
dire_hero_102
radiant_hero_103
dire_hero_103
radiant_hero_104
dire_hero_104
radiant_hero_105
dire_hero_105
radiant_hero_106
dire_hero_106
radiant_hero_108
dire_hero_108
radiant_hero_109
dire_hero_109
radiant_hero_110
dire_hero_110
radiant_hero_112
dire_hero_112
radiant_hero_114
dire_hero_114
radiant_hero_119
dire_hero_119
radiant_hero_120
dire_hero_120
radiant_hero_121
dire_hero_121
radiant_hero_123
dire_hero_123
radiant_hero_126
dire_hero_126
radiant_hero_128
dire_hero_128
radiant_hero_129
dire_hero_129
radiant_hero_131
dire_hero_131
radiant_hero_135
dire_hero_135
radiant_hero_136
dire_hero_136
radiant_hero_137
dire_hero_137
radiant_hero_138
dire_hero_138
radiant_hero_145
dire_hero_145
1 feature
2 is_first_pick_radiant
3 radiant_hero_1
4 dire_hero_1
5 radiant_hero_2
6 dire_hero_2
7 radiant_hero_3
8 dire_hero_3
9 radiant_hero_5
10 dire_hero_5
11 radiant_hero_6
12 dire_hero_6
13 radiant_hero_7
14 dire_hero_7
15 radiant_hero_8
16 dire_hero_8
17 radiant_hero_9
18 dire_hero_9
19 radiant_hero_10
20 dire_hero_10
21 radiant_hero_11
22 dire_hero_11
23 radiant_hero_13
24 dire_hero_13
25 radiant_hero_14
26 dire_hero_14
27 radiant_hero_15
28 dire_hero_15
29 radiant_hero_16
30 dire_hero_16
31 radiant_hero_17
32 dire_hero_17
33 radiant_hero_18
34 dire_hero_18
35 radiant_hero_19
36 dire_hero_19
37 radiant_hero_21
38 dire_hero_21
39 radiant_hero_22
40 dire_hero_22
41 radiant_hero_23
42 dire_hero_23
43 radiant_hero_25
44 dire_hero_25
45 radiant_hero_26
46 dire_hero_26
47 radiant_hero_27
48 dire_hero_27
49 radiant_hero_28
50 dire_hero_28
51 radiant_hero_29
52 dire_hero_29
53 radiant_hero_32
54 dire_hero_32
55 radiant_hero_33
56 dire_hero_33
57 radiant_hero_34
58 dire_hero_34
59 radiant_hero_35
60 dire_hero_35
61 radiant_hero_36
62 dire_hero_36
63 radiant_hero_37
64 dire_hero_37
65 radiant_hero_38
66 dire_hero_38
67 radiant_hero_39
68 dire_hero_39
69 radiant_hero_40
70 dire_hero_40
71 radiant_hero_41
72 dire_hero_41
73 radiant_hero_43
74 dire_hero_43
75 radiant_hero_45
76 dire_hero_45
77 radiant_hero_46
78 dire_hero_46
79 radiant_hero_47
80 dire_hero_47
81 radiant_hero_48
82 dire_hero_48
83 radiant_hero_49
84 dire_hero_49
85 radiant_hero_50
86 dire_hero_50
87 radiant_hero_51
88 dire_hero_51
89 radiant_hero_52
90 dire_hero_52
91 radiant_hero_53
92 dire_hero_53
93 radiant_hero_54
94 dire_hero_54
95 radiant_hero_55
96 dire_hero_55
97 radiant_hero_56
98 dire_hero_56
99 radiant_hero_57
100 dire_hero_57
101 radiant_hero_58
102 dire_hero_58
103 radiant_hero_59
104 dire_hero_59
105 radiant_hero_60
106 dire_hero_60
107 radiant_hero_61
108 dire_hero_61
109 radiant_hero_62
110 dire_hero_62
111 radiant_hero_63
112 dire_hero_63
113 radiant_hero_64
114 dire_hero_64
115 radiant_hero_65
116 dire_hero_65
117 radiant_hero_66
118 dire_hero_66
119 radiant_hero_67
120 dire_hero_67
121 radiant_hero_68
122 dire_hero_68
123 radiant_hero_69
124 dire_hero_69
125 radiant_hero_70
126 dire_hero_70
127 radiant_hero_71
128 dire_hero_71
129 radiant_hero_72
130 dire_hero_72
131 radiant_hero_73
132 dire_hero_73
133 radiant_hero_74
134 dire_hero_74
135 radiant_hero_75
136 dire_hero_75
137 radiant_hero_77
138 dire_hero_77
139 radiant_hero_78
140 dire_hero_78
141 radiant_hero_79
142 dire_hero_79
143 radiant_hero_80
144 dire_hero_80
145 radiant_hero_82
146 dire_hero_82
147 radiant_hero_83
148 dire_hero_83
149 radiant_hero_85
150 dire_hero_85
151 radiant_hero_86
152 dire_hero_86
153 radiant_hero_87
154 dire_hero_87
155 radiant_hero_88
156 dire_hero_88
157 radiant_hero_89
158 dire_hero_89
159 radiant_hero_90
160 dire_hero_90
161 radiant_hero_91
162 dire_hero_91
163 radiant_hero_93
164 dire_hero_93
165 radiant_hero_94
166 dire_hero_94
167 radiant_hero_95
168 dire_hero_95
169 radiant_hero_96
170 dire_hero_96
171 radiant_hero_97
172 dire_hero_97
173 radiant_hero_98
174 dire_hero_98
175 radiant_hero_99
176 dire_hero_99
177 radiant_hero_100
178 dire_hero_100
179 radiant_hero_101
180 dire_hero_101
181 radiant_hero_102
182 dire_hero_102
183 radiant_hero_103
184 dire_hero_103
185 radiant_hero_104
186 dire_hero_104
187 radiant_hero_105
188 dire_hero_105
189 radiant_hero_106
190 dire_hero_106
191 radiant_hero_108
192 dire_hero_108
193 radiant_hero_109
194 dire_hero_109
195 radiant_hero_110
196 dire_hero_110
197 radiant_hero_112
198 dire_hero_112
199 radiant_hero_114
200 dire_hero_114
201 radiant_hero_119
202 dire_hero_119
203 radiant_hero_120
204 dire_hero_120
205 radiant_hero_121
206 dire_hero_121
207 radiant_hero_123
208 dire_hero_123
209 radiant_hero_126
210 dire_hero_126
211 radiant_hero_128
212 dire_hero_128
213 radiant_hero_129
214 dire_hero_129
215 radiant_hero_131
216 dire_hero_131
217 radiant_hero_135
218 dire_hero_135
219 radiant_hero_136
220 dire_hero_136
221 radiant_hero_137
222 dire_hero_137
223 radiant_hero_138
224 dire_hero_138
225 radiant_hero_145
226 dire_hero_145

View File

@@ -0,0 +1,411 @@
feature
dire_h100_p2
dire_h100_p3
dire_h100_p5
dire_h101_p2
dire_h101_p3
dire_h101_p5
dire_h102_p3
dire_h102_p5
dire_h103_p2
dire_h103_p3
dire_h103_p5
dire_h104_p5
dire_h105_p3
dire_h105_p5
dire_h106_p2
dire_h108_p5
dire_h109_p3
dire_h10_p2
dire_h10_p3
dire_h110_p2
dire_h110_p3
dire_h112_p3
dire_h114_p2
dire_h114_p3
dire_h114_p5
dire_h119_p3
dire_h119_p5
dire_h11_p2
dire_h11_p3
dire_h120_p2
dire_h120_p5
dire_h121_p2
dire_h121_p5
dire_h123_p2
dire_h123_p3
dire_h123_p5
dire_h126_p2
dire_h126_p3
dire_h126_p5
dire_h128_p2
dire_h128_p3
dire_h128_p5
dire_h129_p2
dire_h129_p5
dire_h131_p2
dire_h131_p3
dire_h131_p5
dire_h135_p5
dire_h136_p3
dire_h136_p5
dire_h137_p2
dire_h137_p5
dire_h138_p2
dire_h138_p3
dire_h138_p5
dire_h13_p2
dire_h13_p5
dire_h145_p2
dire_h145_p3
dire_h14_p2
dire_h14_p3
dire_h14_p5
dire_h16_p2
dire_h16_p5
dire_h17_p2
dire_h17_p3
dire_h18_p3
dire_h18_p5
dire_h19_p2
dire_h19_p3
dire_h19_p5
dire_h1_p3
dire_h21_p2
dire_h21_p3
dire_h21_p5
dire_h23_p2
dire_h23_p5
dire_h26_p3
dire_h26_p5
dire_h27_p3
dire_h27_p5
dire_h28_p2
dire_h28_p5
dire_h29_p3
dire_h29_p5
dire_h2_p5
dire_h32_p2
dire_h33_p2
dire_h33_p5
dire_h34_p2
dire_h35_p2
dire_h36_p2
dire_h37_p2
dire_h37_p3
dire_h37_p5
dire_h38_p2
dire_h38_p3
dire_h38_p5
dire_h39_p2
dire_h39_p3
dire_h3_p2
dire_h3_p3
dire_h3_p5
dire_h40_p2
dire_h40_p5
dire_h41_p3
dire_h41_p5
dire_h43_p2
dire_h45_p2
dire_h45_p3
dire_h45_p5
dire_h46_p2
dire_h46_p3
dire_h47_p2
dire_h47_p5
dire_h48_p3
dire_h49_p2
dire_h49_p3
dire_h49_p5
dire_h50_p2
dire_h50_p3
dire_h51_p2
dire_h51_p3
dire_h51_p5
dire_h52_p2
dire_h53_p2
dire_h53_p3
dire_h53_p5
dire_h54_p3
dire_h55_p5
dire_h56_p3
dire_h58_p2
dire_h58_p3
dire_h58_p5
dire_h59_p2
dire_h59_p3
dire_h60_p5
dire_h61_p3
dire_h62_p2
dire_h62_p3
dire_h62_p5
dire_h63_p3
dire_h64_p2
dire_h64_p3
dire_h64_p5
dire_h65_p2
dire_h65_p3
dire_h65_p5
dire_h66_p3
dire_h66_p5
dire_h68_p2
dire_h68_p3
dire_h69_p2
dire_h69_p3
dire_h69_p5
dire_h6_p3
dire_h70_p3
dire_h70_p5
dire_h71_p2
dire_h71_p5
dire_h72_p3
dire_h72_p5
dire_h73_p3
dire_h73_p5
dire_h74_p2
dire_h75_p3
dire_h77_p2
dire_h77_p5
dire_h78_p5
dire_h79_p2
dire_h79_p3
dire_h79_p5
dire_h7_p2
dire_h7_p5
dire_h80_p3
dire_h82_p2
dire_h82_p3
dire_h85_p2
dire_h85_p3
dire_h85_p5
dire_h86_p3
dire_h86_p5
dire_h87_p3
dire_h87_p5
dire_h88_p2
dire_h88_p5
dire_h89_p2
dire_h89_p3
dire_h89_p5
dire_h8_p3
dire_h90_p3
dire_h91_p3
dire_h93_p5
dire_h94_p3
dire_h95_p3
dire_h95_p5
dire_h96_p2
dire_h96_p5
dire_h97_p5
dire_h98_p2
dire_h98_p3
dire_h98_p5
dire_h99_p3
dire_h99_p5
dire_h9_p3
radiant_h100_p1
radiant_h100_p2
radiant_h100_p4
radiant_h101_p1
radiant_h101_p2
radiant_h101_p4
radiant_h102_p1
radiant_h102_p4
radiant_h103_p1
radiant_h103_p4
radiant_h104_p4
radiant_h105_p1
radiant_h105_p4
radiant_h106_p2
radiant_h108_p4
radiant_h109_p1
radiant_h10_p1
radiant_h110_p1
radiant_h110_p4
radiant_h114_p1
radiant_h114_p2
radiant_h114_p4
radiant_h119_p1
radiant_h119_p4
radiant_h11_p1
radiant_h11_p2
radiant_h120_p2
radiant_h120_p4
radiant_h121_p1
radiant_h121_p4
radiant_h123_p1
radiant_h123_p2
radiant_h123_p4
radiant_h126_p1
radiant_h126_p2
radiant_h126_p4
radiant_h128_p1
radiant_h128_p2
radiant_h128_p4
radiant_h129_p1
radiant_h129_p2
radiant_h129_p4
radiant_h131_p1
radiant_h131_p2
radiant_h131_p4
radiant_h135_p4
radiant_h136_p1
radiant_h136_p4
radiant_h137_p2
radiant_h137_p4
radiant_h138_p1
radiant_h138_p2
radiant_h138_p4
radiant_h13_p2
radiant_h13_p4
radiant_h145_p1
radiant_h145_p4
radiant_h14_p1
radiant_h14_p2
radiant_h14_p4
radiant_h15_p4
radiant_h16_p2
radiant_h16_p4
radiant_h17_p1
radiant_h17_p2
radiant_h18_p1
radiant_h19_p2
radiant_h19_p4
radiant_h1_p1
radiant_h21_p1
radiant_h21_p2
radiant_h21_p4
radiant_h22_p2
radiant_h23_p4
radiant_h25_p2
radiant_h26_p4
radiant_h27_p1
radiant_h28_p2
radiant_h28_p4
radiant_h29_p4
radiant_h2_p1
radiant_h2_p4
radiant_h32_p1
radiant_h32_p4
radiant_h33_p2
radiant_h33_p4
radiant_h35_p2
radiant_h36_p1
radiant_h36_p2
radiant_h37_p1
radiant_h37_p2
radiant_h38_p2
radiant_h38_p4
radiant_h39_p1
radiant_h39_p2
radiant_h39_p4
radiant_h3_p1
radiant_h3_p2
radiant_h3_p4
radiant_h40_p1
radiant_h40_p2
radiant_h41_p1
radiant_h45_p1
radiant_h45_p4
radiant_h46_p1
radiant_h47_p2
radiant_h47_p4
radiant_h48_p1
radiant_h49_p1
radiant_h49_p2
radiant_h49_p4
radiant_h50_p1
radiant_h50_p4
radiant_h51_p1
radiant_h51_p2
radiant_h51_p4
radiant_h52_p2
radiant_h52_p4
radiant_h53_p1
radiant_h53_p2
radiant_h54_p1
radiant_h55_p4
radiant_h57_p1
radiant_h58_p1
radiant_h58_p2
radiant_h58_p4
radiant_h59_p1
radiant_h59_p2
radiant_h5_p1
radiant_h60_p1
radiant_h60_p4
radiant_h61_p1
radiant_h61_p2
radiant_h61_p4
radiant_h62_p1
radiant_h62_p2
radiant_h62_p4
radiant_h63_p1
radiant_h64_p1
radiant_h64_p2
radiant_h64_p4
radiant_h65_p1
radiant_h65_p2
radiant_h65_p4
radiant_h66_p1
radiant_h66_p2
radiant_h66_p4
radiant_h66_p5
radiant_h67_p1
radiant_h68_p1
radiant_h68_p4
radiant_h69_p1
radiant_h69_p4
radiant_h6_p1
radiant_h70_p1
radiant_h70_p4
radiant_h71_p1
radiant_h71_p2
radiant_h71_p4
radiant_h72_p1
radiant_h72_p4
radiant_h73_p1
radiant_h73_p2
radiant_h73_p4
radiant_h74_p2
radiant_h75_p1
radiant_h75_p4
radiant_h77_p4
radiant_h78_p4
radiant_h79_p1
radiant_h79_p2
radiant_h79_p4
radiant_h7_p2
radiant_h7_p4
radiant_h80_p1
radiant_h80_p2
radiant_h82_p1
radiant_h83_p1
radiant_h85_p1
radiant_h85_p4
radiant_h86_p4
radiant_h87_p1
radiant_h87_p2
radiant_h87_p4
radiant_h88_p1
radiant_h88_p2
radiant_h88_p4
radiant_h89_p1
radiant_h89_p4
radiant_h8_p1
radiant_h90_p1
radiant_h90_p2
radiant_h90_p4
radiant_h94_p1
radiant_h95_p1
radiant_h96_p2
radiant_h96_p4
radiant_h97_p4
radiant_h98_p1
radiant_h98_p2
radiant_h98_p4
radiant_h99_p1
radiant_h99_p4
radiant_h9_p1
radiant_h9_p2
1 feature
2 dire_h100_p2
3 dire_h100_p3
4 dire_h100_p5
5 dire_h101_p2
6 dire_h101_p3
7 dire_h101_p5
8 dire_h102_p3
9 dire_h102_p5
10 dire_h103_p2
11 dire_h103_p3
12 dire_h103_p5
13 dire_h104_p5
14 dire_h105_p3
15 dire_h105_p5
16 dire_h106_p2
17 dire_h108_p5
18 dire_h109_p3
19 dire_h10_p2
20 dire_h10_p3
21 dire_h110_p2
22 dire_h110_p3
23 dire_h112_p3
24 dire_h114_p2
25 dire_h114_p3
26 dire_h114_p5
27 dire_h119_p3
28 dire_h119_p5
29 dire_h11_p2
30 dire_h11_p3
31 dire_h120_p2
32 dire_h120_p5
33 dire_h121_p2
34 dire_h121_p5
35 dire_h123_p2
36 dire_h123_p3
37 dire_h123_p5
38 dire_h126_p2
39 dire_h126_p3
40 dire_h126_p5
41 dire_h128_p2
42 dire_h128_p3
43 dire_h128_p5
44 dire_h129_p2
45 dire_h129_p5
46 dire_h131_p2
47 dire_h131_p3
48 dire_h131_p5
49 dire_h135_p5
50 dire_h136_p3
51 dire_h136_p5
52 dire_h137_p2
53 dire_h137_p5
54 dire_h138_p2
55 dire_h138_p3
56 dire_h138_p5
57 dire_h13_p2
58 dire_h13_p5
59 dire_h145_p2
60 dire_h145_p3
61 dire_h14_p2
62 dire_h14_p3
63 dire_h14_p5
64 dire_h16_p2
65 dire_h16_p5
66 dire_h17_p2
67 dire_h17_p3
68 dire_h18_p3
69 dire_h18_p5
70 dire_h19_p2
71 dire_h19_p3
72 dire_h19_p5
73 dire_h1_p3
74 dire_h21_p2
75 dire_h21_p3
76 dire_h21_p5
77 dire_h23_p2
78 dire_h23_p5
79 dire_h26_p3
80 dire_h26_p5
81 dire_h27_p3
82 dire_h27_p5
83 dire_h28_p2
84 dire_h28_p5
85 dire_h29_p3
86 dire_h29_p5
87 dire_h2_p5
88 dire_h32_p2
89 dire_h33_p2
90 dire_h33_p5
91 dire_h34_p2
92 dire_h35_p2
93 dire_h36_p2
94 dire_h37_p2
95 dire_h37_p3
96 dire_h37_p5
97 dire_h38_p2
98 dire_h38_p3
99 dire_h38_p5
100 dire_h39_p2
101 dire_h39_p3
102 dire_h3_p2
103 dire_h3_p3
104 dire_h3_p5
105 dire_h40_p2
106 dire_h40_p5
107 dire_h41_p3
108 dire_h41_p5
109 dire_h43_p2
110 dire_h45_p2
111 dire_h45_p3
112 dire_h45_p5
113 dire_h46_p2
114 dire_h46_p3
115 dire_h47_p2
116 dire_h47_p5
117 dire_h48_p3
118 dire_h49_p2
119 dire_h49_p3
120 dire_h49_p5
121 dire_h50_p2
122 dire_h50_p3
123 dire_h51_p2
124 dire_h51_p3
125 dire_h51_p5
126 dire_h52_p2
127 dire_h53_p2
128 dire_h53_p3
129 dire_h53_p5
130 dire_h54_p3
131 dire_h55_p5
132 dire_h56_p3
133 dire_h58_p2
134 dire_h58_p3
135 dire_h58_p5
136 dire_h59_p2
137 dire_h59_p3
138 dire_h60_p5
139 dire_h61_p3
140 dire_h62_p2
141 dire_h62_p3
142 dire_h62_p5
143 dire_h63_p3
144 dire_h64_p2
145 dire_h64_p3
146 dire_h64_p5
147 dire_h65_p2
148 dire_h65_p3
149 dire_h65_p5
150 dire_h66_p3
151 dire_h66_p5
152 dire_h68_p2
153 dire_h68_p3
154 dire_h69_p2
155 dire_h69_p3
156 dire_h69_p5
157 dire_h6_p3
158 dire_h70_p3
159 dire_h70_p5
160 dire_h71_p2
161 dire_h71_p5
162 dire_h72_p3
163 dire_h72_p5
164 dire_h73_p3
165 dire_h73_p5
166 dire_h74_p2
167 dire_h75_p3
168 dire_h77_p2
169 dire_h77_p5
170 dire_h78_p5
171 dire_h79_p2
172 dire_h79_p3
173 dire_h79_p5
174 dire_h7_p2
175 dire_h7_p5
176 dire_h80_p3
177 dire_h82_p2
178 dire_h82_p3
179 dire_h85_p2
180 dire_h85_p3
181 dire_h85_p5
182 dire_h86_p3
183 dire_h86_p5
184 dire_h87_p3
185 dire_h87_p5
186 dire_h88_p2
187 dire_h88_p5
188 dire_h89_p2
189 dire_h89_p3
190 dire_h89_p5
191 dire_h8_p3
192 dire_h90_p3
193 dire_h91_p3
194 dire_h93_p5
195 dire_h94_p3
196 dire_h95_p3
197 dire_h95_p5
198 dire_h96_p2
199 dire_h96_p5
200 dire_h97_p5
201 dire_h98_p2
202 dire_h98_p3
203 dire_h98_p5
204 dire_h99_p3
205 dire_h99_p5
206 dire_h9_p3
207 radiant_h100_p1
208 radiant_h100_p2
209 radiant_h100_p4
210 radiant_h101_p1
211 radiant_h101_p2
212 radiant_h101_p4
213 radiant_h102_p1
214 radiant_h102_p4
215 radiant_h103_p1
216 radiant_h103_p4
217 radiant_h104_p4
218 radiant_h105_p1
219 radiant_h105_p4
220 radiant_h106_p2
221 radiant_h108_p4
222 radiant_h109_p1
223 radiant_h10_p1
224 radiant_h110_p1
225 radiant_h110_p4
226 radiant_h114_p1
227 radiant_h114_p2
228 radiant_h114_p4
229 radiant_h119_p1
230 radiant_h119_p4
231 radiant_h11_p1
232 radiant_h11_p2
233 radiant_h120_p2
234 radiant_h120_p4
235 radiant_h121_p1
236 radiant_h121_p4
237 radiant_h123_p1
238 radiant_h123_p2
239 radiant_h123_p4
240 radiant_h126_p1
241 radiant_h126_p2
242 radiant_h126_p4
243 radiant_h128_p1
244 radiant_h128_p2
245 radiant_h128_p4
246 radiant_h129_p1
247 radiant_h129_p2
248 radiant_h129_p4
249 radiant_h131_p1
250 radiant_h131_p2
251 radiant_h131_p4
252 radiant_h135_p4
253 radiant_h136_p1
254 radiant_h136_p4
255 radiant_h137_p2
256 radiant_h137_p4
257 radiant_h138_p1
258 radiant_h138_p2
259 radiant_h138_p4
260 radiant_h13_p2
261 radiant_h13_p4
262 radiant_h145_p1
263 radiant_h145_p4
264 radiant_h14_p1
265 radiant_h14_p2
266 radiant_h14_p4
267 radiant_h15_p4
268 radiant_h16_p2
269 radiant_h16_p4
270 radiant_h17_p1
271 radiant_h17_p2
272 radiant_h18_p1
273 radiant_h19_p2
274 radiant_h19_p4
275 radiant_h1_p1
276 radiant_h21_p1
277 radiant_h21_p2
278 radiant_h21_p4
279 radiant_h22_p2
280 radiant_h23_p4
281 radiant_h25_p2
282 radiant_h26_p4
283 radiant_h27_p1
284 radiant_h28_p2
285 radiant_h28_p4
286 radiant_h29_p4
287 radiant_h2_p1
288 radiant_h2_p4
289 radiant_h32_p1
290 radiant_h32_p4
291 radiant_h33_p2
292 radiant_h33_p4
293 radiant_h35_p2
294 radiant_h36_p1
295 radiant_h36_p2
296 radiant_h37_p1
297 radiant_h37_p2
298 radiant_h38_p2
299 radiant_h38_p4
300 radiant_h39_p1
301 radiant_h39_p2
302 radiant_h39_p4
303 radiant_h3_p1
304 radiant_h3_p2
305 radiant_h3_p4
306 radiant_h40_p1
307 radiant_h40_p2
308 radiant_h41_p1
309 radiant_h45_p1
310 radiant_h45_p4
311 radiant_h46_p1
312 radiant_h47_p2
313 radiant_h47_p4
314 radiant_h48_p1
315 radiant_h49_p1
316 radiant_h49_p2
317 radiant_h49_p4
318 radiant_h50_p1
319 radiant_h50_p4
320 radiant_h51_p1
321 radiant_h51_p2
322 radiant_h51_p4
323 radiant_h52_p2
324 radiant_h52_p4
325 radiant_h53_p1
326 radiant_h53_p2
327 radiant_h54_p1
328 radiant_h55_p4
329 radiant_h57_p1
330 radiant_h58_p1
331 radiant_h58_p2
332 radiant_h58_p4
333 radiant_h59_p1
334 radiant_h59_p2
335 radiant_h5_p1
336 radiant_h60_p1
337 radiant_h60_p4
338 radiant_h61_p1
339 radiant_h61_p2
340 radiant_h61_p4
341 radiant_h62_p1
342 radiant_h62_p2
343 radiant_h62_p4
344 radiant_h63_p1
345 radiant_h64_p1
346 radiant_h64_p2
347 radiant_h64_p4
348 radiant_h65_p1
349 radiant_h65_p2
350 radiant_h65_p4
351 radiant_h66_p1
352 radiant_h66_p2
353 radiant_h66_p4
354 radiant_h66_p5
355 radiant_h67_p1
356 radiant_h68_p1
357 radiant_h68_p4
358 radiant_h69_p1
359 radiant_h69_p4
360 radiant_h6_p1
361 radiant_h70_p1
362 radiant_h70_p4
363 radiant_h71_p1
364 radiant_h71_p2
365 radiant_h71_p4
366 radiant_h72_p1
367 radiant_h72_p4
368 radiant_h73_p1
369 radiant_h73_p2
370 radiant_h73_p4
371 radiant_h74_p2
372 radiant_h75_p1
373 radiant_h75_p4
374 radiant_h77_p4
375 radiant_h78_p4
376 radiant_h79_p1
377 radiant_h79_p2
378 radiant_h79_p4
379 radiant_h7_p2
380 radiant_h7_p4
381 radiant_h80_p1
382 radiant_h80_p2
383 radiant_h82_p1
384 radiant_h83_p1
385 radiant_h85_p1
386 radiant_h85_p4
387 radiant_h86_p4
388 radiant_h87_p1
389 radiant_h87_p2
390 radiant_h87_p4
391 radiant_h88_p1
392 radiant_h88_p2
393 radiant_h88_p4
394 radiant_h89_p1
395 radiant_h89_p4
396 radiant_h8_p1
397 radiant_h90_p1
398 radiant_h90_p2
399 radiant_h90_p4
400 radiant_h94_p1
401 radiant_h95_p1
402 radiant_h96_p2
403 radiant_h96_p4
404 radiant_h97_p4
405 radiant_h98_p1
406 radiant_h98_p2
407 radiant_h98_p4
408 radiant_h99_p1
409 radiant_h99_p4
410 radiant_h9_p1
411 radiant_h9_p2

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

18
docker-compose.yml Normal file
View File

@@ -0,0 +1,18 @@
version: '3.8'
services:
postgres:
image: postgres:15-alpine
container_name: korobka_postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: korobka_db
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
postgres_data:

View File

@@ -0,0 +1,94 @@
import pandas as pd
import psycopg2
from psycopg2.extras import RealDictCursor
def get_db_connection():
return psycopg2.connect(
host="localhost",
port=5432,
database="korobka_db",
user="postgres",
password="postgres"
)
print("Загрузка данных из БД...")
conn = get_db_connection()
# Загружаем матчи
matches_df = pd.read_sql_query("""
SELECT id as match_id, radiant_team_id, dire_team_id, radiant_win
FROM matches
WHERE "source" = 'pro'
ORDER BY id
""", conn)
print(f"Загружено {len(matches_df)} матчей")
# Загружаем детали героев
details_df = pd.read_sql_query("""
SELECT match_id, hero_id, team, players_id, pos
FROM details_match
WHERE "source" = 'pro'
ORDER BY match_id
""", conn)
print(f"Загружено {len(details_df)} записей героев")
conn.close()
# Формируем слоты героев для каждого матча
def slots_from_picks(group):
# --- Radiant (team = 0) ---
r_heroes = group[group['team'] == 0]['hero_id'].tolist()[:5]
r_players = group[group['team'] == 0]['players_id'].tolist()[:5]
r_pos = group[group['team'] == 0]['pos'].tolist()[:5]
# --- Dire (team = 1) ---
d_heroes = group[group['team'] == 1]['hero_id'].tolist()[:5]
d_players = group[group['team'] == 1]['players_id'].tolist()[:5]
d_pos = group[group['team'] == 1]['pos'].tolist()[:5]
row = {}
# --- Добавляем 5 слотов для каждой стороны ---
for i in range(5):
# Герои Radiant / Dire
row[f"r_h{i+1}"] = r_heroes[i] if i < len(r_heroes) else -1
row[f"d_h{i+1}"] = d_heroes[i] if i < len(d_heroes) else -1
# Позиции героев
row[f"rp_h{i+1}"] = r_pos[i] if i < len(r_pos) else -1
row[f"dp_h{i+1}"] = d_pos[i] if i < len(d_pos) else -1
# Игроки Radiant / Dire
row[f"r_p{i+1}"] = r_players[i] if i < len(r_players) else -1
row[f"d_p{i+1}"] = d_players[i] if i < len(d_players) else -1
# Определяем, кто пикал первым (команда 0 = radiant)
fp_team = group.iloc[0]['team'] if len(group) > 0 else 0
row["is_first_pick_radiant"] = 1 if fp_team == 0 else 0
return pd.Series(row)
slots_df = details_df.groupby("match_id").apply(slots_from_picks).reset_index()
# Объединяем с информацией о матчах
dataset = matches_df.merge(slots_df, on="match_id", how="inner")
# Добавляем целевую переменную
dataset['y'] = dataset['radiant_win'].astype(int)
# Выбираем нужные колонки в правильном порядке
final_df = dataset[['match_id', 'is_first_pick_radiant',
'r_h1', 'r_h2', 'r_h3', 'r_h4', 'r_h5',
'd_h1', 'd_h2', 'd_h3', 'd_h4', 'd_h5',
'r_p1', 'r_p2', 'r_p3', 'r_p4', 'r_p5',
'd_p1', 'd_p2', 'd_p3', 'd_p4', 'd_p5',
'rp_h1', 'rp_h2', 'rp_h3', 'rp_h4', 'rp_h5',
'dp_h1', 'dp_h2', 'dp_h3', 'dp_h4', 'dp_h5',
'y']]
# Сохраняем
final_df.to_parquet("data/dataset_from_db.parquet", index=False)
print(f"Сохранено {len(final_df)} записей в data/dataset_from_db.parquet")
print(f"Radiant wins: {final_df['y'].sum()}, Dire wins: {len(final_df) - final_df['y'].sum()}")

View File

@@ -0,0 +1,139 @@
import psycopg2
import pandas as pd
import numpy as np
print("Подключение к базе данных...")
conn = psycopg2.connect(
host="localhost",
port=5432,
database="korobka_db",
user="postgres",
password="postgres"
)
print("Загрузка матчей с известными игроками...")
# Получаем все матчи где есть хотя бы один известный игрок
query = """
SELECT
m.id as match_id,
m.radiant_win,
m.leagueid
FROM matches m
WHERE EXISTS (
SELECT 1
FROM details_match dm
WHERE dm.match_id = m.id
AND dm.players_id IS NOT NULL
AND dm.players_id != 0
)
ORDER BY m.id
"""
matches_df = pd.read_sql(query, conn)
print(f"Найдено матчей: {len(matches_df)}")
# Получаем детали всех этих матчей
query_details = """
SELECT
dm.match_id,
dm.hero_id,
dm.team,
dm.players_id,
dm.pos,
dm."order"
FROM details_match dm
WHERE dm.match_id IN (
SELECT DISTINCT m.id
FROM matches m
WHERE EXISTS (
SELECT 1
FROM details_match dm2
WHERE dm2.match_id = m.id
AND dm2.players_id IS NOT NULL
AND dm2.players_id != 0
)
)
ORDER BY dm.match_id, dm."order"
"""
details_df = pd.read_sql(query_details, conn)
conn.close()
print(f"Загружено {len(details_df)} записей деталей")
# Преобразуем в wide-format
print("\nПреобразование в wide-format...")
rows = []
for match_id, group in details_df.groupby('match_id'):
match_info = matches_df[matches_df['match_id'] == match_id].iloc[0]
row = {
'match_id': match_id,
'y': int(match_info['radiant_win']),
'leagueid': int(match_info['leagueid'])
}
# Radiant (team=0) и Dire (team=1)
radiant_picks = group[group['team'] == 0].sort_values('order')
dire_picks = group[group['team'] == 1].sort_values('order')
# Заполняем героев и игроков для Radiant (до 5)
for i, (idx, pick) in enumerate(radiant_picks.iterrows(), 1):
if i > 5:
break
row[f'r_h{i}'] = int(pick['hero_id'])
row[f'r_p{i}'] = int(pick['players_id']) if pd.notna(pick['players_id']) and pick['players_id'] != 0 else -1
row[f'rp_h{i}'] = int(pick['pos']) if pd.notna(pick['pos']) else -1
# Заполняем пропуски для Radiant
for i in range(len(radiant_picks) + 1, 6):
row[f'r_h{i}'] = -1
row[f'r_p{i}'] = -1
row[f'rp_h{i}'] = -1
# Заполняем героев и игроков для Dire (до 5)
for i, (idx, pick) in enumerate(dire_picks.iterrows(), 1):
if i > 5:
break
row[f'd_h{i}'] = int(pick['hero_id'])
row[f'd_p{i}'] = int(pick['players_id']) if pd.notna(pick['players_id']) and pick['players_id'] != 0 else -1
row[f'dp_h{i}'] = int(pick['pos']) if pd.notna(pick['pos']) else -1
# Заполняем пропуски для Dire
for i in range(len(dire_picks) + 1, 6):
row[f'd_h{i}'] = -1
row[f'd_p{i}'] = -1
row[f'dp_h{i}'] = -1
rows.append(row)
df = pd.DataFrame(rows)
print(f"Создано {len(df)} записей в wide-format")
print(f"Radiant wins: {df['y'].sum()} ({df['y'].mean()*100:.1f}%)")
print(f"Dire wins: {len(df) - df['y'].sum()} ({(1-df['y'].mean())*100:.1f}%)")
# Статистика по игрокам
player_cols = [f'r_p{i}' for i in range(1, 6)] + [f'd_p{i}' for i in range(1, 6)]
all_players = []
for col in player_cols:
all_players.extend(df[col][df[col] > 0].tolist())
unique_players = len(set(all_players))
print(f"\nУникальных игроков в датасете: {unique_players}")
print(f"Всего записей игроков (не -1): {len(all_players)}")
# Статистика по турнирам
print(f"\nУникальных турниров (leagueid): {df['leagueid'].nunique()}")
# Сохранение
output_path = "data/dataset_with_players.parquet"
df.to_parquet(output_path, index=False)
print(f"\n✓ Датасет сохранён: {output_path}")
# Пример первых записей
print("\nПример данных (первые 3 матча):")
print(df.head(3).to_string())

View File

@@ -0,0 +1,127 @@
import os
import pandas as pd
import numpy as np
from catboost import CatBoostClassifier, Pool
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
print("Загрузка датасета...")
df = pd.read_parquet("data/dataset_from_db.parquet")
print(f"Всего записей: {len(df)}")
print(f"Radiant wins: {df['y'].sum()} ({df['y'].mean()*100:.1f}%)")
print(f"Dire wins: {len(df) - df['y'].sum()} ({(1-df['y'].mean())*100:.1f}%)")
# --- Bag-of-Heroes подход ---
# Создаем бинарные признаки для каждого героя в каждой команде
# Получаем все уникальные ID героев из данных
hero_cols_r = [f"r_h{i}" for i in range(1, 6)]
hero_cols_d = [f"d_h{i}" for i in range(1, 6)]
all_hero_ids = set()
for col in hero_cols_r + hero_cols_d:
all_hero_ids.update(df[col].dropna().unique())
all_hero_ids = sorted([int(h) for h in all_hero_ids if h >= 0])
print(f"\nВсего уникальных героев: {len(all_hero_ids)}")
# Создаем новый датафрейм с bag-of-heroes признаками
X = pd.DataFrame()
# Добавляем is_first_pick_radiant
X["is_first_pick_radiant"] = df["is_first_pick_radiant"].astype(int)
# Для каждого героя создаем 2 признака: radiant_hero_{id} и dire_hero_{id}
for hero_id in all_hero_ids:
# Radiant team
X[f"radiant_hero_{hero_id}"] = 0
for col in hero_cols_r:
X.loc[df[col] == hero_id, f"radiant_hero_{hero_id}"] = 1
# Dire team
X[f"dire_hero_{hero_id}"] = 0
for col in hero_cols_d:
X.loc[df[col] == hero_id, f"dire_hero_{hero_id}"] = 1
print(f"Количество признаков: {len(X.columns)}")
print(f" - is_first_pick_radiant: 1")
print(f" - radiant_hero_*: {len(all_hero_ids)}")
print(f" - dire_hero_*: {len(all_hero_ids)}")
# Целевая переменная
y = df["y"].astype(int).copy()
# Разбиение
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y
)
print(f"\nTrain: {len(X_train)} записей")
print(f"Test: {len(X_test)} записей")
# В bag-of-heroes все признаки числовые (0 или 1), категориальных нет
train_pool = Pool(X_train, y_train)
test_pool = Pool(X_test, y_test)
# Модель
model = CatBoostClassifier(
iterations=2500,
learning_rate=0.03,
depth=7,
l2_leaf_reg=2,
bootstrap_type="Bayesian",
bagging_temperature=1.0,
loss_function="Logloss",
eval_metric="AUC",
random_seed=42,
verbose=100,
od_type="Iter",
od_wait=200
)
print("\nНачало обучения...")
model.fit(train_pool, eval_set=test_pool, use_best_model=True)
# --- Оценка качества ---
best_scores = model.get_best_score()
train_auc_cb = best_scores.get("learn", {}).get("AUC", np.nan)
test_auc_cb = best_scores.get("validation", {}).get("AUC", np.nan)
y_train_proba = model.predict_proba(train_pool)[:, 1]
y_test_proba = model.predict_proba(test_pool)[:, 1]
train_auc = roc_auc_score(y_train, y_train_proba)
test_auc = roc_auc_score(y_test, y_test_proba)
print(f"\nCatBoost best AUC (learn/valid): {train_auc_cb:.4f} / {test_auc_cb:.4f}")
print(f"Recomputed AUC (train/test): {train_auc:.4f} / {test_auc:.4f}")
# --- Сохранение ---
os.makedirs("artifacts", exist_ok=True)
model_path = "artifacts/model_bag_of_heroes.cbm"
model.save_model(model_path)
print(f"\nМодель сохранена: {model_path}")
# Порядок фичей
feature_cols = list(X.columns)
pd.DataFrame(feature_cols, columns=["feature"]).to_csv(
"artifacts/feature_order_bag_of_heroes.csv", index=False
)
print("Порядок фичей сохранен в artifacts/feature_order_bag_of_heroes.csv")
# Важность признаков (топ-30)
importance = model.get_feature_importance(train_pool)
importance_df = (
pd.DataFrame({"feature": X_train.columns, "importance": importance})
.sort_values("importance", ascending=False)
.reset_index(drop=True)
)
print("\nВажность признаков (top 30):")
print(importance_df.head(30).to_string(index=False))
importance_df.to_csv("artifacts/feature_importance_bag_of_heroes.csv", index=False)

View File

@@ -0,0 +1,131 @@
import os
import pandas as pd
import numpy as np
from catboost import CatBoostClassifier, Pool
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
print("Загрузка датасета...")
df = pd.read_parquet("data/dataset_from_db.parquet")
print(f"Всего записей (матчей): {len(df)}")
print(f"Radiant wins: {df['y'].sum()} ({df['y'].mean()*100:.1f}%)")
print(f"Dire wins: {len(df) - df['y'].sum()} ({(1-df['y'].mean())*100:.1f}%)")
# --- Создаём признаки на уровне матча ---
print("\nСоздание признаков...")
hero_cols_r = [f"r_h{i}" for i in range(1, 6)]
hero_cols_d = [f"d_h{i}" for i in range(1, 6)]
pos_cols_r = [f"rp_h{i}" for i in range(1, 6)]
pos_cols_d = [f"dp_h{i}" for i in range(1, 6)]
# Создаём признаки: каждый герой на каждой позиции для каждой команды
# Формат: radiant_{hero_id}_pos_{position}, dire_{hero_id}_pos_{position}
rows = []
for idx, row in df.iterrows():
features = {}
# Radiant heroes с позициями
for i in range(5):
hero_id = int(row[hero_cols_r[i]])
position = int(row[pos_cols_r[i]])
if hero_id >= 0 and position >= 0:
features[f"radiant_h{hero_id}_p{position}"] = 1
# Dire heroes с позициями
for i in range(5):
hero_id = int(row[hero_cols_d[i]])
position = int(row[pos_cols_d[i]])
if hero_id >= 0 and position >= 0:
features[f"dire_h{hero_id}_p{position}"] = 1
features['y'] = int(row['y'])
rows.append(features)
df_features = pd.DataFrame(rows).fillna(0)
print(f"Создано признаков: {len(df_features.columns) - 1}")
# Целевая
y = df_features['y'].astype(int)
X = df_features.drop('y', axis=1)
# Разбиение
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y
)
print(f"\nTrain: {len(X_train)} матчей")
print(f"Test: {len(X_test)} матчей")
# Обучение
train_pool = Pool(X_train, y_train)
test_pool = Pool(X_test, y_test)
model = CatBoostClassifier(
iterations=1000,
learning_rate=0.05,
depth=5,
l2_leaf_reg=3,
min_data_in_leaf=10,
bootstrap_type="Bayesian",
bagging_temperature=0.5,
loss_function="Logloss",
eval_metric="AUC",
random_seed=42,
verbose=50,
od_type="Iter",
od_wait=100,
use_best_model=True
)
print("\nНачало обучения...")
model.fit(train_pool, eval_set=test_pool)
# Оценка
best_scores = model.get_best_score()
train_auc_cb = best_scores.get("learn", {}).get("AUC", np.nan)
test_auc_cb = best_scores.get("validation", {}).get("AUC", np.nan)
y_train_proba = model.predict_proba(train_pool)[:, 1]
y_test_proba = model.predict_proba(test_pool)[:, 1]
train_auc = roc_auc_score(y_train, y_train_proba)
test_auc = roc_auc_score(y_test, y_test_proba)
print(f"\nCatBoost best AUC (learn/valid): {train_auc_cb:.4f} / {test_auc_cb:.4f}")
print(f"Recomputed AUC (train/test): {train_auc:.4f} / {test_auc:.4f}")
# Сохранение
os.makedirs("artifacts", exist_ok=True)
model_path = "artifacts/model_from_db_pro_v3.cbm"
model.save_model(model_path)
print(f"\nМодель сохранена: {model_path}")
# Важность (топ-30)
importance = model.get_feature_importance(train_pool)
importance_df = (
pd.DataFrame({"feature": X_train.columns, "importance": importance})
.sort_values("importance", ascending=False)
.reset_index(drop=True)
)
print("\nВажность признаков (top 30):")
print(importance_df.head(30).to_string(index=False))
importance_df.to_csv("artifacts/feature_importance_db.csv", index=False)
# Сохраняем список всех возможных признаков для инференса
all_features = sorted(X.columns.tolist())
pd.DataFrame(all_features, columns=["feature"]).to_csv(
"artifacts/feature_order_db.csv", index=False
)
print(f"Порядок фичей сохранен в artifacts/feature_order_db.csv ({len(all_features)} признаков)")

View File

@@ -0,0 +1,161 @@
import os
import pandas as pd
import numpy as np
from catboost import CatBoostClassifier, Pool
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
print("Загрузка датасета...")
df = pd.read_parquet("data/dataset_from_db.parquet")
print(f"Всего записей (матчей): {len(df)}")
print(f"Radiant wins: {df['y'].sum()} ({df['y'].mean()*100:.1f}%)")
print(f"Dire wins: {len(df) - df['y'].sum()} ({(1-df['y'].mean())*100:.1f}%)")
# --- Преобразование в long-format ---
print("\nПреобразование в long-format...")
hero_cols_r = [f"r_h{i}" for i in range(1, 6)]
hero_cols_d = [f"d_h{i}" for i in range(1, 6)]
pos_cols_r = [f"rp_h{i}" for i in range(1, 6)]
pos_cols_d = [f"dp_h{i}" for i in range(1, 6)]
rows = []
for idx, row in df.iterrows():
match_id = idx
is_first_pick_radiant = int(row.get("is_first_pick_radiant", 0))
radiant_win = int(row["y"])
# Radiant team (5 героев)
for i in range(5):
hero_id = int(row[hero_cols_r[i]])
position = int(row[pos_cols_r[i]])
if hero_id >= 0: # Только валидные герои
rows.append({
"match_id": match_id,
"is_first_pick_radiant": is_first_pick_radiant,
"team": 0, # Radiant
"hero_id": hero_id,
"position": position,
"radiant_win": radiant_win
})
# Dire team (5 героев)
for i in range(5):
hero_id = int(row[hero_cols_d[i]])
position = int(row[pos_cols_d[i]])
if hero_id >= 0: # Только валидные герои
rows.append({
"match_id": match_id,
"is_first_pick_radiant": is_first_pick_radiant,
"team": 1, # Dire
"hero_id": hero_id,
"position": position,
"radiant_win": radiant_win
})
df_long = pd.DataFrame(rows)
print(f"\nLong-format датасет создан:")
print(f"Всего записей (пиков): {len(df_long)}")
print(f"Уникальных матчей: {df_long['match_id'].nunique()}")
print(f"Средних пиков на матч: {len(df_long) / df_long['match_id'].nunique():.1f}")
# Целевая переменная
y = df_long["radiant_win"].astype(int)
# Признаки
feature_cols = ["team", "hero_id", "position"]
X = df_long[feature_cols].copy()
# Убедимся в правильных типах
X["team"] = X["team"].astype(int)
X["hero_id"] = X["hero_id"].astype(int)
X["position"] = X["position"].astype(int)
# Разбиение (важно: разбиваем по match_id, чтобы пики одного матча были в одном сплите)
unique_matches = df_long["match_id"].unique()
train_matches, test_matches = train_test_split(
unique_matches,
test_size=0.1,
random_state=42
)
train_mask = df_long["match_id"].isin(train_matches)
test_mask = df_long["match_id"].isin(test_matches)
X_train = X[train_mask].reset_index(drop=True)
y_train = y[train_mask].reset_index(drop=True)
X_test = X[test_mask].reset_index(drop=True)
y_test = y[test_mask].reset_index(drop=True)
print(f"\nTrain: {len(X_train)} пиков ({len(train_matches)} матчей)")
print(f"Test: {len(X_test)} пиков ({len(test_matches)} матчей)")
# Категориальные признаки
cat_features = ["team", "hero_id", "position"]
train_pool = Pool(X_train, y_train, cat_features=cat_features)
test_pool = Pool(X_test, y_test, cat_features=cat_features)
# Модель с более агрессивной регуляризацией для малого датасета
model = CatBoostClassifier(
iterations=1000,
learning_rate=0.1, # Увеличили learning rate
depth=4, # Уменьшили глубину
l2_leaf_reg=5, # Увеличили регуляризацию
min_data_in_leaf=20, # Добавили минимум данных в листе
bootstrap_type="Bayesian",
bagging_temperature=0.5, # Уменьшили для меньшего разброса
loss_function="Logloss",
eval_metric="AUC",
random_seed=42,
verbose=50,
od_type="Iter",
od_wait=50, # Уменьшили patience
use_best_model=True
)
print("\nНачало обучения...")
model.fit(train_pool, eval_set=test_pool, use_best_model=True)
# --- Оценка качества ---
best_scores = model.get_best_score()
train_auc_cb = best_scores.get("learn", {}).get("AUC", np.nan)
test_auc_cb = best_scores.get("validation", {}).get("AUC", np.nan)
y_train_proba = model.predict_proba(train_pool)[:, 1]
y_test_proba = model.predict_proba(test_pool)[:, 1]
train_auc = roc_auc_score(y_train, y_train_proba)
test_auc = roc_auc_score(y_test, y_test_proba)
print(f"\nCatBoost best AUC (learn/valid): {train_auc_cb:.4f} / {test_auc_cb:.4f}")
print(f"Recomputed AUC (train/test): {train_auc:.4f} / {test_auc:.4f}")
# --- Сохранение ---
os.makedirs("artifacts", exist_ok=True)
model_path = "artifacts/model_from_db_pro_v3.cbm"
model.save_model(model_path)
print(f"\nМодель сохранена: {model_path}")
# Порядок фичей
pd.DataFrame(feature_cols, columns=["feature"]).to_csv(
"artifacts/feature_order_db.csv", index=False
)
print("Порядок фичей сохранен в artifacts/feature_order_db.csv")
# Важность признаков
importance = model.get_feature_importance(train_pool)
importance_df = (
pd.DataFrame({"feature": X_train.columns, "importance": importance})
.sort_values("importance", ascending=False)
.reset_index(drop=True)
)
print("\nВажность признаков:")
print(importance_df.to_string(index=False))
importance_df.to_csv("artifacts/feature_importance_db.csv", index=False)

View File

@@ -0,0 +1,116 @@
import os
import pandas as pd
import numpy as np
from catboost import CatBoostClassifier, Pool
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
print("Загрузка датасета...")
df = pd.read_parquet("data/dataset_from_db.parquet")
print(f"Всего записей: {len(df)}")
print(f"Radiant wins: {df['y'].sum()} ({df['y'].mean()*100:.1f}%)")
print(f"Dire wins: {len(df) - df['y'].sum()} ({(1-df['y'].mean())*100:.1f}%)")
# --- Фичи под новый формат датасета ---
hero_cols_r = [f"r_h{i}" for i in range(1, 6)]
hero_cols_d = [f"d_h{i}" for i in range(1, 5+1)]
# player_cols_r = [f"r_p{i}" for i in range(1, 6)]
# player_cols_d = [f"d_p{i}" for i in range(1, 6)]
pos_cols_r = [f"rp_h{i}" for i in range(1, 6)]
pos_cols_d = [f"dp_h{i}" for i in range(1, 6)]
feature_cols = (
["is_first_pick_radiant"]
+ hero_cols_r + hero_cols_d
# + player_cols_r + player_cols_d # Убрали игроков - мало данных
+ pos_cols_r + pos_cols_d
)
# Целевая
target_col = "y"
# Отделяем признаки/таргет
X = df[feature_cols].copy()
y = df[target_col].astype(int).copy()
# На всякий случай убедимся, что бинарный признак int
X["is_first_pick_radiant"] = X["is_first_pick_radiant"].astype(int)
# Разбиение
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.1,
random_state=42,
stratify=y
)
print(f"\nTrain: {len(X_train)} записей")
print(f"Test: {len(X_test)} записей")
# Категориальные признаки: герои и позиции (их ID — это категории)
cat_features = hero_cols_r + hero_cols_d + pos_cols_r + pos_cols_d
# CatBoost принимает либо индексы, либо имена колонок. Передаем имена.
train_pool = Pool(X_train, y_train, cat_features=cat_features)
test_pool = Pool(X_test, y_test, cat_features=cat_features)
# Модель
model = CatBoostClassifier(
iterations=2500,
learning_rate=0.03,
depth=7,
l2_leaf_reg=2,
bootstrap_type="Bayesian",
bagging_temperature=1.0, # <- вместо subsample
loss_function="Logloss",
eval_metric="AUC",
random_seed=42,
verbose=100,
od_type="Iter",
od_wait=200
)
print("\nНачало обучения...")
model.fit(train_pool, eval_set=test_pool, use_best_model=True)
# --- Оценка качества ---
# Лучшие метрики по мнению CatBoost
best_scores = model.get_best_score()
train_auc_cb = best_scores.get("learn", {}).get("AUC", np.nan)
test_auc_cb = best_scores.get("validation", {}).get("AUC", np.nan)
# Перепроверим AUC напрямую
y_train_proba = model.predict_proba(train_pool)[:, 1]
y_test_proba = model.predict_proba(test_pool)[:, 1]
train_auc = roc_auc_score(y_train, y_train_proba)
test_auc = roc_auc_score(y_test, y_test_proba)
print(f"\nCatBoost best AUC (learn/valid): {train_auc_cb:.4f} / {test_auc_cb:.4f}")
print(f"Recomputed AUC (train/test): {train_auc:.4f} / {test_auc:.4f}")
# --- Сохранение ---
os.makedirs("artifacts", exist_ok=True)
model_path = "artifacts/model_from_db_pro_v3.cbm"
model.save_model(model_path)
print(f"\nМодель сохранена: {model_path}")
# Порядок фичей
pd.DataFrame(feature_cols, columns=["feature"]).to_csv(
"artifacts/feature_order_db.csv", index=False
)
print("Порядок фичей сохранен в artifacts/feature_order_db.csv")
# Важность признаков
importance = model.get_feature_importance(train_pool)
importance_df = (
pd.DataFrame({"feature": X_train.columns, "importance": importance})
.sort_values("importance", ascending=False)
.reset_index(drop=True)
)
print("\nВажность признаков (top 25):")
print(importance_df.head(25).to_string(index=False))
# При желании — сохранить важности целиком
importance_df.to_csv("artifacts/feature_importance_db.csv", index=False)

View File

@@ -0,0 +1,176 @@
import os
import sys
import pandas as pd
import numpy as np
from catboost import CatBoostClassifier, Pool
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
from sklearn.linear_model import LogisticRegression
import pickle
# Добавляем корневую директорию проекта в путь
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
print("Загрузка датасета...")
df = pd.read_parquet("data/dataset_from_db.parquet")
print(f"Всего записей: {len(df)}")
print(f"Radiant wins: {df['y'].sum()} ({df['y'].mean()*100:.1f}%)")
print(f"Dire wins: {len(df) - df['y'].sum()} ({(1-df['y'].mean())*100:.1f}%)")
# Целевая переменная
y = df["y"].astype(int).copy()
# Разбиение на train/test
_, X_test_indices, _, y_test = train_test_split(
df.index, y,
test_size=0.2,
random_state=42,
stratify=y
)
print("\n" + "="*60)
print("Загрузка базовых моделей...")
print("="*60)
# === Модель 1: Heroes + Positions ===
from routes.predict import build_long_format_input, modelPro
# === Модель 2: Bag of Heroes ===
from routes.predict_bag_of_heroes import build_bag_of_heroes_features, modelBagOfHeroes
# === Модель 3: With Players ===
from routes.predict_with_players import build_player_features, modelWithPlayers
print("\n" + "="*60)
print("Генерация предсказаний базовых моделей...")
print("="*60)
# Подготовим данные для всех моделей
hero_cols_r = [f"r_h{i}" for i in range(1, 6)]
hero_cols_d = [f"d_h{i}" for i in range(1, 6)]
player_cols_r = [f"r_p{i}" for i in range(1, 6)]
player_cols_d = [f"d_p{i}" for i in range(1, 6)]
pos_cols_r = [f"rp_h{i}" for i in range(1, 6)]
pos_cols_d = [f"dp_h{i}" for i in range(1, 6)]
predictions_list = []
for idx in df.index:
row_data = df.loc[idx]
# Формируем payload для текущей записи
payload = {
"is_first_pick_radiant": int(row_data.get("is_first_pick_radiant", 0)),
}
# Герои
for col in hero_cols_r + hero_cols_d:
payload[col] = int(row_data.get(col, -1))
# Игроки
for col in player_cols_r + player_cols_d:
payload[col] = int(row_data.get(col, -1))
# Позиции
for col in pos_cols_r + pos_cols_d:
payload[col] = int(row_data.get(col, -1))
# === Предсказание модели 1: Heroes + Positions ===
X_with_pos = build_long_format_input(payload)
pred1 = float(modelPro.predict_proba(X_with_pos)[0, 1])
# === Предсказание модели 2: Bag of Heroes ===
X_bag = build_bag_of_heroes_features(payload)
pred2 = float(modelBagOfHeroes.predict_proba(X_bag)[0, 1])
# === Предсказание модели 3: With Players ===
X_players = build_player_features(payload)
pred3 = float(modelWithPlayers.predict_proba(X_players)[0, 1])
predictions_list.append({
"pred_with_positions": pred1,
"pred_bag_of_heroes": pred2,
"pred_with_players": pred3
})
if (idx + 1) % 100 == 0:
print(f"Обработано {idx + 1}/{len(df)} записей...")
# Создаём DataFrame с предсказаниями
X_meta = pd.DataFrame(predictions_list)
print(f"\nСоздано {len(X_meta)} мета-признаков")
print(f"Колонки: {list(X_meta.columns)}")
# Разбиение на train/test по тем же индексам
X_meta_train = X_meta.loc[~X_meta.index.isin(X_test_indices)]
X_meta_test = X_meta.loc[X_meta.index.isin(X_test_indices)]
y_meta_train = y.loc[~y.index.isin(X_test_indices)]
y_meta_test = y.loc[y.index.isin(X_test_indices)]
print(f"\nMeta Train: {len(X_meta_train)} записей")
print(f"Meta Test: {len(X_meta_test)} записей")
# Обучение мета-модели
print("\n" + "="*60)
print("Обучение мета-модели (Логистическая регрессия)...")
print("="*60)
# Используем логистическую регрессию вместо CatBoost для избежания переобучения
meta_model = LogisticRegression(
random_state=42,
max_iter=1000,
C=1.0 # Регуляризация
)
meta_model.fit(X_meta_train, y_meta_train)
# Оценка качества
y_train_proba = meta_model.predict_proba(X_meta_train)[:, 1]
y_test_proba = meta_model.predict_proba(X_meta_test)[:, 1]
train_auc = roc_auc_score(y_meta_train, y_train_proba)
test_auc = roc_auc_score(y_meta_test, y_test_proba)
print(f"\nLogistic Regression AUC (train/test): {train_auc:.4f} / {test_auc:.4f}")
# Сохранение мета-модели
os.makedirs("artifacts", exist_ok=True)
model_path = "artifacts/model_stacking.pkl"
with open(model_path, 'wb') as f:
pickle.dump(meta_model, f)
print(f"\nМета-модель сохранена: {model_path}")
# Важность признаков (коэффициенты логистической регрессии)
coefficients = meta_model.coef_[0]
intercept = meta_model.intercept_[0]
importance_df = pd.DataFrame({
"feature": X_meta_train.columns,
"coefficient": coefficients
}).sort_values("coefficient", ascending=False).reset_index(drop=True)
print("\nКоэффициенты логистической регрессии:")
print(f"Intercept: {intercept:.4f}")
print(importance_df.to_string(index=False))
# Сохраняем в старом формате для совместимости
importance_df_compat = pd.DataFrame({
"feature": X_meta_train.columns,
"importance": np.abs(coefficients) # Абсолютные значения коэффициентов
})
importance_df_compat.to_csv("artifacts/feature_importance_stacking.csv", index=False)
print("\n" + "="*60)
print("Сравнение моделей на тестовой выборке:")
print("="*60)
# AUC базовых моделей
auc1 = roc_auc_score(y_meta_test, X_meta_test["pred_with_positions"])
auc2 = roc_auc_score(y_meta_test, X_meta_test["pred_bag_of_heroes"])
auc3 = roc_auc_score(y_meta_test, X_meta_test["pred_with_players"])
print(f"Модель 1 (Heroes + Positions): AUC = {auc1:.4f}")
print(f"Модель 2 (Bag of Heroes): AUC = {auc2:.4f}")
print(f"Модель 3 (With Players): AUC = {auc3:.4f}")
print(f"Мета-модель (Stacking): AUC = {test_auc:.4f}")

View File

@@ -0,0 +1,156 @@
import os
import pandas as pd
import numpy as np
from catboost import CatBoostClassifier, Pool
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
print("Загрузка датасета...")
df = pd.read_parquet("data/dataset_with_players.parquet")
print(f"Всего записей (матчей): {len(df)}")
print(f"Radiant wins: {df['y'].sum()} ({df['y'].mean()*100:.1f}%)")
print(f"Dire wins: {len(df) - df['y'].sum()} ({(1-df['y'].mean())*100:.1f}%)")
# --- Создаём признаки на уровне матча ---
print("\nСоздание признаков...")
hero_cols_r = [f"r_h{i}" for i in range(1, 6)]
hero_cols_d = [f"d_h{i}" for i in range(1, 6)]
player_cols_r = [f"r_p{i}" for i in range(1, 6)]
player_cols_d = [f"d_p{i}" for i in range(1, 6)]
pos_cols_r = [f"rp_h{i}" for i in range(1, 6)]
pos_cols_d = [f"dp_h{i}" for i in range(1, 6)]
# Создаём признаки: player_hero_pos для каждой команды
# Формат: radiant_p{player_id}_h{hero_id}_pos{position}, dire_p{player_id}_h{hero_id}_pos{position}
rows = []
for idx, row in df.iterrows():
features = {}
# Radiant heroes с игроками и позициями
for i in range(5):
hero_id = int(row[hero_cols_r[i]])
player_id = int(row[player_cols_r[i]])
position = int(row[pos_cols_r[i]])
# Признак: игрок + герой + позиция
if player_id > 0 and hero_id >= 0 and position >= 0:
features[f"radiant_p{player_id}_h{hero_id}_pos{position}"] = 1
# Признак: только игрок + герой (если позиция неизвестна)
if player_id > 0 and hero_id >= 0:
features[f"radiant_p{player_id}_h{hero_id}"] = 1
# Признак: только игрок + позиция
if player_id > 0 and position >= 0:
features[f"radiant_p{player_id}_pos{position}"] = 1
# Dire heroes с игроками и позициями
for i in range(5):
hero_id = int(row[hero_cols_d[i]])
player_id = int(row[player_cols_d[i]])
position = int(row[pos_cols_d[i]])
# Признак: игрок + герой + позиция
if player_id > 0 and hero_id >= 0 and position >= 0:
features[f"dire_p{player_id}_h{hero_id}_pos{position}"] = 1
# Признак: только игрок + герой (если позиция неизвестна)
if player_id > 0 and hero_id >= 0:
features[f"dire_p{player_id}_h{hero_id}"] = 1
# Признак: только игрок + позиция
if player_id > 0 and position >= 0:
features[f"dire_p{player_id}_pos{position}"] = 1
features['y'] = int(row['y'])
rows.append(features)
if (idx + 1) % 100 == 0:
print(f"Обработано {idx + 1}/{len(df)} матчей...")
df_features = pd.DataFrame(rows).fillna(0)
print(f"\nСоздано признаков: {len(df_features.columns) - 1}")
# Целевая
y = df_features['y'].astype(int)
X = df_features.drop('y', axis=1)
# Разбиение
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=0.2,
random_state=42,
stratify=y
)
print(f"\nTrain: {len(X_train)} матчей")
print(f"Test: {len(X_test)} матчей")
# Обучение
train_pool = Pool(X_train, y_train)
test_pool = Pool(X_test, y_test)
model = CatBoostClassifier(
iterations=1000,
learning_rate=0.05,
depth=5,
l2_leaf_reg=3,
min_data_in_leaf=5,
bootstrap_type="Bayesian",
bagging_temperature=0.5,
loss_function="Logloss",
eval_metric="AUC",
random_seed=42,
verbose=50,
od_type="Iter",
od_wait=100,
use_best_model=True
)
print("\nНачало обучения...")
model.fit(train_pool, eval_set=test_pool)
# Оценка
best_scores = model.get_best_score()
train_auc_cb = best_scores.get("learn", {}).get("AUC", np.nan)
test_auc_cb = best_scores.get("validation", {}).get("AUC", np.nan)
y_train_proba = model.predict_proba(train_pool)[:, 1]
y_test_proba = model.predict_proba(test_pool)[:, 1]
train_auc = roc_auc_score(y_train, y_train_proba)
test_auc = roc_auc_score(y_test, y_test_proba)
print(f"\nCatBoost best AUC (learn/valid): {train_auc_cb:.4f} / {test_auc_cb:.4f}")
print(f"Recomputed AUC (train/test): {train_auc:.4f} / {test_auc:.4f}")
# Сохранение
os.makedirs("artifacts", exist_ok=True)
model_path = "artifacts/model_with_players.cbm"
model.save_model(model_path)
print(f"\nМодель сохранена: {model_path}")
# Важность (топ-30)
importance = model.get_feature_importance(train_pool)
importance_df = (
pd.DataFrame({"feature": X_train.columns, "importance": importance})
.sort_values("importance", ascending=False)
.reset_index(drop=True)
)
print("\nВажность признаков (top 30):")
print(importance_df.head(30).to_string(index=False))
importance_df.to_csv("artifacts/feature_importance_with_players.csv", index=False)
# Сохраняем список всех возможных признаков для инференса
all_features = sorted(X.columns.tolist())
pd.DataFrame(all_features, columns=["feature"]).to_csv(
"artifacts/feature_order_with_players.csv", index=False
)
print(f"\nПорядок фичей сохранен в artifacts/feature_order_with_players.csv ({len(all_features)} признаков)")

2
req.txt Normal file
View File

@@ -0,0 +1,2 @@
requests
psycopg2-binary

0
routes/__init__.py Normal file
View File

24
routes/heroes.py Normal file
View File

@@ -0,0 +1,24 @@
from fastapi import APIRouter
import psycopg2
from psycopg2.extras import RealDictCursor
router = APIRouter()
def get_db_connection():
return psycopg2.connect(
host="localhost",
port=5432,
database="korobka_db",
user="postgres",
password="postgres"
)
@router.get("/heroes")
def get_heroes():
conn = get_db_connection()
cursor = conn.cursor(cursor_factory=RealDictCursor)
cursor.execute("SELECT id, name FROM hero ORDER BY id")
heroes = cursor.fetchall()
cursor.close()
conn.close()
return heroes

62
routes/match.py Normal file
View File

@@ -0,0 +1,62 @@
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from typing import List
import psycopg2
from psycopg2.extras import RealDictCursor
router = APIRouter()
def get_db_connection():
return psycopg2.connect(
host="localhost",
port=5432,
database="korobka_db",
user="postgres",
password="postgres"
)
class HeroDetail(BaseModel):
hero_id: int
team: int
order: int
class MatchData(BaseModel):
id: int
start_time: int
leagueid: int
radiant_team_id: int
dire_team_id: int
radiant_win: bool
heroes: List[HeroDetail]
@router.post("/match/pro/add")
def add_pro_match(match: MatchData):
conn = get_db_connection()
cursor = conn.cursor()
try:
# Добавляем матч в pro_matches
cursor.execute("""
INSERT INTO pro_matches (id, start_time, leagueid, radiant_team_id, dire_team_id, radiant_win)
VALUES (%s, %s, %s, %s, %s, %s)
ON CONFLICT (id) DO NOTHING
""", (match.id, match.start_time, match.leagueid, match.radiant_team_id, match.dire_team_id, match.radiant_win))
# Добавляем детали героев в pro_details_match
for hero in match.heroes:
cursor.execute("""
INSERT INTO pro_details_match (match_id, hero_id, team, "order")
VALUES (%s, %s, %s, %s)
""", (match.id, hero.hero_id, hero.team, hero.order))
conn.commit()
cursor.close()
conn.close()
return {"status": "success", "message": f"Match {match.id} added successfully"}
except Exception as e:
conn.rollback()
cursor.close()
conn.close()
raise HTTPException(status_code=500, detail=str(e))

24
routes/players.py Normal file
View File

@@ -0,0 +1,24 @@
from fastapi import APIRouter
import psycopg2
from psycopg2.extras import RealDictCursor
router = APIRouter()
def get_db_connection():
return psycopg2.connect(
host="localhost",
port=5432,
database="korobka_db",
user="postgres",
password="postgres"
)
@router.get("/pro-players")
def get_pro_players():
conn = get_db_connection()
cursor = conn.cursor(cursor_factory=RealDictCursor)
cursor.execute("SELECT id, name, team_id FROM pro_players ORDER BY id")
players = cursor.fetchall()
cursor.close()
conn.close()
return players

199
routes/predict.py Normal file
View File

@@ -0,0 +1,199 @@
from fastapi import APIRouter, Request
from pydantic import BaseModel, Field
from typing import Optional, Dict, Any
from catboost import CatBoostClassifier
import pandas as pd
import numpy as np
from routes.predict_bag_of_heroes import predict_bag_of_heroes
from routes.predict_with_players import predict_with_players
router = APIRouter()
# =========================
# Загрузка модели
# =========================
modelPro = CatBoostClassifier()
modelPro.load_model("artifacts/model_from_db_pro_v3.cbm")
# =========================
# Загрузка порядка фич
# =========================
def load_feature_order(path: str) -> list[str]:
fo = pd.read_csv(path)
first_col = fo.columns[0]
return fo[first_col].tolist()
FEATURE_ORDER_PRO: list[str] = load_feature_order("artifacts/feature_order_db.csv")
# =========================
# Дефолты для недостающих фич
# =========================
DEFAULTS: Dict[str, Any] = {
"is_first_pick_radiant": 0,
# Radiant heroes
"r_h1": -1, "r_h2": -1, "r_h3": -1, "r_h4": -1, "r_h5": -1,
# Dire heroes
"d_h1": -1, "d_h2": -1, "d_h3": -1, "d_h4": -1, "d_h5": -1,
# # Radiant players
"r_p1": -1, "r_p2": -1, "r_p3": -1, "r_p4": -1, "r_p5": -1,
# # Dire players
"d_p1": -1, "d_p2": -1, "d_p3": -1, "d_p4": -1, "d_p5": -1,
# Radiant positions
"rp_h1": -1, "rp_h2": -1, "rp_h3": -1, "rp_h4": -1, "rp_h5": -1,
# Dire positions
"dp_h1": -1, "dp_h2": -1, "dp_h3": -1, "dp_h4": -1, "dp_h5": -1,
}
# =========================
# Входная схема
# =========================
class DraftPayload(BaseModel):
# флаг первого пика (0 — Dire first pick/неизвестно, 1 — Radiant first pick)
is_first_pick_radiant: Optional[int] = Field(default=DEFAULTS["is_first_pick_radiant"])
# герои (IDs)
r_h1: Optional[int] = Field(default=DEFAULTS["r_h1"])
r_h2: Optional[int] = Field(default=DEFAULTS["r_h2"])
r_h3: Optional[int] = Field(default=DEFAULTS["r_h3"])
r_h4: Optional[int] = Field(default=DEFAULTS["r_h4"])
r_h5: Optional[int] = Field(default=DEFAULTS["r_h5"])
d_h1: Optional[int] = Field(default=DEFAULTS["d_h1"])
d_h2: Optional[int] = Field(default=DEFAULTS["d_h2"])
d_h3: Optional[int] = Field(default=DEFAULTS["d_h3"])
d_h4: Optional[int] = Field(default=DEFAULTS["d_h4"])
d_h5: Optional[int] = Field(default=DEFAULTS["d_h5"])
# игроки (IDs)
r_p1: Optional[int] = Field(default=DEFAULTS["r_p1"])
r_p2: Optional[int] = Field(default=DEFAULTS["r_p2"])
r_p3: Optional[int] = Field(default=DEFAULTS["r_p3"])
r_p4: Optional[int] = Field(default=DEFAULTS["r_p4"])
r_p5: Optional[int] = Field(default=DEFAULTS["r_p5"])
d_p1: Optional[int] = Field(default=DEFAULTS["d_p1"])
d_p2: Optional[int] = Field(default=DEFAULTS["d_p2"])
d_p3: Optional[int] = Field(default=DEFAULTS["d_p3"])
d_p4: Optional[int] = Field(default=DEFAULTS["d_p4"])
d_p5: Optional[int] = Field(default=DEFAULTS["d_p5"])
# позиции героев (1-5)
rp_h1: Optional[int] = Field(default=DEFAULTS["rp_h1"])
rp_h2: Optional[int] = Field(default=DEFAULTS["rp_h2"])
rp_h3: Optional[int] = Field(default=DEFAULTS["rp_h3"])
rp_h4: Optional[int] = Field(default=DEFAULTS["rp_h4"])
rp_h5: Optional[int] = Field(default=DEFAULTS["rp_h5"])
dp_h1: Optional[int] = Field(default=DEFAULTS["dp_h1"])
dp_h2: Optional[int] = Field(default=DEFAULTS["dp_h2"])
dp_h3: Optional[int] = Field(default=DEFAULTS["dp_h3"])
dp_h4: Optional[int] = Field(default=DEFAULTS["dp_h4"])
dp_h5: Optional[int] = Field(default=DEFAULTS["dp_h5"])
# =========================
# Хелперы
# =========================
def build_long_format_input(payload: dict) -> pd.DataFrame:
"""
Конвертирует payload в hero+position combination features для модели.
Создаёт бинарные признаки вида radiant_h{hero_id}_p{position} и dire_h{hero_id}_p{position}
"""
features = {}
# Инициализируем все признаки нулями
for feat in FEATURE_ORDER_PRO:
features[feat] = 0
# Radiant heroes с позициями
for i in range(1, 6):
hero_id = int(payload.get(f"r_h{i}", -1))
position = int(payload.get(f"rp_h{i}", -1))
if hero_id >= 0 and position >= 0:
feature_name = f"radiant_h{hero_id}_p{position}"
if feature_name in features:
features[feature_name] = 1
# Dire heroes с позициями
for i in range(1, 6):
hero_id = int(payload.get(f"d_h{i}", -1))
position = int(payload.get(f"dp_h{i}", -1))
if hero_id >= 0 and position >= 0:
feature_name = f"dire_h{hero_id}_p{position}"
if feature_name in features:
features[feature_name] = 1
# Создаём DataFrame с одной строкой в правильном порядке
df = pd.DataFrame([features], columns=FEATURE_ORDER_PRO)
return df
def proba_percent(p: float) -> float:
"""Перевод вероятности в проценты (0..100) с отсечкой."""
return round(float(np.clip(p * 100.0, 0.0, 100.0)))
# =========================
# Роут
# =========================
@router.post("/draft/predict")
async def predict(request: Request):
body = await request.json()
# Конвертируем все значения героев, игроков и позиций в int
for key in body:
if key.startswith(("r_h", "d_h", "is_first_pick_radiant")):
if body[key] is not None and body[key] != "":
try:
body[key] = int(body[key])
except (ValueError, TypeError):
body[key] = -1
else:
body[key] = -1
elif key.startswith(("rp_h", "dp_h")):
if body[key] == 0:
body[key] = -1
else:
body[key] = -1
# Hero+position combination предсказание
X_pro = build_long_format_input(body)
# Получаем предсказание для матча (одна строка)
radiant_pro = float(modelPro.predict_proba(X_pro)[0, 1])
rp = proba_percent(radiant_pro)
rd = 100.0 - rp
# Предсказание bag-of-heroes модели
bag_prediction = predict_bag_of_heroes(body)
# Предсказание модели с игроками
players_prediction = predict_with_players(body)
# Предсказание стекинг модели (ленивый импорт для избежания циклической зависимости)
try:
from routes.predict_stacking import predict_stacking
stacking_prediction = predict_stacking(body)
except Exception:
stacking_prediction = {"radiant_win": 50, "dire_win": 50}
return {
"pro-with-pos": {
"radiant_win": rp,
"dire_win": rd
},
"pro": {
"radiant_win": bag_prediction["radiant_win"],
"dire_win": bag_prediction["dire_win"]
},
"with-players": {
"radiant_win": players_prediction["radiant_win"],
"dire_win": players_prediction["dire_win"]
},
"stacking": {
"radiant_win": stacking_prediction["radiant_win"],
"dire_win": stacking_prediction["dire_win"]
}
}

View File

@@ -0,0 +1,85 @@
from catboost import CatBoostClassifier
import pandas as pd
import numpy as np
from typing import Dict, Any
# Загрузка модели
modelBagOfHeroes = CatBoostClassifier()
modelBagOfHeroes.load_model("artifacts/model_bag_of_heroes.cbm")
# Загрузка порядка фич
def load_feature_order(path: str) -> list[str]:
fo = pd.read_csv(path)
first_col = fo.columns[0]
return fo[first_col].tolist()
FEATURE_ORDER_BAG: list[str] = load_feature_order("artifacts/feature_order_bag_of_heroes.csv")
def build_bag_of_heroes_features(payload: Dict[str, Any]) -> pd.DataFrame:
"""
Конвертирует payload в bag-of-heroes формат.
payload содержит:
- is_first_pick_radiant
- r_h1, r_h2, r_h3, r_h4, r_h5
- d_h1, d_h2, d_h3, d_h4, d_h5
Возвращает DataFrame с колонками:
- is_first_pick_radiant
- radiant_hero_{1-145}
- dire_hero_{1-145}
"""
# Получаем героев из payload
radiant_heroes = []
dire_heroes = []
for i in range(1, 6):
r_hero = payload.get(f"r_h{i}", -1)
d_hero = payload.get(f"d_h{i}", -1)
if r_hero and r_hero != -1:
radiant_heroes.append(int(r_hero))
if d_hero and d_hero != -1:
dire_heroes.append(int(d_hero))
# Создаем словарь признаков
features = {feat: 0 for feat in FEATURE_ORDER_BAG}
# Устанавливаем is_first_pick_radiant
features["is_first_pick_radiant"] = int(payload.get("is_first_pick_radiant", 0))
# Устанавливаем бинарные признаки для героев Radiant
for hero_id in radiant_heroes:
feat_name = f"radiant_hero_{hero_id}"
if feat_name in features:
features[feat_name] = 1
# Устанавливаем бинарные признаки для героев Dire
for hero_id in dire_heroes:
feat_name = f"dire_hero_{hero_id}"
if feat_name in features:
features[feat_name] = 1
# Создаем DataFrame с правильным порядком колонок
return pd.DataFrame([features], columns=FEATURE_ORDER_BAG)
def predict_bag_of_heroes(payload: Dict[str, Any]) -> Dict[str, float]:
"""
Делает предсказание с использованием bag-of-heroes модели.
Возвращает:
{
"radiant_win": вероятность победы Radiant (0-100),
"dire_win": вероятность победы Dire (0-100)
}
"""
X = build_bag_of_heroes_features(payload)
proba = modelBagOfHeroes.predict_proba(X)[0, 1]
radiant_win = round(float(np.clip(proba * 100.0, 0.0, 100.0)))
dire_win = 100.0 - radiant_win
return {
"radiant_win": radiant_win,
"dire_win": dire_win
}

View File

@@ -0,0 +1,53 @@
import pandas as pd
import numpy as np
import pickle
from typing import Dict, Any
from routes.predict import build_long_format_input, modelPro
from routes.predict_bag_of_heroes import build_bag_of_heroes_features, modelBagOfHeroes
from routes.predict_with_players import build_player_features, modelWithPlayers
# Загрузка мета-модели (Logistic Regression)
with open("artifacts/model_stacking.pkl", 'rb') as f:
modelStacking = pickle.load(f)
def predict_stacking(payload: Dict[str, Any]) -> Dict[str, float]:
"""
Делает предсказание с использованием стекинг-модели.
Сначала получает предсказания от всех базовых моделей,
затем использует их как признаки для мета-модели.
Возвращает:
{
"radiant_win": вероятность победы Radiant (0-100),
"dire_win": вероятность победы Dire (0-100)
}
"""
# === Предсказание модели 1: Heroes + Positions ===
X_with_pos = build_long_format_input(payload)
pred1 = float(modelPro.predict_proba(X_with_pos)[0, 1])
# === Предсказание модели 2: Bag of Heroes ===
X_bag = build_bag_of_heroes_features(payload)
pred2 = float(modelBagOfHeroes.predict_proba(X_bag)[0, 1])
# === Предсказание модели 3: With Players ===
X_players = build_player_features(payload)
pred3 = float(modelWithPlayers.predict_proba(X_players)[0, 1])
# === Мета-модель ===
X_meta = pd.DataFrame([{
"pred_with_positions": pred1,
"pred_bag_of_heroes": pred2,
"pred_with_players": pred3
}])
proba = modelStacking.predict_proba(X_meta)[0, 1]
radiant_win = round(float(np.clip(proba * 100.0, 0.0, 100.0)))
dire_win = 100.0 - radiant_win
return {
"radiant_win": radiant_win,
"dire_win": dire_win
}

View File

@@ -0,0 +1,123 @@
from catboost import CatBoostClassifier
import pandas as pd
import numpy as np
from typing import Dict, Any
# Загрузка модели с игроками
modelWithPlayers = CatBoostClassifier()
modelWithPlayers.load_model("artifacts/model_with_players.cbm")
# Загрузка порядка фич
def load_feature_order(path: str) -> list:
fo = pd.read_csv(path)
first_col = fo.columns[0]
return fo[first_col].tolist()
FEATURE_ORDER_WITH_PLAYERS = load_feature_order("artifacts/feature_order_with_players.csv")
def build_player_features(payload: Dict[str, Any]) -> pd.DataFrame:
"""
Создаёт бинарные признаки для модели с игроками.
Признаки:
- radiant_p{player_id}_h{hero_id}_pos{position}
- radiant_p{player_id}_h{hero_id}
- radiant_p{player_id}_pos{position}
(аналогично для dire)
"""
features = {}
# Инициализируем все признаки нулями
for feat in FEATURE_ORDER_WITH_PLAYERS:
features[feat] = 0
# Radiant: игроки + герои + позиции
for i in range(1, 6):
hero_id = int(payload.get(f"r_h{i}", -1))
player_id = int(payload.get(f"r_p{i}", -1))
position = int(payload.get(f"rp_h{i}", -1))
# Признак: игрок + герой + позиция
if player_id > 0 and hero_id >= 0 and position >= 0:
feature_name = f"radiant_p{player_id}_h{hero_id}_pos{position}"
if feature_name in features:
features[feature_name] = 1
# Признак: только игрок + герой
if player_id > 0 and hero_id >= 0:
feature_name = f"radiant_p{player_id}_h{hero_id}"
if feature_name in features:
features[feature_name] = 1
# Признак: только игрок + позиция
if player_id > 0 and position >= 0:
feature_name = f"radiant_p{player_id}_pos{position}"
if feature_name in features:
features[feature_name] = 1
# Dire: игроки + герои + позиции
for i in range(1, 6):
hero_id = int(payload.get(f"d_h{i}", -1))
player_id = int(payload.get(f"d_p{i}", -1))
position = int(payload.get(f"dp_h{i}", -1))
# Признак: игрок + герой + позиция
if player_id > 0 and hero_id >= 0 and position >= 0:
feature_name = f"dire_p{player_id}_h{hero_id}_pos{position}"
if feature_name in features:
features[feature_name] = 1
# Признак: только игрок + герой
if player_id > 0 and hero_id >= 0:
feature_name = f"dire_p{player_id}_h{hero_id}"
if feature_name in features:
features[feature_name] = 1
# Признак: только игрок + позиция
if player_id > 0 and position >= 0:
feature_name = f"dire_p{player_id}_pos{position}"
if feature_name in features:
features[feature_name] = 1
# Создаём DataFrame с одной строкой в правильном порядке
df = pd.DataFrame([features], columns=FEATURE_ORDER_WITH_PLAYERS)
return df
def predict_with_players(payload: Dict[str, Any]) -> Dict[str, float]:
"""
Делает предсказание с использованием модели с игроками.
Возвращает:
{
"radiant_win": вероятность победы Radiant (0-100),
"dire_win": вероятность победы Dire (0-100)
}
"""
# Проверяем, есть ли хотя бы один игрок в payload
has_players = False
for i in range(1, 6):
if payload.get(f"r_p{i}", -1) > 0 or payload.get(f"d_p{i}", -1) > 0:
has_players = True
break
# Если нет игроков, возвращаем 50/50
if not has_players:
return {
"radiant_win": 50,
"dire_win": 50
}
# Создаём признаки
X = build_player_features(payload)
# Предсказание
proba = modelWithPlayers.predict_proba(X)[0, 1]
radiant_win = round(float(np.clip(proba * 100.0, 0.0, 100.0)))
dire_win = 100 - radiant_win
return {
"radiant_win": radiant_win,
"dire_win": dire_win
}

24
routes/teams.py Normal file
View File

@@ -0,0 +1,24 @@
from fastapi import APIRouter
import psycopg2
from psycopg2.extras import RealDictCursor
router = APIRouter()
def get_db_connection():
return psycopg2.connect(
host="localhost",
port=5432,
database="korobka_db",
user="postgres",
password="postgres"
)
@router.get("/teams")
def get_teams():
conn = get_db_connection()
cursor = conn.cursor(cursor_factory=RealDictCursor)
cursor.execute("SELECT id, name FROM teams ORDER BY id")
teams = cursor.fetchall()
cursor.close()
conn.close()
return teams

236
run.sh Executable file
View File

@@ -0,0 +1,236 @@
#!/usr/bin/env bash
set -euo pipefail
# ===========================
# Конфиг (меняйте по желанию)
# ===========================
PY=python3
VENV=".venv"
# Порт REST-сервиса:
PORT="${PORT:-8000}"
# Принудить IPv4 (полезно при проблемном IPv6 у провайдера):
FORCE_IPV4="${FORCE_IPV4:-1}"
# API-ключи (необязательно, но улучшает стабильность/квоты):
# export OPENDOTA_API_KEY=...
# export STRATZ_TOKEN=...
OPENDOTA_API_KEY="${OPENDOTA_API_KEY:-}"
STRATZ_TOKEN="${STRATZ_TOKEN:-}"
# Использовать STRATZ вместо OpenDota
# только для шага 1 (список матчей): USE_STRATZ_LIST=1
# только для шага 2 (детали/драфт): USE_STRATZ_DETAILS=1
USE_STRATZ_LIST="${USE_STRATZ_LIST:-0}"
USE_STRATZ_DETAILS="${USE_STRATZ_DETAILS:-0}"
# Задержка после каждых 100 запросов (смягчить 429):
SLEEP_PER_100="${SLEEP_PER_100:-1.0}"
# ===========================
# Окружение и зависимости
# ===========================
if [ ! -d "$VENV" ]; then
$PY -m venv "$VENV"
fi
# shellcheck disable=SC1091
source "$VENV/bin/activate"
pip install -U pip
pip install pandas pyarrow requests httpx "urllib3>=2.2" certifi catboost scikit-learn fastapi uvicorn
mkdir -p data artifacts
# ===========================
# Хелперы
# ===========================
export FORCE_IPV4
export OPENDOTA_API_KEY
export STRATZ_TOKEN
export PAGES
export SLEEP_PER_100
# ===========================
# [1/7] Список pub-матчей
# ===========================
# [1b] Паблики (high-rank)
$PY educationML/fetch_public_matches.py
# [2b] Детали пабликов (герои из players)
$PY educationML/fetch_public_details.py
# ===========================
# [2/7] Список pro-матчей
# ===========================
echo "[1/6] Fetch pro matches via OpenDota (pages=$PAGES)"
$PY educationML/fetch_pro_matches_opendota.py
# =========================================
# [2/6] Детали матча + драфт (устойчивый)
# =========================================
if [ "$USE_STRATZ_DETAILS" = "1" ]; then
echo "[2/6] Fetch match details + draft via STRATZ"
$PY - <<'PYCODE'
PYCODE
else
echo "[2/6] Fetch match details + draft via OpenDota (robust)"
$PY - <<'PYCODE'
import os, time, socket, sys, pandas as pd, requests, httpx
from urllib3.util.retry import Retry
from requests.adapters import HTTPAdapter
# IPv4-only (если FORCE_IPV4=1)
if os.getenv("FORCE_IPV4","1") == "1":
_orig = socket.getaddrinfo
def _v4(host, port, family=0, type=0, proto=0, flags=0):
return _orig(host, port, socket.AF_INET, type, proto, flags)
socket.getaddrinfo = _v4
API_KEY = os.getenv("OPENDOTA_API_KEY")
SLEEP_PER_100 = float(os.getenv("SLEEP_PER_100","1.0"))
BASE = "https://api.opendota.com/api/matches/{mid}"
headers = {"User-Agent":"korobkaGames/1.0","Accept":"application/json","Connection":"close"}
# список match_id
pro = pd.read_parquet("data/pro_matches.parquet")
match_ids = pro['match_id'].drop_duplicates().tolist()
# requests с Retry
sess = requests.Session()
retries = Retry(total=6, connect=6, read=6, backoff_factor=0.7,
status_forcelist=[429,500,502,503,504],
allowed_methods=frozenset(["GET"]))
sess.mount("https://", HTTPAdapter(max_retries=retries))
def fetch_one(mid: int):
url = BASE.format(mid=mid)
if API_KEY:
url += f"?api_key={API_KEY}"
try:
r = sess.get(url, headers=headers, timeout=(5,40))
r.raise_for_status()
return r.json()
except requests.exceptions.SSLError:
# fallback: httpx http2 off
with httpx.Client(http2=False, timeout=40, headers=headers) as client:
resp = client.get(url)
resp.raise_for_status()
return resp.json()
match_rows, draft_rows, failed = [], [], []
for i, mid in enumerate(match_ids, 1):
try:
m = fetch_one(int(mid))
match_rows.append({
"match_id": int(mid),
"date": pd.to_datetime(m.get("start_time",0), unit="s"),
"patch": str(m.get("patch")),
"radiant_win": bool(m.get("radiant_win")),
"duration_sec": m.get("duration"),
"league_id": (m.get("league") or {}).get("id"),
"series_type": m.get("series_type"),
})
for pb in (m.get("picks_bans") or []):
draft_rows.append({
"match_id": int(mid),
"is_pick": pb.get("is_pick", False),
"team": pb.get("team"),
"hero_id": pb.get("hero_id"),
"order": pb.get("order")
})
except Exception:
failed.append(int(mid))
if i % 100 == 0:
time.sleep(SLEEP_PER_100)
pd.DataFrame(match_rows).to_parquet("data/matches.parquet", index=False)
pd.DataFrame(draft_rows).to_parquet("data/draft.parquet", index=False)
pd.Series(failed, name="failed_match_id").to_csv("data/matches_failed.csv", index=False)
print(f"Saved via OpenDota: matches={len(match_rows)} draft_rows={len(draft_rows)} failed={len(failed)}")
if not match_rows:
raise SystemExit("OpenDota details: ничего не скачано")
PYCODE
fi
# ===========================
# [3/6] Простой Elo baseline
# ===========================
echo "[3/6] Build Elo"
$PY - <<'PYCODE'
import pandas as pd
matches = pd.read_parquet("data/matches.parquet").sort_values("date")
pro = pd.read_parquet("data/pro_matches.parquet")[['match_id','radiant_name','dire_name']]
df = matches.merge(pro, on='match_id', how='left')
K = 24
elo = {}
def get_elo(t): return elo.get(t, 1500)
def expect(a,b): return 1.0/(1.0+10**((b-a)/400))
rows=[]
for _, r in df.iterrows():
A, B = r['radiant_name'], r['dire_name']
ra, rb = get_elo(A), get_elo(B)
ea, eb = expect(ra,rb), expect(rb,ra)
y = 1.0 if r['radiant_win'] else 0.0
rows.append({
'match_id': r['match_id'],
'date': r['date'],
'elo_radiant': ra, 'elo_dire': rb,
'elo_diff_90': ra - rb # упрощённо без окон
})
elo[A] = ra + K*(y-ea)
elo[B] = rb + K*((1-y)-eb)
pd.DataFrame(rows).to_parquet("data/elo.parquet", index=False)
print("Saved data/elo.parquet")
PYCODE
# [4] вместо старого build_dataset_draft.py
$PY educationML/build_dataset_mixed.py
# ===========================
# [5/6] Обучение модели
# ===========================
echo "[5/6] Train CatBoost"
$PY - <<'PYCODE'
import pandas as pd
from catboost import CatBoostClassifier
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import log_loss, brier_score_loss
df = pd.read_parquet("data/dataset_mixed.parquet").sort_values("date")
cat_cols = ['patch','source','r_h1','r_h2','r_h3','r_h4','r_h5','d_h1','d_h2','d_h3','d_h4','d_h5']
X = df.drop(columns=['y','date','match_id']) if 'match_id' in df.columns else df.drop(columns=['y','date'])
y = df['y']
cat_idx = [X.columns.get_loc(c) for c in cat_cols]
tscv = TimeSeriesSplit(n_splits=5)
ll, br = [], []
for tr, te in tscv.split(X):
model = CatBoostClassifier(
depth=8, iterations=1200, learning_rate=0.03,
loss_function='Logloss', eval_metric='Logloss', verbose=False
)
model.fit(X.iloc[tr], y.iloc[tr], cat_features=cat_idx)
p = model.predict_proba(X.iloc[te])[:,1]
ll.append(log_loss(y.iloc[te], p))
br.append(brier_score_loss(y.iloc[te], p))
print("CV LogLoss=", sum(ll)/len(ll), " Brier=", sum(br)/len(br))
final = CatBoostClassifier(depth=8, iterations=1500, learning_rate=0.03,
loss_function='Logloss', verbose=False)
final.fit(X, y, cat_features=cat_idx)
final.save_model("artifacts/model_draft.cbm")
pd.Series(X.columns).to_csv("artifacts/feature_order.csv", index=False)
print("Saved artifacts/model_draft.cbm and artifacts/feature_order.csv")
PYCODE
# ===========================
# [6/6] REST-сервис предсказаний
# ===========================
echo "[6/6] Start API → http://127.0.0.1:$PORT"
exec uvicorn serve:app --host 0.0.0.0 --port "$PORT"

31
serve.py Normal file
View File

@@ -0,0 +1,31 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from routes.predict import router as predict_router
from routes.heroes import router as heroes_router
from routes.match import router as match_router
from routes.teams import router as teams_router
from routes.players import router as players_router
app = FastAPI()
# CORS настройки
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(predict_router)
app.include_router(heroes_router)
app.include_router(match_router)
app.include_router(teams_router)
app.include_router(players_router)
@app.get("/features")
def features():
# Чтобы легко проверить, что ждёт модель
return {"feature_order": feature_order, "defaults": DEFAULTS}

View File

@@ -0,0 +1,106 @@
import requests
import psycopg2
import time
from psycopg2.extras import RealDictCursor, execute_values
# Подключение к базе данных
conn = psycopg2.connect(
host="localhost",
port=5432,
database="korobka_db",
user="postgres",
password="postgres"
)
cursor = conn.cursor(cursor_factory=RealDictCursor)
# Функция для определения позиции по lane и lane_role
def get_position(lane, lane_role):
"""
lane 2, lane_role 2 -> pos 2 (Mid Core)
lane 1, lane_role 1 -> pos 1 (Safe Lane Carry)
lane 1, lane_role 3 -> pos 5 (Safe Lane Support)
lane 3, lane_role 1 -> pos 3 (Offlane Core)
lane 3, lane_role 3 -> pos 4 (Offlane Support)
"""
if lane == 2 and lane_role == 2:
return 2
elif lane == 1 and lane_role == 1:
return 1
elif lane == 1 and lane_role == 3:
return 5
elif lane == 3 and lane_role == 1:
return 3
elif lane == 3 and lane_role == 3:
return 4
else:
return -1 # Неизвестная комбинация
# Получение ID матчей из БД
cursor.execute("""
select m.id
from matches m
left join details_match dm on dm.match_id = m.id
where m."source" = 'pro' and dm.match_id is null
""")
matches = cursor.fetchall()
match_ids = [match['id'] for match in matches]
print(f"Получено {len(match_ids)} ID матчей из БД")
# Запрос деталей для каждого матча
for idx, match_id in enumerate(match_ids, 1):
url = f"https://api.opendota.com/api/matches/{match_id}"
try:
response = requests.get(url, timeout=30)
if response.status_code == 200:
match_data = response.json()
picks_bans = match_data.get('picks_bans', [])
players = match_data.get('players', [])
print(f"[{idx}/{len(match_ids)}] Match {match_id}: {len(picks_bans)} picks/bans, {len(players)} players")
# Создаем словари из players
hero_to_account = {player.get('hero_id'): player.get('account_id') for player in players if player.get('hero_id')}
hero_to_pos = {player.get('hero_id'): get_position(player.get('lane'), player.get('lane_role')) for player in players if player.get('hero_id')}
# Сохранение picks_bans в БД (только пики) с позициями
data = [
(
match_id,
pick_ban.get('hero_id'),
pick_ban.get('team'),
pick_ban.get('order'),
hero_to_account.get(pick_ban.get('hero_id')), # players_id
hero_to_pos.get(pick_ban.get('hero_id'), -1), # pos
'pro' # source
)
for pick_ban in picks_bans
if pick_ban.get('is_pick')
]
print(f"Вставка {len(data)} записей для матча {match_id}")
execute_values(
cursor,
"""INSERT INTO details_match (match_id, hero_id, team, "order", players_id, pos, source)
VALUES %s""",
data
)
conn.commit()
time.sleep(1) # пауза между запросами
else:
print(f"[{idx}/{len(match_ids)}] Ошибка для матча {match_id}: {response.status_code}")
if response.status_code == 429:
print("Превышен лимит запросов, ждем 10 секунд...")
time.sleep(10)
except Exception as e:
print(f"[{idx}/{len(match_ids)}] Исключение для матча {match_id}: {e}")
cursor.close()
conn.close()
print("Завершено сохранение деталей матчей")

View File

@@ -0,0 +1,79 @@
import requests
import psycopg2
from psycopg2.extras import execute_values
PAGE = 10
url = "https://api.opendota.com/api/proMatches"
all_matches = []
less_than_match_id = None
# Разрешенные лиги
ALLOWED_LEAGUES = [17420] # Замените на нужные ID лиг
for page in range(PAGE):
params = {}
if less_than_match_id:
params['less_than_match_id'] = less_than_match_id
response = requests.get(url, params=params)
if response.status_code == 200:
matches = response.json()
all_matches.extend(matches)
print(f"Страница {page + 1}: получено {len(matches)} матчей")
if matches:
less_than_match_id = matches[-1]['match_id']
else:
print(f"Ошибка на странице {page + 1}: {response.status_code}")
break
print(f"\nВсего получено {len(all_matches)} матчей")
# Подключение к базе данных
conn = psycopg2.connect(
host="localhost",
port=5432,
database="korobka_db",
user="postgres",
password="postgres"
)
cursor = conn.cursor()
# Подготовка данных для вставки (фильтр по leagueid)
data = [
(
match['match_id'],
match.get('start_time'),
match.get('leagueid'),
match.get('radiant_team_id'),
match.get('dire_team_id'),
match.get('radiant_win'),
'pro' # ← добавили source
)
for match in all_matches
if match.get('leagueid') in ALLOWED_LEAGUES
]
print(f"Отфильтровано {len(data)} матчей из {len(all_matches)} по разрешенным лигам")
# Вставка данных в таблицу
execute_values(
cursor,
"""INSERT INTO matches (id, start_time, leagueid, radiant_team_id, dire_team_id, radiant_win, source)
VALUES %s
ON CONFLICT (id) DO UPDATE SET
start_time = EXCLUDED.start_time,
leagueid = EXCLUDED.leagueid,
radiant_team_id = EXCLUDED.radiant_team_id,
dire_team_id = EXCLUDED.dire_team_id,
radiant_win = EXCLUDED.radiant_win,
source = EXCLUDED.source""",
data
)
conn.commit()
cursor.close()
conn.close()
print(f"Успешно сохранено {len(data)} матчей в БД")

234
test_models_accuracy.py Normal file
View File

@@ -0,0 +1,234 @@
import sys
sys.path.insert(0, '.')
import psycopg2
import pandas as pd
import numpy as np
from routes.predict import build_long_format_input, modelPro
from routes.predict_bag_of_heroes import build_bag_of_heroes_features, modelBagOfHeroes
from routes.predict_with_players import build_player_features, modelWithPlayers
from routes.predict_stacking import predict_stacking
# Подключение к БД
conn = psycopg2.connect(
host="localhost",
port=5432,
database="korobka_db",
user="postgres",
password="postgres"
)
# Получаем случайные матчи с известными игроками
query = """
SELECT
m.id as match_id,
m.radiant_win,
m.leagueid
FROM matches m
WHERE EXISTS (
SELECT 1
FROM details_match dm
WHERE dm.match_id = m.id
AND dm.players_id IS NOT NULL
AND dm.players_id != 0
)
ORDER BY RANDOM()
LIMIT 100
"""
matches_df = pd.read_sql(query, conn)
print(f"Загружено {len(matches_df)} случайных матчей")
# Получаем детали этих матчей
match_ids = matches_df['match_id'].tolist()
placeholders = ','.join(['%s'] * len(match_ids))
query_details = f"""
SELECT
dm.match_id,
dm.hero_id,
dm.team,
dm.players_id,
dm.pos,
dm."order"
FROM details_match dm
WHERE dm.match_id IN ({placeholders})
ORDER BY dm.match_id, dm."order"
"""
cursor = conn.cursor()
cursor.execute(query_details, match_ids)
details_rows = cursor.fetchall()
conn.close()
# Преобразуем детали в словарь по match_id
details_by_match = {}
for row in details_rows:
match_id = row[0]
if match_id not in details_by_match:
details_by_match[match_id] = []
details_by_match[match_id].append({
'hero_id': row[1],
'team': row[2],
'players_id': row[3],
'pos': row[4],
'order': row[5]
})
print(f"Загружено деталей для {len(details_by_match)} матчей\n")
# Счётчики правильных предсказаний
correct = {
'model1': 0,
'model2': 0,
'model3': 0,
'stacking': 0
}
# Списки для хранения всех предсказаний (для расчёта AUC)
predictions = {
'model1': [],
'model2': [],
'model3': [],
'stacking': []
}
actuals = []
print("Тестирование моделей...")
print("="*80)
# Проверяем каждый матч
for idx, match_row in matches_df.iterrows():
match_id = match_row['match_id']
radiant_win = match_row['radiant_win']
if match_id not in details_by_match:
continue
details = details_by_match[match_id]
# Формируем payload
payload = {}
# Разделяем на Radiant и Dire
radiant_picks = sorted([d for d in details if d['team'] == 0], key=lambda x: x['order'])
dire_picks = sorted([d for d in details if d['team'] == 1], key=lambda x: x['order'])
# Заполняем Radiant
for i in range(5):
if i < len(radiant_picks):
payload[f'r_h{i+1}'] = radiant_picks[i]['hero_id']
payload[f'r_p{i+1}'] = radiant_picks[i]['players_id'] if radiant_picks[i]['players_id'] else -1
payload[f'rp_h{i+1}'] = radiant_picks[i]['pos'] if radiant_picks[i]['pos'] else -1
else:
payload[f'r_h{i+1}'] = -1
payload[f'r_p{i+1}'] = -1
payload[f'rp_h{i+1}'] = -1
# Заполняем Dire
for i in range(5):
if i < len(dire_picks):
payload[f'd_h{i+1}'] = dire_picks[i]['hero_id']
payload[f'd_p{i+1}'] = dire_picks[i]['players_id'] if dire_picks[i]['players_id'] else -1
payload[f'dp_h{i+1}'] = dire_picks[i]['pos'] if dire_picks[i]['pos'] else -1
else:
payload[f'd_h{i+1}'] = -1
payload[f'd_p{i+1}'] = -1
payload[f'dp_h{i+1}'] = -1
# Предсказания
try:
# Модель 1: Heroes + Positions
X1 = build_long_format_input(payload)
pred1_proba = float(modelPro.predict_proba(X1)[0, 1])
pred1 = pred1_proba >= 0.5
# Модель 2: Bag of Heroes
X2 = build_bag_of_heroes_features(payload)
pred2_proba = float(modelBagOfHeroes.predict_proba(X2)[0, 1])
pred2 = pred2_proba >= 0.5
# Модель 3: With Players
X3 = build_player_features(payload)
pred3_proba = float(modelWithPlayers.predict_proba(X3)[0, 1])
pred3 = pred3_proba >= 0.5
# Стекинг
stack_result = predict_stacking(payload)
pred_stack_proba = stack_result['radiant_win'] / 100.0
pred_stack = pred_stack_proba >= 0.5
# Сохраняем предсказания
predictions['model1'].append(pred1_proba)
predictions['model2'].append(pred2_proba)
predictions['model3'].append(pred3_proba)
predictions['stacking'].append(pred_stack_proba)
actuals.append(int(radiant_win))
# Подсчитываем правильные предсказания
if pred1 == radiant_win:
correct['model1'] += 1
if pred2 == radiant_win:
correct['model2'] += 1
if pred3 == radiant_win:
correct['model3'] += 1
if pred_stack == radiant_win:
correct['stacking'] += 1
# Показываем первые 10 матчей
if idx < 10:
actual_str = "Radiant" if radiant_win else "Dire"
print(f"\nМатч #{idx+1} (ID: {match_id}):")
print(f" Реальный результат: {actual_str} win")
print(f" Модель 1: {pred1_proba*100:.1f}% Radiant ({'' if pred1 == radiant_win else ''})")
print(f" Модель 2: {pred2_proba*100:.1f}% Radiant ({'' if pred2 == radiant_win else ''})")
print(f" Модель 3: {pred3_proba*100:.1f}% Radiant ({'' if pred3 == radiant_win else ''})")
print(f" Стекинг: {pred_stack_proba*100:.1f}% Radiant ({'' if pred_stack == radiant_win else ''})")
except Exception as e:
print(f"Ошибка при обработке матча {match_id}: {e}")
continue
# Итоговая статистика
total = len(actuals)
print("\n" + "="*80)
print("ИТОГОВАЯ СТАТИСТИКА")
print("="*80)
print(f"Всего проверено матчей: {total}\n")
print("Точность (Accuracy):")
print(f" Модель 1 (Heroes + Positions): {correct['model1']}/{total} = {correct['model1']/total*100:.2f}%")
print(f" Модель 2 (Bag of Heroes): {correct['model2']}/{total} = {correct['model2']/total*100:.2f}%")
print(f" Модель 3 (With Players): {correct['model3']}/{total} = {correct['model3']/total*100:.2f}%")
print(f" Мета-модель (Stacking): {correct['stacking']}/{total} = {correct['stacking']/total*100:.2f}%")
# Расчёт AUC
from sklearn.metrics import roc_auc_score
print("\nAUC (Area Under ROC Curve):")
try:
auc1 = roc_auc_score(actuals, predictions['model1'])
print(f" Модель 1 (Heroes + Positions): {auc1:.4f}")
except:
print(f" Модель 1 (Heroes + Positions): N/A")
try:
auc2 = roc_auc_score(actuals, predictions['model2'])
print(f" Модель 2 (Bag of Heroes): {auc2:.4f}")
except:
print(f" Модель 2 (Bag of Heroes): N/A")
try:
auc3 = roc_auc_score(actuals, predictions['model3'])
print(f" Модель 3 (With Players): {auc3:.4f}")
except:
print(f" Модель 3 (With Players): N/A")
try:
auc_stack = roc_auc_score(actuals, predictions['stacking'])
print(f" Мета-модель (Stacking): {auc_stack:.4f}")
except:
print(f" Мета-модель (Stacking): N/A")
print("\n" + "="*80)

73
update_models.sh Executable file
View File

@@ -0,0 +1,73 @@
#!/bin/bash
# Скрипт для обновления базы данных и переобучения моделей
set -e # Остановка при ошибке
echo "======================================"
echo "Начало обновления моделей"
echo "======================================"
echo ""
# Активация виртуального окружения
source .venv/bin/activate
# 1. Парсинг про матчей
echo "======================================"
echo "1. Парсинг про матчей..."
echo "======================================"
python start/parse_pro_matches.py
echo ""
# 2. Парсинг деталей про матчей
echo "======================================"
echo "2. Парсинг деталей про матчей..."
echo "======================================"
python start/parse_pro_details_match.py
echo ""
# 3. Построение датасета
echo "======================================"
echo "3. Построение датасета из БД..."
echo "======================================"
python educationML/build_dataset_pro.py
echo ""
# 4. Обучение модели Long-Format (Heroes + Positions)
echo "======================================"
echo "4. Обучение модели Long-Format..."
echo "======================================"
python educationML/train_model_pro.py
echo ""
# 5. Обучение модели Bag of Heroes
echo "======================================"
echo "5. Обучение модели Bag of Heroes..."
echo "======================================"
python educationML/train_model_bag_of_heroes.py
echo ""
# 6. Построение датасета с игроками
echo "======================================"
echo "6. Построение датасета с игроками..."
echo "======================================"
python educationML/build_dataset_with_players.py
echo ""
# 7. Обучение модели With Players
echo "======================================"
echo "7. Обучение модели With Players..."
echo "======================================"
python educationML/train_model_with_players.py
echo ""
# 8. Обучение мета-модели (Stacking)
echo "======================================"
echo "8. Обучение мета-модели (Stacking)..."
echo "======================================"
python educationML/train_model_stacking.py
echo ""
echo "======================================"
echo "✅ Все модели успешно обновлены!"
echo "======================================"

9
лиги.txt Normal file
View File

@@ -0,0 +1,9 @@
18038 - FISSURE PLAYGROUND 2 - Closed Qualifiers - EEU
//18667 - FISSURE PLAYGROUND 2 - Closed Qualifiers - China
//18668 - FISSURE PLAYGROUND 2 - Closed Qualifiers - Americas
//18700 - FISSURE PLAYGROUND 2 - Closed Qualifiers - Americas (SA)
//18702 - RES Unchained - A Blast Dota Slam IV Qualifier EU
//18704 - RES Unchained - A Blast Dota Slam IV Qualifier SEA
//18706 - BLAST Slam IV: China Closed Qualifier
18863 - FISSURE PLAYGROUND 2
18920 - PGL Wallachia 2025 Season 6"