diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -125,62 +125,62 @@ REPO_ID = "arabovs-ai-lab/PectinProductionModels" AVAILABLE_MODELS = { "best_model": { "subfolder": "best_model", - "description": "🎯 Best overall model (Gradient Boosting) - optimal performance", + "description": "🎯 Best overall model (Gradient Boosting) - оптимальная производительность", "color": "#FF6B6B" }, "gradient_boosting": { "subfolder": "gradient_boosting", - "description": "📈 Gradient Boosting - best for multi-task regression", + "description": "📈 Gradient Boosting - лучшая для многозадачной регрессии", "color": "#4ECDC4" }, "random_forest": { "subfolder": "random_forest", - "description": "🌲 Random Forest - reliable and stable", + "description": "🌲 Random Forest - надежная и стабильная", "color": "#45B7D1" }, "xgboost": { "subfolder": "xgboost", - "description": "⚡ XGBoost - high performance on tabular data", + "description": "⚡ XGBoost - высокая производительность на табличных данных", "color": "#96CEB4" }, "linear_regression": { "subfolder": "linear_regression", - "description": "📊 Linear Regression - basic linear model", + "description": "📊 Linear Regression - базовая линейная модель", "color": "#FECA57" }, "extra_trees": { "subfolder": "extra_trees", - "description": "🌳 Extra Trees - extreme random forests", + "description": "🌳 Extra Trees - экстремальные случайные леса", "color": "#FF9FF3" }, "k_neighbors": { "subfolder": "k-neighbors", - "description": "📏 K-Neighbors - nearest neighbors method", + "description": "📏 K-Neighbors - метод ближайших соседей", "color": "#54A0FF" }, "lasso_regression": { "subfolder": "lasso_regression", - "description": "🎯 Lasso Regression - L1 regularization", + "description": "🎯 Lasso Regression - L1-регуляризация", "color": "#5F27CD" }, "multilayer_perceptron": { "subfolder": "multilayer_perceptron", - "description": "🧠 Neural Network MLP - multilayer perceptron", + "description": "🧠 Neural Network MLP - многослойный перцептрон", "color": "#00D2D3" }, "ridge_regression": { "subfolder": "ridge_regression", - "description": "🏔️ Ridge Regression - L2 regularization", + "description": "🏔️ Ridge Regression - L2-регуляризация", "color": "#FF9F43" }, "support_vector_regression": { "subfolder": "support_vector_regression", - "description": "🔗 Support Vector Regression - support vector method", + "description": "🔗 Support Vector Regression - метод опорных векторов", "color": "#A3CB38" } } # Sample types available -SAMPLE_TYPES = ["AP(M)", "Abr.", "KrP", "Aiv.", "Tkv", "Rv.", "AP(F)"] +SAMPLE_TYPES = ["ЯП(М)", "Абр.", "КрП", "Айв.", "Ткв", "Рв.", "ЯП(Ф)"] # Column mapping for Russian to English COLUMN_MAPPING = { @@ -199,26 +199,26 @@ COLUMN_MAPPING = { # Target descriptions TARGET_DESCRIPTIONS = { 'pectin_yield': { - 'name': 'Pectin yield', - 'description': 'Percentage of pectin yield from raw material', + 'name': 'Выход пектина', + 'description': 'Процент выхода пектина из сырья', 'unit': '%', 'range': (0, 100) }, 'galacturonic_acid': { - 'name': 'Galacturonic acid', - 'description': 'Galacturonic acid content in pectin', + 'name': 'Галактуроновая кислота', + 'description': 'Содержание галактуроновой кислоты в пектине', 'unit': '%', 'range': (0, 100) }, 'molecular_weight': { - 'name': 'Molecular weight', - 'description': 'Pectin molecular weight', + 'name': 'Молекулярная масса', + 'description': 'Молекулярная масса пектина', 'unit': 'Da', 'range': (0, 500000) }, 'esterification_degree': { - 'name': 'Esterification degree', - 'description': 'Pectin esterification degree', + 'name': 'Степень этерификации', + 'description': 'Степень этерификации пектина', 'unit': '%', 'range': (0, 100) } @@ -227,52 +227,52 @@ TARGET_DESCRIPTIONS = { # Metric descriptions METRIC_DESCRIPTIONS = { 'mae': { - 'name': 'MAE (Mean Absolute Error)', - 'description': 'Mean absolute difference between prediction and true value', + 'name': 'MAE (Средняя абсолютная ошибка)', + 'description': 'Среднее абсолютное значение разницы между предсказанием и истинным значением', 'better_lower': True, 'perfect_value': 0 }, 'mse': { - 'name': 'MSE (Mean Squared Error)', - 'description': 'Mean of squared differences between prediction and true value', + 'name': 'MSE (Средняя квадратичная ошибка)', + 'description': 'Среднее квадратов разниц между предсказанием и истинным значением', 'better_lower': True, 'perfect_value': 0 }, 'rmse': { - 'name': 'RMSE (Root Mean Squared Error)', - 'description': 'Square root of mean squared error, same dimension as target variable', + 'name': 'RMSE (Среднеквадратичная ошибка)', + 'description': 'Корень из средней квадратичной ошибки, имеет ту же размерность что и целевая переменная', 'better_lower': True, 'perfect_value': 0 }, 'r2': { - 'name': 'R² (Coefficient of Determination)', - 'description': 'Proportion of variance in dependent variable explained by the model', + 'name': 'R² (Коэффициент детерминации)', + 'description': 'Доля дисперсии зависимой переменной, объясняемая моделью', 'better_lower': False, 'perfect_value': 1 }, 'mape': { - 'name': 'MAPE (Mean Absolute Percentage Error)', - 'description': 'Mean absolute error as percentage of true values', + 'name': 'MAPE (Средняя абсолютная процентная ошибка)', + 'description': 'Средняя абсолютная ошибка в процентах от истинных значений', 'better_lower': True, 'perfect_value': 0 }, 'correlation': { - 'name': 'Pearson Correlation', - 'description': 'Measure of linear dependence between predictions and true values', + 'name': 'Корреляция Пирсона', + 'description': 'Мера линейной зависимости между предсказаниями и истинными значениями', 'better_lower': False, 'perfect_value': 1 } } # Helper function to download and cache model files -@st.cache_resource(show_spinner="🔄 Loading model and support files...") +@st.cache_resource(show_spinner="🔄 Загрузка модели и вспомогательных файлов...") def load_pectin_model(model_name: str): """Load pectin model and supporting files from Hugging Face Hub""" try: model_info = AVAILABLE_MODELS[model_name] subfolder = model_info["subfolder"] - st.info(f"🔄 Attempting to load model {model_name} from repository {REPO_ID}") + st.info(f"🔄 Попытка загрузки модели {model_name} из репозитория {REPO_ID}") # Download model file try: @@ -281,15 +281,15 @@ def load_pectin_model(model_name: str): filename=f"{subfolder}/model.pkl", repo_type="model" ) - st.success(f"✅ Model {model_name} loaded successfully") + st.success(f"✅ Модель {model_name} загружена успешно") except Exception as e: - st.error(f"❌ Error loading model {model_name}: {str(e)}") + st.error(f"❌ Ошибка загрузки модели {model_name}: {str(e)}") st.markdown('
', unsafe_allow_html=True) - st.error("**Possible causes of 403 error:**") - st.error("1. Repository doesn't exist or is private") - st.error("2. No internet access") - st.error("3. Hugging Face authorization issues") - st.error("4. Model files missing in repository") + st.error("**Возможные причины ошибки 403:**") + st.error("1. Репозиторий не существует или приватный") + st.error("2. Нет доступа к интернету") + st.error("3. Проблемы с авторизацией Hugging Face") + st.error("4. Файлы модели отсутствуют в репозитории") st.markdown('
', unsafe_allow_html=True) return {"status": "error", "error": str(e)} @@ -300,9 +300,9 @@ def load_pectin_model(model_name: str): filename="scaler.pkl", repo_type="model" ) - st.success("✅ Scaler loaded successfully") + st.success("✅ Scaler загружен успешно") except Exception as e: - st.error(f"❌ Error loading scaler: {str(e)}") + st.error(f"❌ Ошибка загрузки scaler: {str(e)}") return {"status": "error", "error": f"Scaler error: {str(e)}"} try: @@ -311,9 +311,9 @@ def load_pectin_model(model_name: str): filename="label_encoder.pkl", repo_type="model" ) - st.success("✅ Label encoder loaded successfully") + st.success("✅ Label encoder загружен успешно") except Exception as e: - st.error(f"❌ Error loading label encoder: {str(e)}") + st.error(f"❌ Ошибка загрузки label encoder: {str(e)}") return {"status": "error", "error": f"Encoder error: {str(e)}"} try: @@ -322,9 +322,9 @@ def load_pectin_model(model_name: str): filename="model_metadata.json", repo_type="model" ) - st.success("✅ Metadata loaded successfully") + st.success("✅ Метаданные загружены успешно") except Exception as e: - st.error(f"❌ Error loading metadata: {str(e)}") + st.error(f"❌ Ошибка загрузки метаданных: {str(e)}") return {"status": "error", "error": f"Metadata error: {str(e)}"} # Load all artifacts @@ -338,7 +338,7 @@ def load_pectin_model(model_name: str): with open(metadata_path, 'r', encoding='utf-8') as f: metadata = json.load(f) - st.success(f"✅ All model {model_name} artifacts loaded and ready to use") + st.success(f"✅ Все артефакты модели {model_name} загружены и готовы к использованию") return { "model": model, @@ -349,13 +349,13 @@ def load_pectin_model(model_name: str): } except Exception as e: - st.error(f"❌ Error loading model artifacts: {str(e)}") + st.error(f"❌ Ошибка загрузки артефактов модели: {str(e)}") return {"status": "error", "error": f"Loading error: {str(e)}"} except Exception as e: - st.error(f"❌ Critical error loading model {model_name}: {str(e)}") + st.error(f"❌ Критическая ошибка загрузки модели {model_name}: {str(e)}") st.markdown('
', unsafe_allow_html=True) - st.error("**Error details:**") + st.error("**Детали ошибки:**") st.code(traceback.format_exc()) st.markdown('
', unsafe_allow_html=True) return {"status": "error", "error": str(e)} @@ -363,7 +363,7 @@ def load_pectin_model(model_name: str): # Fallback to demo mode if models cannot be loaded def create_demo_model(): """Create demo model for testing when real models are unavailable""" - st.warning("🎭 Using demo mode with test data") + st.warning("🎭 Используется демо-режим с тестовыми данными") class DemoModel: def predict(self, X): @@ -417,7 +417,7 @@ def preprocess_single_input(input_data: Dict, model_artifacts: Dict) -> Optional if sample_value in label_encoder.classes_: df['sample_encoded'] = label_encoder.transform([sample_value])[0] else: - st.warning(f"⚠️ Raw material type '{sample_value}' not found in encoder. Using default value.") + st.warning(f"⚠️ Тип сырья '{sample_value}' не найден в энкодере. Используется значение по умолчанию.") default_class = label_encoder.classes_[0] df['sample_encoded'] = label_encoder.transform([default_class])[0] @@ -442,7 +442,7 @@ def preprocess_single_input(input_data: Dict, model_artifacts: Dict) -> Optional return X_scaled except Exception as e: - st.error(f"❌ Data preprocessing error: {e}") + st.error(f"❌ Ошибка предобработки данных: {e}") st.code(traceback.format_exc()) return None @@ -469,28 +469,28 @@ def predict_single(input_data: Dict, model_artifacts: Dict) -> Optional[Dict]: return results except Exception as e: - st.error(f"❌ Prediction error: {e}") + st.error(f"❌ Ошибка предсказания: {e}") st.code(traceback.format_exc()) return None def detect_file_encoding(uploaded_file): - """Detects file encoding""" + """Определяет кодировку файла""" try: - # Try different encodings + # Пробуем разные кодировки for encoding in ['utf-8', 'cp1251', 'iso-8859-1', 'windows-1251']: try: content = uploaded_file.getvalue().decode(encoding) - uploaded_file.seek(0) # Reset position + uploaded_file.seek(0) # Сбрасываем позицию return encoding, content except UnicodeDecodeError: continue return 'utf-8', uploaded_file.getvalue().decode('utf-8', errors='ignore') except Exception as e: - st.error(f"❌ Encoding detection error: {e}") + st.error(f"❌ Ошибка определения кодировки: {e}") return 'utf-8', uploaded_file.getvalue().decode('utf-8', errors='ignore') def parse_russian_number(value): - """Parses Russian numbers with commas as decimal separators""" + """Парсит русские числа с запятыми как десятичными разделителями""" if pd.isna(value) or value == '': return 0.0 @@ -498,46 +498,46 @@ def parse_russian_number(value): return float(value) if isinstance(value, str): - # Replace commas with dots and remove spaces + # Заменяем запятые на точки и убираем пробелы cleaned = value.replace(',', '.').replace(' ', '') try: return float(cleaned) except ValueError: - # If conversion fails, return 0 + # Если не получается, возвращаем 0 return 0.0 return 0.0 def read_flexible_pectin_file(uploaded_file, skiprows=None): - """Universal function for reading pectin files with different formats""" + """Универсальная функция для чтения файлов пектина с разными форматами""" try: file_extension = uploaded_file.name.lower() - st.info(f"📂 Reading file: {uploaded_file.name}, extension: {file_extension}") + st.info(f"📂 Чтение файла: {uploaded_file.name}, расширение: {file_extension}") if file_extension.endswith('.xlsx'): - # For Excel files + # Для Excel файлов if skiprows is None: - # Try different skip row options + # Пробуем разные варианты пропуска строк for skip in [0, 1, 2, 3]: try: df = pd.read_excel(uploaded_file, skiprows=skip, engine='openpyxl') - if len(df.columns) > 3: # If enough columns - st.info(f"✅ Found data structure with {skip} rows skipped") + if len(df.columns) > 3: # Если есть достаточно колонок + st.info(f"✅ Найдена структура данных с пропуском {skip} строк") return df except Exception as e: - st.warning(f"⚠️ Failed to read with {skip} rows skipped: {e}") + st.warning(f"⚠️ Не удалось прочитать с пропуском {skip} строк: {e}") continue - # If nothing works, read without skipping + # Если ничего не сработало, читаем без пропуска return pd.read_excel(uploaded_file, engine='openpyxl') else: return pd.read_excel(uploaded_file, skiprows=skiprows, engine='openpyxl') elif file_extension.endswith('.csv') or file_extension.endswith('.txt'): - # For CSV/TXT files + # Для CSV/TXT файлов encoding, content = detect_file_encoding(uploaded_file) - st.info(f"📖 Detected encoding: {encoding}") + st.info(f"📖 Определена кодировка: {encoding}") - # Try different separators + # Пробуем разные разделители separators = ['\t', ',', ';', '|'] for sep in separators: @@ -547,45 +547,45 @@ def read_flexible_pectin_file(uploaded_file, skiprows=None): else: df = pd.read_csv(io.StringIO(content), sep=sep, encoding=encoding) - if len(df.columns) > 3: # If enough columns - st.info(f"✅ Found separator: '{sep}' with {skiprows or 0} rows skipped") + if len(df.columns) > 3: # Если есть достаточно колонок + st.info(f"✅ Найден разделитель: '{sep}' с пропуском {skiprows or 0} строк") return df except Exception as e: - st.warning(f"⚠️ Failed to read with separator '{sep}': {e}") + st.warning(f"⚠️ Не удалось прочитать с разделителем '{sep}': {e}") continue - # If nothing works, try without specifying separator + # Если ничего не сработало, пробуем без указания разделителя try: return pd.read_csv(io.StringIO(content), encoding=encoding) except Exception as e: - st.error(f"❌ Failed to read file: {e}") + st.error(f"❌ Не удалось прочитать файл: {e}") return None else: - st.error("❌ Unsupported file format") + st.error("❌ Неподдерживаемый формат файла") return None except Exception as e: - st.error(f"❌ File reading error: {e}") + st.error(f"❌ Ошибка чтения файла: {e}") st.code(traceback.format_exc()) return None def validate_file_structure(uploaded_file, skiprows=None): - """Validates file structure before processing""" + """Проверяет структуру файла перед обработкой""" try: - st.info("🔍 Starting file structure validation...") + st.info("🔍 Начинаем проверку структуры файла...") df = read_flexible_pectin_file(uploaded_file, skiprows) if df is None or df.empty: - st.error("❌ File is empty or contains no data") + st.error("❌ Файл пустой или не содержит данных") return False, None - st.success(f"✅ File successfully read! Size: {df.shape}") - st.info("📋 File structure (first 5 rows):") + st.success(f"✅ Файл успешно прочитан! Размер: {df.shape}") + st.info("📋 Структура файла (первые 5 строк):") st.dataframe(df.head(), width='stretch') - st.info(f"📊 File columns: {list(df.columns)}") + st.info(f"📊 Колонки файла: {list(df.columns)}") - # Check for required columns (Russian or English) + # Проверяем наличие необходимых колонок (русских или английских) required_russian = ['Образец \nпектина', 't, мин', 'T, °C', 'P, атм', 'pH'] required_english = ['sample', 'time_min', 'temperature_c', 'pressure_atm', 'ph'] @@ -593,21 +593,21 @@ def validate_file_structure(uploaded_file, skiprows=None): has_english = any(col in df.columns for col in required_english) if not has_russian and not has_english: - st.error(f"❌ Missing required columns. Need either Russian: {required_russian}, or English: {required_english}") + st.error(f"❌ Отсутствуют обязательные колонки. Нужны либо русские: {required_russian}, либо английские: {required_english}") return False, df - st.success("✅ All required columns present") + st.success("✅ Все необходимые колонки присутствуют") return True, df except Exception as e: - st.error(f"❌ Failed to read file: {e}") + st.error(f"❌ Не удалось прочитать файл: {e}") st.code(traceback.format_exc()) return False, None def calculate_regression_metrics(y_true, y_pred, target_name): - """Calculates regression metrics""" + """Вычисляет метрики регрессии""" try: - # Remove NaN values + # Удаляем NaN значения mask = ~(np.isnan(y_true) | np.isnan(y_pred)) y_true_clean = y_true[mask] y_pred_clean = y_pred[mask] @@ -617,7 +617,7 @@ def calculate_regression_metrics(y_true, y_pred, target_name): metrics = {} - # Basic metrics + # Базовые метрики metrics['mae'] = mean_absolute_error(y_true_clean, y_pred_clean) metrics['mse'] = mean_squared_error(y_true_clean, y_pred_clean) metrics['rmse'] = np.sqrt(metrics['mse']) @@ -634,7 +634,7 @@ def calculate_regression_metrics(y_true, y_pred, target_name): else: metrics['mape'] = np.nan - # Correlation + # Корреляция if len(y_true_clean) > 1: correlation, p_value = stats.pearsonr(y_true_clean, y_pred_clean) metrics['correlation'] = correlation @@ -643,10 +643,10 @@ def calculate_regression_metrics(y_true, y_pred, target_name): metrics['correlation'] = np.nan metrics['correlation_p_value'] = np.nan - # Number of samples + # Количество образцов metrics['n_samples'] = len(y_true_clean) - # True value range + # Диапазон истинных значений metrics['true_min'] = np.min(y_true_clean) metrics['true_max'] = np.max(y_true_clean) metrics['true_mean'] = np.mean(y_true_clean) @@ -655,11 +655,11 @@ def calculate_regression_metrics(y_true, y_pred, target_name): return metrics except Exception as e: - st.error(f"❌ Error calculating metrics for {target_name}: {e}") + st.error(f"❌ Ошибка вычисления метрик для {target_name}: {e}") return None def get_metric_quality(metric_name, value, target_name): - """Determines metric quality""" + """Определяет качество метрики""" if np.isnan(value): return "unknown" @@ -689,7 +689,7 @@ def get_metric_quality(metric_name, value, target_name): else: return "poor" elif metric_name in ['mae', 'mse', 'rmse']: - # Evaluate relative to target variable range + # Оцениваем относительно диапазона целевой переменной target_range = TARGET_DESCRIPTIONS[target_name]['range'][1] - TARGET_DESCRIPTIONS[target_name]['range'][0] relative_error = value / target_range * 100 @@ -703,7 +703,7 @@ def get_metric_quality(metric_name, value, target_name): return "unknown" def create_metric_card(metric_name, value, target_name, width=200): - """Creates metric card""" + """Создает карточку с метрикой""" quality = get_metric_quality(metric_name, value, target_name) metric_info = METRIC_DESCRIPTIONS.get(metric_name, {}) @@ -733,19 +733,19 @@ def create_metric_card(metric_name, value, target_name, width=200): return card_html def process_batch_file_single_model(uploaded_file, model_artifacts, model_name, skiprows=None): - """Process file with single model""" + """Обработка файла с одной моделью""" try: - st.info(f"🔄 Starting file processing with model {model_name}...") - # Read file + st.info(f"🔄 Начинаем обработку файла с моделью {model_name}...") + # Читаем файл df = read_flexible_pectin_file(uploaded_file, skiprows) if df is None or df.empty: - st.error("❌ File is empty or contains no data") + st.error("❌ Файл пустой или не содержит данных") return None - st.success(f"✅ File loaded: {len(df)} records") + st.success(f"✅ Файл загружен: {len(df)} записей") - # Apply Russian to English column mapping + # Применяем маппинг русских названий на английские columns_renamed = [] for rus_name, eng_name in COLUMN_MAPPING.items(): if rus_name in df.columns: @@ -753,28 +753,28 @@ def process_batch_file_single_model(uploaded_file, model_artifacts, model_name, columns_renamed.append(f"{rus_name} -> {eng_name}") if columns_renamed: - st.info(f"✅ Renamed columns: {columns_renamed}") + st.info(f"✅ Переименованы колонки: {columns_renamed}") - # Convert numeric columns with Russian formats + # Преобразуем числовые колонки с русскими форматами numeric_columns = ['time_min', 'temperature_c', 'pressure_atm', 'ph', 'pectin_yield', 'galacturonic_acid', 'molecular_weight', 'esterification_degree'] for col in numeric_columns: if col in df.columns: df[col] = df[col].apply(parse_russian_number) - st.info(f"✅ Converted numeric data in column: {col}") + st.info(f"✅ Преобразованы числовые данные в колонке: {col}") - # Check required columns + # Проверяем необходимые колонки required_columns = ['sample', 'time_min', 'temperature_c', 'pressure_atm', 'ph'] missing_columns = [col for col in required_columns if col not in df.columns] if missing_columns: - st.error(f"❌ Missing required columns: {missing_columns}") - st.info("📋 Available columns in file:") + st.error(f"❌ Отсутствуют обязательные колонки: {missing_columns}") + st.info("📋 Доступные колонки в файле:") st.write(list(df.columns)) return None - # Process data + # Обрабатываем данные predictions = [] processed_data = [] @@ -785,26 +785,26 @@ def process_batch_file_single_model(uploaded_file, model_artifacts, model_name, for idx, row in df.iterrows(): try: - status_text.text(f"Processing record {idx+1} of {len(df)}...") + status_text.text(f"Обработка записи {idx+1} из {len(df)}...") progress_bar.progress((idx + 1) / len(df)) - # Convert row to dictionary + # Преобразуем строку в словарь input_data = row.to_dict() - # Remove unnecessary columns + # Убираем ненужные колонки for col in list(input_data.keys()): if col not in required_columns and not col.startswith('predicted_'): - if col != 'solid_liquid_ratio': # Keep solid_liquid_ratio if present + if col != 'solid_liquid_ratio': # Сохраняем Т:Ж если есть del input_data[col] - # Check and clean data + # Проверяем и очищаем данные for key in list(input_data.keys()): if pd.isna(input_data[key]) or input_data[key] == '': if key in required_columns: - st.warning(f"⚠️ Row {idx+1}: missing value in column '{key}'") + st.warning(f"⚠️ Строка {idx+1}: пропущено значение в колонке '{key}'") del input_data[key] - # Check for required fields + # Проверяем наличие обязательных полей if all(field in input_data for field in required_columns): prediction = predict_single(input_data, model_artifacts) if prediction: @@ -816,29 +816,29 @@ def process_batch_file_single_model(uploaded_file, model_artifacts, model_name, processed_data.append(input_data) else: missing = [field for field in required_columns if field not in input_data] - st.warning(f"⚠️ Row {idx+1}: missing fields {missing}") + st.warning(f"⚠️ Строка {idx+1}: отсутствуют поля {missing}") predictions.append({}) processed_data.append(input_data) except Exception as e: - st.error(f"❌ Error processing row {idx+1}: {e}") + st.error(f"❌ Ошибка обработки строки {idx+1}: {e}") predictions.append({}) processed_data.append(row.to_dict() if 'row' in locals() else {}) - status_text.text("✅ Processing completed!") + status_text.text("✅ Обработка завершена!") progress_bar.empty() - st.info(f"📊 Successfully processed: {successful_predictions} of {len(df)} records") + st.info(f"📊 Успешно обработано: {successful_predictions} из {len(df)} записей") - # Create results DataFrame + # Создаем DataFrame с результатами result_df = pd.DataFrame(processed_data) - # Add predictions with model prefix + # Добавляем предсказания с префиксом модели target_columns = model_artifacts["metadata"].get('target_columns', []) for i, target in enumerate(target_columns): result_df[f'{model_name}_{target}'] = [pred.get(target, None) for pred in predictions] - # Save original data if present + # Сохраняем оригинальные данные если они есть original_columns = ['pectin_yield', 'galacturonic_acid', 'molecular_weight', 'esterification_degree'] for col in original_columns: if col in df.columns: @@ -847,33 +847,33 @@ def process_batch_file_single_model(uploaded_file, model_artifacts, model_name, return result_df except Exception as e: - st.error(f"❌ Critical file processing error: {e}") + st.error(f"❌ Критическая ошибка обработки файла: {e}") st.markdown('
', unsafe_allow_html=True) - st.error("**Error details:**") + st.error("**Детали ошибки:**") st.code(traceback.format_exc()) st.markdown('
', unsafe_allow_html=True) return None def process_batch_file_multiple_models(uploaded_file, selected_models, skiprows=None): - """Process file with multiple models and compare results""" + """Обработка файла с несколькими моделями и сравнение результатов""" try: - st.info("🔄 Starting file processing with multiple models...") + st.info("🔄 Начинаем обработку файла с несколькими моделями...") - # First read and preprocess file once + # Сначала читаем и предобрабатываем файл один раз df = read_flexible_pectin_file(uploaded_file, skiprows) if df is None or df.empty: - st.error("❌ File is empty or contains no data") + st.error("❌ Файл пустой или не содержит данных") return None, {}, {}, {} - st.success(f"✅ File loaded: {len(df)} records") + st.success(f"✅ Файл загружен: {len(df)} записей") - # Apply Russian to English column mapping + # Применяем маппинг русских названий на английские for rus_name, eng_name in COLUMN_MAPPING.items(): if rus_name in df.columns: df = df.rename(columns={rus_name: eng_name}) - # Convert numeric columns with Russian formats + # Преобразуем числовые колонки с русскими форматами numeric_columns = ['time_min', 'temperature_c', 'pressure_atm', 'ph', 'pectin_yield', 'galacturonic_acid', 'molecular_weight', 'esterification_degree'] @@ -881,22 +881,22 @@ def process_batch_file_multiple_models(uploaded_file, selected_models, skiprows= if col in df.columns: df[col] = df[col].apply(parse_russian_number) - # Check required columns + # Проверяем необходимые колонки required_columns = ['sample', 'time_min', 'temperature_c', 'pressure_atm', 'ph'] missing_columns = [col for col in required_columns if col not in df.columns] if missing_columns: - st.error(f"❌ Missing required columns: {missing_columns}") + st.error(f"❌ Отсутствуют обязательные колонки: {missing_columns}") return None, {}, {}, {} - # Create base DataFrame with source data + # Создаем базовый DataFrame с исходными данными base_columns = required_columns.copy() if 'solid_liquid_ratio' in df.columns: base_columns.append('solid_liquid_ratio') result_df = df[base_columns].copy() - # Add original target variables if present + # Добавляем оригинальные целевые переменные если есть original_columns = ['pectin_yield', 'galacturonic_acid', 'molecular_weight', 'esterification_degree'] has_ground_truth = False for col in original_columns: @@ -904,7 +904,7 @@ def process_batch_file_multiple_models(uploaded_file, selected_models, skiprows= result_df[f'original_{col}'] = df[col] has_ground_truth = True - # Process data for each model + # Обрабатываем данные для каждой модели all_predictions = {} model_results = {} all_metrics = {} @@ -914,17 +914,17 @@ def process_batch_file_multiple_models(uploaded_file, selected_models, skiprows= for model_idx, model_name in enumerate(selected_models): try: - status_text.text(f"🔄 Processing with model {model_name} ({model_idx+1}/{len(selected_models)})...") + status_text.text(f"🔄 Обработка моделью {model_name} ({model_idx+1}/{len(selected_models)})...") progress_bar.progress(model_idx / len(selected_models)) - # Load model + # Загружаем модель model_artifacts = load_pectin_model(model_name) if model_artifacts["status"] not in ["success", "demo"]: - st.error(f"❌ Failed to load model {model_name}") + st.error(f"❌ Не удалось загрузить модель {model_name}") continue - # Get predictions for all rows + # Получаем предсказания для всех строк model_predictions = [] successful_predictions = 0 @@ -940,10 +940,10 @@ def process_batch_file_multiple_models(uploaded_file, selected_models, skiprows= model_predictions.append({}) except Exception as e: - st.error(f"❌ Prediction error with model {model_name} for row {idx+1}: {e}") + st.error(f"❌ Ошибка предсказания моделью {model_name} для строки {idx+1}: {e}") model_predictions.append({}) - # Save predictions + # Сохраняем предсказания all_predictions[model_name] = model_predictions model_results[model_name] = { 'successful': successful_predictions, @@ -951,12 +951,12 @@ def process_batch_file_multiple_models(uploaded_file, selected_models, skiprows= 'artifacts': model_artifacts } - # Add predictions to result + # Добавляем предсказания в результат target_columns = model_artifacts["metadata"].get('target_columns', []) for target in target_columns: result_df[f'{model_name}_{target}'] = [pred.get(target, None) for pred in model_predictions] - # Calculate metrics if ground truth exists + # Вычисляем метрики если есть ground truth model_metrics = {} if has_ground_truth: for target in target_columns: @@ -973,44 +973,44 @@ def process_batch_file_multiple_models(uploaded_file, selected_models, skiprows= all_metrics[model_name] = model_metrics - st.success(f"✅ Model {model_name}: {successful_predictions}/{len(df)} successful predictions") + st.success(f"✅ Модель {model_name}: {successful_predictions}/{len(df)} успешных предсказаний") except Exception as e: - st.error(f"❌ Error processing with model {model_name}: {e}") + st.error(f"❌ Ошибка обработки моделью {model_name}: {e}") continue progress_bar.progress(1.0) - status_text.text("✅ All models processed!") + status_text.text("✅ Все модели обработаны!") return result_df, all_predictions, model_results, all_metrics except Exception as e: - st.error(f"❌ Critical file processing error: {e}") + st.error(f"❌ Критическая ошибка обработки файла: {e}") st.markdown('
', unsafe_allow_html=True) - st.error("**Error details:**") + st.error("**Детали ошибки:**") st.code(traceback.format_exc()) st.markdown('
', unsafe_allow_html=True) return None, {}, {}, {} def create_comparison_visualizations(result_df, selected_models, target_columns, all_metrics): - """Creates visualizations for model comparison""" + """Создает визуализации для сравнения моделей""" try: - st.markdown("### 📊 Visual Model Comparison") + st.markdown("### 📊 Визуальное сравнение моделей") - # Create tabs for different visualization types + # Создаем вкладки для разных типов визуализаций viz_tab1, viz_tab2, viz_tab3, viz_tab4 = st.tabs([ - "📈 Target Variable Comparison", - "📋 Summary Statistics", - "🔍 Detailed Analysis", - "📉 Quality Metrics" + "📈 Сравнение по целевым переменным", + "📋 Сводная статистика", + "🔍 Детальный анализ", + "📉 Метрики качества" ]) with viz_tab1: - # Graphs for each target variable + # Графики для каждой целевой переменной for target in target_columns: st.markdown(f"#### {TARGET_DESCRIPTIONS[target]['name']}") - # Collect data for graph + # Собираем данные для графика plot_data = [] for model in selected_models: col_name = f'{model}_{target}' @@ -1025,10 +1025,10 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, }) if len(plot_data) > 0: - # Create subplot for different graph types + # Создаем subplot для разных типов графиков fig = make_subplots( rows=1, cols=2, - subplot_titles=('Prediction Distribution', 'Mean Value Comparison'), + subplot_titles=('Распределение предсказаний', 'Сравнение средних значений'), specs=[[{"type": "box"}, {"type": "bar"}]] ) @@ -1044,7 +1044,7 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, row=1, col=1 ) - # Bar plot with mean values + # Bar plot со средними значениями models = [data['model'] for data in plot_data] means = [data['mean'] for data in plot_data] stds = [data['std'] for data in plot_data] @@ -1062,17 +1062,17 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, fig.update_layout( height=400, - title_text=f"Model Comparison: {TARGET_DESCRIPTIONS[target]['name']}", + title_text=f"Сравнение моделей: {TARGET_DESCRIPTIONS[target]['name']}", showlegend=False ) st.plotly_chart(fig) else: - st.info(f"ℹ️ No data to display for target variable {target}") + st.info(f"ℹ️ Нет данных для отображения по целевой переменной {target}") with viz_tab2: - # Model summary statistics - st.markdown("#### 📋 Model Summary Statistics") + # Сводная статистика по моделям + st.markdown("#### 📋 Сводная статистика по моделям") stats_data = [] for target in target_columns: @@ -1082,73 +1082,73 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, values = result_df[col_name].dropna() if len(values) > 0: stats_data.append({ - 'Model': model, - 'Target Variable': TARGET_DESCRIPTIONS[target]['name'], - 'Mean': float(values.mean()), - 'Standard Deviation': float(values.std()), - 'Minimum': float(values.min()), - 'Maximum': float(values.max()), - 'Prediction Count': int(len(values)) + 'Модель': model, + 'Целевая переменная': TARGET_DESCRIPTIONS[target]['name'], + 'Среднее': float(values.mean()), + 'Стандартное отклонение': float(values.std()), + 'Минимум': float(values.min()), + 'Максимум': float(values.max()), + 'Количество предсказаний': int(len(values)) }) if stats_data: stats_df = pd.DataFrame(stats_data) - # Format numeric columns + # Форматируем числовые колонки formatted_stats = stats_df.copy() - numeric_columns = ['Mean', 'Standard Deviation', 'Minimum', 'Maximum'] + numeric_columns = ['Среднее', 'Стандартное отклонение', 'Минимум', 'Максимум'] for col in numeric_columns: if col in formatted_stats.columns: - if col == 'Standard Deviation': + if col == 'Стандартное отклонение': formatted_stats[col] = formatted_stats[col].apply(lambda x: f"{x:.4f}") else: formatted_stats[col] = formatted_stats[col].apply(lambda x: f"{x:.2f}") st.dataframe(formatted_stats) - # Pivot table for easy comparison + # Pivot таблица для удобного сравнения try: pivot_mean = stats_df.pivot_table( - index='Model', - columns='Target Variable', - values='Mean', + index='Модель', + columns='Целевая переменная', + values='Среднее', aggfunc='first' ) - # Format pivot table + # Форматируем pivot таблицу formatted_pivot = pivot_mean.copy() for col in formatted_pivot.columns: formatted_pivot[col] = formatted_pivot[col].apply(lambda x: f"{float(x):.2f}" if pd.notna(x) else "N/A") - st.markdown("#### 📊 Mean Value Comparison") + st.markdown("#### 📊 Сравнение средних значений") st.dataframe(formatted_pivot) except Exception as e: - st.warning(f"Failed to create pivot table: {e}") + st.warning(f"Не удалось создать сводную таблицу: {e}") else: - st.info("ℹ️ No data for statistical analysis") + st.info("ℹ️ Нет данных для статистического анализа") with viz_tab3: - # Detailed analysis - FIXED VERSION - st.markdown("#### 🔍 Detailed Prediction Analysis") + # Детальный анализ - ИСПРАВЛЕННАЯ ВЕРСИЯ + st.markdown("#### 🔍 Детальный анализ предсказаний") - # Check that result_df is not empty and has data for analysis + # Проверяем, что result_df не пустой и есть данные для анализа if len(result_df) == 0: - st.info("ℹ️ No data for detailed analysis") + st.info("ℹ️ Нет данных для детального анализа") return try: - # Create safe descriptions for dropdown + # Создаем безопасные описания для выпадающего списка options = [] for i in range(len(result_df)): try: - # Safe data extraction with column presence check - sample = "Unknown" + # Безопасное извлечение данных с проверкой наличия колонок + sample = "Неизвестно" time_min = "N/A" temperature_c = "N/A" if 'sample' in result_df.columns: sample_val = result_df.iloc[i]['sample'] - sample = str(sample_val) if pd.notna(sample_val) else "Unknown" + sample = str(sample_val) if pd.notna(sample_val) else "Неизвестно" if 'time_min' in result_df.columns: time_val = result_df.iloc[i]['time_min'] @@ -1158,23 +1158,23 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, temp_val = result_df.iloc[i]['temperature_c'] temperature_c = f"{float(temp_val):.1f}" if pd.notna(temp_val) else "N/A" - description = f"Record {i+1}: {sample} - t={time_min}min, T={temperature_c}°C" + description = f"Запись {i+1}: {sample} - t={time_min}мин, T={temperature_c}°C" options.append((i, description)) except Exception as e: - # If error occurs, create basic description - options.append((i, f"Record {i+1} (format error)")) + # Если произошла ошибка, создаем базовое описание + options.append((i, f"Запись {i+1} (ошибка формата)")) - # Create dropdown + # Создаем выпадающий список selected_row_idx = st.selectbox( - "Select record for detailed analysis:", + "Выберите запись для детального анализа:", options=[opt[0] for opt in options], - format_func=lambda x: next((opt[1] for opt in options if opt[0] == x), f"Record {x+1}") + format_func=lambda x: next((opt[1] for opt in options if opt[0] == x), f"Запись {x+1}") ) if selected_row_idx is not None and 0 <= selected_row_idx < len(result_df): row_data = result_df.iloc[selected_row_idx] - # Create prediction comparison graph for selected row + # Создаем график сравнения предсказаний для выбранной строки comparison_data = [] for target in target_columns: for model in selected_models: @@ -1184,13 +1184,13 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, if pd.notna(value): try: comparison_data.append({ - 'Model': model, - 'Target Variable': TARGET_DESCRIPTIONS[target]['name'], - 'Value': float(value), - 'Color': AVAILABLE_MODELS[model]['color'] + 'Модель': model, + 'Целевая переменная': TARGET_DESCRIPTIONS[target]['name'], + 'Значение': float(value), + 'Цвет': AVAILABLE_MODELS[model]['color'] }) except (TypeError, ValueError) as e: - st.warning(f"Failed to convert value for {model}_{target}: {value}") + st.warning(f"Не удалось преобразовать значение для {model}_{target}: {value}") continue if comparison_data: @@ -1198,27 +1198,27 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, fig = px.bar( comp_df, - x='Target Variable', - y='Value', - color='Model', + x='Целевая переменная', + y='Значение', + color='Модель', barmode='group', - title=f"Prediction Comparison for Record {selected_row_idx+1}", + title=f"Сравнение предсказаний для записи {selected_row_idx+1}", color_discrete_map={model: AVAILABLE_MODELS[model]['color'] for model in selected_models} ) st.plotly_chart(fig) else: - st.info("ℹ️ No prediction data for selected record") + st.info("ℹ️ Нет данных предсказаний для выбранной записи") - # Show detailed values in table - st.markdown("#### 📋 Numerical Prediction Values") + # Показываем детальные значения в таблице + st.markdown("#### 📋 Численные значения предсказаний") detail_data = [] for target in target_columns: try: - row_detail = {'Target Variable': TARGET_DESCRIPTIONS[target]['name']} + row_detail = {'Целевая переменная': TARGET_DESCRIPTIONS[target]['name']} - # Add each model's predictions + # Добавляем предсказания каждой модели for model in selected_models: col_name = f'{model}_{target}' if col_name in result_df.columns: @@ -1227,69 +1227,69 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, try: row_detail[model] = f"{float(value):.2f}" except (TypeError, ValueError): - row_detail[model] = 'Error' + row_detail[model] = 'Ошибка' else: row_detail[model] = 'N/A' - # Add original value if present + # Добавляем оригинальное значение если есть orig_col = f'original_{target}' if orig_col in result_df.columns and pd.notna(row_data[orig_col]): try: - row_detail['Original Value'] = f"{float(row_data[orig_col]):.2f}" + row_detail['Оригинальное значение'] = f"{float(row_data[orig_col]):.2f}" except (TypeError, ValueError): - row_detail['Original Value'] = 'Error' + row_detail['Оригинальное значение'] = 'Ошибка' detail_data.append(row_detail) except Exception as e: - st.warning(f"Error processing target variable {target}: {e}") + st.warning(f"Ошибка обработки целевой переменной {target}: {e}") continue if detail_data: detail_df = pd.DataFrame(detail_data) st.dataframe(detail_df) else: - st.info("ℹ️ No detailed data to display") + st.info("ℹ️ Нет детальных данных для отображения") else: - st.error("❌ Invalid record selected for analysis") + st.error("❌ Выбрана некорректная запись для анализа") except Exception as e: - st.error(f"❌ Error in detailed analysis: {str(e)}") + st.error(f"❌ Ошибка в детальном анализе: {str(e)}") st.markdown('
', unsafe_allow_html=True) - st.error("**Error details:**") + st.error("**Детали ошибки:**") st.code(traceback.format_exc()) st.markdown('
', unsafe_allow_html=True) with viz_tab4: - # Quality metrics tab - st.markdown("#### 📉 Model Quality Metrics") + # Вкладка с метриками качества + st.markdown("#### 📉 Метрики качества моделей") if not all_metrics or not any(all_metrics.values()): st.info(""" - ℹ️ **Original values (ground truth) in uploaded file required for metric calculation** + ℹ️ **Для вычисления метрик необходимы оригинальные значения (ground truth) в загруженном файле** - Ensure your file contains columns with actual values: - - `pectin_yield` (PV, %) - - `galacturonic_acid` (GK, %) - - `molecular_weight` (Mw, D) - - `esterification_degree` (SE, %) + Убедитесь, что ваш файл содержит колонки с настоящими значениями: + - `pectin_yield` (ПВ, %) + - `galacturonic_acid` (ГК, %) + - `molecular_weight` (Mw, Д) + - `esterification_degree` (СЭ, %) """) return - # Create metrics table for each model and target variable + # Создаем таблицу с метриками для каждой модели и целевой переменной metrics_data = [] for model_name, model_metrics in all_metrics.items(): for target_name, metrics in model_metrics.items(): for metric_name, metric_value in metrics.items(): if metric_name in ['mae', 'mse', 'rmse', 'r2', 'mape', 'correlation']: try: - # Convert value to float for safe formatting + # Преобразуем значение в float для безопасного форматирования float_value = float(metric_value) if metric_value is not None else float('nan') metrics_data.append({ - 'Model': model_name, - 'Target Variable': TARGET_DESCRIPTIONS[target_name]['name'], - 'Metric': METRIC_DESCRIPTIONS[metric_name]['name'], - 'Value': float_value, + 'Модель': model_name, + 'Целевая переменная': TARGET_DESCRIPTIONS[target_name]['name'], + 'Метрика': METRIC_DESCRIPTIONS[metric_name]['name'], + 'Значение': float_value, 'metric_type': metric_name, 'target_type': target_name }) @@ -1299,121 +1299,285 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, if metrics_data: metrics_df = pd.DataFrame(metrics_data) - # Pivot table for easy comparison + # Pivot таблица для удобного сравнения try: pivot_metrics = metrics_df.pivot_table( - index=['Model', 'Target Variable'], - columns='Metric', - values='Value', + index=['Модель', 'Целевая переменная'], + columns='Метрика', + values='Значение', aggfunc='first' ).reset_index() - # Format numeric columns + # Форматируем числовые колонки formatted_pivot = pivot_metrics.copy() for col in formatted_pivot.columns: - if col not in ['Model', 'Target Variable']: + if col not in ['Модель', 'Целевая переменная']: formatted_pivot[col] = formatted_pivot[col].apply( lambda x: f"{float(x):.4f}" if pd.notna(x) else "N/A" ) st.dataframe(formatted_pivot) except Exception as e: - st.warning(f"Failed to create metrics pivot table: {e}") + st.warning(f"Не удалось создать сводную таблицу метрик: {e}") st.dataframe(metrics_df) - # Metrics visualization - st.markdown("#### 📊 Quality Metrics Visualization") + # Визуализация метрик + st.markdown("#### 📊 Визуализация метрик качества") - # Select metric for visualization + # Выбор метрики для визуализации available_metrics = [m for m in metrics_df['metric_type'].unique() if m in METRIC_DESCRIPTIONS] if available_metrics: selected_metric = st.selectbox( - "Select metric for visualization:", + "Выберите метрику для визуализации:", options=available_metrics, format_func=lambda x: METRIC_DESCRIPTIONS.get(x, {}).get('name', x) ) if selected_metric: - # Filter data for selected metric + # Фильтруем данные для выбранной метрики metric_data = metrics_df[metrics_df['metric_type'] == selected_metric] if len(metric_data) > 0: - # Convert values to float for plotting + # Преобразуем значения в float для построения графика metric_data = metric_data.copy() - metric_data['Numeric Value'] = metric_data['Value'].apply( + metric_data['Числовое значение'] = metric_data['Значение'].apply( lambda x: float(x) if pd.notna(x) else float('nan') ) fig = px.bar( metric_data, - x='Model', - y='Numeric Value', - color='Target Variable', + x='Модель', + y='Числовое значение', + color='Целевая переменная', barmode='group', - title=f"{METRIC_DESCRIPTIONS[selected_metric]['name']} by Models", - labels={'Numeric Value': METRIC_DESCRIPTIONS[selected_metric]['name']} + title=f"{METRIC_DESCRIPTIONS[selected_metric]['name']} по моделям", + labels={'Числовое значение': METRIC_DESCRIPTIONS[selected_metric]['name']} ) - # Add ideal value line + # Добавляем линию идеального значения perfect_value = METRIC_DESCRIPTIONS[selected_metric].get('perfect_value', 0) better_lower = METRIC_DESCRIPTIONS[selected_metric].get('better_lower', True) if not better_lower: - # For metrics where higher is better (R², correlation) + # Для метрик где лучше большее значение (R², корреляция) fig.add_hline( y=perfect_value, line_dash="dash", line_color="green", - annotation_text="Ideal Value" + annotation_text="Идеальное значение" ) st.plotly_chart(fig) else: - st.info("ℹ️ No metrics available for visualization") + st.info("ℹ️ Нет доступных метрик для визуализации") else: - st.info("ℹ️ No metrics data to display") + st.info("ℹ️ Нет данных метрик для отображения") except Exception as e: - st.error(f"❌ Error creating visualizations: {str(e)}") + st.error(f"❌ Ошибка создания визуализаций: {str(e)}") st.markdown('
', unsafe_allow_html=True) - st.error("**Error details:**") + st.error("**Детали ошибки:**") st.code(traceback.format_exc()) st.markdown('
', unsafe_allow_html=True) def display_metrics_dashboard(all_metrics, selected_models): - """Creates metrics quality dashboard""" + """Создает дашборд с метриками качества""" try: - st.markdown("### 📊 Quality Metrics Dashboard") + st.markdown("### 📊 Дашборд метрик качества") if not all_metrics or not any(all_metrics.values()): st.info(""" - ℹ️ **Original values in uploaded file required for quality metrics display** + ℹ️ **Для отображения метрик качества необходимы оригинальные значения в загруженном файле** - Ensure your file contains columns with actual values: - - `pectin_yield` (PV, %) - - `galacturonic_acid` (GK, %) - - `molecular_weight` (Mw, D) - - `esterification_degree` (SE, %) + Убедитесь, что ваш файл содержит колонки с настоящими значениями: + - `pectin_yield` (ПВ, %) + - `galacturonic_acid` (ГК, %) + - `molecular_weight` (Mw, Д) + - `esterification_degree` (СЭ, %) """) return - # Create tabs for different metric views - metric_tab1, metric_tab2, metric_tab3 = st.tabs(["🎯 Metric Cards", "📈 Comparative Graphs", "📋 Detailed Table"]) + # Создаем вкладки для разных представлений метрик + metric_tab1, metric_tab2, metric_tab3 = st.tabs(["🎯 Карточки метрик", "📈 Сравнительные графики", "📋 Детальная таблица"]) with metric_tab1: - st.markdown("#### 🎯 Main Quality Metrics") + st.markdown("#### 🎯 Основные метрики качества") - # Show metric cards for each model and target variable + # Показываем карточки с метриками для каждой модели и целевой переменной for model_name in selected_models: if model_name in all_metrics and all_metrics[model_name]: - st.markdown(f"##### 🧮 Model: {model_name}") + st.markdown(f"##### 🧮 Модель: {model_name}") model_metrics = all_metrics[model_name] cols = st.columns(4) for idx, (target_name, metrics) in enumerate(model_metrics.items()): with cols[idx % 4]: - # Show main metrics in cards + # Показываем основные метрики в карточках + for metric_name in ['r2', 'mae', 'rmse', 'mape']: + if metric_name in metrics and metrics[metric_name] is not None: + try: + value = float(metrics[metric_name]) + card_html = create_metric_card( + metric_name, + value, + target_name + ) + st.markdown(card_html, unsafe_allow_html=True) + except (TypeError, ValueError): + continue + + with metric_tab2: + st.markdown("#### 📈 Сравнение метрик между моделями") + + # Создаем сравнительные графики для основных метрик + comparison_metrics = ['r2', 'mae', 'rmse', 'mape'] + + for metric in comparison_metrics: + st.markdown(f"##### {METRIC_DESCRIPTIONS[metric]['name']}") + + # Собираем данные для графика + plot_data = [] + for model_name in selected_models: + if model_name in all_metrics: + for target_name, metrics in all_metrics[model_name].items(): + if metric in metrics and metrics[metric] is not None: + try: + value = float(metrics[metric]) + plot_data.append({ + 'Модель': model_name, + 'Целевая переменная': TARGET_DESCRIPTIONS[target_name]['name'], + 'Значение': value, + 'Метрика': METRIC_DESCRIPTIONS[metric]['name'] + }) + except (TypeError, ValueError): + continue + + if plot_data: + plot_df = pd.DataFrame(plot_data) + + fig = px.bar( + plot_df, + x='Модель', + y='Значение', + color='Целевая переменная', + barmode='group', + title=f"Сравнение {METRIC_DESCRIPTIONS[metric]['name']}", + labels={'Значение': METRIC_DESCRIPTIONS[metric]['name']} + ) + + # Добавляем линию идеального значения если применимо + perfect_value = METRIC_DESCRIPTIONS[metric].get('perfect_value', 0) + if METRIC_DESCRIPTIONS[metric].get('better_lower', True): + # Для метрик где лучше меньшее значение + fig.add_hline(y=perfect_value, line_dash="dash", line_color="red") + else: + # Для метрик где лучше большее значение + fig.add_hline(y=perfect_value, line_dash="dash", line_color="green") + + st.plotly_chart(fig, use_container_width=True) + else: + st.info(f"ℹ️ Нет данных для метрики {METRIC_DESCRIPTIONS[metric]['name']}") + + with metric_tab3: + st.markdown("#### 📋 Детальная таблица метрик") + + # Создаем подробную таблицу со всеми метриками + detailed_data = [] + for model_name in selected_models: + if model_name in all_metrics: + for target_name, metrics in all_metrics[model_name].items(): + row = { + 'Модель': model_name, + 'Целевая переменная': TARGET_DESCRIPTIONS[target_name]['name'], + 'Количество образцов': int(metrics.get('n_samples', 0)) + } + + # Добавляем все вычисленные метрики + for metric_name in ['mae', 'mse', 'rmse', 'r2', 'mape', 'correlation']: + if metric_name in metrics and metrics[metric_name] is not None: + try: + value = float(metrics[metric_name]) + row[METRIC_DESCRIPTIONS[metric_name]['name']] = value + except (TypeError, ValueError): + row[METRIC_DESCRIPTIONS[metric_name]['name']] = 'N/A' + + detailed_data.append(row) + + if detailed_data: + detailed_df = pd.DataFrame(detailed_data) + + # Форматируем числовые колонки + format_dict = {} + for metric_name in ['mae', 'mse', 'rmse', 'mape']: + display_name = METRIC_DESCRIPTIONS[metric_name]['name'] + if display_name in detailed_df.columns: + # Создаем форматированную версию + detailed_df[display_name] = detailed_df[display_name].apply( + lambda x: f"{float(x):.4f}" if x != 'N/A' and pd.notna(x) else 'N/A' + ) + + for metric_name in ['r2', 'correlation']: + display_name = METRIC_DESCRIPTIONS[metric_name]['name'] + if display_name in detailed_df.columns: + detailed_df[display_name] = detailed_df[display_name].apply( + lambda x: f"{float(x):.3f}" if x != 'N/A' and pd.notna(x) else 'N/A' + ) + + st.dataframe(detailed_df, width='stretch') + + # Скачивание таблицы метрик + csv_metrics = detailed_df.to_csv(index=False, encoding='utf-8-sig') + st.download_button( + label="📥 Скачать таблицу метрик (CSV)", + data=csv_metrics, + file_name=f"pectin_metrics_comparison_{pd.Timestamp.now().strftime('%Y%m%d_%H%M')}.csv", + mime="text/csv" + ) + else: + st.info("ℹ️ Нет данных для создания детальной таблицы метрик") + + except Exception as e: + st.error(f"❌ Ошибка создания дашборда метрик: {str(e)}") + st.markdown('
', unsafe_allow_html=True) + st.error("**Детали ошибки:**") + st.code(traceback.format_exc()) + st.markdown('
', unsafe_allow_html=True) + +def display_metrics_dashboard(all_metrics, selected_models): + """Создает дашборд с метриками качества""" + try: + st.markdown("### 📊 Дашборд метрик качества") + + if not all_metrics or not any(all_metrics.values()): + st.info(""" + ℹ️ **Для отображения метрик качества необходимы оригинальные значения в загруженном файле** + + Убедитесь, что ваш файл содержит колонки с настоящими значениями: + - `pectin_yield` (ПВ, %) + - `galacturonic_acid` (ГК, %) + - `molecular_weight` (Mw, Д) + - `esterification_degree` (СЭ, %) + """) + return + + # Создаем вкладки для разных представлений метрик + metric_tab1, metric_tab2, metric_tab3 = st.tabs(["🎯 Карточки метрик", "📈 Сравнительные графики", "📋 Детальная таблица"]) + + with metric_tab1: + st.markdown("#### 🎯 Основные метрики качества") + + # Показываем карточки с метриками для каждой модели и целевой переменной + for model_name in selected_models: + if model_name in all_metrics and all_metrics[model_name]: + st.markdown(f"##### 🧮 Модель: {model_name}") + + model_metrics = all_metrics[model_name] + cols = st.columns(4) + + for idx, (target_name, metrics) in enumerate(model_metrics.items()): + with cols[idx % 4]: + # Показываем основные метрики в карточках for metric_name in ['r2', 'mae', 'rmse', 'mape']: if metric_name in metrics and not np.isnan(metrics[metric_name]): card_html = create_metric_card( @@ -1424,25 +1588,25 @@ def display_metrics_dashboard(all_metrics, selected_models): st.markdown(card_html, unsafe_allow_html=True) with metric_tab2: - st.markdown("#### 📈 Metric Comparison Between Models") + st.markdown("#### 📈 Сравнение метрик между моделями") - # Create comparative graphs for main metrics + # Создаем сравнительные графики для основных метрик comparison_metrics = ['r2', 'mae', 'rmse', 'mape'] for metric in comparison_metrics: st.markdown(f"##### {METRIC_DESCRIPTIONS[metric]['name']}") - # Collect data for graph + # Собираем данные для графика plot_data = [] for model_name in selected_models: if model_name in all_metrics: for target_name, metrics in all_metrics[model_name].items(): if metric in metrics and not np.isnan(metrics[metric]): plot_data.append({ - 'Model': model_name, - 'Target Variable': TARGET_DESCRIPTIONS[target_name]['name'], - 'Value': metrics[metric], - 'Metric': METRIC_DESCRIPTIONS[metric]['name'] + 'Модель': model_name, + 'Целевая переменная': TARGET_DESCRIPTIONS[target_name]['name'], + 'Значение': metrics[metric], + 'Метрика': METRIC_DESCRIPTIONS[metric]['name'] }) if plot_data: @@ -1450,40 +1614,40 @@ def display_metrics_dashboard(all_metrics, selected_models): fig = px.bar( plot_df, - x='Model', - y='Value', - color='Target Variable', + x='Модель', + y='Значение', + color='Целевая переменная', barmode='group', - title=f"Comparison of {METRIC_DESCRIPTIONS[metric]['name']}", - labels={'Value': METRIC_DESCRIPTIONS[metric]['name']} + title=f"Сравнение {METRIC_DESCRIPTIONS[metric]['name']}", + labels={'Значение': METRIC_DESCRIPTIONS[metric]['name']} ) - # Add ideal value line if applicable + # Добавляем линию идеального значения если применимо perfect_value = METRIC_DESCRIPTIONS[metric].get('perfect_value', 0) if METRIC_DESCRIPTIONS[metric].get('better_lower', True): - # For metrics where lower is better + # Для метрик где лучше меньшее значение fig.add_hline(y=perfect_value, line_dash="dash", line_color="red") else: - # For metrics where higher is better + # Для метрик где лучше большее значение fig.add_hline(y=perfect_value, line_dash="dash", line_color="green") st.plotly_chart(fig, use_container_width=True) with metric_tab3: - st.markdown("#### 📋 Detailed Metrics Table") + st.markdown("#### 📋 Детальная таблица метрик") - # Create detailed table with all metrics + # Создаем подробную таблицу со всеми метриками detailed_data = [] for model_name in selected_models: if model_name in all_metrics: for target_name, metrics in all_metrics[model_name].items(): row = { - 'Model': model_name, - 'Target Variable': TARGET_DESCRIPTIONS[target_name]['name'], - 'Sample Count': metrics.get('n_samples', 0) + 'Модель': model_name, + 'Целевая переменная': TARGET_DESCRIPTIONS[target_name]['name'], + 'Количество образцов': metrics.get('n_samples', 0) } - # Add all calculated metrics + # Добавляем все вычисленные метрики for metric_name in ['mae', 'mse', 'rmse', 'r2', 'mape', 'correlation']: if metric_name in metrics: row[METRIC_DESCRIPTIONS[metric_name]['name']] = metrics[metric_name] @@ -1493,7 +1657,7 @@ def display_metrics_dashboard(all_metrics, selected_models): if detailed_data: detailed_df = pd.DataFrame(detailed_data) - # Format numeric columns + # Форматируем числовые колонки format_dict = {} for metric_name in ['mae', 'mse', 'rmse', 'mape']: display_name = METRIC_DESCRIPTIONS[metric_name]['name'] @@ -1507,90 +1671,90 @@ def display_metrics_dashboard(all_metrics, selected_models): st.dataframe(detailed_df.style.format(format_dict), width='stretch') - # Download metrics table + # Скачивание таблицы метрик csv_metrics = detailed_df.to_csv(index=False, encoding='utf-8-sig') st.download_button( - label="📥 Download Metrics Table (CSV)", + label="📥 Скачать таблицу метрик (CSV)", data=csv_metrics, file_name=f"pectin_metrics_comparison_{pd.Timestamp.now().strftime('%Y%m%d_%H%M')}.csv", mime="text/csv" ) except Exception as e: - st.error(f"❌ Error creating metrics dashboard: {e}") + st.error(f"❌ Ошибка создания дашборда метрик: {e}") # Main app layout st.markdown('

