import numpy as np
from numpy import inf
import pandas as pd


def get_data_describe(data:pd.DataFrame,
                      data_dict:pd.DataFrame=pd.DataFrame(),
                      missing: list = [np.nan, None, ''],                      
                      key:list=['#Sum','#Min','#Mean','#Q1','#Median','#Q3','#Max','#Std','Top1','Top2','Top3'],
                      precision:int = 2,
                      language:str='en')->pd.DataFrame:
    """
    获取目标数据集的描述统计信息
    Args:
        data:DataFrame,需要进行描述统计分析的数据集
        data_dict:DataFrame,数据字典，包含列名（特征英文名Name）和描述(特征中文名 Label)两列的数据集
        missing:list,缺失值列表
        key:list,额外增加的统计量关键词,例如：['#Sum','#Min','#Q1','#Median','#Q3','#Max','#Mode','#Var','#Std','#Mean','#Kurt','#Skew','#StdMean','#Range','#Cv','#Sum_of_squares','Top1','Top2','Top3']
        precision:数据精度，小数点位数，默认为2
        language:str,数据结果标题列显示语言，默认为 'en',可手动修改为'cn'

    Returns:
        描述统计结果数据集
    """
    # 获取数据描述
    df = data.copy()

    result=[]
    col_total=len(df)
    for col in df.columns:
        # 数据类型
        col_dtype = str(df[col].dtype)

        # 缺失值替换
        df[col].replace(missing, [np.nan] * len(missing), inplace=True)

        # 数据角色
        if col.lower() == 'y' or col.lower() == 'target':
            col_role = 'target'
        elif col.lower().find('_id') > 0:
            col_role = 'ID'
        else:
            col_role = 'feature'

        # 统计量计算
        col_n = len(df[df[col].notnull()])
        col_missing= len(df[df[col].isna()])
        col_unique=df[col].nunique()

        # 如果列是数值型，并且不是全为空，则计算统计量
        if col_dtype in ('int64', 'float64','float32') and len(df[df[col].notnull()]):
            col_sum = df[col].sum()
            col_min = df[col].min()
            col_max = df[col].max()
            col_mean = df[col].mean()
            col_median = df[col].median()
            col_mode = df[col].mode().iloc[0]
            col_std = df[col].std()
            col_var = df[col].var()
            col_sem = df[col].sem()
            col_skew = df[col].skew()
            col_kurt = df[col].kurt()
            col_q1= df[col].quantile(0.25)
            col_q3 = df[col].quantile(0.75)
            col_range = col_max - col_min
            col_sum_of_squares = df[col].pow(2).sum()

            if col_mean != 0:
                col_cv = col_std / col_mean
            else:
                col_cv = np.nan
        else:
            col_sum = np.nan
            col_min = np.nan
            col_max = np.nan
            col_mean = np.nan
            col_median = np.nan
            col_mode = np.nan
            col_std = np.nan
            col_var = np.nan
            col_sem = np.nan
            col_skew = np.nan
            col_kurt = np.nan
            col_q1 = np.nan
            col_q3 = np.nan
            col_range = np.nan
            col_sum_of_squares = np.nan
            col_cv =np.nan

        if len(df[df[col].notnull()]) > 0:
            col_top_list = df[col].value_counts().index
            if len(col_top_list) >= 3:
                col_top3 = col_top_list[2]
                col_top2 = col_top_list[1]
                col_top1 = col_top_list[0]
            elif len(col_top_list) >= 2:
                col_top3 = np.nan
                col_top2 = col_top_list[1]
                col_top1 = col_top_list[0]
            elif len(col_top_list) >= 1:
                col_top3 = np.nan
                col_top2 = np.nan
                col_top1 = col_top_list[0]
            else:
                col_top3 = np.nan
                col_top2 = np.nan
                col_top1 = np.nan
        result.append([col,'',col_dtype,col_role,col_total,col_n,col_n/col_total,col_missing,col_missing/col_total,col_unique,col_unique/col_total,col_sum,
                       col_min,col_mean,col_q1,col_median,col_q3,col_max,col_range,col_mode,col_var,col_std,col_cv,col_sem,col_skew,col_kurt,col_sum_of_squares,col_top1,col_top2,col_top3])
    result=pd.DataFrame(result,columns=['Name','Describe','Type','Role','#Count','#N','%N','#Missing','%Missing','#Unique','%Unique',
                                        '#Sum','#Min','#Mean','#Q1','#Median','#Q3','#Max','#Range','#Mode','#Var','#Std','#Cv','#StdMean','#Skew','#Kurt','#Sum_of_squares','Top1','Top2','Top3'])

    # 设置显示格式
    result['#Count'] = result['#Count'].apply(lambda x: round(x, precision))
    result['#N'] = result['#N'].apply(lambda x: round(x, precision))
    result['#Missing'] = result['#Missing'].apply(lambda x: round(x, precision))
    result['#Unique'] = result['#Unique'].apply(lambda x: round(x, precision))
    result['#Sum'] = result['#Sum'].apply(lambda x: round(x, precision))
    result['#Min'] = result['#Min'].apply(lambda x: round(x, precision))
    result['#Mean'] = result['#Mean'].apply(lambda x: round(x, precision))
    result['#Q1'] = result['#Q1'].apply(lambda x: round(x, precision))
    result['#Median'] = result['#Median'].apply(lambda x: round(x, precision))
    result['#Q3'] = result['#Q3'].apply(lambda x: round(x, precision))
    result['#Max'] = result['#Max'].apply(lambda x: round(x, precision))
    result['#Range'] = result['#Range'].apply(lambda x: round(x, precision))
    result['#Mode'] = result['#Mode'].apply(lambda x: round(x, precision))
    result['#Var'] = result['#Var'].apply(lambda x: round(x, precision))
    result['#Std'] = result['#Std'].apply(lambda x: round(x, precision))
    result['#Sum_of_squares'] = result['#Sum_of_squares'].apply(lambda x: round(x, precision))
    result['#StdMean'] = result['#StdMean'].apply(lambda x: round(x, precision))
    result['#Skew'] = result['#Skew'].apply(lambda x: round(x, precision))
    result['#Kurt'] = result['#Kurt'].apply(lambda x: round(x, precision))
    result['%N'] = result['%N'].apply(lambda x:format(x,'.' + str(precision) + '%'))
    result['%Missing'] = result['%Missing'].apply(lambda x:format(x,'.' + str(precision) + '%'))
    result['%Unique'] = result['%Unique'].apply(lambda x:format(x,'.' + str(precision) + '%'))
    result['#Cv'] = result['#Cv'].apply(lambda x: format(x, '.' + str(precision) + '%'))


    result.index.name='No.'
    result.reset_index(drop=True,inplace=True)
    if language == 'cn':
        col_list = ['Name', 'Describe', 'Type', 'Role', '#Count', '#N', '%N', '#Missing', '%Missing', '#Unique','%Unique'] + key
        result = result[col_list]
        result.rename(columns={'Name':'名称','Describe':'描述','Type':'类型','Role':'角色','#Count':'#数量','#Missing':'#缺失值',
                               '%Missing':'%缺失值','#Unique':'#唯一值','%Unique':'%唯一值','#Sum':'#合计','#Min':'#最小值',
                               '#Mean':'#均值','#Median':'#中位数','#Max':'#最大值','#Mode':'#众数','#Var':'#方差','#Std':'#标准差',
                               '#Skew':'#偏度','#Kurt':'#峰度','#Q1':'下四分位数','Q3':'上四分位数','#StdMean':'#标准误差','#Range':'#极差','#Cv':'#变异系数',
                               '#Sum_of_squares':'#平方和'},
                      inplace=True)
    else:
        col_list = ['Name','Describe','Type','Role','#Count','#N','%N','#Missing','%Missing','#Unique','%Unique'] + key
        result = result[col_list]

    if not data_dict.empty:
        result = result.merge(data_dict, on='Name', how='left')
        result['Describe'][result.iloc[:, -1].notnull()] = result.iloc[:, -1]
        result = result.iloc[:,:-1]

    return result
