import streamlit as st import pandas as pd import numpy as np import pickle import json import plotly.graph_objects as go import plotly.express as px # Настройка страницы st.set_page_config( page_title="Классификатор пациентов", layout="wide", initial_sidebar_state="expanded", menu_items={ 'About': "Классификатор пациентов для определения группы лечения" } ) # Применяем кастомный CSS st.markdown(""" """, unsafe_allow_html=True) # Загрузка модели и данных @st.cache_resource def load_model(): with open('model.pkl', 'rb') as file: model = pickle.load(file) with open('scaler.pkl', 'rb') as file: scaler = pickle.load(file) with open('data.json', 'r', encoding='utf-8') as f: data = json.load(f) return model, scaler, data def create_gauge_chart(value, title): fig = go.Figure(go.Indicator( mode = "gauge+number", value = value * 100, domain = {'x': [0, 1], 'y': [0, 1]}, title = {'text': title}, gauge = { 'axis': {'range': [None, 100]}, 'bar': {'color': "darkblue"}, 'steps': [ {'range': [0, 33], 'color': "lightgray"}, {'range': [33, 66], 'color': "gray"}, {'range': [66, 100], 'color': "darkgray"} ], 'threshold': { 'line': {'color': "red", 'width': 4}, 'thickness': 0.75, 'value': value * 100 } } )) fig.update_layout(height=200) return fig model, scaler, data = load_model() feature_list = data['features'] categorical_features = data['categorical_features'] numeric_features = data['numeric_features'] categorical_options = data['categorical_options'] group_names = data['group_names'] numeric_defaults = data['numeric_defaults'] categorical_defaults = data['categorical_defaults'] # Основной заголовок st.title('Классификатор пациентов') ## Примеры: # Примеры типичных случаев st.header('Примеры типичных случаев') # Создаем примеры для каждой группы (замените значения на реальные типичные случаи) example_cases = { 'контр': { 'title': 'Пример контрольной группы', 'description': 'Типичный случай для контрольной группы', 'values': { 'Шкала_ПВА 0': 3, 'HADS_Тревога 0': 5, 'HADS_Тревога 3': 5, 'HADS_Депрессия 0': 7, 'HADS_Депрессия 2': 3, 'Пенсильванская_шкала_ПВА 4': 0, 'HADS_Депрессия 3': 7, 'HADS_Депрессия 4': 4, 'CIWAAr 0': 7, 'CIWAAr 1': 4, 'ЭМОТИВ_ЛЕОНГАРД': 18, 'Отяг_наслед_алк': 0, 'Форма_употреб': 0, 'Патология_родов': 0, 'СП_в_ран_дестве': 0, 'ЧМТ': 0, } }, 'топирамат': { 'title': 'Пример группы топирамата', 'description': 'Типичный случай для группы топирамата', 'values': { 'Шкала_ПВА 0': 12, 'HADS_Тревога 0': 3, 'HADS_Тревога 3': 2, 'HADS_Депрессия 0': 7, 'HADS_Депрессия 2': 5, 'Пенсильванская_шкала_ПВА 4': 8, 'HADS_Депрессия 3': 3, 'HADS_Депрессия 4': 3, 'CIWAAr 0': 12, 'CIWAAr 1': 17, 'ЭМОТИВ_ЛЕОНГАРД': 21, 'Отяг_наслед_алк': 1, 'Форма_употреб': 1, 'Патология_родов': 0, 'СП_в_ран_дестве': 0, 'ЧМТ': 1, } }, 'леветирацетам': { 'title': 'Пример группы леветирацетама', 'description': 'Типичный случай для группы леветирацетама', 'values': { 'Шкала_ПВА 0': 6, 'HADS_Тревога 0': 2, 'HADS_Тревога 3': 2, 'HADS_Депрессия 0': 6, 'HADS_Депрессия 2': 6, 'Пенсильванская_шкала_ПВА 4': 5, 'HADS_Депрессия 3': 4, 'HADS_Депрессия 4': 3, 'CIWAAr 0': 11, 'CIWAAr 1': 2, 'ЭМОТИВ_ЛЕОНГАРД': 18, 'Отяг_наслед_алк': 1, 'Форма_употреб': 2, 'Патология_родов': 0, 'СП_в_ран_дестве': 0, 'ЧМТ': 0, } } } # Отображение примеров в виде карточек col1, col2, col3 = st.columns(3) def create_example_card(title, description, values, key): with st.container(): st.markdown(f"""

{title}

{description}

