Представь, пожалуйста, что ты программист и тебе необходимо помочь мне решить проблему шаг за шагом. у меня есть код предназначенный для обработки и анализа данных.
import openpyxl
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.metrics import r2_score
from sklearn.model_selection import train_test_split
import optuna


# Функция загрузки данных из Excel
def load_data_from_excel(excel_file, columns_to_check):
    workbook = openpyxl.load_workbook(excel_file, data_only=True)
    sheet = workbook.active

    numeric_data = []

    for row in sheet.iter_rows(min_row=1):
        row_values = []
        is_full_numeric = True

        for col in columns_to_check:
            cell_value = row[col - 1].value
            if isinstance(cell_value, (int, float)):
                row_values.append(cell_value)
            else:
                is_full_numeric = False
                break

        if is_full_numeric:
            numeric_data.append(row_values)

    df = pd.DataFrame(numeric_data)
    # Удаление первой строки
    df = df.drop(index=0).reset_index(drop=True)
    return df

# Функция сортировки данных по температуре и расходу
def sort_data_by_temperature_and_flow_rate(df, temperature_index, flow_rate_index):
    return df.sort_values(by=[temperature_index, flow_rate_index])

# Функция получения уникальных значений температуры
def get_unique_temperatures(df, temperature_index):
    return df.iloc[:, temperature_index].unique()

# Функция поиска номинального значения температуры
def find_nominal_temperature(unique_temperatures):
    print(f"Уникальные значения температуры: {unique_temperatures}")
    nominal_temperature = float(input("Введите номинальное значение температуры обратной сетевой воды (например, 70): "))
    return nominal_temperature

# Функция расчета дельт
def calculate_deltas(df, temperature_index, power_index, nominal_temperature):
    df['Дельта'] = 0.0
    nominal_subset = df[df.iloc[:, temperature_index] == nominal_temperature]

    unique_temperatures = get_unique_temperatures(df, temperature_index)

    for temp in unique_temperatures:
        if temp == nominal_temperature:
            continue

        subset = df[df.iloc[:, temperature_index] == temp]
        if len(subset) != len(nominal_subset):
            print(f"Warning: Length mismatch between Tоб={temp} и Tоб={nominal_temperature}. Skipping this segment.")
            continue

        df.loc[subset.index, 'Дельта'] = subset.iloc[:, power_index].values - nominal_subset.iloc[:, power_index].values

    filtered_df = df[df.iloc[:, temperature_index] != nominal_temperature]

    return filtered_df

# Функция нормализации дельт
def normalize_deltas(df, temperature_index, delta_index, target_value, tolerance=1e-6):
    unique_temperatures = get_unique_temperatures(df, temperature_index)
    first_deltas = {}

    for temp in unique_temperatures:
        subset = df[df.iloc[:, temperature_index] == temp]
        if not subset.empty:
            first_delta = subset.iloc[0, delta_index]
            first_deltas[temp] = first_delta

    first_temp = unique_temperatures[0]
    first_delta = first_deltas[first_temp]
    k_first = target_value / first_delta if abs(first_delta) > tolerance else 0.0
    print(f"Коэффициент k для Tоб {first_temp}: {k_first}")

    k_values = {first_temp: k_first}
    for temp, first_delta in first_deltas.items():
        if temp == first_temp:
            continue
        if abs(first_delta) < tolerance:
            k_values[temp] = 0.0
        else:
            k_values[temp] = target_value / first_delta

    df['Модифицированное значение'] = 0.0
    for temp, k in k_values.items():
        df.loc[df.iloc[:, temperature_index] == temp, 'Модифицированное значение'] = df.loc[df.iloc[:, temperature_index] == temp, 'Дельта'] * k

    return df

# Функция сохранения данных
def save_data(df, output_file):
    df.to_csv(output_file, index=False)

