diff --git "a/app.py" "b/app.py" --- "a/app.py" +++ "b/app.py" @@ -122,66 +122,65 @@ st.markdown( REPO_ID = "arabovs-ai-lab/PectinProductionModels" # Available models configuration -# Available models configuration - ОБНОВЛЕННАЯ ВЕРСИЯ AVAILABLE_MODELS = { "best_model": { "subfolder": "best_model", - "description": "🎯 Best overall model (Gradient Boosting) - оптимальная производительность", + "description": "🎯 Best overall model (Gradient Boosting) - optimal performance", "color": "#FF6B6B" }, "gradient_boosting": { "subfolder": "gradient_boosting", - "description": "📈 Gradient Boosting - лучшая для многозадачной регрессии", + "description": "📈 Gradient Boosting - best for multi-task regression", "color": "#4ECDC4" }, "random_forest": { "subfolder": "random_forest", - "description": "🌲 Random Forest - надежная и стабильная", + "description": "🌲 Random Forest - reliable and stable", "color": "#45B7D1" }, "xgboost": { "subfolder": "xgboost", - "description": "⚡ XGBoost - высокая производительность на табличных данных", + "description": "⚡ XGBoost - high performance on tabular data", "color": "#96CEB4" }, "linear_regression": { "subfolder": "linear_regression", - "description": "📊 Linear Regression - базовая линейная модель", + "description": "📊 Linear Regression - basic linear model", "color": "#FECA57" }, "extra_trees": { "subfolder": "extra_trees", - "description": "🌳 Extra Trees - экстремальные случайные леса", + "description": "🌳 Extra Trees - extreme random forests", "color": "#FF9FF3" }, "k_neighbors": { "subfolder": "k-neighbors", - "description": "📏 K-Neighbors - метод ближайших соседей", + "description": "📏 K-Neighbors - nearest neighbors method", "color": "#54A0FF" }, "lasso_regression": { "subfolder": "lasso_regression", - "description": "🎯 Lasso Regression - L1-регуляризация", + "description": "🎯 Lasso Regression - L1 regularization", "color": "#5F27CD" }, "multilayer_perceptron": { "subfolder": "multilayer_perceptron", - "description": "🧠 Neural Network MLP - многослойный перцептрон", + "description": "🧠 Neural Network MLP - multilayer perceptron", "color": "#00D2D3" }, "ridge_regression": { "subfolder": "ridge_regression", - "description": "🏔️ Ridge Regression - L2-регуляризация", + "description": "🏔️ Ridge Regression - L2 regularization", "color": "#FF9F43" }, "support_vector_regression": { "subfolder": "support_vector_regression", - "description": "🔗 Support Vector Regression - метод опорных векторов", + "description": "🔗 Support Vector Regression - support vector method", "color": "#A3CB38" } } # Sample types available -SAMPLE_TYPES = ["ЯП(М)", "Абр.", "КрП", "Айв.", "Ткв", "Рв.", "ЯП(Ф)"] +SAMPLE_TYPES = ["AP(M)", "Abr.", "KrP", "Aiv.", "Tkv", "Rv.", "AP(F)"] # Column mapping for Russian to English COLUMN_MAPPING = { @@ -200,26 +199,26 @@ COLUMN_MAPPING = { # Target descriptions TARGET_DESCRIPTIONS = { 'pectin_yield': { - 'name': 'Выход пектина', - 'description': 'Процент выхода пектина из сырья', + 'name': 'Pectin yield', + 'description': 'Percentage of pectin yield from raw material', 'unit': '%', 'range': (0, 100) }, 'galacturonic_acid': { - 'name': 'Галактуроновая кислота', - 'description': 'Содержание галактуроновой кислоты в пектине', + 'name': 'Galacturonic acid', + 'description': 'Galacturonic acid content in pectin', 'unit': '%', 'range': (0, 100) }, 'molecular_weight': { - 'name': 'Молекулярная масса', - 'description': 'Молекулярная масса пектина', + 'name': 'Molecular weight', + 'description': 'Pectin molecular weight', 'unit': 'Da', 'range': (0, 500000) }, 'esterification_degree': { - 'name': 'Степень этерификации', - 'description': 'Степень этерификации пектина', + 'name': 'Esterification degree', + 'description': 'Pectin esterification degree', 'unit': '%', 'range': (0, 100) } @@ -228,52 +227,52 @@ TARGET_DESCRIPTIONS = { # Metric descriptions METRIC_DESCRIPTIONS = { 'mae': { - 'name': 'MAE (Средняя абсолютная ошибка)', - 'description': 'Среднее абсолютное значение разницы между предсказанием и истинным значением', + 'name': 'MAE (Mean Absolute Error)', + 'description': 'Mean absolute difference between prediction and true value', 'better_lower': True, 'perfect_value': 0 }, 'mse': { - 'name': 'MSE (Средняя квадратичная ошибка)', - 'description': 'Среднее квадратов разниц между предсказанием и истинным значением', + 'name': 'MSE (Mean Squared Error)', + 'description': 'Mean of squared differences between prediction and true value', 'better_lower': True, 'perfect_value': 0 }, 'rmse': { - 'name': 'RMSE (Среднеквадратичная ошибка)', - 'description': 'Корень из средней квадратичной ошибки, имеет ту же размерность что и целевая переменная', + 'name': 'RMSE (Root Mean Squared Error)', + 'description': 'Square root of mean squared error, same dimension as target variable', 'better_lower': True, 'perfect_value': 0 }, 'r2': { - 'name': 'R² (Коэффициент детерминации)', - 'description': 'Доля дисперсии зависимой переменной, объясняемая моделью', + 'name': 'R² (Coefficient of Determination)', + 'description': 'Proportion of variance in dependent variable explained by the model', 'better_lower': False, 'perfect_value': 1 }, 'mape': { - 'name': 'MAPE (Средняя абсолютная процентная ошибка)', - 'description': 'Средняя абсолютная ошибка в процентах от истинных значений', + 'name': 'MAPE (Mean Absolute Percentage Error)', + 'description': 'Mean absolute error as percentage of true values', 'better_lower': True, 'perfect_value': 0 }, 'correlation': { - 'name': 'Корреляция Пирсона', - 'description': 'Мера линейной зависимости между предсказаниями и истинными значениями', + 'name': 'Pearson Correlation', + 'description': 'Measure of linear dependence between predictions and true values', 'better_lower': False, 'perfect_value': 1 } } # Helper function to download and cache model files -@st.cache_resource(show_spinner="🔄 Загрузка модели и вспомогательных файлов...") +@st.cache_resource(show_spinner="🔄 Loading model and support files...") 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"🔄 Попытка загрузки модели {model_name} из репозитория {REPO_ID}") + st.info(f"🔄 Attempting to load model {model_name} from repository {REPO_ID}") # Download model file try: @@ -282,15 +281,15 @@ def load_pectin_model(model_name: str): filename=f"{subfolder}/model.pkl", repo_type="model" ) - st.success(f"✅ Модель {model_name} загружена успешно") + st.success(f"✅ Model {model_name} loaded successfully") except Exception as e: - st.error(f"❌ Ошибка загрузки модели {model_name}: {str(e)}") + st.error(f"❌ Error loading model {model_name}: {str(e)}") st.markdown('
', unsafe_allow_html=True) - st.error("**Возможные причины ошибки 403:**") - st.error("1. Репозиторий не существует или приватный") - st.error("2. Нет доступа к интернету") - st.error("3. Проблемы с авторизацией Hugging Face") - st.error("4. Файлы модели отсутствуют в репозитории") + 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.markdown('
', unsafe_allow_html=True) return {"status": "error", "error": str(e)} @@ -301,9 +300,9 @@ def load_pectin_model(model_name: str): filename="scaler.pkl", repo_type="model" ) - st.success("✅ Scaler загружен успешно") + st.success("✅ Scaler loaded successfully") except Exception as e: - st.error(f"❌ Ошибка загрузки scaler: {str(e)}") + st.error(f"❌ Error loading scaler: {str(e)}") return {"status": "error", "error": f"Scaler error: {str(e)}"} try: @@ -312,9 +311,9 @@ def load_pectin_model(model_name: str): filename="label_encoder.pkl", repo_type="model" ) - st.success("✅ Label encoder загружен успешно") + st.success("✅ Label encoder loaded successfully") except Exception as e: - st.error(f"❌ Ошибка загрузки label encoder: {str(e)}") + st.error(f"❌ Error loading label encoder: {str(e)}") return {"status": "error", "error": f"Encoder error: {str(e)}"} try: @@ -323,9 +322,9 @@ def load_pectin_model(model_name: str): filename="model_metadata.json", repo_type="model" ) - st.success("✅ Метаданные загружены успешно") + st.success("✅ Metadata loaded successfully") except Exception as e: - st.error(f"❌ Ошибка загрузки метаданных: {str(e)}") + st.error(f"❌ Error loading metadata: {str(e)}") return {"status": "error", "error": f"Metadata error: {str(e)}"} # Load all artifacts @@ -339,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"✅ Все артефакты модели {model_name} загружены и готовы к использованию") + st.success(f"✅ All model {model_name} artifacts loaded and ready to use") return { "model": model, @@ -350,13 +349,13 @@ def load_pectin_model(model_name: str): } except Exception as e: - st.error(f"❌ Ошибка загрузки артефактов модели: {str(e)}") + st.error(f"❌ Error loading model artifacts: {str(e)}") return {"status": "error", "error": f"Loading error: {str(e)}"} except Exception as e: - st.error(f"❌ Критическая ошибка загрузки модели {model_name}: {str(e)}") + st.error(f"❌ Critical error loading model {model_name}: {str(e)}") st.markdown('
', unsafe_allow_html=True) - st.error("**Детали ошибки:**") + st.error("**Error details:**") st.code(traceback.format_exc()) st.markdown('
', unsafe_allow_html=True) return {"status": "error", "error": str(e)} @@ -364,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("🎭 Используется демо-режим с тестовыми данными") + st.warning("🎭 Using demo mode with test data") class DemoModel: def predict(self, X): @@ -418,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"⚠️ Тип сырья '{sample_value}' не найден в энкодере. Используется значение по умолчанию.") + st.warning(f"⚠️ Raw material type '{sample_value}' not found in encoder. Using default value.") default_class = label_encoder.classes_[0] df['sample_encoded'] = label_encoder.transform([default_class])[0] @@ -443,7 +442,7 @@ def preprocess_single_input(input_data: Dict, model_artifacts: Dict) -> Optional return X_scaled except Exception as e: - st.error(f"❌ Ошибка предобработки данных: {e}") + st.error(f"❌ Data preprocessing error: {e}") st.code(traceback.format_exc()) return None @@ -470,28 +469,28 @@ def predict_single(input_data: Dict, model_artifacts: Dict) -> Optional[Dict]: return results except Exception as e: - st.error(f"❌ Ошибка предсказания: {e}") + st.error(f"❌ Prediction error: {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) # Сбрасываем позицию + uploaded_file.seek(0) # Reset position return encoding, content except UnicodeDecodeError: continue return 'utf-8', uploaded_file.getvalue().decode('utf-8', errors='ignore') except Exception as e: - st.error(f"❌ Ошибка определения кодировки: {e}") + st.error(f"❌ Encoding detection error: {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 @@ -499,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: - # Если не получается, возвращаем 0 + # If conversion fails, return 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"📂 Чтение файла: {uploaded_file.name}, расширение: {file_extension}") + st.info(f"📂 Reading file: {uploaded_file.name}, extension: {file_extension}") if file_extension.endswith('.xlsx'): - # Для Excel файлов + # For Excel files 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: # Если есть достаточно колонок - st.info(f"✅ Найдена структура данных с пропуском {skip} строк") + if len(df.columns) > 3: # If enough columns + st.info(f"✅ Found data structure with {skip} rows skipped") return df except Exception as e: - st.warning(f"⚠️ Не удалось прочитать с пропуском {skip} строк: {e}") + st.warning(f"⚠️ Failed to read with {skip} rows skipped: {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'): - # Для CSV/TXT файлов + # For CSV/TXT files encoding, content = detect_file_encoding(uploaded_file) - st.info(f"📖 Определена кодировка: {encoding}") + st.info(f"📖 Detected encoding: {encoding}") - # Пробуем разные разделители + # Try different separators separators = ['\t', ',', ';', '|'] for sep in separators: @@ -548,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: # Если есть достаточно колонок - st.info(f"✅ Найден разделитель: '{sep}' с пропуском {skiprows or 0} строк") + if len(df.columns) > 3: # If enough columns + st.info(f"✅ Found separator: '{sep}' with {skiprows or 0} rows skipped") return df except Exception as e: - st.warning(f"⚠️ Не удалось прочитать с разделителем '{sep}': {e}") + st.warning(f"⚠️ Failed to read with separator '{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"❌ Не удалось прочитать файл: {e}") + st.error(f"❌ Failed to read file: {e}") return None else: - st.error("❌ Неподдерживаемый формат файла") + st.error("❌ Unsupported file format") return None except Exception as e: - st.error(f"❌ Ошибка чтения файла: {e}") + st.error(f"❌ File reading error: {e}") st.code(traceback.format_exc()) return None def validate_file_structure(uploaded_file, skiprows=None): - """Проверяет структуру файла перед обработкой""" + """Validates file structure before processing""" try: - st.info("🔍 Начинаем проверку структуры файла...") + st.info("🔍 Starting file structure validation...") df = read_flexible_pectin_file(uploaded_file, skiprows) if df is None or df.empty: - st.error("❌ Файл пустой или не содержит данных") + st.error("❌ File is empty or contains no data") return False, None - st.success(f"✅ Файл успешно прочитан! Размер: {df.shape}") - st.info("📋 Структура файла (первые 5 строк):") + st.success(f"✅ File successfully read! Size: {df.shape}") + st.info("📋 File structure (first 5 rows):") st.dataframe(df.head(), width='stretch') - st.info(f"📊 Колонки файла: {list(df.columns)}") + st.info(f"📊 File columns: {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'] @@ -594,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"❌ Отсутствуют обязательные колонки. Нужны либо русские: {required_russian}, либо английские: {required_english}") + st.error(f"❌ Missing required columns. Need either Russian: {required_russian}, or English: {required_english}") return False, df - st.success("✅ Все необходимые колонки присутствуют") + st.success("✅ All required columns present") return True, df except Exception as e: - st.error(f"❌ Не удалось прочитать файл: {e}") + st.error(f"❌ Failed to read file: {e}") st.code(traceback.format_exc()) return False, None def calculate_regression_metrics(y_true, y_pred, target_name): - """Вычисляет метрики регрессии""" + """Calculates regression metrics""" try: - # Удаляем NaN значения + # Remove NaN values mask = ~(np.isnan(y_true) | np.isnan(y_pred)) y_true_clean = y_true[mask] y_pred_clean = y_pred[mask] @@ -618,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']) @@ -635,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 @@ -644,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) @@ -656,11 +655,11 @@ def calculate_regression_metrics(y_true, y_pred, target_name): return metrics except Exception as e: - st.error(f"❌ Ошибка вычисления метрик для {target_name}: {e}") + st.error(f"❌ Error calculating metrics for {target_name}: {e}") return None def get_metric_quality(metric_name, value, target_name): - """Определяет качество метрики""" + """Determines metric quality""" if np.isnan(value): return "unknown" @@ -690,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 @@ -704,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, {}) @@ -734,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"🔄 Начинаем обработку файла с моделью {model_name}...") - # Читаем файл + st.info(f"🔄 Starting file processing with model {model_name}...") + # Read file df = read_flexible_pectin_file(uploaded_file, skiprows) if df is None or df.empty: - st.error("❌ Файл пустой или не содержит данных") + st.error("❌ File is empty or contains no data") return None - st.success(f"✅ Файл загружен: {len(df)} записей") + st.success(f"✅ File loaded: {len(df)} records") - # Применяем маппинг русских названий на английские + # Apply Russian to English column mapping columns_renamed = [] for rus_name, eng_name in COLUMN_MAPPING.items(): if rus_name in df.columns: @@ -754,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"✅ Переименованы колонки: {columns_renamed}") + st.info(f"✅ Renamed columns: {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"✅ Преобразованы числовые данные в колонке: {col}") + st.info(f"✅ Converted numeric data in column: {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_columns}") - st.info("📋 Доступные колонки в файле:") + st.error(f"❌ Missing required columns: {missing_columns}") + st.info("📋 Available columns in file:") st.write(list(df.columns)) return None - # Обрабатываем данные + # Process data predictions = [] processed_data = [] @@ -786,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"Обработка записи {idx+1} из {len(df)}...") + status_text.text(f"Processing record {idx+1} of {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': # Сохраняем Т:Ж если есть + if col != 'solid_liquid_ratio': # Keep solid_liquid_ratio if present 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"⚠️ Строка {idx+1}: пропущено значение в колонке '{key}'") + st.warning(f"⚠️ Row {idx+1}: missing value in column '{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: @@ -817,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"⚠️ Строка {idx+1}: отсутствуют поля {missing}") + st.warning(f"⚠️ Row {idx+1}: missing fields {missing}") predictions.append({}) processed_data.append(input_data) except Exception as e: - st.error(f"❌ Ошибка обработки строки {idx+1}: {e}") + st.error(f"❌ Error processing row {idx+1}: {e}") predictions.append({}) processed_data.append(row.to_dict() if 'row' in locals() else {}) - status_text.text("✅ Обработка завершена!") + status_text.text("✅ Processing completed!") progress_bar.empty() - st.info(f"📊 Успешно обработано: {successful_predictions} из {len(df)} записей") + st.info(f"📊 Successfully processed: {successful_predictions} of {len(df)} records") - # Создаем DataFrame с результатами + # Create results 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: @@ -848,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"❌ Критическая ошибка обработки файла: {e}") + st.error(f"❌ Critical file processing error: {e}") st.markdown('
', unsafe_allow_html=True) - st.error("**Детали ошибки:**") + st.error("**Error details:**") 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("🔄 Начинаем обработку файла с несколькими моделями...") + st.info("🔄 Starting file processing with multiple models...") - # Сначала читаем и предобрабатываем файл один раз + # First read and preprocess file once df = read_flexible_pectin_file(uploaded_file, skiprows) if df is None or df.empty: - st.error("❌ Файл пустой или не содержит данных") + st.error("❌ File is empty or contains no data") return None, {}, {}, {} - st.success(f"✅ Файл загружен: {len(df)} записей") + st.success(f"✅ File loaded: {len(df)} records") - # Применяем маппинг русских названий на английские + # 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'] @@ -882,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_columns}") + st.error(f"❌ Missing required columns: {missing_columns}") return None, {}, {}, {} - # Создаем базовый DataFrame с исходными данными + # Create base DataFrame with source data 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: @@ -905,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 = {} @@ -915,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"🔄 Обработка моделью {model_name} ({model_idx+1}/{len(selected_models)})...") + status_text.text(f"🔄 Processing with model {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"❌ Не удалось загрузить модель {model_name}") + st.error(f"❌ Failed to load model {model_name}") continue - # Получаем предсказания для всех строк + # Get predictions for all rows model_predictions = [] successful_predictions = 0 @@ -941,10 +940,10 @@ def process_batch_file_multiple_models(uploaded_file, selected_models, skiprows= model_predictions.append({}) except Exception as e: - st.error(f"❌ Ошибка предсказания моделью {model_name} для строки {idx+1}: {e}") + st.error(f"❌ Prediction error with model {model_name} for row {idx+1}: {e}") model_predictions.append({}) - # Сохраняем предсказания + # Save predictions all_predictions[model_name] = model_predictions model_results[model_name] = { 'successful': successful_predictions, @@ -952,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] - # Вычисляем метрики если есть ground truth + # Calculate metrics if ground truth exists model_metrics = {} if has_ground_truth: for target in target_columns: @@ -974,44 +973,44 @@ def process_batch_file_multiple_models(uploaded_file, selected_models, skiprows= all_metrics[model_name] = model_metrics - st.success(f"✅ Модель {model_name}: {successful_predictions}/{len(df)} успешных предсказаний") + st.success(f"✅ Model {model_name}: {successful_predictions}/{len(df)} successful predictions") except Exception as e: - st.error(f"❌ Ошибка обработки моделью {model_name}: {e}") + st.error(f"❌ Error processing with model {model_name}: {e}") continue progress_bar.progress(1.0) - status_text.text("✅ Все модели обработаны!") + status_text.text("✅ All models processed!") return result_df, all_predictions, model_results, all_metrics except Exception as e: - st.error(f"❌ Критическая ошибка обработки файла: {e}") + st.error(f"❌ Critical file processing error: {e}") st.markdown('
', unsafe_allow_html=True) - st.error("**Детали ошибки:**") + st.error("**Error details:**") 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("### 📊 Визуальное сравнение моделей") + st.markdown("### 📊 Visual Model Comparison") - # Создаем вкладки для разных типов визуализаций + # 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}' @@ -1026,10 +1025,10 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, }) if len(plot_data) > 0: - # Создаем subplot для разных типов графиков + # Create subplot for different graph types fig = make_subplots( rows=1, cols=2, - subplot_titles=('Распределение предсказаний', 'Сравнение средних значений'), + subplot_titles=('Prediction Distribution', 'Mean Value Comparison'), specs=[[{"type": "box"}, {"type": "bar"}]] ) @@ -1045,7 +1044,7 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, row=1, col=1 ) - # Bar plot со средними значениями + # Bar plot with mean values models = [data['model'] for data in plot_data] means = [data['mean'] for data in plot_data] stds = [data['std'] for data in plot_data] @@ -1063,17 +1062,17 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, fig.update_layout( height=400, - title_text=f"Сравнение моделей: {TARGET_DESCRIPTIONS[target]['name']}", + title_text=f"Model Comparison: {TARGET_DESCRIPTIONS[target]['name']}", showlegend=False ) st.plotly_chart(fig) else: - st.info(f"ℹ️ Нет данных для отображения по целевой переменной {target}") + st.info(f"ℹ️ No data to display for target variable {target}") with viz_tab2: - # Сводная статистика по моделям - st.markdown("#### 📋 Сводная статистика по моделям") + # Model summary statistics + st.markdown("#### 📋 Model Summary Statistics") stats_data = [] for target in target_columns: @@ -1083,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, - 'Целевая переменная': TARGET_DESCRIPTIONS[target]['name'], - 'Среднее': float(values.mean()), - 'Стандартное отклонение': float(values.std()), - 'Минимум': float(values.min()), - 'Максимум': float(values.max()), - 'Количество предсказаний': int(len(values)) + '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)) }) if stats_data: stats_df = pd.DataFrame(stats_data) - # Форматируем числовые колонки + # Format numeric columns formatted_stats = stats_df.copy() - numeric_columns = ['Среднее', 'Стандартное отклонение', 'Минимум', 'Максимум'] + numeric_columns = ['Mean', 'Standard Deviation', 'Minimum', 'Maximum'] for col in numeric_columns: if col in formatted_stats.columns: - if col == 'Стандартное отклонение': + if col == 'Standard Deviation': 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 таблица для удобного сравнения + # Pivot table for easy comparison try: pivot_mean = stats_df.pivot_table( - index='Модель', - columns='Целевая переменная', - values='Среднее', + index='Model', + columns='Target Variable', + values='Mean', aggfunc='first' ) - # Форматируем pivot таблицу + # Format pivot table 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("#### 📊 Сравнение средних значений") + st.markdown("#### 📊 Mean Value Comparison") st.dataframe(formatted_pivot) except Exception as e: - st.warning(f"Не удалось создать сводную таблицу: {e}") + st.warning(f"Failed to create pivot table: {e}") else: - st.info("ℹ️ Нет данных для статистического анализа") + st.info("ℹ️ No data for statistical analysis") with viz_tab3: - # Детальный анализ - ИСПРАВЛЕННАЯ ВЕРСИЯ - st.markdown("#### 🔍 Детальный анализ предсказаний") + # Detailed analysis - FIXED VERSION + st.markdown("#### 🔍 Detailed Prediction Analysis") - # Проверяем, что result_df не пустой и есть данные для анализа + # Check that result_df is not empty and has data for analysis if len(result_df) == 0: - st.info("ℹ️ Нет данных для детального анализа") + st.info("ℹ️ No data for detailed analysis") return try: - # Создаем безопасные описания для выпадающего списка + # Create safe descriptions for dropdown options = [] for i in range(len(result_df)): try: - # Безопасное извлечение данных с проверкой наличия колонок - sample = "Неизвестно" + # Safe data extraction with column presence check + sample = "Unknown" 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 "Неизвестно" + sample = str(sample_val) if pd.notna(sample_val) else "Unknown" if 'time_min' in result_df.columns: time_val = result_df.iloc[i]['time_min'] @@ -1159,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"Запись {i+1}: {sample} - t={time_min}мин, T={temperature_c}°C" + description = f"Record {i+1}: {sample} - t={time_min}min, T={temperature_c}°C" options.append((i, description)) except Exception as e: - # Если произошла ошибка, создаем базовое описание - options.append((i, f"Запись {i+1} (ошибка формата)")) + # If error occurs, create basic description + options.append((i, f"Record {i+1} (format error)")) - # Создаем выпадающий список + # 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"Запись {x+1}") + format_func=lambda x: next((opt[1] for opt in options if opt[0] == x), f"Record {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: @@ -1185,13 +1184,13 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, if pd.notna(value): try: comparison_data.append({ - 'Модель': model, - 'Целевая переменная': TARGET_DESCRIPTIONS[target]['name'], - 'Значение': float(value), - 'Цвет': AVAILABLE_MODELS[model]['color'] + 'Model': model, + 'Target Variable': TARGET_DESCRIPTIONS[target]['name'], + 'Value': float(value), + 'Color': AVAILABLE_MODELS[model]['color'] }) except (TypeError, ValueError) as e: - st.warning(f"Не удалось преобразовать значение для {model}_{target}: {value}") + st.warning(f"Failed to convert value for {model}_{target}: {value}") continue if comparison_data: @@ -1199,27 +1198,27 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, fig = px.bar( comp_df, - x='Целевая переменная', - y='Значение', - color='Модель', + x='Target Variable', + y='Value', + color='Model', barmode='group', - title=f"Сравнение предсказаний для записи {selected_row_idx+1}", + title=f"Prediction Comparison for Record {selected_row_idx+1}", color_discrete_map={model: AVAILABLE_MODELS[model]['color'] for model in selected_models} ) st.plotly_chart(fig) else: - st.info("ℹ️ Нет данных предсказаний для выбранной записи") + st.info("ℹ️ No prediction data for selected record") - # Показываем детальные значения в таблице - st.markdown("#### 📋 Численные значения предсказаний") + # Show detailed values in table + st.markdown("#### 📋 Numerical Prediction Values") detail_data = [] for target in target_columns: try: - row_detail = {'Целевая переменная': TARGET_DESCRIPTIONS[target]['name']} + row_detail = {'Target Variable': 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: @@ -1228,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] = 'Ошибка' + row_detail[model] = 'Error' 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['Оригинальное значение'] = f"{float(row_data[orig_col]):.2f}" + row_detail['Original Value'] = f"{float(row_data[orig_col]):.2f}" except (TypeError, ValueError): - row_detail['Оригинальное значение'] = 'Ошибка' + row_detail['Original Value'] = 'Error' detail_data.append(row_detail) except Exception as e: - st.warning(f"Ошибка обработки целевой переменной {target}: {e}") + st.warning(f"Error processing target variable {target}: {e}") continue if detail_data: detail_df = pd.DataFrame(detail_data) st.dataframe(detail_df) else: - st.info("ℹ️ Нет детальных данных для отображения") + st.info("ℹ�� No detailed data to display") else: - st.error("❌ Выбрана некорректная запись для анализа") + st.error("❌ Invalid record selected for analysis") except Exception as e: - st.error(f"❌ Ошибка в детальном анализе: {str(e)}") + st.error(f"❌ Error in detailed analysis: {str(e)}") st.markdown('
', unsafe_allow_html=True) - st.error("**Детали ошибки:**") + st.error("**Error details:**") st.code(traceback.format_exc()) st.markdown('
', unsafe_allow_html=True) with viz_tab4: - # Вкладка с метриками качества - st.markdown("#### 📉 Метрики качества моделей") + # Quality metrics tab + st.markdown("#### 📉 Model Quality Metrics") if not all_metrics or not any(all_metrics.values()): st.info(""" - ℹ️ **Для вычисления метрик необходимы оригинальные значения (ground truth) в загруженном файле** + ℹ️ **Original values (ground truth) in uploaded file required for metric calculation** - Убедитесь, что ваш файл содержит колонки с настоящими значениями: - - `pectin_yield` (ПВ, %) - - `galacturonic_acid` (ГК, %) - - `molecular_weight` (Mw, Д) - - `esterification_degree` (СЭ, %) + Ensure your file contains columns with actual values: + - `pectin_yield` (PV, %) + - `galacturonic_acid` (GK, %) + - `molecular_weight` (Mw, D) + - `esterification_degree` (SE, %) """) 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: - # Преобразуем значение в float для безопасного форматирования + # Convert value to float for safe formatting float_value = float(metric_value) if metric_value is not None else float('nan') metrics_data.append({ - 'Модель': model_name, - 'Целевая переменная': TARGET_DESCRIPTIONS[target_name]['name'], - 'Метрика': METRIC_DESCRIPTIONS[metric_name]['name'], - 'Значение': float_value, + 'Model': model_name, + 'Target Variable': TARGET_DESCRIPTIONS[target_name]['name'], + 'Metric': METRIC_DESCRIPTIONS[metric_name]['name'], + 'Value': float_value, 'metric_type': metric_name, 'target_type': target_name }) @@ -1300,285 +1299,121 @@ def create_comparison_visualizations(result_df, selected_models, target_columns, if metrics_data: metrics_df = pd.DataFrame(metrics_data) - # Pivot таблица для удобного сравнения + # Pivot table for easy comparison try: pivot_metrics = metrics_df.pivot_table( - index=['Модель', 'Целевая переменная'], - columns='Метрика', - values='Значение', + index=['Model', 'Target Variable'], + columns='Metric', + values='Value', aggfunc='first' ).reset_index() - # Форматируем числовые колонки + # Format numeric columns formatted_pivot = pivot_metrics.copy() for col in formatted_pivot.columns: - if col not in ['Модель', 'Целевая переме��ная']: + if col not in ['Model', 'Target Variable']: 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"Не удалось создать сводную таблицу метрик: {e}") + st.warning(f"Failed to create metrics pivot table: {e}") st.dataframe(metrics_df) - # Визуализация метрик - st.markdown("#### 📊 Визуализация метрик качества") + # Metrics visualization + st.markdown("#### 📊 Quality Metrics Visualization") - # Выбор метрики для визуализации + # 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: - # Преобразуем значения в float для построения графика + # Convert values to float for plotting metric_data = metric_data.copy() - metric_data['Числовое значение'] = metric_data['Значение'].apply( + metric_data['Numeric Value'] = metric_data['Value'].apply( lambda x: float(x) if pd.notna(x) else float('nan') ) fig = px.bar( metric_data, - x='Модель', - y='Числовое значение', - color='Целевая переменная', + x='Model', + y='Numeric Value', + color='Target Variable', barmode='group', - title=f"{METRIC_DESCRIPTIONS[selected_metric]['name']} по моделям", - labels={'Числовое значение': METRIC_DESCRIPTIONS[selected_metric]['name']} + title=f"{METRIC_DESCRIPTIONS[selected_metric]['name']} by Models", + labels={'Numeric Value': 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: - # Для метрик где лучше большее значение (R², корреляция) + # For metrics where higher is better (R², correlation) fig.add_hline( y=perfect_value, line_dash="dash", line_color="green", - annotation_text="Идеальное значение" + annotation_text="Ideal Value" ) st.plotly_chart(fig) else: - st.info("ℹ️ Нет доступных метрик для визуализации") + st.info("ℹ️ No metrics available for visualization") else: - st.info("ℹ️ Нет данных метрик для отображения") + st.info("ℹ️ No metrics data to display") except Exception as e: - st.error(f"❌ Ошибка создания визуализаций: {str(e)}") + st.error(f"❌ Error creating visualizations: {str(e)}") st.markdown('
', unsafe_allow_html=True) - st.error("**Детали ошибки:**") + st.error("**Error details:**") 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("### 📊 Дашборд метрик качества") + st.markdown("### 📊 Quality Metrics Dashboard") if not all_metrics or not any(all_metrics.values()): st.info(""" - ℹ️ **Для отображения метрик качества необходимы оригинальные значения в загруженном файле** + ℹ️ **Original values in uploaded file required for quality metrics display** - Убедитесь, что ваш файл содержит колонки с настоящими значениями: - - `pectin_yield` (ПВ, %) - - `galacturonic_acid` (ГК, %) - - `molecular_weight` (Mw, Д) - - `esterification_degree` (СЭ, %) + Ensure your file contains columns with actual values: + - `pectin_yield` (PV, %) + - `galacturonic_acid` (GK, %) + - `molecular_weight` (Mw, D) + - `esterification_degree` (SE, %) """) return - # Создаем вкладки для разных представлений метрик - metric_tab1, metric_tab2, metric_tab3 = st.tabs(["🎯 Карточки метрик", "📈 Сравнительные графики", "📋 Детальная таблица"]) + # Create tabs for different metric views + metric_tab1, metric_tab2, metric_tab3 = st.tabs(["🎯 Metric Cards", "📈 Comparative Graphs", "📋 Detailed Table"]) with metric_tab1: - st.markdown("#### 🎯 Основные метрики качества") + st.markdown("#### 🎯 Main Quality Metrics") - # Показываем карточки с метриками для каждой модели и целевой переменной + # 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_name}") + st.markdown(f"##### 🧮 Model: {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 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]: - # Показываем основные метрики в карточках + # Show main metrics in cards 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( @@ -1589,25 +1424,25 @@ def display_metrics_dashboard(all_metrics, selected_models): st.markdown(card_html, unsafe_allow_html=True) with metric_tab2: - st.markdown("#### 📈 Сравнение метрик между моделями") + st.markdown("#### 📈 Metric Comparison Between Models") - # Создаем сравнительные графики для основных метрик + # 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_name, - 'Целевая переменная': TARGET_DESCRIPTIONS[target_name]['name'], - 'Значение': metrics[metric], - 'Метрика': METRIC_DESCRIPTIONS[metric]['name'] + 'Model': model_name, + 'Target Variable': TARGET_DESCRIPTIONS[target_name]['name'], + 'Value': metrics[metric], + 'Metric': METRIC_DESCRIPTIONS[metric]['name'] }) if plot_data: @@ -1615,40 +1450,40 @@ def display_metrics_dashboard(all_metrics, selected_models): fig = px.bar( plot_df, - x='Модель', - y='Значение', - color='Целевая переменная', + x='Model', + y='Value', + color='Target Variable', barmode='group', - title=f"Сравнение {METRIC_DESCRIPTIONS[metric]['name']}", - labels={'Значение': METRIC_DESCRIPTIONS[metric]['name']} + title=f"Comparison of {METRIC_DESCRIPTIONS[metric]['name']}", + labels={'Value': 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("#### 📋 Детальная таблица метрик") + st.markdown("#### 📋 Detailed Metrics Table") - # Создаем подробную таблицу со всеми метриками + # 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_name, - 'Целевая переменная': TARGET_DESCRIPTIONS[target_name]['name'], - 'Количество образцов': metrics.get('n_samples', 0) + 'Model': model_name, + 'Target Variable': TARGET_DESCRIPTIONS[target_name]['name'], + 'Sample Count': 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] @@ -1658,7 +1493,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'] @@ -1672,90 +1507,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="📥 Скачать таблицу метрик (CSV)", + label="📥 Download Metrics Table (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"❌ Ошибка создания дашборда метрик: {e}") + st.error(f"❌ Error creating metrics dashboard: {e}") # Main app layout st.markdown('