🧪 Pectin Production Predictor

', unsafe_allow_html=True) # Sidebar with st.sidebar: - st.markdown("### ⚙️ Model Settings") + st.markdown("### ⚙️ Настройки модели") selected_model = st.selectbox( - "Select model:", + "Выберите модель:", options=list(AVAILABLE_MODELS.keys()), format_func=lambda x: f"{x} - {AVAILABLE_MODELS[x]['description'].split(' - ')[0]}" ) model_info = AVAILABLE_MODELS[selected_model] - st.info(f"**Description:** {model_info['description']}") + st.info(f"**Описание:** {model_info['description']}") # Load model - with st.spinner(f"Loading {selected_model}..."): + with st.spinner(f"Загружаем {selected_model}..."): model_artifacts = load_pectin_model(selected_model) if model_artifacts["status"] == "success": - st.success("✅ Model ready to use") + st.success("✅ Модель готова к использованию") # Display model info metadata = model_artifacts["metadata"] - st.markdown("### 📊 Model Information") - st.write(f"**Features:** {len(metadata.get('feature_columns', []))}") - st.write(f"**Target Variables:** {len(metadata.get('target_columns', []))}") - st.write(f"**Vocabulary Size:** {len(model_artifacts['label_encoder'].classes_)}") + st.markdown("### 📊 Информация о модели") + st.write(f"**Признаки:** {len(metadata.get('feature_columns', []))}") + st.write(f"**Целевые переменные:** {len(metadata.get('target_columns', []))}") + st.write(f"**Размер словаря:** {len(model_artifacts['label_encoder'].classes_)}") elif model_artifacts["status"] == "demo": - st.warning("🎭 Working in demo mode") - st.info("To use real models check:") - st.info("1. Internet access") - st.info("2. Hugging Face repository availability") - st.info("3. Correct REPO_ID in settings") + st.warning("🎭 Работаем в демо-режиме") + st.info("Для использования реальных моделей проверьте:") + st.info("1. Доступ к интернету") + st.info("2. Наличие репозитория на Hugging Face") + st.info("3. Правильность REPO_ID в настройках") else: - st.error("❌ Failed to load model") - st.markdown("### 🔧 Troubleshooting") + st.error("❌ Не удалось загрузить модель") + st.markdown("### 🔧 Решение проблем") st.info(""" - 1. **Check internet connection** - 2. **Ensure repository exists**: https://huggingface.co/arabovs-ai-lab/PectinProductionModels - 3. **Use local models** - place files in models/ folder - 4. **Change REPO_ID** to your repository + 1. **Проверьте интернет-соединение** + 2. **Убедитесь что репозиторий существует**: https://huggingface.co/arabovs-ai-lab/PectinProductionModels + 3. **Используйте локальные модели** - поместите файлы в папку models/ + 4. **Измените REPO_ID** на ваш репозиторий """) - if st.button("🔄 Try Again"): + if st.button("🔄 Попробовать снова"): st.cache_resource.clear() st.rerun() # Main content tabs -tab1, tab2, tab3, tab4 = st.tabs(["🎯 Single Prediction", "📁 Batch Processing", "📊 Model Comparison", "🔄 Multi-Model Processing"]) +tab1, tab2, tab3, tab4 = st.tabs(["🎯 Одиночное предсказание", "📁 Пакетная обработка", "📊 Сравнение моделей", "🔄 Мультимодельная обработка"]) with tab1: - st.markdown('
🎯 Single Prediction
', unsafe_allow_html=True) + st.markdown('
🎯 Одиночное предсказание
', unsafe_allow_html=True) col1, col2 = st.columns([1, 1]) with col1: - st.markdown("### 📝 Process Parameters") + st.markdown("### 📝 Параметры процесса") with st.form("single_prediction_form"): - sample = st.selectbox("Raw Material Type:", options=SAMPLE_TYPES, help="Select type of raw material") - time_min = st.number_input("Extraction Time (min):", min_value=0.0, max_value=300.0, value=5.0, step=1.0) - temperature_c = st.number_input("Temperature (°C):", min_value=0.0, max_value=200.0, value=120.0, step=1.0) - pressure_atm = st.number_input("Pressure (atm):", min_value=0.0, max_value=10.0, value=1.0, step=0.1) + sample = st.selectbox("Тип сырья:", options=SAMPLE_TYPES, help="Выберите тип исходного сырья") + time_min = st.number_input("Время экстракции (мин):", min_value=0.0, max_value=300.0, value=5.0, step=1.0) + temperature_c = st.number_input("Температура (°C):", min_value=0.0, max_value=200.0, value=120.0, step=1.0) + pressure_atm = st.number_input("Давление (атм):", min_value=0.0, max_value=10.0, value=1.0, step=0.1) ph = st.number_input("pH:", min_value=0.0, max_value=14.0, value=2.5, step=0.1) - submitted = st.form_submit_button("🎯 Make Prediction") + submitted = st.form_submit_button("🎯 Выполнить предсказание") with col2: - st.markdown("### 📊 Prediction Results") + st.markdown("### 📊 Результаты предсказания") if submitted: input_data = { @@ -1601,22 +1765,22 @@ with tab1: 'ph': ph } - with st.spinner("🔮 Making prediction..."): + with st.spinner("🔮 Выполняем предсказание..."): prediction = predict_single(input_data, model_artifacts) if prediction: st.markdown('
', unsafe_allow_html=True) if model_artifacts.get("status") == "demo": - st.warning("🎭 Demo mode: using test data") + st.warning("🎭 Демо-режим: используются тестовые данные") else: - st.success("✅ Prediction successful!") + st.success("✅ Предсказание успешно выполнено!") # Display predictions as metrics target_descriptions = { - 'pectin_yield': 'Pectin Yield (%)', - 'galacturonic_acid': 'GA (%)', - 'molecular_weight': 'Mol. Weight (Da)', - 'esterification_degree': 'ED (%)' + 'pectin_yield': 'Выход пектина (%)', + 'galacturonic_acid': 'ГК (%)', + 'molecular_weight': 'Мол. масса (Da)', + 'esterification_degree': 'СЭ (%)' } cols = st.columns(2) @@ -1631,118 +1795,118 @@ with tab1: st.markdown('
', unsafe_allow_html=True) # Show input parameters - with st.expander("📋 Input Parameters"): + with st.expander("📋 Параметры ввода"): st.json(input_data) with tab2: - st.markdown('
📁 Batch File Processing
', unsafe_allow_html=True) + st.markdown('
📁 Пакетная обработка файлов
', unsafe_allow_html=True) st.markdown("""
-