# Целевая функция для Optuna
def objective(trial, Gt_train, Gt_test, mod_train, mod_test):
    degree = trial.suggest_int('degree', 1, 5)  # Ограничиваем степень полинома для стабильности

    poly = PolynomialFeatures(degree=degree)
    Gt_train_poly = poly.fit_transform(Gt_train)
    Gt_test_poly = poly.transform(Gt_test)

    model = LinearRegression()
    model.fit(Gt_train_poly, mod_train)

    mod_pred = model.predict(Gt_test_poly)

    r2 = r2_score(mod_test, mod_pred)

    return -r2

# Функция построения графика зависимости модифицированного значения от Gт
def plot_mod_value_vs_gt_optuna(data, ax):
    Gt = data['Gт'].values.reshape(-1, 1)
    mod_value = data['Модифицированное значение'].values

    # Нормировка данных
    scaler = StandardScaler()
    Gt_scaled = scaler.fit_transform(Gt)

    Gt_train, Gt_test, mod_train, mod_test = train_test_split(Gt_scaled, mod_value, test_size=0.2, random_state=42)

    study = optuna.create_study(direction='minimize')
    study.optimize(lambda trial: objective(trial, Gt_train, Gt_test, mod_train, mod_test), n_trials=50)

    best_degree = study.best_params['degree']
    best_r2 = -study.best_value

    poly = PolynomialFeatures(degree=best_degree)
    Gt_poly = poly.fit_transform(Gt_scaled)

    model = LinearRegression()
    model.fit(Gt_poly, mod_value)

    mod_value_pred = model.predict(Gt_poly)

    # Преобразуем обратно для построения графика
    Gt_original = scaler.inverse_transform(Gt_scaled)
    order = np.argsort(Gt_original.flatten())

    ax.scatter(data['Gт'], data['Модифицированное значение'], label='Исходные данные', color='blue', alpha=0.6)
    ax.plot(Gt_original[order].flatten(), mod_value_pred[order], '-', label=f'Poly deg {best_degree}, R²: {best_r2:.2f}', color='red')

    ax.set_title('Зависимость f(Pот, Gтурб)')
    ax.set_xlabel('Gт')
    ax.set_ylabel('Модифицированное значение')
    ax.legend()
    ax.grid(True) # Добавляем сетку

    coef = model.coef_[::-1]
    intercept = model.intercept_
    equation = f"y = {intercept:.4f} "
    for i, c in enumerate(coef[1:], start=1):
        equation += f"+ ({c:.15f}) * x^{i} "
    print(f"Уравнение полинома для графика 'Модифицированное значение' от 'Gт' (степень {best_degree}):")
    print(equation)

# Функция построения графика зависимости модифицированного значения от Дельта
def plot_mod_value_vs_delta(data, ax):
    unique_tob_values = data['Tоб'].unique()

    for tob_value in unique_tob_values:
        df_filtered = data[data['Tоб'] == tob_value]
        df_filtered = df_filtered.sort_values(by='Gт')

        X = df_filtered['Дельта'].values.reshape(-1, 1)
        y = df_filtered['Модифицированное значение'].values

        model = LinearRegression()
        model.fit(X, y)

        slope = model.coef_[0]
        intercept = model.intercept_

        ax.plot(df_filtered['Дельта'], df_filtered['Модифицированное значение'], label=f'Tоб: {tob_value}')

        print(f"Уравнение для Tоб {tob_value}: y = {slope:.4f} * x + {intercept:.10f}")

    ax.set_title('Зависимость f(dNe, dtоб)')
    ax.set_xlabel('Дельта')
    ax.set_ylabel('Модифицированное значение')
    ax.legend()
    ax.grid(True)  # Добавляем сетку

