import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from sklearn.cluster import KMeans
import xgboost as xgb
import warnings
import random
import os
from pathlib import Path

warnings.filterwarnings("ignore")

# ✅ 设置随机种子以保证结果可复现
def set_seed(seed=42):
    random.seed(seed)
    np.random.seed(seed)
    os.environ["PYTHONHASHSEED"] = str(seed)

set_seed(42)

# ✅ 高斯隶属函数
def gauss_mf(x, mu, sigma):
    return np.exp(-((x - mu) ** 2) / (2 * sigma ** 2))

# ✅ 构建高斯隶属函数参数
def create_gaussian_mfs(X, n_mfs=3, sigma_scale=1.0):
    mf_params = []
    for i in range(X.shape[1]):
        col = X[:, i].reshape(-1, 1)
        kmeans = KMeans(n_clusters=n_mfs, random_state=42).fit(col)
        centers = np.sort(kmeans.cluster_centers_.flatten())
        sigmas = []
        for mu in centers:
            close_points = col[np.abs(col - mu) < 0.2 * np.std(col)]
            sigma = np.std(close_points) if len(close_points) > 1 else np.std(col)
            sigma = max(sigma, 1e-5) * sigma_scale
            sigmas.append(sigma)
        mf_params.append(list(zip(centers, sigmas)))
    return mf_params

# ✅ 模糊化处理
def fuzzify_data(X, mf_params):
    X_fuzzy = []
    for i in range(X.shape[1]):
        col = X[:, i]
        fuzzy_col = np.array([gauss_mf(col, mu, sigma) for mu, sigma in mf_params[i]]).T
        X_fuzzy.append(fuzzy_col)
    return np.hstack(X_fuzzy)

# ✅ 读取数据
file_path = r"D:\相似度量\火成岩.xlsx"
df = pd.read_excel(file_path)
X = df.iloc[:, [4, 5, 6]].values  # 原始特征数据（未归一化）
y = df.iloc[:, 3].values          # 原始目标变量

# ✅ 数据划分（使用原始数据索引）
train_indices = np.arange(494)
test_indices = np.arange(494, 544)
X_train_raw = X[train_indices]    # 原始训练集特征
X_test_raw = X[test_indices]      # 原始测试集特征
y_train = y[train_indices]        # 原始训练集目标
y_test = y[test_indices]          # 原始测试集目标

# ✅ 新增：显示原始数据的训练集和测试集首尾行
print("\n==== 原始训练集数据预览 ====")
print("训练集行数:", len(X_train_raw))
print("训练集特征前几行:")
print(pd.DataFrame(X_train_raw, columns=df.columns[4:7]).head().to_string())
print("\n训练集特征后几行:")
print(pd.DataFrame(X_train_raw, columns=df.columns[4:7]).tail().to_string())
print("\n训练集目标变量前几行:", y_train[:5])
print("训练集目标变量后几行:", y_train[-5:])

print("\n==== 原始测试集数据预览 ====")
print("测试集行数:", len(X_test_raw))
print("测试集特征前几行:")
print(pd.DataFrame(X_test_raw, columns=df.columns[4:7]).head().to_string())
print("\n测试集特征后几行:")
print(pd.DataFrame(X_test_raw, columns=df.columns[4:7]).tail().to_string())
print("\n测试集目标变量前几行:", y_test[:5])
print("测试集目标变量后几行:", y_test[-5:])

# ✅ 归一化到0-1（仅对特征数据归一化，保留原始数据用于预览）
scaler = MinMaxScaler()
X_scaled = scaler.fit_transform(X)  # 归一化后的数据用于模型训练

# ✅ 使用最优模糊参数
n_mfs = 3
sigma_scale = 2

# ✅ 生成模糊特征（基于归一化后的数据）
mf_params = create_gaussian_mfs(X_scaled[:494], n_mfs, sigma_scale)  # 使用归一化后的训练数据
X_train_fuzzy = fuzzify_data(X_scaled[:494], mf_params)
X_test_fuzzy = fuzzify_data(X_scaled[494:544], mf_params)

# ✅ 构建第一层模型：XGBoost + Ridge
xgb_model = xgb.XGBRegressor(
    objective='reg:squarederror',
    n_estimators=84,
    max_depth=5,
    learning_rate=0.05059780700946059,
    subsample=0.7426977796496858,
    colsample_bytree=0.7356262744010642,
    min_child_weight=5,
    reg_alpha=6.855485924725297,
    reg_lambda=0.5647626483981432,
    random_state=42,
    n_jobs=-1
)

ridge_model = Ridge(alpha=1.0)

# ✅ 训练第一层模型
xgb_model.fit(X_train_fuzzy, y_train)
ridge_model.fit(X_train_fuzzy, y_train)

