# interpreter.py """ Модуль интерпретации результатов статистического анализа. Выдаёт содержательные комментарии на русском языке о качестве модели, значимости факторов, адекватности и рекомендациях по дальнейшим действиям. """ import numpy as np def interpret_regression(r2, r2_adj, f_stat, f_pvalue, alpha): """ Интерпретирует качество регрессионной модели. Аргументы: r2 (float): коэффициент детерминации r2_adj (float): скорректированный R² f_stat (float): F-статистика f_pvalue (float): p-value для F-критерия alpha (float): уровень значимости Возвращает: str: текстовое заключение """ parts = [] # ----- Оценка значимости модели в целом ----- if f_pvalue < alpha: parts.append("✅ Модель статистически значима (p < α).") else: parts.append("❌ Модель статистически незначима (p ≥ α).") # ----- Оценка R² ----- if r2 >= 0.9: parts.append(f"📊 R² = {r2:.3f} — очень высокий, модель объясняет более 90% вариации.") elif r2 >= 0.7: parts.append(f"📊 R² = {r2:.3f} — хороший, модель объясняет {r2*100:.1f}% вариации.") elif r2 >= 0.5: parts.append(f"📊 R² = {r2:.3f} — умеренный, модель объясняет около {r2*100:.1f}% вариации.") else: parts.append(f"📊 R² = {r2:.3f} — низкий, модель плохо объясняет данные.") # ----- Сравнение R² и скорректированного R² ----- diff = r2 - r2_adj if diff > 0.1: parts.append(f"⚠️ Разница между R² и скорректированным R² велика ({diff:.3f}) — возможно, модель переобучена или содержит лишние факторы.") elif diff > 0.05: parts.append(f"ℹ️ Разница между R² и скорректированным R² составляет {diff:.3f} — стоит проверить, все ли факторы значимы.") else: parts.append(f"✅ R² и скорректированный R² близки — модель хорошо сбалансирована.") return "\n".join(parts) def interpret_coefficients(beta, t_stats, p_values, factor_names, alpha): """ Интерпретирует значимость коэффициентов регрессии. Аргументы: beta (list): коэффициенты (включая свободный член) t_stats (list): t-статистики для каждого коэффициента p_values (list или None): p-values для каждого коэффициента factor_names (list): имена факторов (первый должен быть "Свободный член") alpha (float): уровень значимости Возвращает: str: текстовое заключение """ parts = [] significant = [] not_significant = [] # Проверяем, что factor_names содержит "Свободный член" if factor_names[0] != "Свободный член": # Если нет, добавляем factor_names = ["Свободный член"] + factor_names for i, name in enumerate(factor_names): if i == 0: continue # пропускаем свободный член if i >= len(t_stats): break if p_values is not None and p_values[i] < alpha: significant.append(name) elif p_values is None and abs(t_stats[i]) > 2.0: # грубая оценка significant.append(name) else: not_significant.append(name) if not significant: parts.append("❌ Ни один фактор не является статистически значимым.") else: parts.append(f"✅ Значимые факторы: {', '.join(significant)}.") if not_significant: parts.append(f"⚠️ Незначимые факторы: {', '.join(not_significant)}. Рекомендуется исключить их из модели.") # ----- Оценка силы влияния (по величине коэффициентов) ----- if len(significant) >= 2: coefs = {name: abs(beta[i]) for i, name in enumerate(factor_names) if name in significant} if coefs: max_name = max(coefs, key=coefs.get) max_val = coefs[max_name] parts.append(f"💪 Самый сильный фактор — {max_name} (коэффициент {max_val:.4f}).") return "\n".join(parts) def interpret_anova(anova, alpha): """ Интерпретирует результаты дисперсионного анализа (ANOVA). Аргументы: anova (dict): результат функции anova_regression alpha (float): уровень значимости Возвращает: str: текстовое заключение """ parts = [] # ----- Проверка адекватности (lack-of-fit) ----- if not np.isnan(anova.get("F_lof", np.nan)): p_lof = anova["p_lof"] if p_lof < alpha: parts.append(f"⚠️ Модель НЕ АДЕКВАТНА (p_lof = {p_lof:.4f} < {alpha}). Линейная модель плохо описывает данные — вероятно, присутствует кривизна.") parts.append(" 🔄 Рекомендуется перейти к квадратичной модели (ЦКП, Бокса-Бенкена).") else: parts.append(f"✅ Модель адекватна (p_lof = {p_lof:.4f} ≥ {alpha}). Линейное приближение достаточно.") else: parts.append("ℹ️ Недостаточно центральных точек для оценки адекватности (lack-of-fit).") # ----- Сравнение дисперсий ----- if not np.isnan(anova.get("MS_pe", np.nan)) and not np.isnan(anova.get("MS_lof", np.nan)): ms_pe = anova["MS_pe"] ms_lof = anova["MS_lof"] if ms_lof > ms_pe * 3: parts.append(f"⚠️ Дисперсия неадекватности ({ms_lof:.6f}) намного превышает чистую ошибку ({ms_pe:.6f}) — модель систематически ошибается.") elif ms_lof > ms_pe: parts.append(f"ℹ️ Дисперсия неадекватности ({ms_lof:.6f}) больше чистой ошибки ({ms_pe:.6f}), но разница не критична.") else: parts.append(f"✅ Дисперсия неадекватности ({ms_lof:.6f}) меньше или сравнима с чистой ошибкой ({ms_pe:.6f}).") return "\n".join(parts) def interpret_center_points(center_mean, center_var, n_center, alpha): """ Интерпретирует анализ центральных точек. Аргументы: center_mean (float): среднее отклика в центре center_var (float): дисперсия в центре n_center (int): количество центральных точек alpha (float): уровень значимости Возвращает: str: текстовое заключение """ parts = [] if n_center < 2: parts.append("⚠️ Меньше двух центральных точек — оценка воспроизводимости невозможна.") return "\n".join(parts) std = np.sqrt(center_var) if center_var > 0 else 0.0 parts.append(f"📌 Центральных точек: {n_center}") parts.append(f"📌 Среднее отклика в центре: {center_mean:.6f}") parts.append(f"📌 Стандартное отклонение в центре: {std:.6f}") # ----- Оценка воспроизводимости ----- if center_var < 1e-6: parts.append("✅ Дисперсия в центре очень мала — эксперимент высоковоспроизводим.") elif center_var < 1e-4: parts.append("✅ Дисперсия в центре мала — воспроизводимость хорошая.") elif center_var < 1e-3: parts.append("ℹ️ Дисперсия в центре умеренная — стоит обратить внимание на точность измерений.") else: parts.append("⚠️ Дисперсия в центре велика — возможны проблемы с воспроизводимостью эксперимента.") # ----- Относительная оценка (по сравнению со средним) ----- if center_mean != 0: rel_std = std / abs(center_mean) * 100 if rel_std < 1: parts.append(f"📊 Относительное стандартное отклонение: {rel_std:.2f}% — отличная точность.") elif rel_std < 5: parts.append(f"📊 Относительное стандартное отклонение: {rel_std:.2f}% — хорошая точность.") elif rel_std < 10: parts.append(f"📊 Относительное стандартное отклонение: {rel_std:.2f}% — удовлетворительная точность.") else: parts.append(f"⚠️ Относительное стандартное отклонение: {rel_std:.2f}% — низкая точность, возможно, есть выбросы или систематическая ошибка.") return "\n".join(parts) def interpret_model_adequacy(anova, alpha): """ Даёт общую рекомендацию: переходить на квадратичную модель или нет. Аргументы: anova (dict): результат ANOVA alpha (float): уровень значимости Возвращает: str: краткая рекомендация """ if not np.isnan(anova.get("p_lof", np.nan)): p_lof = anova["p_lof"] if p_lof < alpha: return ( "⚠️ РЕКОМЕНДАЦИЯ: модель НЕ АДЕКВАТНА.\n" " Линейное приближение недостаточно. Перейдите к квадратичной модели\n" " (Центральный композиционный план или план Бокса-Бенкена).\n" " Это позволит точно определить положение экстремума." ) else: return ( "✅ РЕКОМЕНДАЦИЯ: модель АДЕКВАТНА.\n" " Линейное приближение достаточно. Можете использовать крутое восхождение\n" " для движения к оптимуму или уточнять модель дальше." ) else: return ( "ℹ️ Недостаточно данных для проверки адекватности.\n" " Добавьте центральные точки в план, чтобы оценить воспроизводимость\n" " и проверить, нужна ли квадратичная модель." ) def full_interpretation( r2, r2_adj, f_stat, f_pvalue, beta, t_stats, p_values, factor_names, anova, center_mean, center_var, n_center, alpha ): """ Собирает все интерпретации в единый отчёт. Аргументы: все вышеперечисленные Возвращает: str: полное заключение """ lines = [] lines.append("=" * 60) lines.append("📋 ИНТЕРПРЕТАЦИЯ РЕЗУЛЬТАТОВ") lines.append("=" * 60) lines.append("\n▶ 1. КАЧЕСТВО МОДЕЛИ") lines.append(interpret_regression(r2, r2_adj, f_stat, f_pvalue, alpha)) lines.append("\n▶ 2. ЗНАЧИМОСТЬ ФАКТОРОВ") lines.append(interpret_coefficients(beta, t_stats, p_values, factor_names, alpha)) lines.append("\n▶ 3. АДЕКВАТНОСТЬ (ANOVA)") lines.append(interpret_anova(anova, alpha)) if n_center >= 2: lines.append("\n▶ 4. ВОСПРОИЗВОДИМОСТЬ") lines.append(interpret_center_points(center_mean, center_var, n_center, alpha)) lines.append("\n▶ 5. ОБЩАЯ РЕКОМЕНДАЦИЯ") lines.append(interpret_model_adequacy(anova, alpha)) lines.append("\n" + "=" * 60) return "\n".join(lines)