import pandas as pd
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline
from sklearn.metrics import r2_score, mean_squared_error
import matplotlib.pyplot as plt
import joblib
from matplotlib import rcParams

# 设置中文字体支持
rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans']
rcParams['axes.unicode_minus'] = False  # 解决负号显示问题


def fit_circulating_water_flow(data):
    """
    加载数据并拟合循环水流量与运行参数的关系

    Returns:
        model: 训练好的多项式回归模型
        metrics: 包含R²和MSE的评估指标字典
    """

    # 提取特征和目标变量
    X = data[['循泵运行方式数字编码', '冷却塔进水蝶阀开启数量']]
    y = data['循环水流量']

    # 构建二阶多项式回归模型
    model = Pipeline([
        ('poly', PolynomialFeatures(degree=2, include_bias=False)),
        ('linear', LinearRegression())
    ])

    # 训练模型
    model.fit(X, y)

    # 计算评估指标
    y_pred = model.predict(X)
    r2 = r2_score(y, y_pred)
    mse = mean_squared_error(y, y_pred)

    metrics = {
        'r2_score': r2,
        'mse': mse,
        'rmse': np.sqrt(mse)
    }

    return model, metrics


def test_fitting_effect(model, show_plot=True):
    """
    测试拟合效果并可视化结果

    Args:
        model: 已训练的模型
        show_plot: 是否显示图表

    Returns:
        test_results: 测试结果字典
    """
    # 加载原始数据用于测试
    file_path = r"D:\国华燃机\data.csv"
    data = pd.read_csv(file_path)

    # 准备测试数据
    X_test = data[['循泵运行方式数字编码', '冷却塔进水蝶阀开启数量']]
    y_actual = data['循环水流量']

    # 预测值
    y_predicted = model.predict(X_test)

    # 计算残差
    residuals = y_actual - y_predicted

    # 统计信息
    test_results = {
        'actual_values': y_actual.values,
        'predicted_values': y_predicted,
        'residuals': residuals,
        'mean_residual': np.mean(residuals),
        'std_residual': np.std(residuals)
    }

    if show_plot:
        # 创建图表展示拟合效果
        fig, axes = plt.subplots(2, 2, figsize=(12, 10))

        # 实际值 vs 预测值散点图
        axes[0, 0].scatter(y_actual, y_predicted, alpha=0.6)
        axes[0, 0].plot([y_actual.min(), y_actual.max()],
                        [y_actual.min(), y_actual.max()], 'r--', lw=2)
        axes[0, 0].set_xlabel('实际循环水流量')
        axes[0, 0].set_ylabel('预测循环水流量')
        axes[0, 0].set_title('实际值 vs 预测值')

        # 残差分布直方图
        axes[0, 1].hist(residuals, bins=30, alpha=0.7)
        axes[0, 1].set_xlabel('残差')
        axes[0, 1].set_ylabel('频次')
        axes[0, 1].set_title('残差分布')

        # 残差 vs 预测值
        axes[1, 0].scatter(y_predicted, residuals, alpha=0.6)
        axes[1, 0].axhline(y=0, color='r', linestyle='--')
        axes[1, 0].set_xlabel('预测循环水流量')
        axes[1, 0].set_ylabel('残差')
        axes[1, 0].set_title('残差 vs 预测值')

        # Q-Q图检验残差正态性
        from scipy import stats
        stats.probplot(residuals, dist="norm", plot=axes[1, 1])
        axes[1, 1].set_title('残差Q-Q图')

        plt.tight_layout()
        plt.show()

    return test_results


def evaluate_fitting_success(metrics, test_results):
    """
    评估拟合是否成功

    Args:
        metrics: 模型评估指标
        test_results: 测试结果

    Returns:
        evaluation: 评估结论
    """
    r2 = metrics['r2_score']
    rmse = metrics['rmse']
    mean_residual = test_results['mean_residual']
    std_residual = test_results['std_residual']

    # 评估标准
    r2_threshold = 0.8  # R² > 0.8 认为拟合较好
    residual_threshold = 0.1  # 平均残差接近0

    evaluation = {
        'is_successful': r2 > r2_threshold and abs(mean_residual) < residual_threshold,
        'r2_rating': '优秀' if r2 > 0.9 else '良好' if r2 > 0.8 else '一般' if r2 > 0.7 else '较差',
        'residual_rating': '理想' if abs(mean_residual) < 0.05 else '可接受' if abs(mean_residual) < 0.1 else '需改进',
        'recommendations': []
    }

    # 根据评估结果提供建议
    if r2 <= 0.7:
        evaluation['recommendations'].append("考虑使用更高阶的多项式或其它非线性模型")
    if abs(mean_residual) > 0.1:
        evaluation['recommendations'].append("检查是否存在系统性偏差，可能需要添加偏置项")
    if std_residual > rmse * 0.5:
        evaluation['recommendations'].append("残差波动较大，建议检查异常数据点")

    return evaluation


def save_model(model, filename='circulating_water_flow_model.pkl'):
    """保存训练好的模型"""
    joblib.dump(model, filename)
    print(f"模型已保存到 {filename}")


def load_model(filename='circulating_water_flow_model.pkl'):
    """加载已保存的模型"""
    model = joblib.load(filename)
    return model


def predict_new_condition(model, pump_mode, valve_count):
    """
    预测新的工况点

    Args:
        model: 已加载的模型
        pump_mode: 循泵运行方式数字编码
        valve_count: 冷却塔进水蝶阀开启数量

    Returns:
        predicted_flow: 预测的循环水流量
    """
    X = np.array([[pump_mode, valve_count]])
    predicted_flow = model.predict(X)
    return predicted_flow[0]


