File size: 13,194 Bytes
e62d71b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ddbec9
e62d71b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ddbec9
e62d71b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ad9c97
e62d71b
 
 
8ad9c97
e62d71b
 
8ad9c97
e62d71b
 
 
 
 
 
8ad9c97
e62d71b
 
8ad9c97
e62d71b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
import pandas as pd
import datetime
import os
import base64
from catboost import CatBoostClassifier, Pool
import streamlit as st

st.set_page_config(
    page_title="Hockey Match Prediction",
    page_icon="🏒",
    layout="wide"
)

# Функция загрузки данных
@st.cache_data
def load_data():
    df = pd.read_csv("rink_master_47816_wteams.csv")
    df['gameDate'] = pd.to_datetime(df['gameDate'])
    
    # Извлечение года и месяца, и создание нового столбца Season
    df["Year"] = df["gameDate"].dt.year
    df["Month"] = df["gameDate"].dt.month
    df["Season"] = df["Year"].astype(str) + "-" + (df["Year"] + 1).astype(str)
    
    # Создание SeasonWeight и NormalizedWeight
    seasons = df["Season"].unique()
    season_weights = {season: i + 1 for i, season in enumerate(sorted(seasons))}
    max_season_weight = max(season_weights.values())
    min_season_weight = min(season_weights.values())
    df["SeasonWeight"] = df["Season"].map(season_weights)
    df["NormalizedWeight"] = (df["SeasonWeight"] - min_season_weight) / (
        max_season_weight - min_season_weight
    )
    df["Weights"] = df.groupby("Season")["NormalizedWeight"].transform("mean")
    
    
    return df

data = load_data()


# Определение результата
def determine_result(row):
    if (
        row["Win"] != 0
        or row["regulationWins"] != 0
        or row["regulationAndOtWins"] != 0
        or row["shootoutWins"] != 0
    ):
        return 1  # Победа
    elif row["Loss"] != 0 or row["OTLoss"] != 0:
        return 0  # Поражение
    else:
        return -1  # Неопределено

data["Result"] = data.apply(determine_result, axis=1)

# Маппинг команд на числовые значения
fullname_to_code = {
    "New Jersey Devils": 1, "New York Islanders": 2, "New York Rangers": 3,
    "Philadelphia Flyers": 4, "Pittsburgh Penguins": 5, "Boston Bruins": 6,
    "Buffalo Sabres": 7, "Montréal Canadiens": 8, "Ottawa Senators": 9,
    "Toronto Maple Leafs": 10, "Carolina Hurricanes": 11, "Florida Panthers": 12,
    "Tampa Bay Lightning": 13, "Washington Capitals": 14, "Chicago Blackhawks": 15,
    "Detroit Red Wings": 16, "Nashville Predators": 17, "St. Louis Blues": 18,
    "Calgary Flames": 19, "Colorado Avalanche": 20, "Edmonton Oilers": 21,
    "Vancouver Canucks": 22, "Anaheim Ducks": 23, "Dallas Stars": 24,
    "Los Angeles Kings": 25, "San Jose Sharks": 26, "Columbus Blue Jackets": 27,
    "Minnesota Wild": 28, "Winnipeg Jets": 29, "Arizona Coyotes": 30,
    "Vegas Golden Knights": 31, "Seattle Kraken": 32,
}

data["Team"] = data["Team"].map(fullname_to_code)
data["Opponent"] = data["Opponent"].map(fullname_to_code)

# Разделение данных на обучающую и тестовую выборки
train = data[data["gameDate"] < "2023-10-10"]
test = data[data["gameDate"] >= "2023-10-10"]

# Определение колонок, которые будут удалены
features_to_drop = [
    "Result", "gameDate", "gameID", "gamesPlayed", "Win", "Loss", "Tie",
    "OTLoss", "points", "pointPct", "regulationWins", "regulationAndOtWins",
    "shootoutWins", "goalsFor", "goalsAgainst", "goalsForPerGame",
    "goalsAgainstPerGame", "powerPlayPct", "penaltyKillPct", "powerPlayNetPct",
    "penaltyKillNetPct", "shotsForPerGame", "shotsAgainstPerGame",
    "faceoffWinPct", "Year", "Month", "Season", "NonRegulationTime",
    "SeasonWeight", "NormalizedWeight"
]