# ✅ 获取一级预测
xgb_train_preds = xgb_model.predict(X_train_fuzzy)
ridge_train_preds = ridge_model.predict(X_train_fuzzy)

xgb_test_preds = xgb_model.predict(X_test_fuzzy)
ridge_test_preds = ridge_model.predict(X_test_fuzzy)

# ✅ 拼接一级输出作为二级输入
meta_X_train = np.vstack([xgb_train_preds, ridge_train_preds]).T
meta_X_test = np.vstack([xgb_test_preds, ridge_test_preds]).T

# ✅ 构建并训练元模型
meta_model = Ridge(alpha=15.038528651298671)
meta_model.fit(meta_X_train, y_train)

# ✅ 最终预测
final_train_preds = meta_model.predict(meta_X_train)
final_test_preds = meta_model.predict(meta_X_test)

# ✅ 评估函数
def evaluate(y_true, y_pred):
    mse = mean_squared_error(y_true, y_pred)
    rmse = np.sqrt(mse)
    mae = mean_absolute_error(y_true, y_pred)
    r2 = r2_score(y_true, y_pred)
    return mse, rmse, mae, r2

# ✅ 输出性能指标
train_mse, train_rmse, train_mae, train_r2 = evaluate(y_train, final_train_preds)
test_mse, test_rmse, test_mae, test_r2 = evaluate(y_test, final_test_preds)

print("\n==== 双基学习器堆叠模型（XGBoost + Ridge → Ridge）性能 ====")
print(f"训练集: MSE = {train_mse:.4f}, RMSE = {train_rmse:.4f}, MAE = {train_mae:.4f}, R² = {train_r2:.4f}")
print(f"测试集: MSE = {test_mse:.4f}, RMSE = {test_rmse:.4f}, MAE = {test_mae:.4f}, R² = {test_r2:.4f}")

# ✅ 可视化预测结果
plt.figure(figsize=(12, 5))

# 训练集结果
plt.subplot(1, 2, 1)
plt.scatter(y_train, final_train_preds, color='blue', alpha=0.6, label='预测值')
plt.plot([min(y_train), max(y_train)], [min(y_train), max(y_train)], 'r--', label='理想预测线')
plt.xlabel('实际值')
plt.ylabel('预测值')
plt.title(f'训练集 (R² = {train_r2:.4f})')
plt.grid(True, alpha=0.3)
plt.legend()

# 测试集结果
plt.subplot(1, 2, 2)
plt.scatter(y_test, final_test_preds, color='green', alpha=0.6, label='预测值')
plt.plot([min(y_test), max(y_test)], [min(y_test), max(y_test)], 'r--', label='理想预测线')
plt.xlabel('实际值')
plt.ylabel('预测值')
plt.title(f'测试集 (R² = {test_r2:.4f})')
plt.grid(True, alpha=0.3)
plt.legend()

plt.tight_layout()
plt.show()

# ✅ 构建 ⟨T, I, F⟩ 字符串格式
def build_tif_string_format(X_fuzzy, n_mfs=3):
    n_features = X_fuzzy.shape[1] // n_mfs
    tif_columns = []
    for i in range(n_features):
        start = i * n_mfs
        end = start + n_mfs
        mf_values = X_fuzzy[:, start:end]
        T = mf_values[:, 1]
        I = mf_values[:, 0]
        F = mf_values[:, 2]
        tif_col = [f"⟨{t:.4f}, {i_val:.4f}, {f:.4f}⟩" for t, i_val, f in zip(T, I, F)]
        tif_columns.append(tif_col)
    return np.array(tif_columns).T

# ✅ 构建替换后的 DataFrame（保留原始结构）
df_train_full = df.iloc[:494, :].copy()
df_test_full = df.iloc[494:544, :].copy()
tif_train = build_tif_string_format(X_train_fuzzy, n_mfs=3)
tif_test = build_tif_string_format(X_test_fuzzy, n_mfs=3)

# ✅ 替换特征列为模糊化后的中智数格式
feature_cols = df.columns[[4, 5, 6]]
df_train_full.loc[:, feature_cols] = tif_train
df_test_full.loc[:, feature_cols] = tif_test

# ✅ 合并保存
desktop_path = os.path.join(Path.home(), "Desktop")
os.makedirs(desktop_path, exist_ok=True)
tif_file_path = os.path.join(desktop_path, "火成岩_单值中智数.xlsx")
df_all = pd.concat([df_train_full, df_test_full], ignore_index=True)
df_all.to_excel(tif_file_path, index=False)

print(f"✅ 模糊化后的单值中智数文件已保存：\n{tif_file_path}") 增加一句代码打印出绘制高斯隶属度函数的参数