# coding=utf-8

"""
模型公共模块
"""
from __future__ import division

import numpy as np
import pandas as pd
from sklearn import metrics
from scipy import stats
import xgboost as xgb


def get_type(data):
    """
    判断字符串是否是整型，即转化成int是否会丢精度
    return 0: int 1:float 2:not int or float
    """
    try:
        data_f = float(data)
    except:
        return 2
    if abs(int(data_f) - data_f) > 0.000001:
        return 1
    else:
        return 0


def to_series(data):
    """
    根据数据精度，构造相应类型的Series
    return Series
    """
    data_type = 0
    for i in data:
        tmp_type = get_type(i)
        if tmp_type == 2:
            data_type = tmp_type
            break
        elif tmp_type == 1:
            data_type = tmp_type
    if data_type == 0:
        data_series = pd.Series([int(float(i)) for i in data], dtype=int)
    elif data_type == 1:
        data_series = pd.Series([float(i) for i in data], dtype=float)
    else:
        data_series = pd.Series(data)
    return data_series


def feature_index_translate_name(data, feature_map):
    """
    translate feature index to name
    return feature name
    """
    tmp_data = data[1:]
    name = list(feature_map['name'][feature_map['index'] == int(tmp_data)])
    if len(name) == 0:
        return data
    else:
        return name[0]


def get_best_acc(y_prob, y_true):
    """
    get best acc for max f1_score
    return best acc
    """
    best_acc = 0.0
    max_f1_score = 0.0
    best_threshold = 0.5
    for clf_threshold in range(1, 100):
        threshold = 0.01 * clf_threshold
        y_pred = [1 if x > threshold else 0 for x in y_prob]
        f1_score = metrics.f1_score(y_true, y_pred)
        acc = metrics.accuracy_score(y_true, y_pred)
        if (f1_score > max_f1_score) or (f1_score == max_f1_score and acc > best_acc):
            best_acc = acc
            best_threshold = threshold
            max_f1_score = f1_score
    return [best_acc, best_threshold, max_f1_score]


def get_feature_importance(model, feature_map_file=None):
    """
    get and save model feature importances
    return feature importances
    """
    feature_importances = pd.DataFrame([model.get_fscore()])
    feature_importances = feature_importances.T
    feature_importances = feature_importances.sort(columns=[0], ascending=False)
    feature_importances = feature_importances.reset_index()
    if feature_map_file is not None:
        feature_map = pd.read_table(feature_map_file, sep='\t', iterator=False,
                                    header=None, names=['name', 'index'])
        feature_importances['name'] = feature_importances['index'].apply(
            feature_index_translate_name, args=(feature_map,))
    return feature_importances


def xgb_train(train_data, test_data=None, n_estimators=300, learning_rate=0.1, subsample=0.5,
              max_depth=5, nthread=20):
    """
    use xgboost train model and save model
    return model
    """
    param = {}
    param['silent'] = 0
    param['objective'] = 'reg:linear'
    param['bst:eta'] = float(learning_rate)
    param['bst:max_depth'] = int(max_depth)
    param['nthread'] = int(nthread)
    param['eval_metric'] = ['logloss', 'rmse']
    param['subsample'] = float(subsample)
    # param['num_feature'] = 'sqrt'
    num_round = int(n_estimators)

    if test_data is not None:
        eval_list = [(test_data, 'test'), (train_data, 'train')]
    else:
        eval_list = [(train_data, 'train')]

    bst = xgb.train(param, train_data, num_round, eval_list)
    return bst


def xgb_predict(data, model, threshold=0.5):
    """
    use xgboost model predict data
    return [prob, predict_label]
    """
    model.set_param('nthread', 1)
    y_pred = pd.Series(model.predict(data))
    # y_prob = pd.Series(model.predict(data))
    # y_pred = pd.Series([1 if x > threshold else 0 for x in y_prob])
    # return [y_prob, y_pred]
    return [y_pred, y_pred]


def classification_evaluate(y_true, y_prob, y_pred, threshold=None):
    """
    evaluate xgboost model predict result
    return [result_score, best_acc, result_auc, precision, recall, f1_score, coeff]
    """
    y_pred = pd.Series(y_pred)
    coeff = stats.spearmanr(y_true, y_pred, axis=0)
    df = pd.DataFrame({"y_true": y_true, "y_pred": y_pred, "abs_delta": abs(y_true-y_pred)})
    print df.head(10)
    print df.sort_values(by="abs_delta", ascending=False).head(10)

    print "the shape of the origin sample is", df.shape
    print 'Spearmanr coeff is %5.6f' % (coeff[0])
    mse = metrics.mean_squared_error(y_true, y_pred)
    mae = metrics.mean_absolute_error(y_true, y_pred)
    print "origin sample: mean_squared_error is %5.6f" % mse
    print "origin sample: mean_absolute_error is %5.6f" % mae
    print "origin sample: r2_score is %5.6f" % metrics.r2_score(y_true, y_pred)
    for i in [1, 10, 20, 50]:
        print "-------the threshold is %d-----------" % i
        df_new = df[df.y_true >= i]
        print "the shape of the filter sample is", df_new.shape
        y_true = np.array(df_new.y_true)
        y_pred = np.array(df_new.y_pred)
        coeff = stats.spearmanr(y_true, y_pred, axis=0)
        print 'Spearmanr coeff is %5.6f' % (coeff[0])
        f_mse = metrics.mean_squared_error(y_true, y_pred)
        f_mae = metrics.mean_absolute_error(y_true, y_pred)
        print "filter sample: mean_squared_error is %5.6f" % f_mse
        print "filter sample: mean_absolute_error is %5.6f" % f_mae
        res = sum(abs((y_true - y_pred) / y_true)) / len(y_true)
        print "filter sample: mean_absolute_percent of y_true  is %5.6f" % res
    for i in [1, 10, 20, 50]:
        print "-------the threshold is %d-----------" % i
        df_new = df[df.y_pred >= i]
        print "the shape of the filter sample is", df_new.shape
        y_true = np.array(df_new.y_true)
        y_pred = np.array(df_new.y_pred)
        coeff = stats.spearmanr(y_true, y_pred, axis=0)
        print 'Spearmanr coeff is %5.6f' % (coeff[0])
        f_mse = metrics.mean_squared_error(y_true, y_pred)
        f_mae = metrics.mean_absolute_error(y_true, y_pred)
        print "filter sample: mean_squared_error is %5.6f" % f_mse
        print "filter sample: mean_absolute_error is %5.6f" % f_mae
        res = sum(abs((y_true - y_pred) / y_pred)) / len(y_true)
        print "filter sample: mean_absolute_percent of y_pred is %5.6f" % res
    return mse, mae