def find_outliers(data, model):
    X = data[['循泵运行方式数字编码', '冷却塔进水蝶阀开启数量']]
    y_actual = data['循环水流量']
    y_pred = model.predict(X)
    residuals = y_actual - y_pred

    # 找出残差绝对值大于某个阈值的数据点
    outlier_threshold = 4000
    outliers = data[np.abs(residuals) > outlier_threshold]

    return outliers


def remove_outliers_and_save(data, model, output_file='data-删除流量异常数据.csv'):
    """
    删除偏差较大的数据点并保存到新文件

    Args:
        data: 原始数据
        model: 训练好的模型
        output_file: 输出文件路径

    Returns:
        cleaned_data: 清理后的数据
    """
    # 预测值
    X = data[['循泵运行方式数字编码', '冷却塔进水蝶阀开启数量']]
    y_pred = model.predict(X)

    # 计算残差
    residuals = data['循环水流量'] - y_pred

    # 设置阈值，删除残差绝对值大于5000的数据点
    outlier_threshold = 5000
    mask = np.abs(residuals) <= outlier_threshold

    # 过滤数据
    cleaned_data = data[mask].reset_index(drop=True)

    # 保存到新文件
    cleaned_data.to_csv(output_file, index=False)
    print(f"已保存清理后的数据到 {output_file}")
    print(f"原始数据量: {len(data)}")
    print(f"清理后数据量: {len(cleaned_data)}")
    print(f"删除数据量: {len(data) - len(cleaned_data)}")

    return cleaned_data


def find_identical_inputs_large_output_diff(data, threshold=None):
    """
    查找输入参数完全相同但输出结果差异较大的数据点

    Args:
        data: 包含训练数据的DataFrame
        threshold: 输出差异的阈值，如果为None则使用数据标准差的10%

    Returns:
        large_diff_groups: 包含相同输入但输出差异大的数据组
    """
    # 按照输入参数分组
    grouped = data.groupby(['循泵运行方式数字编码', '冷却塔进水蝶阀开启数量'])

    # 如果没有指定阈值，使用循环水流量标准差的10%作为默认阈值
    if threshold is None:
        threshold = data['循环水流量'].std() * 0.1

    large_diff_groups = []

    # 遍历每个分组
    for (pump_mode, valve_count), group in grouped:
        # 如果同一组中有多个数据点
        if len(group) > 1:
            flow_rates = group['循环水流量'].values
            max_diff = np.max(flow_rates) - np.min(flow_rates)

            # 如果最大差异超过阈值
            if max_diff > threshold:
                large_diff_groups.append({
                    'pump_mode': pump_mode,
                    'valve_count': valve_count,
                    'count': len(group),
                    'flow_rates': flow_rates,
                    'max_diff': max_diff,
                    'mean_flow': np.mean(flow_rates),
                    'std_flow': np.std(flow_rates),
                    'data_indices': group.index.tolist()
                })

    # 按最大差异降序排列
    large_diff_groups.sort(key=lambda x: x['max_diff'], reverse=True)

    return large_diff_groups


def display_identical_input_differences(groups, top_n=10):
    """
    显示相同输入参数但输出差异较大的组

    Args:
        groups: 具有相同输入但输出差异大的数据组
        top_n: 显示前N个最显著的组
    """
    if not groups:
        print("未找到输入参数相同但输出差异较大的数据组")
        return

    print(f"找到 {len(groups)} 组输入参数相同但输出差异较大的数据")
    print(f"显示前 {min(top_n, len(groups))} 组:")
    print("-" * 80)

    for i, group in enumerate(groups[:top_n]):
        print(f"第 {i + 1} 组:")
        print(f"  输入参数: 循泵模式={group['pump_mode']}, 蝶阀数={group['valve_count']}")
        print(f"  数据点数量: {group['count']}")
        print(f"  流量范围: [{np.min(group['flow_rates']):.2f}, {np.max(group['flow_rates']):.2f}]")
        print(f"  最大差异: {group['max_diff']:.2f}")
        print(f"  平均流量: {group['mean_flow']:.2f}")
        print(f"  流量标准差: {group['std_flow']:.2f}")
        print(f"  数据索引: {group['data_indices']}")
        print()


def train_model(data):
    # 训练模型
    model, metrics = fit_circulating_water_flow(data)
    print(f"R²得分: {metrics['r2_score']:.4f}")
    print(f"RMSE: {metrics['rmse']:.4f}")

    outliers = find_outliers(data, model)
    print(f"发现 {len(outliers)} 个异常数据点，共{data.shape}条数据")

    output_file = 'data-删除流量异常数据.csv'
    # 删除异常数据并保存
    cleaned_data = remove_outliers_and_save(data, model, output_file)
    # 重新加载删除后的数据拟合并测试
    data1 = pd.read_csv(output_file)
    model, metrics = fit_circulating_water_flow(data1)
    print(f"R²得分: {metrics['r2_score']:.4f}")
    print(f"RMSE: {metrics['rmse']:.4f}")
    # 保存模型
    save_model(model)

    # 测试效果
    test_results = test_fitting_effect(model)
    evaluation = evaluate_fitting_success(metrics, test_results)
    print(f"拟合是否成功: {evaluation['is_successful']}")
    print(f"R²评级: {evaluation['r2_rating']}")
    print(f"残差评级: {evaluation['residual_rating']}")

    if evaluation['recommendations']:
        print("改进建议:")
        for rec in evaluation['recommendations']:
            print(f"- {rec}")


if __name__ == "__main__":
    # 加载数据
    file_path = r"D:\国华燃机\data.csv"
    data = pd.read_csv(file_path)
    identical_groups = find_identical_inputs_large_output_diff(data)
    display_identical_input_differences(identical_groups)

    # train_model(data)
