import sys
import os

# 添加项目根目录到Python路径
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', '..')))

from myffe.tools.Loggerv1_1 import log_class
from myffe.tools.color_printer import ColorPrinter
from myffe.tools.evaluation_decorator import with_evaluation

@log_class(type='use')
class Normalizer:
    def __init__(self, shortcut_norm=None):
        self.shortcut_norm = shortcut_norm
    
    @with_evaluation
    def __call__(self, data, data_label=None):
        if self.shortcut_norm is None:
            ColorPrinter.print_error('错误: 缺少必要的参数 shortcut_norm')
            return data
        return self.MY_Normalization(data, norm_params=self.shortcut_norm)
    
    def MY_Normalization(self, data, norm_params=None):
        import pandas as pd
        import numpy as np
        
        if 'normalizer_method_choice' not in norm_params:
            ColorPrinter.print_error('错误: 缺少必要的参数 normalizer_method_choice')
            return data
        
        normalizer_method_choice = norm_params['normalizer_method_choice']
        
        # 获取要归一化的列
        normalized_col = norm_params.get('normalized_col')             
        
        # 检查数据大小
        data_size = data.memory_usage(deep=True).sum() / (1024 * 1024)  # 转换为MB
        ColorPrinter.print_info(f'数据大小: {data_size:.2f} MB')
        
        # 归一化方法映射
        normalizer_config = {
            'L1归一化': {
                'params': {'norm': 'l1'},
                'message': '使用 L1 归一化'
            },
            'L2归一化': {
                'params': {'norm': 'l2'},
                'message': '使用 L2 归一化'
            },
            'Max归一化': {
                'params': {'norm': 'max'},
                'message': '使用 Max 归一化'
            },
            'Robust归一化': {
                'params': {'norm': 'l2'},
                'message': '使用 Robust 归一化'
            },
            '幂次归一化': {
                'params': {},
                'message': '使用 幂次归一化'
            },
            '对数归一化': {
                'params': {},
                'message': '使用 对数归一化'
            },
            '平方根归一化': {
                'params': {},
                'message': '使用 平方根归一化'
            }
        }
        
        # 为不同列应用不同的归一化方法
        ColorPrinter.print_info('为不同列应用不同的归一化方法')
        
        try:
            from sklearn.preprocessing import Normalizer
            from sklearn.preprocessing import RobustScaler
            
            # 创建数据副本
            normalized_data = data.copy()
            
            # 如果没有指定列，跳过处理
            if not normalized_col:
                ColorPrinter.print_info('未指定归一化列，跳过归一化处理')
                return data
            
            # 检查长度是否匹配
            if len(normalized_col) != len(normalizer_method_choice):
                ColorPrinter.print_error(f'错误: 列数量 ({len(normalized_col)}) 与方法数量 ({len(normalizer_method_choice)}) 不匹配')
                return data
            
            # 对每列应用对应的归一化方法
            for col, method in zip(normalized_col, normalizer_method_choice):
                if col in data.columns and method in normalizer_config:
                    config = normalizer_config[method]
                    ColorPrinter.print_info(f'对列 {col} 使用 {method}')
                    
                    if method == 'Robust归一化':
                        # 使用RobustScaler进行归一化
                        scaler = RobustScaler()
                        # 对单个列进行归一化
                        normalized_data[[col]] = pd.DataFrame(
                            scaler.fit_transform(normalized_data[[col]]),
                            columns=[col],
                            index=normalized_data.index
                        )
                    elif method == '幂次归一化':
                        # 使用幂次归一化
                        # 这里使用平方作为默认幂次
                        normalized_data[col] = normalized_data[col] ** 2
                        # 然后进行L2归一化
                        normalizer = Normalizer(norm='l2')
                        normalized_data[[col]] = pd.DataFrame(
                            normalizer.fit_transform(normalized_data[[col]]),
                            columns=[col],
                            index=normalized_data.index
                        )
                    elif method == '对数归一化':
                        # 使用对数归一化
                        # 确保值为正
                        min_val = normalized_data[col].min()
                        if min_val <= 0:
                            normalized_data[col] = normalized_data[col] - min_val + 0.001
                        normalized_data[col] = np.log10(normalized_data[col])
                    elif method == '平方根归一化':
                        # 使用平方根归一化
                        # 确保值非负
                        normalized_data[col] = normalized_data[col].clip(lower=0)
                        normalized_data[col] = np.sqrt(normalized_data[col])
                    else:
                        # 使用Normalizer进行归一化
                        normalizer = Normalizer(**config['params'])
                        # 对单个列进行归一化
                        normalized_data[[col]] = pd.DataFrame(
                            normalizer.fit_transform(normalized_data[[col]]),
                            columns=[col],
                            index=normalized_data.index
                        )
                else:
                    ColorPrinter.print_error(f'列 {col} 不存在或归一化方法 {method} 无效')
            
            return normalized_data
        except Exception as e:
            ColorPrinter.print_error(f'归一化过程出错: {e}')
            return data