🎯 Supported Formats

+

🎯 Поддерживаемые форматы

-

📊 Required Fields

+

📊 Обязательные поля

-

🔧 Processing Features

+

🔧 Особенности обработки

""", unsafe_allow_html=True) uploaded_file = st.file_uploader( - "Upload data file", + "Загрузите файл с данными", type=['xlsx', 'csv', 'txt'], - help="Excel, CSV and text files with experimental data supported" + help="Поддерживаются Excel, CSV и текстовые файлы с данными экспериментов" ) if uploaded_file is not None: - st.markdown("### ⚙️ Processing Settings") + st.markdown("### ⚙️ Настройки обработки") col_set1, col_set2 = st.columns(2) with col_set1: skip_rows = st.number_input( - "Skip rows at file beginning:", + "Пропустить строк в начале файла:", min_value=0, max_value=10, value=0, - help="Use if file has headers or empty rows at beginning" + help="Используйте если в файле есть заголовки или пустые строки в начале" ) with col_set2: - file_preview = st.checkbox("Show file preview", value=True) + file_preview = st.checkbox("Показать предпросмотр файла", value=True) - # Button for file structure validation - if st.button("🔍 Validate File Structure", type="secondary"): + # Кнопка для проверки структуры файла + if st.button("🔍 Проверить структуру файла", type="secondary"): is_valid, preview_df = validate_file_structure(uploaded_file, skip_rows) if is_valid: - st.success("✅ File ready for processing!") + st.success("✅ Файл готов к обработке!") if file_preview and preview_df is not None: - st.markdown("### 👀 Data Preview") + st.markdown("### 👀 Предпросмотр данных") st.dataframe(preview_df.head(10), width='stretch') - if st.button("🚀 Process File", type="primary"): - with st.spinner("🔄 Processing file..."): + if st.button("🚀 Обработать файл", type="primary"): + with st.spinner("🔄 Обрабатываем файл..."): result_df = process_batch_file_single_model(uploaded_file, model_artifacts, selected_model, skip_rows) if result_df is not None: - st.success(f"✅ Successfully processed {len(result_df)} records") + st.success(f"✅ Успешно обработано {len(result_df)} записей") if model_artifacts.get("status") == "demo": - st.warning("🎭 Demo mode: using test data") + st.warning("🎭 Демо-режим: используются тестовые данные") - # Show results - st.markdown("### 📈 Batch Processing Results") + # Показываем результаты + st.markdown("### 📈 Результаты пакетной обработки") - # Create nice DataFrame for display + # Создаем красивый DataFrame для отображения display_columns = ['sample', 'time_min', 'temperature_c', 'pressure_atm', 'ph'] prediction_columns = [col for col in result_df.columns if col.startswith(f'{selected_model}_')] display_columns.extend(prediction_columns) - # Select only existing columns + # Выбираем только существующие колонки existing_columns = [col for col in display_columns if col in result_df.columns] display_df = result_df[existing_columns] st.dataframe(display_df, width='stretch') - # Download results + # Скачивание результатов csv = result_df.to_csv(index=False, encoding='utf-8-sig') st.download_button( - label="📥 Download Full Results (CSV)", + label="📥 Скачать полные результаты (CSV)", data=csv, file_name=f"pectin_predictions_{selected_model}_{pd.Timestamp.now().strftime('%Y%m%d_%H%M')}.csv", mime="text/csv", - help="Download all data with predictions in CSV format" + help="Скачать все данные с предсказаниями в CSV формате" ) - # Show statistics - st.markdown("### 📊 Prediction Statistics") + # Показываем статистику + st.markdown("### 📊 Статистика предсказаний") if prediction_columns: stats_data = [] for col in prediction_columns: pred_values = result_df[col].dropna() if len(pred_values) > 0: stats_data.append({ - 'Parameter': col.replace(f'{selected_model}_', ''), - 'Mean': pred_values.mean(), - 'Standard Deviation': pred_values.std(), - 'Minimum': pred_values.min(), - 'Maximum': pred_values.max(), - 'Successful Predictions': len(pred_values) + 'Параметр': col.replace(f'{selected_model}_', ''), + 'Среднее': pred_values.mean(), + 'Стандартное отклонение': pred_values.std(), + 'Минимум': pred_values.min(), + 'Максимум': pred_values.max(), + 'Успешных предсказаний': len(pred_values) }) if stats_data: @@ -1750,31 +1914,31 @@ with tab2: st.dataframe(stats_df, width='stretch') with tab3: - st.markdown('
📊 Model Comparison
', unsafe_allow_html=True) + st.markdown('
📊 Сравнение моделей
', unsafe_allow_html=True) - st.info("Compare predictions of different models on the same data") + st.info("Сравните предсказания разных моделей на одних и тех же данных") col1, col2 = st.columns([1, 1]) with col1: - st.markdown("### 📝 Test Parameters") - compare_sample = st.selectbox("Raw Material Type for Comparison:", options=SAMPLE_TYPES, key="compare_sample") - compare_time = st.slider("Extraction Time (min):", 0.0, 300.0, 5.0, key="compare_time") - compare_temp = st.slider("Temperature (°C):", 0.0, 200.0, 120.0, key="compare_temp") - compare_pressure = st.slider("Pressure (atm):", 0.0, 10.0, 1.0, key="compare_pressure") + st.markdown("### 📝 Тестовые параметры") + compare_sample = st.selectbox("Тип сырья для сравнения:", options=SAMPLE_TYPES, key="compare_sample") + compare_time = st.slider("Время экстракции (мин):", 0.0, 300.0, 5.0, key="compare_time") + compare_temp = st.slider("Температура (°C):", 0.0, 200.0, 120.0, key="compare_temp") + compare_pressure = st.slider("Давление (атм):", 0.0, 10.0, 1.0, key="compare_pressure") compare_ph = st.slider("pH:", 0.0, 14.0, 2.5, key="compare_ph") with col2: - st.markdown("### 🎯 Model Selection") + st.markdown("### 🎯 Выбор моделей") models_to_compare = st.multiselect( - "Select models for comparison:", + "Выберите моделей для сравнения:", options=list(AVAILABLE_MODELS.keys()), default=["best_model", "gradient_boosting", "random_forest"] ) - if st.button("🔍 Compare Models", type="primary"): + if st.button("🔍 Сравнить модели", type="primary"): if not models_to_compare: - st.warning("⚠️ Select at least one model for comparison") + st.warning("⚠️ Выберите хотя бы одну модель для сравнения") else: input_data = { 'sample': compare_sample, @@ -1786,9 +1950,9 @@ with tab3: comparison_results = [] - with st.spinner("🔍 Comparing models..."): + with st.spinner("🔍 Выполняем сравнение моделей..."): for model_name in models_to_compare: - with st.spinner(f"Loading {model_name}..."): + with st.spinner(f"Загружаем {model_name}..."): model_artifacts_compare = load_pectin_model(model_name) if model_artifacts_compare["status"] in ["success", "demo"]: @@ -1800,19 +1964,19 @@ with tab3: }) if comparison_results: - st.success("✅ Comparison completed!") + st.success("✅ Сравнение завершено!") if any("demo" in str(r) for r in comparison_results): - st.warning("🎭 Some models working in demo mode") + st.warning("🎭 Некоторые модели работают в демо-режиме") # Create comparison dataframe compare_df = pd.DataFrame(comparison_results) # Display comparison table - st.markdown("### 📋 Prediction Comparison") + st.markdown("### 📋 Сравнение предсказаний") st.dataframe(compare_df.set_index('model'), width='stretch') # Visualization - st.markdown("### 📊 Visual Comparison") + st.markdown("### 📊 Визуальное сравнение") # Melt dataframe for plotting plot_df = compare_df.melt(id_vars=['model'], var_name='parameter', value_name='value') @@ -1824,176 +1988,176 @@ with tab3: y='value', color='parameter', barmode='group', - title="Prediction Comparison by Models", - labels={'value': 'Value', 'model': 'Model', 'parameter': 'Parameter'} + title="Сравнение предсказаний по моделям", + labels={'value': 'Значение', 'model': 'Модель', 'parameter': 'Параметр'} ) st.plotly_chart(fig, use_container_width=True) # Show input parameters - with st.expander("📋 Comparison Parameters"): + with st.expander("📋 Параметры сравнения"): st.json(input_data) with tab4: - st.markdown('
🔄 Multi-Model File Processing
', unsafe_allow_html=True) + st.markdown('
🔄 Мультимодельная обработка файлов
', unsafe_allow_html=True) st.markdown("""
-