""", unsafe_allow_html=True) if st.button('Применить этот пример', key=key): st.session_state['current_example'] = values with col1: create_example_card( example_cases['контр']['title'], example_cases['контр']['description'], example_cases['контр']['values'], 'example_control' ) with col2: create_example_card( example_cases['топирамат']['title'], example_cases['топирамат']['description'], example_cases['топирамат']['values'], 'example_topirama' ) with col3: create_example_card( example_cases['леветирацетам']['title'], example_cases['леветирацетам']['description'], example_cases['леветирацетам']['values'], 'example_levetiracetam' ) # # Описание в основной части # with st.container(): # st.markdown(""" # ### О системе # Данная система помогает классифицировать пациентов по группам на основе введенных параметров. # #### Как использовать: # 1. Заполните все поля в форме слева # 2. Нажмите кнопку "Выполнить классификацию" # 3. Получите результат с вероятностями принадлежности к каждой группе # #### Группы классификации: # - Контрольная группа # - Группа топирамата # - Группа леветирацетама # """) # Боковая панель для ввода данных with st.sidebar: st.header('Ввод данных') # Создаем вкладки для разных типов параметров tab1, tab2 = st.tabs(["📊 Непрерывные", "📋 Категориальные"]) input_data = {} # Получаем значения из примера, если он выбран current_example = st.session_state.get('current_example', {}) with tab1: # Числовые признаки for i in range(0, len(numeric_features), 2): col1, col2 = st.columns(2) with col1: if i < len(numeric_features): feature = numeric_features[i] # Явно преобразуем значение в float default_value = float(current_example.get(feature, numeric_defaults[feature])) input_data[feature] = st.number_input( f'{feature}', value=default_value, step=0.01, # Добавляем шаг format="%.2f", help=f"Среднее: {float(numeric_defaults[feature]):.2f}" ) with col2: if i + 1 < len(numeric_features): feature = numeric_features[i + 1] # Явно преобразуем значение в float default_value = float(current_example.get(feature, numeric_defaults[feature])) input_data[feature] = st.number_input( f'{feature}', value=default_value, step=0.01, # Добавляем шаг format="%.2f", help=f"Среднее: {float(numeric_defaults[feature]):.2f}" ) with tab2: # Категориальные признаки for i in range(0, len(categorical_features), 2): col1, col2 = st.columns(2) with col1: if i < len(categorical_features): feature = categorical_features[i] options = categorical_options[feature] default_value = current_example.get(feature, categorical_defaults[feature]) default_idx = options.index(default_value) if default_value in options else 0 input_data[feature] = st.selectbox( f'{feature}', options, index=default_idx, help=f"Типичное: {categorical_defaults[feature]}" ) with col2: if i + 1 < len(categorical_features): feature = categorical_features[i + 1] options = categorical_options[feature] default_value = current_example.get(feature, categorical_defaults[feature]) default_idx = options.index(default_value) if default_value in options else 0 input_data[feature] = st.selectbox( f'{feature}', options, index=default_idx, help=f"Типичное: {categorical_defaults[feature]}" ) # Кнопка классификации if st.button('Выполнить классификацию', use_container_width=True): with st.spinner('Выполняется классификация...'): # Преобразование входных данных input_df = pd.DataFrame([input_data]) # One-hot encoding для категориальных признаков input_df_encoded = pd.get_dummies(input_df, columns=categorical_features) # Масштабирование числовых признаков if numeric_features: input_df_encoded[numeric_features] = scaler.transform(input_df_encoded[numeric_features]) # Убедитесь, что все необходимые столбцы присутствуют for col in feature_list: if col not in input_df_encoded.columns: input_df_encoded[col] = 0 X_pred = input_df_encoded[feature_list] prediction = model.predict(X_pred) probabilities = model.predict_proba(X_pred)[0] # Сохраняем результаты в session state st.session_state['prediction'] = prediction[0] st.session_state['probabilities'] = probabilities st.session_state['input_data'] = input_df # Отображение результатов в основной части if 'prediction' in st.session_state: st.header('Результаты классификации') # Создаем контейнер для основных метрик with st.container(): col1, col2, col3 = st.columns(3) with col1: st.markdown("""

Предсказанная группа

{}

""".format(group_names[str(st.session_state['prediction'])]), unsafe_allow_html=True) with col2: max_prob = max(st.session_state['probabilities']) st.markdown("""

Уверенность модели

{:.1%}

""".format(max_prob), unsafe_allow_html=True) with col3: second_best_prob = sorted(st.session_state['probabilities'])[-2] st.markdown("""

Вторая вероятная группа

{:.1%}

""".format(second_best_prob), unsafe_allow_html=True) # Визуализация вероятностей st.subheader('Распределение вероятностей по группам') # График вероятностей prob_df = pd.DataFrame({ 'Группа': [group_names[str(i)] for i in range(len(group_names))], 'Вероятность': st.session_state['probabilities'] }) fig = px.bar(prob_df, x='Группа', y='Вероятность', color='Вероятность', color_continuous_scale='Blues', text=prob_df['Вероятность'].apply(lambda x: f'{x:.1%}')) fig.update_layout( height=400, yaxis_range=[0, 1], yaxis_tickformat='.0%', showlegend=False ) st.plotly_chart(fig, use_container_width=True) # Детальная информация with st.expander("Детальная информация"): tab1, tab2 = st.tabs(["📊 Введенные данные", "ℹ️ Пояснение"]) with tab1: # Отображаем введенные данные в виде двух таблиц col1, col2 = st.columns(2) with col1: st.subheader("Числовые параметры") numeric_data = st.session_state['input_data'][numeric_features] st.dataframe(numeric_data.T.style.format("{:.2f}")) with col2: st.subheader("Категориальные параметры") categorical_data = st.session_state['input_data'][categorical_features] st.dataframe(categorical_data.T) with tab2: st.markdown(""" ### Как интерпретировать результаты: - **Предсказанная группа** - основная рекомендованная группа для пациента - **Уверенность модели** - вероятность принадлежности к предсказанной группе - **Вторая вероятная группа** - вероятность принадлежности ко второй наиболее вероятной группе ### Примечания: - Чем выше уверенность модели, тем надежнее предсказание - При уверенности ниже 50% рекомендуется дополнительное обследование - Результаты модели носят рекомендательный характер """) # Добавьте кнопку сброса примера if st.session_state.get('current_example'): if st.button('Сбросить пример'): del st.session_state['current_example'] st.experimental_rerun() # Добавляем footer st.markdown(""" ---
© 2023 Классификатор пациентов | Версия 1.0
""", unsafe_allow_html=True)