code_to_fullname = {v: k for k, v in fullname_to_code.items()}  # Создание обратного маппинга

# Убедитесь, что колонки для удаления существуют в данных
features_to_drop = [col for col in features_to_drop if col in train.columns]

# Обновление признаков, включая Weight
X_train = train.drop(columns=features_to_drop)
y_train = train["Result"]
X_test = test.drop(columns=features_to_drop)
y_test = test["Result"]


# Функция для загрузки модели CatBoost
@st.cache_resource
def load_catboost_model(file_path):
    try:
        model = CatBoostClassifier()
        model.load_model(file_path)
        # st.write(f"Тип загруженной модели: {type(model)}")  # Для отладки
        return model
    except Exception as e:
        st.write(f"Ошибка при загрузке модели CatBoost: {e}")
        return None

model_path = "catboost_model.cb"
model = load_catboost_model(model_path)

# # Проверка признаков
# model_feature_names = model.feature_names_
# st.write("Признаки модели:", model_feature_names)
# st.write("Признаки в данных для обучения:", X_train.columns.tolist())

# Маппинг для homeRoad
home_road_mapping = {
    1: "На выезде",
    0: "Дома"
}

win_mapping = {
    1: "Победа",
    0: "Не победа: Поражение или Ничья"
}


# Функция для предсказания исхода
def predict_winner(row, model):
    # print(f"Тип модели в predict_winner: {type(model)}")  # Для отладки
    try:
        # Подготовка входных данных
        features = pd.DataFrame([row], columns=X_train.columns).fillna(0)
        
        # Создание объекта Pool для CatBoost
        pool = Pool(data=features, feature_names=X_train.columns.tolist())

        # Сделайте предсказание
        prediction = model.predict(pool)
        prediction_proba = model.predict_proba(pool)
        # st.write(f"Предсказание: {prediction}, вероятность: {prediction_proba}")  # Для отладки
        
        # Верните результат и вероятность
        result = 'Победа с вероятностью' if prediction[0] == 1 else 'Не победа: Поражение или Ничья с вероятностью'
        probability = prediction_proba[0][1] if prediction[0] == 1 else prediction_proba[0][0]
        
        return result, probability
        
    except Exception as e:
        print(f"Ошибка при предсказании: {e}")
        return "Ошибка"


st.markdown(
    """
    <style>
    @import url('https://fonts.googleapis.com/css2?family=Anton:wght@400;700&display=swap');
    
    .title {
        font-size: 48px;
        font-weight: 700;
        color: #0A74DA; /* Темно-голубой цвет */
        font-family: 'Anton', sans-serif; /* шрифт Anton */
        text-transform: uppercase; /* все буквы заглавные */
        text-shadow: 2px 2px 4px #000000; /* тень текста */
        margin-bottom: 20px;
        text-align: center; /* Центрирование текста */
    }

    .title-container {
        background: rgba(255, 255, 255, 1.0); /* Белый фон */
        padding: 10px;
        border-radius: 10px;
        margin-bottom: 20px;
        display: inline-block;
        width: 100%; /* Занимает всю ширину, чтобы текст был по центру */
    }

    .stApp {
        display: flex;
        justify-content: center;
        align-items: center;
        min-height: 100vh;
        flex-direction: column;
    }

    .stMarkdown, .stTable, .stDataFrame, .stForm, .stTextInput, .stDateInput, .stSelectbox {
        background: rgba(255, 255, 255, 1.0); /* Белый фон */
        border-radius: 10px;
        padding: 10px;
        margin-bottom: 20px;
        display: inline-block; /* Чтобы контейнеры не растягивались */
        max-width: fit-content; /* Максимальная ширина по содержимому */
    }

    </style>
    """,
    unsafe_allow_html=True
)


st.markdown('<div class="title-container"><h1 class="title">Предсказание исходов хоккейных матчей NHL 🏒🥅🏆</h1></div>', unsafe_allow_html=True)


# Добавление навигации по страницам
st.sidebar.markdown("## Навигация")
page = st.sidebar.selectbox("Выберите страницу", ["Основная", "Графики"])

