Compare commits

...

2 Commits

2 changed files with 141 additions and 26 deletions
+100
View File
@@ -208,3 +208,103 @@ def anova_regression(y, y_pred, X, center_indices=None):
"MS_pe": MS_pe, "MS_lof": MS_lof,
"F_lof": F_lof, "p_lof": p_lof
}
# core/statistics.py (дополнение)
def build_quadratic_X(plan):
"""
Строит расширенную матрицу регрессоров для квадратичной модели.
plan: numpy array (N x k) – матрица плана в кодированных переменных.
Возвращает: X (N x p), где p = 1 + 2k + k*(k-1)//2
(свободный член, линейные, квадраты, попарные произведения)
"""
N, k = plan.shape
X = np.ones((N, 1)) # свободный член
X = np.hstack([X, plan]) # линейные (k столбцов)
X = np.hstack([X, plan**2]) # квадраты (k столбцов)
# взаимодействия (k*(k-1)//2 столбцов)
for i in range(k):
for j in range(i+1, k):
X = np.hstack([X, (plan[:, i] * plan[:, j]).reshape(-1, 1)])
return X
def fit_quadratic_model(plan, y):
"""
Выполняет регрессию для квадратичной модели по матрице плана.
Возвращает словарь с результатами (аналогично compute_regression, но с добавлением структуры).
"""
X = build_quadratic_X(plan)
reg = compute_regression(X, y) # используем существующую функцию
# Добавляем информацию о структуре модели
k = plan.shape[1]
p_total = X.shape[1]
reg["k"] = k
reg["X"] = X
reg["plan"] = plan
reg["p_total"] = p_total
# Можно добавить имена коэффициентов (для вывода)
coef_names = ["Свободный член"]
coef_names += [f"x{i+1}" for i in range(k)]
coef_names += [f"x{i+1}²" for i in range(k)]
for i in range(k):
for j in range(i+1, k):
coef_names.append(f"x{i+1}·x{j+1}")
reg["coef_names"] = coef_names
return reg
def analyze_quadratic_model(beta, k, X, y):
"""
Анализирует квадратичную модель: находит стационарную точку и тип экстремума.
beta – коэффициенты (включая все члены модели)
k – число факторов
X – матрица регрессоров (для расчёта предсказанных значений)
y отклик
Возвращает словарь с результатами.
"""
# Извлекаем коэффициенты
b0 = beta[0]
b_lin = beta[1:1+k] # линейные
b_quad = beta[1+k:1+2*k] # квадраты
b_inter = beta[1+2*k:] # взаимодействия
# Строим матрицу B (гессиан) для квадратичной формы
B = np.diag(2 * b_quad)
idx = 0
for i in range(k):
for j in range(i+1, k):
B[i, j] = b_inter[idx]
B[j, i] = b_inter[idx]
idx += 1
# Стационарная точка: x* = -0.5 * inv(B) * b_lin
try:
x_star = -0.5 * np.linalg.solve(B, b_lin)
except np.linalg.LinAlgError:
x_star = None
# Предсказанное значение в стационарной точке
if x_star is not None:
y_star = b0 + np.dot(b_lin, x_star) + np.dot(x_star, B @ x_star) / 2
else:
y_star = None
# Тип экстремума
if x_star is not None:
eigvals = np.linalg.eigvalsh(B)
if np.all(eigvals < 0):
extremum_type = "Максимум"
elif np.all(eigvals > 0):
extremum_type = "Минимум"
elif np.any(eigvals < 0) and np.any(eigvals > 0):
extremum_type = "Седловая точка"
else:
extremum_type = "Неопределён (есть нулевые собственные значения)"
else:
extremum_type = "Не найден (матрица B вырождена)"
return {
"x_star": x_star,
"y_star": y_star,
"extremum_type": extremum_type,
"B": B,
"eigenvalues": np.linalg.eigvalsh(B) if x_star is not None else None
}
+41 -26
View File
@@ -2,8 +2,11 @@
"""
Главное меню программы.
Загружает проект при старте и предоставляет доступ ко всем подменю.
Обрабатывает прерывание Ctrl+C для корректного выхода.
"""
from core.fs import load_project
import sys
from core.fs import load_project, save_project_partial
from editor import editor_menu
from plan import plan_menu
from response import response_menu
@@ -15,33 +18,45 @@ PROJECT_FILE = "project.json"
def main():
mixture, design, responses = load_project(PROJECT_FILE)
while True:
print("\n" + "=" * 50)
print("ГЛАВНОЕ МЕНЮ")
print("1. Редактор состава")
print("2. Планирование эксперимента")
print("3. Ввод значений отклика")
print("4. Анализ значений")
print("5. Крутое восхождение")
print("6. Выход")
print("=" * 50)
choice = input("Выберите пункт: ").strip()
try:
while True:
print("\n" + "=" * 50)
print("ГЛАВНОЕ МЕНЮ")
print("1. Редактор состава")
print("2. Планирование эксперимента")
print("3. Ввод значений отклика")
print("4. Анализ значений")
print("5. Крутое восхождение")
print("6. Выход")
print("=" * 50)
choice = input("Выберите пункт: ").strip()
if choice == "6":
print("До свидания! (данные не сохранены автоматически)")
break
elif choice == "1":
editor_menu(mixture)
elif choice == "2":
design = plan_menu(mixture, design)
elif choice == "3":
responses = response_menu(design, responses)
elif choice == "4":
analysis_menu(mixture, design, responses)
elif choice == "5":
ascent_menu(mixture, design, responses)
if choice == "6":
print("До свидания! (данные не сохранены автоматически)")
break
elif choice == "1":
editor_menu(mixture)
elif choice == "2":
design = plan_menu(mixture, design)
elif choice == "3":
responses = response_menu(design, responses)
elif choice == "4":
analysis_menu(mixture, design, responses)
elif choice == "5":
ascent_menu(mixture, design, responses)
else:
print("Неверный пункт.")
except KeyboardInterrupt:
print("\n\nОбнаружено прерывание (Ctrl+C).")
# Спрашиваем, хочет ли пользователь сохранить текущий проект
save_choice = input("Сохранить текущий проект перед выходом? (y/n, Enter = нет): ").strip().lower()
if save_choice == 'y':
save_project_partial(PROJECT_FILE, mixture, design, responses)
print("Проект сохранён.")
else:
print("Неверный пункт.")
print("Выход без сохранения.")
print("До свидания!")
sys.exit(0)
if __name__ == "__main__":
main()