🎯 Multi-Model Processing Capabilities

+

🎯 Возможности мультимодельной обработки

-

📊 Output Data

+

📊 Выходные данные

""", unsafe_allow_html=True) - # Model selection for multi-model processing - st.markdown("### 🎯 Model Selection for Processing") + # Выбор моделей для мультимодельной обработки + st.markdown("### 🎯 Выбор моделей для обработки") multi_models = st.multiselect( - "Select models to apply:", + "Выберите модели для применения:", options=list(AVAILABLE_MODELS.keys()), default=["best_model", "gradient_boosting", "random_forest"], - help="Select one or more models for file processing" + help="Выберите одну или несколько моделей для обработки файла" ) uploaded_file_multi = st.file_uploader( - "Upload data file for multi-model processing", + "Загрузите файл с данными для мультимодельной обработки", type=['xlsx', 'csv', 'txt'], key="multi_uploader" ) if uploaded_file_multi is not None and multi_models: - st.markdown("### ⚙️ Processing Settings") + st.markdown("### ⚙️ Настройки обработки") col_set1, col_set2 = st.columns(2) with col_set1: skip_rows_multi = st.number_input( - "Skip rows at file beginning:", + "Пропустить строк в начале файла:", min_value=0, max_value=10, value=0, key="multi_skip", - help="Use if file has headers or empty rows at beginning" + help="Используйте если в файле есть заголовки или пустые строки в начале" ) with col_set2: - enable_viz = st.checkbox("Enable advanced visualization", value=True) - enable_metrics = st.checkbox("Enable metric calculation", value=True) + enable_viz = st.checkbox("Включить расширенную визуализацию", value=True) + enable_metrics = st.checkbox("Включить расчет метрик", value=True) - if st.button("🚀 Run Multi-Model Processing", type="primary"): + if st.button("🚀 Запустить мультимодельную обработку", type="primary"): if not multi_models: - st.warning("⚠️ Select at least one model for processing") + st.warning("⚠️ Выберите хотя бы одну модель для обработки") else: - with st.spinner("🔄 Processing file with multiple models..."): + with st.spinner("🔄 Обрабатываем файл несколькими моделями..."): result_df, all_predictions, model_results, all_metrics = process_batch_file_multiple_models( uploaded_file_multi, multi_models, skip_rows_multi ) if result_df is not None: - st.success(f"✅ Multi-model processing completed! Processed {len(result_df)} records") + st.success(f"✅ Мультимодельная обработка завершена! Обработано {len(result_df)} записей") - # Show model summary - st.markdown("### 📊 Model Summary") + # Показываем сводку по моделям + st.markdown("### 📊 Сводка по моделям") summary_data = [] for model_name in multi_models: if model_name in model_results: summary_data.append({ - 'Model': model_name, - 'Successful Predictions': model_results[model_name]['successful'], - 'Total Records': model_results[model_name]['total'], - 'Success Rate': f"{(model_results[model_name]['successful'] / model_results[model_name]['total'] * 100):.1f}%", - 'Mode': 'Demo' if model_results[model_name]['artifacts'].get('status') == 'demo' else 'Real' + 'Модель': model_name, + 'Успешных предсказаний': model_results[model_name]['successful'], + 'Всего записей': model_results[model_name]['total'], + 'Процент успеха': f"{(model_results[model_name]['successful'] / model_results[model_name]['total'] * 100):.1f}%", + 'Режим': 'Демо' if model_results[model_name]['artifacts'].get('status') == 'demo' else 'Реальный' }) if summary_data: summary_df = pd.DataFrame(summary_data) st.dataframe(summary_df, width='stretch') - # Show results - st.markdown("### 📈 Processing Results") + # Показываем результаты + st.markdown("### 📈 Результаты обработки") - # Create simplified DataFrame for display + # Создаем упрощенный DataFrame для отображения display_columns = ['sample', 'time_min', 'temperature_c', 'pressure_atm', 'ph'] - # Add one prediction from each model for compactness + # Добавляем по одному предсказанию от каждой модели для компактности for model_name in multi_models: if f'{model_name}_pectin_yield' in result_df.columns: display_columns.append(f'{model_name}_pectin_yield') - display_df = result_df[display_columns].head(10) # Show only first 10 rows + display_df = result_df[display_columns].head(10) # Показываем только первые 10 строк st.dataframe(display_df, width='stretch') - # Download full results + # Скачивание полных результатов csv = result_df.to_csv(index=False, encoding='utf-8-sig') st.download_button( - label="📥 Download Full Results (CSV)", + label="📥 Скачать полные результаты (CSV)", data=csv, file_name=f"pectin_predictions_multimodel_{pd.Timestamp.now().strftime('%Y%m%d_%H%M')}.csv", mime="text/csv", - help="Download all data with predictions from all models in CSV format" + help="Скачать все данные с предсказаниями всех моделей в CSV формате" ) - # Display quality metrics if enabled and ground truth exists + # Отображаем метрики качества если включено и есть ground truth if enable_metrics: display_metrics_dashboard(all_metrics, multi_models) - # Model comparison visualization + # Визуализация сравнения моделей if enable_viz and len(multi_models) > 1: target_columns = ['pectin_yield', 'galacturonic_acid', 'molecular_weight', 'esterification_degree'] create_comparison_visualizations(result_df, multi_models, target_columns, all_metrics) - # Detailed divergence analysis + # Детальный анализ расхождений if len(multi_models) > 1: - st.markdown("### 🔍 Model Divergence Analysis") + st.markdown("### 🔍 Анализ расхождений между моделями") - # Calculate standard deviations for each record across all models + # Вычисляем стандартные отклонения для каждой записи по всем моделям divergence_data = [] for target in target_columns: model_cols = [f'{model}_{target}' for model in multi_models if f'{model}_{target}' in result_df.columns] if len(model_cols) > 1: - # Calculate standard deviation by rows + # Вычисляем стандартное отклонение по строкам result_df[f'std_{target}'] = result_df[model_cols].std(axis=1) - # Find records with largest divergences + # Находим записи с наибольшими расхождениями max_std_idx = result_df[f'std_{target}'].idxmax() max_std = result_df.loc[max_std_idx, f'std_{target}'] if not pd.isna(max_std) and max_std > 0: divergence_data.append({ - 'Target Variable': TARGET_DESCRIPTIONS[target]['name'], - 'Maximum std': max_std, - 'Record': max_std_idx + 1, - 'Parameters': f"{result_df.loc[max_std_idx, 'sample']}, t={result_df.loc[max_std_idx, 'time_min']}min" + 'Целевая переменная': TARGET_DESCRIPTIONS[target]['name'], + 'Максимальное std': max_std, + 'Запись': max_std_idx + 1, + 'Параметры': f"{result_df.loc[max_std_idx, 'sample']}, t={result_df.loc[max_std_idx, 'time_min']}мин" }) if divergence_data: - st.info("📊 Records with largest model divergences:") + st.info("📊 Записи с наибольшими расхождениями между моделями:") divergence_df = pd.DataFrame(divergence_data) st.dataframe(divergence_df, width='stretch') else: - st.info("ℹ️ No significant model divergences detected") + st.info("ℹ️ Значительных расхождений между моделями не обнаружено") # Footer st.markdown("---") st.markdown( """
-

🧪 Pectin Production Predictor | Machine learning models for pectin production parameter prediction

-

📚 Model repository: Hugging Face Hub

+

🧪 Pectin Production Predictor | Модели машинного обучения для прогнозирования параметров производства пектина

+

📚 Репозиторий моделей: Hugging Face Hub

""", unsafe_allow_html=True