Переработан интерфейс программы, расчёт занчений в процессе исправления.

This commit is contained in:
2026-05-18 15:53:24 +05:00
parent e6e86e50a3
commit 3ddd05acff
38 changed files with 2820 additions and 3555 deletions
+352
View File
@@ -0,0 +1,352 @@
"""
Планирование эксперимента (DoE)
Модуль для генерации полнофакторных планов экспериментов
и анализа результатов.
Основные функции:
- generate_factorial_design() - генерация плана
- analyze_experiment() - статистический анализ
- calculate_factor_levels() - расчёт уровней факторов
"""
from typing import List, Dict, Tuple, Optional
import random
import numpy as np
# Типы расчёта шага
FACTOR_TYPES = {
'absolute': 'абс', # абсолютный шаг
'relative': '%', # относительный шаг (процент от нулевого уровня)
}
def calculate_factor_levels(
center_value: float,
step_value: float,
step_type: str,
base_value: float = None
) -> Tuple[float, float]:
"""
РАССЧИТЫВАЕТ ВЕРХНИЙ И НИЖНИЙ УРОВНИ ФАКТОРА
Параметры:
center_value: нулевой уровень фактора (центральная точка)
step_value: значение шага
step_type: тип шага ("абс" - абсолютный, "%" - относительный)
base_value: базовое значение для относительного шага (если None, используется center_value)
Возвращает:
(high_level, low_level): верхний и нижний уровни
Пример:
>>> calculate_factor_levels(100, 10, "%")
(110.0, 90.0)
>>> calculate_factor_levels(100, 20, "абс")
(120.0, 80.0)
"""
# Определяем абсолютное значение шага
if step_type == "%":
base = base_value if base_value is not None else center_value
step_abs = center_value * step_value / 100
else: # "абс" или "absolute"
step_abs = step_value
high_level = center_value + step_abs
low_level = center_value - step_abs
return high_level, low_level
def generate_factorial_design(
factors: List[Dict],
center_points: int = 3,
randomize: bool = True
) -> List[Dict]:
"""
ГЕНЕРИРУЕТ ПОЛНОФАКТОРНЫЙ ПЛАН ЭКСПЕРИМЕНТА
Создаёт матрицу планирования для 2^k полнофакторного эксперимента
с добавлением центральных точек.
ПАРАМЕТРЫ:
----------
factors : List[Dict]
Список факторов. Каждый фактор - словарь с ключами:
- name (str): название фактора
- low (float): нижний уровень (-1)
- high (float): верхний уровень (+1)
- center (float): нулевой уровень (0)
- unit (str): единица измерения
- step (float, опционально): шаг варьирования
- step_type (str, опционально): тип шага ("абс" или "%")
Минимально необходимые ключи: name, low, high, center, unit
center_points : int
Количество центральных точек (повторений в центре плана)
По умолчанию 3
randomize : bool
Перемешивать ли порядок опытов случайным образом
По умолчанию True
ВОЗВРАЩАЕТ:
-----------
List[Dict]
Список экспериментов. Каждый эксперимент - словарь:
- для каждого фактора: "Фактор_N" с полями:
- coded: кодированное значение (-1, 0, +1)
- natural: натуральное значение
- name: название фактора
- unit: единица измерения
- is_center (bool): является ли точка центральной
- center_num (int): номер центральной точки (если is_center)
ПРИМЕР ИСПОЛЬЗОВАНИЯ:
---------------------
>>> factors = [
... {'name': 'Температура', 'low': 25, 'high': 37, 'center': 31, 'unit': '°C'},
... {'name': 'pH', 'low': 6.5, 'high': 7.5, 'center': 7.0, 'unit': ''}
... ]
>>> design = generate_factorial_design(factors, center_points=2)
>>> print(len(design)) # 2^2 + 2 = 6
6
>>> design[0]['Фактор_1']['coded'] # первый фактор в первом опыте
-1
"""
k = len(factors)
if k == 0:
return []
n_factorial = 2 ** k
design = []
# Генерация факторных точек (все комбинации уровней)
for i in range(n_factorial):
experiment = {}
for j in range(k):
# Кодированный уровень: -1 для 0, +1 для 1 в бите
# (k-1-j) для правильного порядка факторов
coded_level = -1 if (i >> (k - 1 - j)) & 1 == 0 else 1
# Натуральное значение
natural_value = factors[j]['low'] if coded_level == -1 else factors[j]['high']
experiment[f"Фактор_{j+1}"] = {
'coded': coded_level,
'natural': natural_value,
'name': factors[j]['name'],
'unit': factors[j].get('unit', '')
}
design.append(experiment)
# Добавление центральных точек
for i in range(center_points):
center_experiment = {}
for j in range(k):
center_experiment[f"Фактор_{j+1}"] = {
'coded': 0,
'natural': factors[j]['center'],
'name': factors[j]['name'],
'unit': factors[j].get('unit', '')
}
center_experiment['is_center'] = True
center_experiment['center_num'] = i + 1
design.append(center_experiment)
# Перемешивание порядка
if randomize:
random.shuffle(design)
return design
def analyze_experiment(
results: List[List[float]],
design: List[Dict],
responses: List[Dict]
) -> Dict:
"""
ПРОВОДИТ СТАТИСТИЧЕСКИЙ АНАЛИЗ РЕЗУЛЬТАТОВ ЭКСПЕРИМЕНТА
ПАРАМЕТРЫ:
----------
results : List[List[float]]
Матрица результатов. Каждая строка - эксперимент,
каждый столбец - отклик.
Размер: [n_experiments, n_responses]
design : List[Dict]
План эксперимента, возвращённый generate_factorial_design()
responses : List[Dict]
Список откликов. Каждый отклик - словарь с ключами:
- name (str): название отклика
- unit (str): единица измерения
ВОЗВРАЩАЕТ:
-----------
Dict
Словарь с анализом для каждого отклика:
{
'Название отклика': {
'mean': float, # среднее значение всех опытов
'variance': float, # общая дисперсия
'std_dev': float, # стандартное отклонение
'cv': float, # коэффициент вариации (%)
'factorial_values': list, # значения в факторных точках
'center_values': list, # значения в центральных точках
'center_variance': float, # дисперсия воспроизводимости
'n_factorial': int, # количество факторных точек
'n_center': int, # количество центральных точек
'fisher_ratio': float, # критерий Фишера (если применимо)
'model_adequate': bool|None # адекватность модели
}
}
ПРИМЕР ИСПОЛЬЗОВАНИЯ:
---------------------
>>> factors = [{'name': 'X', 'low': 0, 'high': 10, 'center': 5, 'unit': ''}]
>>> design = generate_factorial_design(factors, center_points=2)
>>> results = [[2.0], [8.0], [5.1], [4.9]] # 2 факторные + 2 центральные
>>> responses = [{'name': 'Выход', 'unit': '%'}]
>>> analysis = analyze_experiment(results, design, responses)
>>> print(analysis['Выход']['mean'])
5.0
"""
analysis = {}
for resp_idx, response in enumerate(responses):
resp_name = response.get('name', f'Отклик_{resp_idx+1}')
# Собираем все значения для этого отклика
y_values = [results[i][resp_idx] for i in range(len(results))]
# Базовые статистики
mean_y = np.mean(y_values)
variance = np.var(y_values, ddof=1) if len(y_values) > 1 else 0
std_dev = np.std(y_values, ddof=1) if len(y_values) > 1 else 0
cv = (std_dev / mean_y) * 100 if mean_y != 0 else 0
# Разделяем факторные и центральные точки
factorial_y = []
center_y = []
for i, exp in enumerate(design):
if exp.get('is_center', False):
center_y.append(y_values[i])
else:
factorial_y.append(y_values[i])
# Анализ воспроизводимости
center_variance = np.var(center_y, ddof=1) if len(center_y) > 1 else 0
# Критерий Фишера для проверки адекватности модели
fisher_ratio = None
model_adequate = None
if len(center_y) > 1 and len(factorial_y) > 1:
factorial_variance = np.var(factorial_y, ddof=1)
if factorial_variance > 0 and center_variance > 0:
fisher_ratio = max(factorial_variance, center_variance) / min(factorial_variance, center_variance)
# Критическое значение F (приблизительное, для p=0.05)
# Более точное требует знания степеней свободы
model_adequate = fisher_ratio < 4.0
analysis[resp_name] = {
'mean': mean_y,
'variance': variance,
'std_dev': std_dev,
'cv': cv,
'factorial_values': factorial_y,
'center_values': center_y,
'center_variance': center_variance,
'n_factorial': len(factorial_y),
'n_center': len(center_y),
'fisher_ratio': fisher_ratio,
'model_adequate': model_adequate
}
return analysis
def create_factor_from_reagent(
reagent: Dict,
total_volume: float,
volume_unit: str,
step_percent: float = 10.0
) -> Dict:
"""
СОЗДАЁТ ФАКТОР ИЗ РЕАГЕНТА (для интеграции калькулятора и DoE)
Преобразует рассчитанный реагент в фактор для планирования эксперимента.
Параметры:
reagent: рассчитанный реагент (из calculate_medium_composition)
total_volume: общий объём среды
volume_unit: единица объёма
step_percent: шаг варьирования в процентах от нулевого уровня
Возвращает:
Dict: фактор для использования в generate_factorial_design()
"""
center_value = reagent.get('undiluted_amount', reagent.get('calculated_amount', 0))
step_value = center_value * step_percent / 100
high_level, low_level = calculate_factor_levels(
center_value, step_value, "абс"
)
return {
'name': reagent['name'],
'center': center_value,
'low': low_level,
'high': high_level,
'step': step_value,
'step_type': 'абс',
'unit': reagent.get('unit', volume_unit),
'percentage': reagent.get('percentage', 0),
'dilution_factor': reagent.get('dilution_factor', 1.0)
}
def create_factor_from_reagent(
reagent: Dict,
total_volume: float,
volume_unit: str,
step_percent: float = 10.0
) -> Dict:
"""
СОЗДАЁТ ФАКТОР ИЗ РЕАГЕНТА (для интеграции калькулятора и DoE)
Преобразует рассчитанный реагент в фактор для планирования эксперимента.
Параметры:
reagent: рассчитанный реагент (из calculate_medium_composition)
total_volume: общий объём среды
volume_unit: единица объёма
step_percent: шаг варьирования в процентах от нулевого уровня
Возвращает:
Dict: фактор для использования в generate_factorial_design()
"""
center_value = reagent.get('undiluted_amount', reagent.get('calculated_amount', 0))
step_value = center_value * step_percent / 100
high_level, low_level = calculate_factor_levels(
center_value, step_value, "абс"
)
return {
'name': reagent['name'],
'center': center_value,
'low': low_level,
'high': high_level,
'step': step_value,
'step_type': 'абс',
'unit': reagent.get('unit', volume_unit),
'percentage': reagent.get('percentage', 0),
'dilution_factor': reagent.get('dilution_factor', 1.0)
}