# Обновленная функция построения графиков с учетом разных давлений
def main_plot(data_list, pt_values):
    fig, (ax_gt, ax_delta) = plt.subplots(1, 2, figsize=(14, 6), gridspec_kw={'width_ratios': [1, 1]})

    for idx, (data, pt) in enumerate(zip(data_list, pt_values)):
        print(f"Обработка давления Pt = {pt}")

        # График зависимости модифицированного значения от Gт
        Gt = data['Gт'].values.reshape(-1, 1)
        mod_value = data['Модифицированное значение'].values

        # Нормировка данных
        scaler = StandardScaler()
        Gt_scaled = scaler.fit_transform(Gt)

        Gt_train, Gt_test, mod_train, mod_test = train_test_split(Gt_scaled, mod_value, test_size=0.2, random_state=42)

        study = optuna.create_study(direction='minimize')
        study.optimize(lambda trial: objective(trial, Gt_train, Gt_test, mod_train, mod_test), n_trials=50)

        best_degree = study.best_params['degree']
        best_r2 = -study.best_value

        poly = PolynomialFeatures(degree=best_degree)
        Gt_poly = poly.fit_transform(Gt_scaled)

        model = LinearRegression()
        model.fit(Gt_poly, mod_value)

        mod_value_pred = model.predict(Gt_poly)

        # Преобразуем обратно для построения графика
        Gt_original = scaler.inverse_transform(Gt_scaled)
        order = np.argsort(Gt_original.flatten())

        ax_gt.scatter(data['Gт'], data['Модифицированное значение'], label=f'Исходные данные Pt={pt}', color=plt.cm.tab10(idx % 10), alpha=0.6)
        ax_gt.plot(Gt_original[order].flatten(), mod_value_pred[order], '-', label=f'Poly deg {best_degree}, R²: {best_r2:.2f}, Pt={pt}', color=plt.cm.tab10(idx % 10))

        # Вывод уравнения полиномиальной регрессии
        coef = model.coef_[::-1]
        intercept = model.intercept_
        equation = f"y = {intercept:.4f} "
        for i, c in enumerate(coef[1:], start=1):
            equation += f"+ ({c:.15f}) * x^{i} "
        print(f"Уравнение полинома для графика 'Модифицированное значение' от 'Gт' (степень {best_degree}), Pt={pt}:")
        print(equation)

        # График зависимости модифицированного значения от Дельта
        unique_tob_values = data['Tоб'].unique()

        for tob_value in unique_tob_values:
            df_filtered = data[data['Tоб'] == tob_value]
            df_filtered = df_filtered.sort_values(by='Gт')

            X = df_filtered['Дельта'].values.reshape(-1, 1)
            y = df_filtered['Модифицированное значение'].values

            model = LinearRegression()
            model.fit(X, y)

            slope = model.coef_[0]
            intercept = model.intercept_

            ax_delta.plot(df_filtered['Дельта'], df_filtered['Модифицированное значение'], label=f'Tоб: {tob_value}, Pt={pt}')

            print(f"Уравнение для Tоб {tob_value}, Pt={pt}: y = {slope:.4f} * x + {intercept:.10f}")

    ax_gt.set_title('Зависимость f(Pот, Gтурб)')
    ax_gt.set_xlabel('Gт')
    ax_gt.set_ylabel('Модифицированное значение')
    ax_gt.legend()
    ax_gt.grid(True)  # Добавляем сетку

    ax_delta.set_title('Зависимость f(dNe, dtоб)')
    ax_delta.set_xlabel('Дельта')
    ax_delta.set_ylabel('Модифицированное значение')
    ax_delta.legend()
    ax_delta.grid(True)  # Добавляем сетку

    plt.tight_layout()
    plt.show()

def remove_outliers_iqr(df, column_name):
    Q1 = df[column_name].quantile(0.25)
    Q3 = df[column_name].quantile(0.75)
    IQR = Q3 - Q1

    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR

    return df[(df[column_name] >= lower_bound) & (df[column_name] <= upper_bound)]

