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('', 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('', unsafe_allow_html=True)
st.markdown("""
-
🎯 Поддерживаемые форматы
+
🎯 Supported Formats
- - Excel (.xlsx) - автоматическое определение структуры
- - CSV/TXT - с разными разделителями и кодировками
+ - Excel (.xlsx) - automatic structure detection
+ - CSV/TXT - with different separators and encodings
-
📊 Обязательные поля
+
📊 Required Fields
- - sample (Образец пектина) - тип сырья
- - time_min (t, мин) - время экстракции
- - temperature_c (T, °C) - температура
- - pressure_atm (P, атм) - давление
- - ph (pH) - уровень кислотности
+ - sample (Pectin Sample) - raw material type
+ - time_min (t, min) - extraction time
+ - temperature_c (T, °C) - temperature
+ - pressure_atm (P, atm) - pressure
+ - ph (pH) - acidity level
-
🔧 Особенности обработки
+
🔧 Processing Features
- - Автоматическое определение кодировки и разделителей
- - Поддержка русских чисел с запятыми (2,08 → 2.08)
- - Гибкая настройка пропуска строк
+ - Automatic encoding and separator detection
+ - Support for Russian numbers with commas (2,08 → 2.08)
+ - Flexible row skipping settings
""", 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('', 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('', unsafe_allow_html=True)
st.markdown("""
-
🎯 Возможности мультимодельной обработки
+
🎯 Multi-Model Processing Capabilities
- - Применение нескольких моделей к одному файлу данных
- - Сравнение результатов разных алгоритмов
- - Визуальный анализ распределений предсказаний
- - Статистическое сравнение производительности моделей
- - Метрики качества если есть настоящие значения
+ - Apply multiple models to one data file
+ - Compare results of different algorithms
+ - Visual analysis of prediction distributions
+ - Statistical comparison of model performance
+ - Quality metrics if actual values available
-
📊 Выходные данные
+
📊 Output Data
- - Объединенная таблица с предсказаниями всех моделей
- - Графики сравнения по целевым переменным
- - Статистические сводки по каждой модели
- - Детальный анализ отдельных записей
- - Метрики качества для оценки точности
+ - Combined table with predictions from all models
+ - Comparison graphs by target variables
+ - Statistical summaries for each model
+ - Detailed analysis of individual records
+ - Quality metrics for accuracy assessment
""", 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