import os
import pandas as pd 
import numpy as np
from sklearn.decomposition import PCA
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from sklearn.manifold import LocallyLinearEmbedding as LLE

from .base import baseml


class DimentionReduction(baseml):  # reduction
    """BaseML中的降维模块,包含['PCA', 'LDA', 'LLE']降维算法.

    Attributes:
        algorithm: 算法名称
        model: 实例化的模型
    """
    def __init__(self, algorithm = 'PCA', n_components = 2, params = {}):
        """rdc类的构造函数

        Args:
            algorithm (str, optional): 采用的降维算法. Defaults to 'PCA'.
            n_components (int, optional): 降维后保留的特征数. Defaults to 2.
            params (dict, optional): params (dict, optional): 参数字典,可自定义参数放入,参数名称详见sklearn官方文档. Defaults to {}.
        """

        super(DimentionReduction,self).__init__()   # 继承父类的构造方法
        self.algorithm = algorithm
        self.n_components = n_components

        if self.algorithm == 'PCA':     # 主成分分析
            if len(params) > 1:
                self.model = PCA(**params)
            else:
                self.model = PCA(n_components=n_components)
        elif self.algorithm == 'LDA':   # 线性判别分析
            if len(params) > 1:
                self.model = LDA(**params)
            else:
                self.model = LDA(n_components=n_components)
        elif self.algorithm == 'LLE':   # 局部线性嵌入
            if len(params) > 1:
                self.model = LLE(**params)
            else:
                self.model = LLE(n_components=n_components)

    def train(self, validate = True):
        """训练模型.

        Args:
            validate (bool, optional): 是否需要验证模型，并输出方差贡献率. Defaults to True.
        """
        if self.algorithm == 'LDA':
            if len(self.y_train) == 0:
                raise Exception("使用LDA时必须输入y标签")
            self.model.fit(self.x_train, self.y_train)
        else:
            self.model.fit(self.x_train)

        if validate and self.algorithm != 'LLE':
            explained_var = self.model.explained_variance_ratio_ # 获取贡献率
            print('累计方差贡献率为：{}'.format(explained_var))   



    def inference(self, data = np.nan):
        """使用模型进行降维

        Args:
            data (numpy, optional): 放进来降维的数据,不填默认使用self.x_train.

        Returns:
            pred: 返回降维结果，保留的特征数为刚开始输进来的.
        """
        if data is not np.nan: # 对data进行了指定
            self.x_test = data
        else:
            self.x_test = self.x_train

        if self.x_test is not []:
            pred = self.model.transform(self.x_test)
            return pred

    def fit_transform(self):
        # 一步到位地返回降维结果
        return self.model.fit_transform(self.x_train)