🧪 Pectin Production Predictor

', unsafe_allow_html=True) # Sidebar with st.sidebar: - st.markdown("### ⚙️ Настройки модели") + st.markdown("### ⚙️ Model Settings") 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"**Описание:** {model_info['description']}") + st.info(f"**Description:** {model_info['description']}") # Load model - with st.spinner(f"Загружаем {selected_model}..."): + with st.spinner(f"Loading {selected_model}..."): model_artifacts = load_pectin_model(selected_model) if model_artifacts["status"] == "success": - st.success("✅ Модель готова к использованию") + st.success("✅ Model ready to use") # Display model info metadata = model_artifacts["metadata"] - 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_)}") + 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_)}") elif model_artifacts["status"] == "demo": - st.warning("🎭 Работаем в демо-режиме") - st.info("Для использования реальных моделей проверьте:") - st.info("1. Доступ к интернету") - st.info("2. Наличие репозитория на Hugging Face") - st.info("3. Правильность REPO_ID в настройках") + 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") else: - st.error("❌ Не удалось загрузить модель") - st.markdown("### 🔧 Решение проблем") + st.error("❌ Failed to load model") + st.markdown("### 🔧 Troubleshooting") st.info(""" - 1. **Проверьте интернет-соединение** - 2. **Убедитесь что репозиторий существует**: https://huggingface.co/arabovs-ai-lab/PectinProductionModels - 3. **Используйте локальные модели** - поместите файлы в папку models/ - 4. **Измените REPO_ID** на ваш репозиторий + 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 """) - if st.button("🔄 Попробовать снова"): + if st.button("🔄 Try Again"): st.cache_resource.clear() st.rerun() # Main content tabs -tab1, tab2, tab3, tab4 = st.tabs(["🎯 Одиночное предсказание", "📁 Пакетная обработка", "📊 Сравнение моделей", "🔄 Мультимодельная обработка"]) +tab1, tab2, tab3, tab4 = st.tabs(["🎯 Single Prediction", "📁 Batch Processing", "📊 Model Comparison", "🔄 Multi-Model Processing"]) with tab1: - st.markdown('
🎯 Одиночное предсказание
', unsafe_allow_html=True) + st.markdown('
🎯 Single Prediction
', unsafe_allow_html=True) col1, col2 = st.columns([1, 1]) with col1: - st.markdown("### 📝 Параметры процесса") + st.markdown("### 📝 Process Parameters") with st.form("single_prediction_form"): - 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) + 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) ph = st.number_input("pH:", min_value=0.0, max_value=14.0, value=2.5, step=0.1) - submitted = st.form_submit_button("🎯 Выполнить предсказание") + submitted = st.form_submit_button("🎯 Make Prediction") with col2: - st.markdown("### 📊 Результаты предсказания") + st.markdown("### 📊 Prediction Results") if submitted: input_data = { @@ -1766,22 +1601,22 @@ with tab1: 'ph': ph } - with st.spinner("🔮 Выполняем предсказание..."): + with st.spinner("🔮 Making prediction..."): prediction = predict_single(input_data, model_artifacts) if prediction: st.markdown('
', unsafe_allow_html=True) if model_artifacts.get("status") == "demo": - st.warning("🎭 Демо-режим: используются тестовые данные") + st.warning("🎭 Demo mode: using test data") else: - st.success("✅ Предсказание успешно выполнено!") + st.success("✅ Prediction successful!") # Display predictions as metrics target_descriptions = { - 'pectin_yield': 'Выход пектина (%)', - 'galacturonic_acid': 'ГК (%)', - 'molecular_weight': 'Мол. масса (Da)', - 'esterification_degree': 'СЭ (%)' + 'pectin_yield': 'Pectin Yield (%)', + 'galacturonic_acid': 'GA (%)', + 'molecular_weight': 'Mol. Weight (Da)', + 'esterification_degree': 'ED (%)' } cols = st.columns(2) @@ -1796,118 +1631,118 @@ with tab1: st.markdown('
', unsafe_allow_html=True) # Show input parameters - with st.expander("📋 Параметры ввода"): + with st.expander("📋 Input Parameters"): st.json(input_data) with tab2: - st.markdown('
📁 Пакетная обработка файлов
', unsafe_allow_html=True) + st.markdown('
📁 Batch File Processing
', 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 и текстовые файлы с данными экспериментов" + help="Excel, CSV and text files with experimental data supported" ) if uploaded_file is not None: - st.markdown("### ⚙️ Настройки обработки") + st.markdown("### ⚙️ Processing Settings") 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="Используйте если в файле есть заголовки или пустые строки в начале" + help="Use if file has headers or empty rows at beginning" ) with col_set2: - file_preview = st.checkbox("Показать предпросмотр файла", value=True) + file_preview = st.checkbox("Show file preview", value=True) - # Кнопка для проверки структуры файла - if st.button("🔍 Проверить структуру файла", type="secondary"): + # Button for file structure validation + if st.button("🔍 Validate File Structure", type="secondary"): is_valid, preview_df = validate_file_structure(uploaded_file, skip_rows) if is_valid: - st.success("✅ Файл готов к обработке!") + st.success("✅ File ready for processing!") if file_preview and preview_df is not None: - st.markdown("### 👀 Предпросмотр данных") + st.markdown("### 👀 Data Preview") st.dataframe(preview_df.head(10), width='stretch') - if st.button("🚀 Обработать файл", type="primary"): - with st.spinner("🔄 Обрабатываем файл..."): + if st.button("🚀 Process File", type="primary"): + with st.spinner("🔄 Processing file..."): result_df = process_batch_file_single_model(uploaded_file, model_artifacts, selected_model, skip_rows) if result_df is not None: - st.success(f"✅ Успешно обработано {len(result_df)} записей") + st.success(f"✅ Successfully processed {len(result_df)} records") if model_artifacts.get("status") == "demo": - st.warning("🎭 Демо-режим: используются тестовые данные") + st.warning("🎭 Demo mode: using test data") - # Показываем результаты - st.markdown("### 📈 Результаты пакетной обработки") + # Show results + st.markdown("### 📈 Batch Processing Results") - # Создаем красивый DataFrame для отображения + # Create nice DataFrame for display 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="📥 Скачать полные результаты (CSV)", + label="📥 Download Full Results (CSV)", data=csv, file_name=f"pectin_predictions_{selected_model}_{pd.Timestamp.now().strftime('%Y%m%d_%H%M')}.csv", mime="text/csv", - help="Скачать все данные с предсказаниями в CSV формате" + help="Download all data with predictions in CSV format" ) - # Показываем статистику - st.markdown("### 📊 Статистика предсказаний") + # Show statistics + st.markdown("### 📊 Prediction Statistics") if prediction_columns: stats_data = [] for col in prediction_columns: pred_values = result_df[col].dropna() if len(pred_values) > 0: stats_data.append({ - 'Параметр': col.replace(f'{selected_model}_', ''), - 'Среднее': pred_values.mean(), - 'Стандартное отклонение': pred_values.std(), - 'Минимум': pred_values.min(), - 'Максимум': pred_values.max(), - 'Успешных предсказаний': len(pred_values) + '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) }) if stats_data: @@ -1915,31 +1750,31 @@ with tab2: st.dataframe(stats_df, width='stretch') with tab3: - st.markdown('
📊 Сравнение моделей
', unsafe_allow_html=True) + st.markdown('
📊 Model Comparison
', unsafe_allow_html=True) - st.info("Сравните предсказания разных моделей на одних и тех же данных") + st.info("Compare predictions of different models on the same data") col1, col2 = st.columns([1, 1]) with col1: - 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") + 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") compare_ph = st.slider("pH:", 0.0, 14.0, 2.5, key="compare_ph") with col2: - st.markdown("### 🎯 Выбор моделей") + st.markdown("### 🎯 Model Selection") models_to_compare = st.multiselect( - "Выберите моделей для сравнения:", + "Select models for comparison:", options=list(AVAILABLE_MODELS.keys()), default=["best_model", "gradient_boosting", "random_forest"] ) - if st.button("🔍 Сравнить модели", type="primary"): + if st.button("🔍 Compare Models", type="primary"): if not models_to_compare: - st.warning("⚠️ Выберите хотя бы одну модель для сравнения") + st.warning("⚠️ Select at least one model for comparison") else: input_data = { 'sample': compare_sample, @@ -1951,9 +1786,9 @@ with tab3: comparison_results = [] - with st.spinner("🔍 Выполняем сравнение моделей..."): + with st.spinner("🔍 Comparing models..."): for model_name in models_to_compare: - with st.spinner(f"Загружаем {model_name}..."): + with st.spinner(f"Loading {model_name}..."): model_artifacts_compare = load_pectin_model(model_name) if model_artifacts_compare["status"] in ["success", "demo"]: @@ -1965,19 +1800,19 @@ with tab3: }) if comparison_results: - st.success("✅ Сравнение завершено!") + st.success("✅ Comparison completed!") if any("demo" in str(r) for r in comparison_results): - st.warning("🎭 Некоторые модели работают в демо-режиме") + st.warning("🎭 Some models working in demo mode") # Create comparison dataframe compare_df = pd.DataFrame(comparison_results) # Display comparison table - st.markdown("### 📋 Сравнение предсказаний") + st.markdown("### 📋 Prediction Comparison") st.dataframe(compare_df.set_index('model'), width='stretch') # Visualization - st.markdown("### 📊 Визуальное сравнение") + st.markdown("### 📊 Visual Comparison") # Melt dataframe for plotting plot_df = compare_df.melt(id_vars=['model'], var_name='parameter', value_name='value') @@ -1989,176 +1824,176 @@ with tab3: y='value', color='parameter', barmode='group', - title="Сравнение предсказаний по моделям", - labels={'value': 'Значение', 'model': 'Модель', 'parameter': 'Параметр'} + title="Prediction Comparison by Models", + labels={'value': 'Value', 'model': 'Model', 'parameter': 'Parameter'} ) st.plotly_chart(fig, use_container_width=True) # Show input parameters - with st.expander("📋 Параметры сравнения"): + with st.expander("📋 Comparison Parameters"): st.json(input_data) with tab4: - st.markdown('
🔄 Мультимодельная обработка файлов
', unsafe_allow_html=True) + st.markdown('
🔄 Multi-Model File Processing
', unsafe_allow_html=True) st.markdown("""
-

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

+

🎯 Multi-Model Processing Capabilities

-

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

+

📊 Output Data

""", unsafe_allow_html=True) - # Выбор моделей для мультимодельной обработки - st.markdown("### 🎯 Выбор моделей для обработки") + # Model selection for multi-model processing + st.markdown("### 🎯 Model Selection for Processing") multi_models = st.multiselect( - "Выберите модели для применения:", + "Select models to apply:", options=list(AVAILABLE_MODELS.keys()), default=["best_model", "gradient_boosting", "random_forest"], - help="Выберите одну или несколько моделей для обработки файла" + help="Select one or more models for file processing" ) 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("### ⚙️ Настройки обработки") + st.markdown("### ⚙️ Processing Settings") 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="Используйте если в файле есть заголовки или пустые строки в начале" + help="Use if file has headers or empty rows at beginning" ) with col_set2: - enable_viz = st.checkbox("Включить расширенную визуализацию", value=True) - enable_metrics = st.checkbox("Включить расчет метрик", value=True) + enable_viz = st.checkbox("Enable advanced visualization", value=True) + enable_metrics = st.checkbox("Enable metric calculation", value=True) - if st.button("🚀 Запустить мультимодельную обработку", type="primary"): + if st.button("🚀 Run Multi-Model Processing", type="primary"): if not multi_models: - st.warning("⚠️ Выберите хотя бы одну модель для обработки") + st.warning("⚠️ Select at least one model for processing") else: - with st.spinner("🔄 Обрабатываем файл несколькими моделями..."): + with st.spinner("🔄 Processing file with multiple models..."): 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"✅ Мультимодельная обработка завершена! Обработано {len(result_df)} записей") + st.success(f"✅ Multi-model processing completed! Processed {len(result_df)} records") - # Показываем сводку по моделям - st.markdown("### 📊 Сводка по моделям") + # Show model summary + st.markdown("### 📊 Model Summary") summary_data = [] for model_name in multi_models: if model_name in model_results: summary_data.append({ - 'Модель': 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 'Реальный' + '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' }) if summary_data: summary_df = pd.DataFrame(summary_data) st.dataframe(summary_df, width='stretch') - # Показываем результаты - st.markdown("### 📈 Результаты обработки") + # Show results + st.markdown("### 📈 Processing Results") - # Создаем упрощенный DataFrame для отображения + # Create simplified DataFrame for display 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) # Показываем только первые 10 строк + display_df = result_df[display_columns].head(10) # Show only first 10 rows st.dataframe(display_df, width='stretch') - # Скачивание полных результатов + # Download full results csv = result_df.to_csv(index=False, encoding='utf-8-sig') st.download_button( - label="📥 Скачать полные результаты (CSV)", + label="📥 Download Full Results (CSV)", data=csv, file_name=f"pectin_predictions_multimodel_{pd.Timestamp.now().strftime('%Y%m%d_%H%M')}.csv", mime="text/csv", - help="Скачать все данные с предсказаниями всех моделей в CSV формате" + help="Download all data with predictions from all models in CSV format" ) - # Отображаем метрики качества если включено и есть ground truth + # Display quality metrics if enabled and ground truth exists 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("### 🔍 Анализ расхождений между моделями") + st.markdown("### 🔍 Model Divergence Analysis") - # Вычисляем стандартные отклонения для каждой записи по всем моделям + # 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_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']}мин" + '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" }) if divergence_data: - st.info("📊 Записи с наибольшими расхождениями между моделями:") + st.info("📊 Records with largest model divergences:") divergence_df = pd.DataFrame(divergence_data) st.dataframe(divergence_df, width='stretch') else: - st.info("ℹ️ Значительных расхождений между моделями не обнаружено") + st.info("ℹ️ No significant model divergences detected") # Footer st.markdown("---") st.markdown( """
-

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

-

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

+

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

+

📚 Model repository: Hugging Face Hub

""", unsafe_allow_html=True