背景:现在有一些客户数据以及他们是否会购买贷款的统计,公司希望在此数据基础上。对用户数据进行分析,找出那些更加有可能购买贷款的客户,(有监督学习, 二元分类)。
说明:尽可能多地比较了常见模型

  1. import numpy as np
  2. import pandas as pd
  3. import matplotlib.pyplot as plt
  4. import seaborn as sns
  5. %matplotlib inline
  6. sns.set() # 重置sns的默认设置
  7. sns.set_style("whitegrid") # 设置seaborn的主题样式

1.读取文件

  1. data = pd.read_excel('Personal_Loan.xlsx','Data') # 读取文件"P_L.xlsx" 中的"Data"表
  2. data.head()

2.数据概况

  1. data.info() # 数据概况
  2. <class 'pandas.core.frame.DataFrame'>
  3. RangeIndex: 5000 entries, 0 to 4999
  4. Data columns (total 14 columns):
  5. ID 5000 non-null int64
  6. Age 5000 non-null int64
  7. Experience 5000 non-null int64
  8. Income 5000 non-null int64
  9. ZIP Code 5000 non-null int64
  10. Family 5000 non-null int64
  11. CCAvg 5000 non-null float64
  12. Education 5000 non-null int64
  13. Mortgage 5000 non-null int64
  14. Personal Loan 5000 non-null int64
  15. Securities Account 5000 non-null int64
  16. CD Account 5000 non-null int64
  17. Online 5000 non-null int64
  18. CreditCard 5000 non-null int64
  19. dtypes: float64(1), int64(13)
  20. memory usage: 547.0 KB
  1. data.isnull().sum() # 没有空数据
  1. ID 0
  2. Age 0
  3. Experience 0
  4. Income 0
  5. ZIP Code 0
  6. Family 0
  7. CCAvg 0
  8. Education 0
  9. Mortgage 0
  10. Personal Loan 0
  11. Securities Account 0
  12. CD Account 0
  13. Online 0
  14. CreditCard 0
  15. dtype: int64
  • 简要分析数据各列意义

    • 二分类 (0/1)

      • Personal Loan 是否接受贷款 (标注)
      • Securities Account 是否有安全账号
      • CD account 是否有存款证书
      • Online 是否开通网银
      • CreditCard 是否开通信用卡
    • 定距分类 Interval (变量值可以比较大小 差值有意义)

      • Age 年龄
      • Experience 就职时间
      • Income 收入
      • CCAvg 每月的信用卡消费
      • Mortgage 房屋可抵押款
    • 定序分类 Ordinal:差值无明确意义 (例如:教育程度,有高低之分 没有高多少的含义)

      • Family 家庭成员数
      • Education 受教育程度
    • 定类变量 Norminal (数值大小没有意义)

      • ID 区分用户
      • ZIP Code 美国的邮政编码
  1. len(data[data.Experience < 0])

52

  • 数据目标:

    • 分析”客户是否接收贷款的二值分类问题”,通过建模,得到”数据特征”—“数据标注”的关系。
  • 数据预处理:

    1. 异常数据识别:

      • 观察到,Experience 最小值为-3 有52条记录小于0,(认为) ‘-2’ 代表2年后入职, 不予处理。只是这么认为,实际有可能是异常数据,需要剔除或者填充。
    2. 定序数据处理:

      • 定序数据通常需要进行独热化 转化为 只包含0,1的矩阵形式(稀疏矩阵)。
    3. 规范化处理:

      • 减弱量纲对数据的影响,进行最小最大标准化, 或者Z分数标准化

预处理函数

  1. def minmax(ser):
  2. """
  3. 输入Series 进行最小最大标准化
  4. """
  5. ser = ser.agg(lambda x : (x-x.min())/(x.max()-x.min()))
  6. return ser.astype(np.float64)
  7. def zscore(ser):
  8. """
  9. 输入Series 进行Z分数标准化
  10. """
  11. ser = ser.agg(lambda x : (x-x.mean()) / x.std())
  12. return ser.astype(np.float64)

3.数据特征了解

  1. # 丢弃两列数据,这两列数据的数值没有意义
  2. data.drop(['ID','ZIP Code'],inplace=True, axis=1)

先画出相关性矩阵,了解各个变量之间的相关性。

  1. corr = data.corr() # 生成相关性矩阵
  2. mask = np.zeros_like(corr) # 生成同样size的0填充矩阵
  3. mask[np.triu_indices_from(mask)] = True # 生成一个上三角矩阵 作为热力图的遮罩
  4. plt.figure(figsize=(10,8))
  5. sns.set_context(context='notebook') # seaborn调整默认元素的大小
  6. sns.heatmap(corr, mask=mask, annot=True, fmt='.2f')
  7. plt.xticks(rotation=90)