def find_temperature_pairs(pressure_temp_data):
    """
    Находит соответствующие пары температур для одинаковых дельт между разными давлениями.

    :param pressure_temp_data: словарь, где ключ — давление, значение — словарь с номинальной температурой и списком температур
    :return: словарь, где ключ — дельта, значение — список температур из разных давлений
    """
    # Словарь для хранения температур по дельтам
    delta_to_temps = {}

    # Вычисляем дельты и собираем температуры по дельтам
    for pressure, data in pressure_temp_data.items():
        nominal = data['nominal']
        temperatures = data['temperatures']
        deltas = [int(round(temp - nominal)) for temp in temperatures]
        data['deltas'] = deltas  # Сохраняем дельты обратно в данные

        for delta, temp in zip(deltas, temperatures):
            if delta not in delta_to_temps:
                delta_to_temps[delta] = []
            delta_to_temps[delta].append(temp)

    # Ищем дельты, которые присутствуют в нескольких давлениях
    result = {}
    for delta, temps in delta_to_temps.items():
        if len(temps) > 1:
            result[delta] = temps

    # Сортируем результаты по дельтам
    sorted_result = {delta: result[delta] for delta in sorted(result)}

    # Выводим результат
    print("Соответствующие пары температур для общих дельт:")
    for delta, temps in sorted_result.items():
        print(f"{delta}: {temps}")

    return sorted_result

if __name__ == "__main__":
    excel_file = '/content/drive/MyDrive/Final_test_data.xlsx'
    final_output_file = 'final_output_with_delta.csv'

    temperature_index = 9  # Индекс колонки 'Tоб'
    flow_rate_index = 0    # Индекс колонки 'Gт'
    power_index = 2        # Индекс колонки 'Ne'
    pressure_index = 11    # Индекс колонки 'Pt'

    # Загрузка данных из Excel файла
    df = load_data_from_excel(excel_file, [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
                                           13, 14, 15, 16, 17, 18, 19, 20, 21,
                                           22, 23, 24, 25, 26, 27, 28])

    # Округление давления до десятых долей
    df['Pt_rounded'] = df.iloc[:, pressure_index].round(1)

    # Получение уникальных значений давления
    unique_pt_values = df['Pt_rounded'].unique()
    print(f"Уникальные значения давления: {unique_pt_values}")

    data_list = []
    pt_values = []
    pressure_temp_data = {}  # Для хранения температур и номинальных температур

    for pt in unique_pt_values:
        print(f"\nОбработка давления Pt = {pt}")
        df_pt = df[df['Pt_rounded'] == pt].copy().drop(columns=['Pt_rounded'])

        # Сортировка данных
        sorted_df = sort_data_by_temperature_and_flow_rate(df_pt, temperature_index, flow_rate_index)

        # Находжение уникальных значений температуры
        unique_temperatures = get_unique_temperatures(sorted_df, temperature_index)

        # Поиск номинального значения температуры
        nominal_temperature = find_nominal_temperature(unique_temperatures)

        # Сохраняем данные для функции поиска пар температур
        pressure_temp_data[pt] = {
            'nominal': nominal_temperature,
            'temperatures': sorted(unique_temperatures.tolist())
        }

        # Расчет дельты
        result_df = calculate_deltas(sorted_df, temperature_index, power_index, nominal_temperature)

        # Запрос целевого значения для первых "Дельта"
        target_value = float(input("Введите целевое значение для первых 'Дельта' (например, 1.0): "))

        # Нормализация дельты
        normalized_df = normalize_deltas(result_df, temperature_index, result_df.columns.get_loc('Дельта'), target_value)

        # Удаление выбросов по столбцу 'Модифицированное значение'
        cleaned_df = remove_outliers_iqr(normalized_df, 'Модифицированное значение')

        # Автоматическое переименование столбцов после создания новых столбцов
        column_names = [
            'Gт', 'Qт', 'Ne', 'qe', 'Gcн', 'Ipв', 'Toхл', 'Qот', 'w',
            'Tоб', 'Tпр', 'Pt', 'Pдиаф', 'Psн', 'Pk', 'Gк',
            'G:Д03', 'G:С29', 'T:Д03', 'P:Д03', 'P:Ч13', 'T:Ч13',
            'P:Ч17', 'P:Ч20', 'P:Ч29', 'T:Р27', 'T:ДФ', 'Дельта', 'Модифицированное значение'
        ]

        # Проверяем, совпадает ли количество столбцов
        if len(column_names) == cleaned_df.shape[1]:
            cleaned_df.columns = column_names
        else:
            print(f"Количество столбцов в DataFrame ({cleaned_df.shape[1]}) не соответствует ожидаемому ({len(column_names)}). Автоматическое переименование невозможно.")
            continue

        # Сохранение результата
        output_file = f'final_output_with_delta_Pt_{pt}.csv'
        save_data(cleaned_df, output_file)
        print(f"Обработка давления Pt = {pt} завершена.")

        data_list.append(cleaned_df)
        pt_values.append(pt)

    # Вызов функции для поиска пар температур
    find_temperature_pairs(pressure_temp_data)

    # Построение графиков
    main_plot(data_list, pt_values)