if page == "Основная":
    st.sidebar.title("Поиск по фильтрам")

    selected_date = st.sidebar.date_input("Выберите дату", value=datetime.date(2023, 10, 8), key="date_input")
    selected_team = st.sidebar.selectbox("Выберите команду", options=["Все"] + list(fullname_to_code.keys()), key="team_select")
    selected_opponent = st.sidebar.selectbox("Выберите оппонента", options=["Все"] + list(fullname_to_code.keys()), key="opponent_select")
    selected_home_road = st.sidebar.selectbox("Где играет команда?", options=["Все", "Дома", "На выезде"], key="home_road_select")

    # Фильтрация данных по выбранным критериям
    filtered_data = data[data['gameDate'] == pd.to_datetime(selected_date)]

    if selected_team != "Все":
        filtered_data = filtered_data[filtered_data['Team'] == fullname_to_code[selected_team]]

    if selected_opponent != "Все":
        filtered_data = filtered_data[filtered_data['Opponent'] == fullname_to_code[selected_opponent]]

    if selected_home_road != "Все":
        filtered_data = filtered_data[filtered_data['homeRoad'] == (1 if selected_home_road == "Да" else 0)]

    if not filtered_data.empty:
        st.write(f"Игры на {selected_date}:")

        col1, col2, col3, col4, col5 = st.columns(5)
        col1.write("Команда")
        col2.write("Оппонент")
        col3.write("Где играет команда?")
        col4.write("Актуальный исход матча")
        col5.write("Предсказание")

        for index, row in filtered_data.iterrows():
            col1, col2, col3, col4, col5 = st.columns(5)
            col1.write(code_to_fullname[row['Team']])
            col2.write(code_to_fullname[row['Opponent']])
            col3.write(home_road_mapping.get(row['homeRoad'], 'Неизвестно'))
            col4.write(win_mapping.get(row['Win'], 'Нет'))
            if col5.button('Предсказание', key=index):
                row_dict = row.to_dict()
                prediction, probability = predict_winner(row_dict, model)
                st.write(f"Предсказание для игры {code_to_fullname[row['Team']]} vs {code_to_fullname[row['Opponent']]}: {prediction}  {probability:.2f}")
    else:
        st.write("Нет игр на выбранную дату.")

    # Установка фонового изображения
    background_image_path = "7.jpeg"

    if os.path.exists(background_image_path):
        with open(background_image_path, "rb") as image_file:
            encoded_image = base64.b64encode(image_file.read()).decode()

        st.markdown(
            f"""
            <style>
            .stApp {{
                background-image: url("data:image/jpeg;base64,{encoded_image}");
                background-size: cover;
                background-repeat: no-repeat;
                background-position: center center;
            }}
            .stSidebar {{
                background: rgba(255, 255, 255, 0.8);
            }}
            .stButton {{
                background-color: #0A74DA;
                color: white;
            }}
            </style>
            """,
            unsafe_allow_html=True
        )
    else:
        st.error(f"Изображение не найдено по пути: {background_image_path}")

elif page == "Графики":
    st.title("Графики и Анализ")

    # Импортирование библиотек для графиков
    import matplotlib.pyplot as plt
    import seaborn as sns
    
    # Отображение локального изображения
    image_path = "=graphs/1.png"  # Укажите путь к вашему изображению
    st.image(image_path, use_column_width=True)

    # Отображение второго локального изображения
    image_path2 = "graphs/2.png"  # Укажите путь ко второму изображению
    st.image(image_path2, use_column_width=True)

    image_path3 = "graphs/3.png"  # Укажите путь ко второму изображению
    st.image(image_path3, use_column_width=True)

    st.write("Процент побед в домашних играх: 54.55%")
    st.write("Процент побед в выездных играх: 45.45%")
    st.write("Домашняя арена увеличивает вероятность победы на: 9.10%")

    image_path4 = "graphs/4.png"  # Укажите путь ко второму изображению
    st.image(image_path4, use_column_width=True)

    image_path5 = "graphs/5.png"  # Укажите путь ко второму изображению
    st.image(image_path5, use_column_width=True)