分类挖掘方法实例 - 图1

可以看出,’Personal Loan’ 和 ‘Income , CCAvg’相关性相对比较明显。

或者,我们可以过滤出相关性小于0.2的部分

  1. plt.figure(figsize=(10,8.2))
  2. sns.set_context(context='notebook')
  3. sns.heatmap(corr,mask=corr.abs()<0.2, # 过滤掉相关性绝对值小于0.2的部分
  4. annot=True, # 打开文字注解
  5. fmt='.2f',linewidths=0.01, # 文字格式,线型线宽
  6. linecolor='black')

分类挖掘方法实例 - 图2

数据规范化

  1. data2 = pd.get_dummies(data, columns=['Family','Education']) # 对指定列进行规范化
  2. for each in ['Age','Experience','Income','CCAvg']:
  3. data2[each] = zscore(data2[each])
  4. data2.head()
  1. data2.columns
  1. Index(['Age', 'Experience', 'Income', 'CCAvg', 'Mortgage', 'Personal Loan',
  2. 'Securities Account', 'CD Account', 'Online', 'CreditCard', 'Family_1',
  3. 'Family_2', 'Family_3', 'Family_4', 'Education_1', 'Education_2',
  4. 'Education_3'],
  5. dtype='object')

对于像Education,Family 之类的定序数据,差值没有明确的意义,Education中:3-2不等于1,只是表明不同了种类,可以进行独热化。
三个不同Education的人的表示不再是(1,2,3),而是(001,010,100)。

4.分类特征、标注,拆分训练集 验证集 测试集

  1. label_v = data2['Personal Loan'].values # 标注的值
  2. feature_data = data2.drop('Personal Loan', axis=1) # 表示特征的矩阵
  3. feature_v = feature_data.values # 表示特征的值
  4. feature_names = feature_data.columns # 特征名称

用 sklearn 中分离数据集的方法进行拆分,
最终训练集:验证集:测试集 = 6:2:2
要求不高时,也可以直接用训练集:测试集 = 8:2
训练集:生成模型。
验证集:模型对比,调整必要的模型参数,直到在验证集上表现良好。
测试集:模型已经建立,仅测试模型的泛化能力。

  1. from sklearn.model_selection import train_test_split # 分离 训练集 验证集 测试集
  2. X_t, X_test, Y_t, Y_test = train_test_split(feature_v, label_v, test_size=0.2) # X_test 占0.2
  3. X_train, X_val, Y_train, Y_val = train_test_split(X_t, Y_t, test_size=0.25) # X_train 占 0.6 X_val 占0.2

模型的保存

  1. from sklearn.externals import joblib
  2. # joblib.dump(clf, name) 保存模型
  3. # joblib.load(clf) 读取模型

导入几种常见的分类模型

  1. from sklearn.externals.six import StringIO # 文件流输出,与模型无关
  2. from sklearn.neighbors import KNeighborsClassifier # KNN
  3. from sklearn.naive_bayes import GaussianNB, BernoulliNB # 朴素贝叶斯 高斯贝特斯 伯努力贝叶斯
  4. from sklearn.tree import DecisionTreeClassifier, export_graphviz # 决策树
  5. from sklearn.svm import SVC # 支持向量机
  6. # 集成方法
  7. from sklearn.ensemble import RandomForestClassifier, AdaBoostClassifier # 随机森林 AdaBoost
  8. from sklearn.ensemble import GradientBoostingClassifier # 梯度提升决策树
  9. # 线性模型
  10. from sklearn.linear_model import LogisticRegression # 逻辑回归
  11. # 模型评估
  12. from sklearn.metrics import accuracy_score, recall_score, f1_score # 正确率 召回率 F分数