код возвращает:
Уникальные значения давления: [1.2 1.5]

Обработка давления Pt = 1.2
Уникальные значения температуры: [38 43 48 53 58 63 68]
Введите номинальное значение температуры обратной сетевой воды (например, 70): 53
Введите целевое значение для первых 'Дельта' (например, 1.0): 0.05
<ipython-input-2-21318f1e057b>:100: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df['Модифицированное значение'] = 0.0
Коэффициент k для Tоб 38: 0.17857142857142785
Обработка давления Pt = 1.2 завершена.

Обработка давления Pt = 1.5
Уникальные значения температуры: [50 55 60 65 70]
Введите номинальное значение температуры обратной сетевой воды (например, 70): 70
Введите целевое значение для первых 'Дельта' (например, 1.0): 0.063
<ipython-input-2-21318f1e057b>:100: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df['Модифицированное значение'] = 0.0
[I 2024-12-01 15:23:17,296] A new study created in memory with name: no-name-37c2588d-74c1-4386-97f4-9c75e311b336
[I 2024-12-01 15:23:17,329] Trial 0 finished with value: -0.9332533314015535 and parameters: {'degree': 4}. Best is trial 0 with value: -0.9332533314015535.
[I 2024-12-01 15:23:17,336] Trial 1 finished with value: -0.9333805529648781 and parameters: {'degree': 3}. Best is trial 1 with value: -0.9333805529648781.
[I 2024-12-01 15:23:17,343] Trial 2 finished with value: -0.9333805529648781 and parameters: {'degree': 3}. Best is trial 1 with value: -0.9333805529648781.
[I 2024-12-01 15:23:17,350] Trial 3 finished with value: -0.9333863839137906 and parameters: {'degree': 2}. Best is trial 3 with value: -0.9333863839137906.
[I 2024-12-01 15:23:17,357] Trial 4 finished with value: -0.9332533314015535 and parameters: {'degree': 4}. Best is trial 3 with value: -0.9333863839137906.
[I 2024-12-01 15:23:17,364] Trial 5 finished with value: -0.9250701650474981 and parameters: {'degree': 1}. Best is trial 3 with value: -0.9333863839137906.
[I 2024-12-01 15:23:17,369] Trial 6 finished with value: -0.9333863839137906 and parameters: {'degree': 2}. Best is trial 3 with value: -0.9333863839137906.
[I 2024-12-01 15:23:17,378] Trial 7 finished with value: -0.9333805529648781 and parameters: {'degree': 3}. Best is trial 3 with value: -0.9333863839137906.
[I 2024-12-01 15:23:17,385] Trial 8 finished with value: -0.9332533314015535 and parameters: {'degree': 4}. Best is trial 3 with value: -0.9333863839137906.
[I 2024-12-01 15:23:17,392] Trial 9 finished with value: -0.9250701650474981 and parameters: {'degree': 1}. Best is trial 3 with value: -0.9333863839137906.
[I 2024-12-01 15:23:17,405] Trial 10 finished with value: -0.9334174302934306 and parameters: {'degree': 5}. Best is trial 10 with value: -0.9334174302934306.
[I 2024-12-01 15:23:17,429] Trial 11 finished with value: -0.9334174302934306 and parameters: {'degree': 5}. Best is trial 10 with value: -0.9334174302934306.
Коэффициент k для Tоб 50: 0.1499999999999943
Обработка давления Pt = 1.5 завершена.
Соответствующие пары температур для общих дельт:
-15: [38, 55]
-10: [43, 60]
-5: [48, 65]
0: [53, 70]
Обработка давления Pt = 1.2
[I 2024-12-01 15:23:17,988] Trial 49 finished with value: -0.9334174302934306 and parameters: {'degree': 5}. Best is trial 10 with value: -0.9334174302934306.
[I 2024-12-01 15:23:18,028] A new study created in memory with name: no-name-d0b87c67-f820-4529-abee-f940dc930ad8
[I 2024-12-01 15:23:18,188] Trial 15 finished with value: -0.7963931740390235 and parameters: {'degree': 2}. Best is trial 9 with value: -0.7963931740390235.
Уравнение полинома для графика 'Модифицированное значение' от 'Gт' (степень 5), Pt=1.2:
y = 0.0975 + (0.000065387994208) * x^1 + (-0.001644694602162) * x^2 + (0.003248404921449) * x^3 + (0.034656644759113) * x^4 + (0.000000000000000) * x^5 
Уравнение для Tоб 38, Pt=1.2: y = 0.1786 * x + 0.0000000000
Уравнение для Tоб 43, Pt=1.2: y = 0.2174 * x + -0.0000000000
Уравнение для Tоб 48, Pt=1.2: y = 0.4545 * x + 0.0000000000
Уравнение для Tоб 58, Pt=1.2: y = -0.4167 * x + 0.0000000000
Уравнение для Tоб 63, Pt=1.2: y = -0.2000 * x + 0.0000000000
Уравнение для Tоб 68, Pt=1.2: y = -0.1220 * x + 0.0000000000
Обработка давления Pt = 1.5
[I 2024-12-01 15:23:18,739] Trial 49 finished with value: -0.7946933132748997 and parameters: {'degree': 4}. Best is trial 9 with value: -0.7963931740390235.
Уравнение полинома для графика 'Модифицированное значение' от 'Gт' (степень 2), Pt=1.5:
y = 0.0811 + (0.017758515960458) * x^1 + (0.000000000000000) * x^2 
Уравнение для Tоб 50, Pt=1.5: y = 0.1500 * x + -0.0000000000
Уравнение для Tоб 55, Pt=1.5: y = 0.1969 * x + 0.0000000000
Уравнение для Tоб 60, Pt=1.5: y = 0.2423 * x + -0.0000000000
Уравнение для Tоб 65, Pt=1.5: y = 0.4200 * x + 0.0000000000
Я бы хотел немного изменить логику расчёта столбца Модифицированное значение. Для первого из давлений логика должна остаться прежней, мы запрашиваем Целевое значение, и приводим первые значения столбца Модифицированное значение для всех температур данного значения в эту точку. Для всех последующих давлений после первого целевое значение итерируется и подбирается таким образом. Мы строим модели линейной регресси для всех температур первого давления, для которых есть соответствия из последующих давлений. Из уравнений моделей берем коэффициенты k линейной регрессии и итерируем целевую точку для последующих давлений таким образом, чтобы коэффициенты были или равны или максимально близки. 
Пример для списков соответствий температур:
Соответствующие пары температур для общих дельт:
-15: [38, 55]
-10: [43, 60]
-5: [48, 65]
0: [53, 70]
-5: [48, 65] для 48 запрашиваем целевое значение, строим модель регресии, находим коэффициент и для 65 находим такое целевое значение, при котором коэффициент регрессии будет одинаковым или очень близким с 48 и так для всех температур, которые окажутся в списке подгоняем под 48
10: [43, 60] для 43 запрашиваем целевое значение, строим модель регресии, находим коэффициент и для 60 находим такое целевое значение, при котором коэффициент регрессии будет одинаковым или очень близким с 43 и так для всех температур, которые окажутся в списке подгоняем под 43
-15: [38, 55] для 38 запрашиваем целевое значение, строим модель регресии, находим коэффициент и для 55 находим такое целевое значение, при котором коэффициент регрессии будет одинаковым или очень близким с 38 и так для всех температур, которые окажутся в списке подгоняем под 38
И так для всех найденных дельт.