# model.py - 客户流失预测模型（随机森林优化版）
import pandas as pd
import numpy as np
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import (
    accuracy_score, classification_report,
    roc_auc_score, confusion_matrix
)
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
# 导入你已稳定的data.py中的函数
from src.data import load_data, split_data

def build_preprocessor(X):
    """构建特征预处理管道：区分数值/类别特征，分别处理"""
    # 分离数值特征和类别特征列名
    numeric_cols = X.select_dtypes(include=['int64', 'float64']).columns.tolist()
    categorical_cols = X.select_dtypes(include=['object']).columns.tolist()
    
    # 定义数值特征处理器（标准化）
    numeric_transformer = Pipeline(steps=[
        ('scaler', StandardScaler())
    ])
    
    # 定义类别特征处理器（独热编码）
    categorical_transformer = Pipeline(steps=[
        ('onehot', OneHotEncoder(sparse_output=False, drop='first', handle_unknown='ignore'))
    ])
    
    # 合并处理器
    preprocessor = ColumnTransformer(
        transformers=[
            ('num', numeric_transformer, numeric_cols),
            ('cat', categorical_transformer, categorical_cols)
        ])
    
    return preprocessor

if __name__ == "__main__":
    # ========== 1. 加载并切分数据 ==========
    print("📌 开始加载数据集...")
    df = load_data()
    X_train, X_test, y_train, y_test = split_data(df)
    print("✅ 数据加载&切分完成")
    
    # ========== 2. 构建预处理+模型管道 ==========
    print("\n📌 构建预处理管道和随机森林模型...")
    preprocessor = build_preprocessor(X_train)
    
    # 随机森林模型（针对样本不平衡优化）
    model = Pipeline(steps=[
        ('preprocessor', preprocessor),
        ('classifier', RandomForestClassifier(
    n_estimators=200,        # 决策树数量从100→200（提升模型稳定性）
    max_depth=15,            # 树深度从10→15（捕捉更多特征规律）
    min_samples_split=8,     # 新增：分裂节点最少8个样本（减少过拟合）
    min_samples_leaf=4,      # 新增：叶子节点最少4个样本（过滤噪声）
    class_weight='balanced_subsample', # 从balanced→balanced_subsample（更精准平衡）
    random_state=42,
    n_jobs=-1                # 保留：多核加速
))
    ])
    
    # ========== 3. 模型训练 ==========
    print("📌 开始训练随机森林模型...")
    model.fit(X_train, y_train)
    print("✅ 模型训练完成")
    
    # ========== 4. 模型评估 ==========
    print("\n📊 模型评估结果：")
    # 预测概率和类别
    y_pred = model.predict(X_test)
    y_pred_proba = model.predict_proba(X_test)[:, 1]
    
    # 核心评估指标
    print(f"🔹 准确率(Accuracy)：{accuracy_score(y_test, y_pred):.4f}")
    print(f"🔹 AUC值：{roc_auc_score(y_test, y_pred_proba):.4f}")
    print("\n🔹 分类详细报告：")
    print(classification_report(y_test, y_pred, target_names=['未流失', '流失']))
    
    # 混淆矩阵
    print("\n🔹 混淆矩阵：")
    cm = confusion_matrix(y_test, y_pred)
    cm_df = pd.DataFrame(
        cm, 
        index=['实际未流失', '实际流失'],
        columns=['预测未流失', '预测流失']
    )
    print(cm_df)
    
    # ========== 5. 特征重要性分析 ==========
    print("\n📈 特征重要性TOP10：")
    # 获取特征名
    preprocessor.fit(X_train)
    feature_names = []
    # 数值特征名
    feature_names.extend(preprocessor.transformers_[0][2])
    # 类别特征编码后的名字
    cat_features = preprocessor.named_transformers_['cat'].named_steps['onehot']
    feature_names.extend(cat_features.get_feature_names_out(preprocessor.transformers_[1][2]))
    
    # 获取特征重要性
    importances = model.named_steps['classifier'].feature_importances_
    # 排序并输出TOP10
    importance_df = pd.DataFrame({
        '特征名': feature_names,
        '重要性': importances
    }).sort_values('重要性', ascending=False).head(10)
    print(importance_df)