"""
案例:
    电信客户流失分析.
目的:
    1. 演示逻辑回归的相关操作, 主要是: 二分法(流失, 不流失)
    2. 演示逻辑回归的评估操作, 主要是: 混淆矩阵, 准确率, 召回率, F1值, ROC曲线, AUC值, 分类评估报告(了解)
"""
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.linear_model import LogisticRegression
#                             准确率           精确率            召回率        F1值        roc曲线          分类评估报告
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, \
    classification_report
from sklearn.model_selection import train_test_split


# 1. 定义函数, 用于实现: 数据预处理.
def dm01_数据预处理():
    # 1. 读取数据.
    data = pd.read_csv('./data/churn.csv')
    data.info()

    # 2. 因为 gender 和 Churn 是二分类字符串变量,需要把他们拆分为二分类
    # TODO 机器学习模型无法直接处理字符串类型的类别特征，需要将分类变量转换为数值型表示。
    data = pd.get_dummies(data)
    data.info()
    print(data.head())

    # 3. 删除列, 因为热编码之后, 会多出一个列, 我们删除掉.
    data.drop(['gender_Male', 'Churn_No'], axis=1, inplace=True)
    print(data.head())

    # 4. 修改列名.
    data.rename(columns={'Churn_Yes': 'flag'}, inplace=True)
    print(data.head())

    # 5. 我们查看下数据集中, 标签 是否是 均衡的.
    print(data.flag.value_counts())  # False -> 不流失, True -> 流失


# 2. 定义函数, 用于显示: 月度会员的流失情况.
def dm02_会员流失可视化情况():
    # 1. 读取数据.
    data = pd.read_csv('./data/churn.csv')
    # 2. 对上述的数据做 热编码处理.
    data = pd.get_dummies(data)
    # 3. 删除列, 因为热编码之后, 会多出一个列, 我们删除掉.
    data.drop(['gender_Male', 'Churn_No'], axis=1, inplace=True)
    # 4. 修改列名.
    data.rename(columns={'Churn_Yes': 'flag'}, inplace=True)
    # 5. 查看数据集的分布情况.
    print(data.flag.value_counts())
    print(data.columns)  # 查看所有列名.

    # 6. 通过计数柱状图, 绘制(月度)会员的流失情况.
    # 参数x意思是: x轴的列名(是否是月度会员, 0 -> 不是会员, 1 -> 是会员)
    # 参数hue意思是: 根据hue的值, 将数据进行分类(False -> 不流失, True -> 流失)
    sns.countplot(data, x='Contract_Month', hue='flag')
    plt.show()


# 3. 定义函数, 用于实现: 逻辑回归模型的训练和评估.
def dm03_逻辑回归模型训练评估():
    # 1. 读取数据.
    data = pd.read_csv('./data/churn.csv')
    # 2. 对上述的数据做 热编码处理.
    data = pd.get_dummies(data)
    # 3. 删除列, 因为热编码之后, 会多出一个列, 我们删除掉.
    data.drop(['gender_Male', 'Churn_No'], axis=1, inplace=True)
    # 4. 修改列名.
    data.rename(columns={'Churn_Yes': 'flag'}, inplace=True)
    # 5. 查看数据集, 从中筛除: 特征列 和 标签列.
    # print(data.head(10))    # 特征列: Contract_Month, PaymentElectronic, internet_other
    # print(data.columns)     # 标签列: flag

    # 6. 拆分训练集和测试集.
    x = data[['Contract_Month', 'PaymentElectronic', 'internet_other']]
    y = data['flag']
    # print(len(x), len(y))
    # print(x.head(10))
    # print(y.head(10))
    x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=22)

    # 7. 创建逻辑回归模型, 并训练.
    estimator = LogisticRegression()
    estimator.fit(x_train, y_train)

    # 8. 模型预测.
    y_predict = estimator.predict(x_test)
    print(f'预测值为: {y_predict}')

    # 9. 模型评估.
    # 9.1 准确率.
    print(f'准确率: {estimator.score(x_test, y_test)}')
    print(f'准确率: {accuracy_score(y_test, y_predict)}')  # 真实值, 预测值.
    print('-' * 22)
    # 9.2 精确率.
    print(f'精确率: {precision_score(y_test, y_predict)}')
    print('-' * 22)
    # 9.3 召回率.
    print(f'召回率: {recall_score(y_test, y_predict)}')
    print('-' * 22)
    # 9.4 F1值
    print(f'F1值: {f1_score(y_test, y_predict)}')
    print('-' * 22)
    # 9.5 roc曲线
    print(f'roc曲线: {roc_auc_score(y_test, y_predict)}')
    print('-' * 22)
    # 9.6 分类评估报告
    # 参数macro avg意思是: 宏平均, 是指: 所有的分类器, 都按照 macro 的方式, 计算平均值/
    # 不考虑样本的权重, 直接平均, 跟样本的数量, 权重无关, 所有特征权重都一样, 适合于 数据集比较平衡的情况.

    # 参数weighted avg意思是: 权重平均, 是指: 所有的分类器, 都按照 weighted 的方式, 计算平均值/
    # 考虑样本的权重, 根据样本的权重, 计算平均值, 适合于 数据集比较不平衡的情况.
    print(f'分类评估报告: {classification_report(y_test, y_predict)}')


# 4. 在main函数中测试.
if __name__ == '__main__':
    # dm01_数据预处理()
    dm02_会员流失可视化情况()
    # dm03_逻辑回归模型训练评估()