"""
参数说明文档:

shortcut_norm 参数字典:
    - normalizer_method_choice: 归一化方式选择
      类型: 列表
      可选值: ['L1归一化', 'L2归一化', 'Max归一化', 'Robust归一化', '幂次归一化', '对数归一化', '平方根归一化']
      默认值: 无（必须指定）
      说明: 为每列指定归一化方法，列表长度必须与normalized_col相同
        - 'L1归一化': 使用L1范数进行归一化，每个样本的特征值除以L1范数
        - 'L2归一化': 使用L2范数进行归一化，每个样本的特征值除以L2范数（最常用）
        - 'Max归一化': 使用最大值进行归一化，每个样本的特征值除以最大值
        - 'Robust归一化': 使用RobustScaler进行归一化，基于中位数和四分位数，对异常值不敏感
        - '幂次归一化': 使用平方作为默认幂次，然后进行L2归一化，适合偏度较大的数据
        - '对数归一化': 使用对数变换，适合取值范围大的正数数据
        - '平方根归一化': 使用平方根变换，适合非负数据
    - normalized_col: 要归一化的列
      类型: 列表
      可选值: 数据集中的列名
      默认值: 无（必须指定）
      说明: 指定要进行归一化的列，列表长度必须与normalizer_method_choice相同

使用示例:
    # 为不同列应用不同的归一化方法
    norm_params = {
        'normalizer_method_choice': ['L1归一化', 'L2归一化', 'Max归一化'],
        'normalized_col': ['feature1', 'feature2', 'feature3']
    }
    normalizer = Normalizer(shortcut_norm=norm_params)
    normalized_data = normalizer(data)
"""


if __name__ == "__main__":
    import pandas as pd
    import numpy as np
    
    ColorPrinter.print_any("=" * 60)
    ColorPrinter.print_info("归一化处理测试程序")
    ColorPrinter.print_any("=" * 60)
    
    # 创建测试数据
    np.random.seed(42)
    test_data = pd.DataFrame({
        'feature1': np.random.randint(1, 100, 10),
        'feature2': np.random.randint(1, 100, 10),
        'feature3': np.random.randint(1, 100, 10)
    })
    
    ColorPrinter.print_any("\n原始数据:")
    ColorPrinter.print_any(test_data)
    ColorPrinter.print_any("\n数据统计:")
    ColorPrinter.print_any(test_data.describe())
    
    # 测试为不同列应用不同的归一化方法
    ColorPrinter.print_any(f"\n{'=' * 60}")
    ColorPrinter.print_info("测试: 为不同列应用不同的归一化方法")
    ColorPrinter.print_any('=' * 60)
    
    norm_params = {
        'normalizer_method_choice': ['L1归一化', 'L2归一化', 'Max归一化'],
        'normalized_col': ['feature1', 'feature2', 'feature3']
    }
    
    normalizer = Normalizer(shortcut_norm=norm_params)
    normalized_data = normalizer(test_data.copy())
    
    ColorPrinter.print_any("\n归一化后的数据:")
    ColorPrinter.print_any(normalized_data)
    ColorPrinter.print_any("\n归一化后数据统计:")
    ColorPrinter.print_any(normalized_data.describe())
    
    ColorPrinter.print_any("\n" + "=" * 60)
    ColorPrinter.print_success("测试完成！")
    ColorPrinter.print_any("=" * 60)