构建一个模型列表用来比较各个模型的泛化能力

  1. models=[]
  2. models.append(("KNN", KNeighborsClassifier(n_neighbors=3))) # KNN 近邻数为3
  3. models.append(("GaussianNB", GaussianNB())) # 高斯贝叶斯
  4. models.append(("BernoulliNB", BernoulliNB())) # 伯努利贝叶斯
  5. models.append(("DecisionTreeGini", DecisionTreeClassifier())) # 默认基尼不纯度的决策树
  6. models.append(("DecisionTreeEntropy", DecisionTreeClassifier(criterion='entropy'))) # 熵增益的决策树
  7. models.append(("SVM Classifier", SVC(C=1000,gamma='scale'))) # SVC
  8. models.append(("RadomForest", RandomForestClassifier())) # 并联投票的随机森林
  9. models.append(("OriginalRandomForest", RandomForestClassifier(n_estimators=11, max_features=None))) # 11棵树的森林 每棵树选择特征全部
  10. models.append(("Adaboost", AdaBoostClassifier(n_estimators=100))) # 100个弱分类器串联分权的集成方法 梯度提升决策树
  11. models.append(("LogisticRegression", LogisticRegression(C=1000, tol=1e-10, solver="sag", max_iter=10000))) # 逻辑回归-随机梯度下降-最大迭代1000
  12. models.append(("GBDT", GradientBoostingClassifier(max_depth=6, n_estimators=100))) # 集成方法 GBDT 梯度提升树

绘制决策树
import re, random, time
import pydotplus # 决策树绘制

  1. result_lst = pd.DataFrame()
  2. for clf_name, clf in models:
  3. clf.fit(X_train, Y_train) # 生成模型
  4. xy_lst = [(X_train,Y_train),(X_val,Y_val)]
  5. # 如果是决策树模型, 绘制决策树
  6. if re.search(str.lower('tree'), str.lower(clf_name)):
  7. dot_data = StringIO()
  8. export_graphviz(clf, out_file=dot_data,feature_names=feature_names,
  9. class_names=["Y","N"],
  10. filled=True,rounded=True,
  11. special_characters=True)
  12. graph = pydotplus.graph_from_dot_data(dot_data.getvalue())
  13. file_name = clf_name + time.strftime('%H%M') + ".pdf"
  14. graph.write_pdf(file_name)
  15. for i in range(len(xy_lst)):
  16. X_part = xy_lst[i][0]
  17. Y_part = xy_lst[i][1]
  18. Y_pred = clf.predict(X_part) # 预测模型
  19. if i == 1 :
  20. # 画出两个ROC曲线
  21. if clf_name in ["DecisionTreeGini","SVM Classifier"]:
  22. Y_pred_pro = clf.predict_proba(X_part)[:,1]
  23. fpr_rat, tpr_rat ,_ = roc_curve(Y_part, Y_pred_pro)
  24. plt.figure(1)
  25. plt.plot([0, 1], [0, 1], 'k--')
  26. plt.plot(fpr_rat, tpr_rat, label=clf_name)
  27. plt.legend(loc='lower right')
  28. plt.figure(2)
  29. plt.plot([0, 1], [0, 1], 'k--')
  30. plt.plot(fpr_rat, tpr_rat, label=clf_name)
  31. plt.xlim([0,0.3])
  32. plt.ylim([0.6,1.0])
  33. plt.legend(loc='lower right')
  34. if i == 0 : # 生成结果的DataFrame的columns
  35. add = str("train") + "-"
  36. else:
  37. add = str("val") + "-"
  38. result_lst.at[clf_name, add+"ACC"] = accuracy_score(Y_part,Y_pred) # 正确率
  39. result_lst.at[clf_name, add+"REC"] = recall_score(Y_part,Y_pred) # 召回率
  40. result_lst.at[clf_name, add+'F1'] = f1_score(Y_part,Y_pred) # F分数
  41. result_lst = result_lst.sort_index(axis=1) # 对columns排序 将相同指标放在一起

5. 模型对比

  1. plt.figure(figsize=(8,6))
  2. sns.set_context('notebook')
  3. sns.heatmap(result_lst,annot=True)
  • 各个模型在数据集上的评价:

    • Accuracy:正确率 (识别正确的)
    • Precision:精准率 (所有识别为正的样本中, 识别正确的比例)
    • Recall:召回率 (所有实际为正的样本中, 识别正确的比例)
    • 精准率和召回率的存在,是为了避免样本分布不均,正类极少的情况下,正确率很高,但是模型泛化能力仍然较差。
  • F1:F1分数 比较均衡 (2 precision recall)/(precision+recall) 无论哪一个数值小,F分数都会下降。

  • 各个比率均越接近1, 表现越好。

  • 分类挖掘方法实例 - 图3

  • 可以看出,作为集成方法的随机森林,表现较好。不同模型参数调整对模型泛化能力都有影响,这里就不一一比较了。

  • Gini树 和 SVM的ROC曲线:

分类挖掘方法实例 - 图4

分类挖掘方法实例 - 图5

  • Gini树的混淆矩阵

分类挖掘方法实例 - 图6