美文网首页
用KNN和GBDT判断美国某区域居民年收入

用KNN和GBDT判断美国某区域居民年收入

作者: apricoter | 来源:发表于2019-03-01 18:16 被阅读45次

    读取数据

    import numpy as np
    import pandas as pd
    #读取数据
    data=pd.read_excel("F:\income.xlsx")
    #预览数据前五行
    data.head()
    

    对某区域居民的人口普查

    #查看各变量类型
    data.info()
    

    int为数值型,object为离散型
    workclass为工作类型,fnlwgt为序号,relationship为家庭成员关系,race为种族,capital-gain为资本收益,native-country为国籍,因变量为需要预测居民的年收入是否超过5万美元

    数据预处理

    重复观测,缺失值,异常值,对字符型数据数值转换

    #查看缺失值并合计个数
    data.isnull().sum(axis = 0)
    

    有三个离散型变量有缺失且数目比较多,用众数替换

    #使用各个离散型变量的众数来替换缺失值
    data.fillna(value = {'workclass':data.workclass.mode()[0],
                                  'occupation':data.occupation.mode()[0],
                                  'native-country':data['native-country'].mode()[0]}, inplace = True)
    #再次查看缺失值并合计个数
    data.isnull().sum(axis = 0)
    

    探索性分析

    #数值型变量统计描述
    data.describe()
    
    #离散型变量统计描述
    data.describe(include=["object"])
    

    以受教育水平为例,一共有16种不同的水平,3万多居民中,高中毕业的最多,有10501名

    针对数值型变量,描述分布,以年龄为例,观测分布,确定峰度,偏度

    #分布形状
    #绘制不同收入水平下的年龄核密度图
    import seaborn as sns
    import matplotlib.pyplot as plt
    #设置绘图风格
    plt.style.use("ggplot")
    # 中文乱码和坐标轴负号的处理
    plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
    plt.rcParams['axes.unicode_minus'] = False
    # 取出收入<=50k
    Age1 = data.age[data.income == ' <=50K']#<=之前要空一格
    # 取出收入>50k
    Age2 = data.age[data.income == " >50K"]
    # 绘制收入<=50k的年龄的核密度图
    sns.distplot(Age1, hist = False, kde_kws = {'color':'red', 'linestyle':'-'}, 
                 norm_hist = True, label = '<=50K')
    # 绘制收入>50k的年龄的核密度图
    sns.distplot(Age2, hist = False, kde_kws = {'color':'black', 'linestyle':'--'}, 
                 norm_hist = True, label = '>50K')
    plt.title('不同收入水平下的年龄核密度图')
    # 显示图例
    plt.legend()
    # 显示图形
    plt.show()
    

    在不同的收入水平下,年龄的核密度分布图表明,对于年收入超过5万美元的居民,几乎呈现正态分布,而收入低于5万美元的居民,年龄呈现右偏分布,即年龄偏大的居民人数比年龄偏下的人数多

    同样,针对离散型变量,以对比居民的收入水平高低在种族之间的差异为例,可以发现这些离散型变量是否影响收入水平

    data.groupby(by = ['race','income']).aggregate(np.size)
    
    data.groupby(by = ['race','income']).aggregate(np.size).loc[:,'age']
    
    # 构造不同收入水平下各种族人数的数据
    race = pd.DataFrame(data.groupby(by = ['race','income']).aggregate(np.size).loc[:,'age'])
    race
    
    # 重设行索引
    race = race.reset_index()
    race
    
    # 变量重命名
    race.rename(columns={'age':'counts'}, inplace=True)
    
    # 排序
    race.sort_values(by = ['race','counts'], ascending=False, inplace=True)
    race
    
    # 设置图框比例,并绘图
    plt.figure(figsize=(9,5))
    sns.barplot(x="race", y="counts", hue = 'income', data=race)
    plt.show()
    

    在相同的种族下,居民年收入高低的人数差异,在白种人中,年收入低于5万和高于5万的居民比例大致为3;1

    数据建模

    对离散变量重编码,这样的字符型变量不可直接建模,需要重编码

    # 离散变量的重编码
    for feature in data.columns:
        if data[feature].dtype == 'object':
            data[feature] = pd.Categorical(data[feature]).codes
    data.head()
    

    所有的字符型变量都变成了整数型变量

    又教育水平和教育时长产生信息冗余,且序号fnlwgt无意义,故删除变量

    # 删除变量
    data.drop(['education','fnlwgt'], axis = 1, inplace = True)
    data.head()
    

    拆分数据集

    # 导入第三方包
    from sklearn import model_selection
    predictors = data.columns[:-1]
    # 将数据集拆分为训练集和测试集,且测试集的比例为25%
    X_train, X_test, y_train, y_test = model_selection.train_test_split(data[predictors], data.income, 
                                                                        test_size = 0.25, random_state = 1234)
    
    print('训练数据集共有%d条观测' %X_train.shape[0])
    print('测试数据集共有%d条观测' %X_test.shape[0])
    

    sklearn模块提供了网格搜索法完成模型的参数选择
    KNN模型

    # K近邻模型的网格搜索法
    # 导入k近邻模型的类
    from sklearn.neighbors import KNeighborsClassifier
    # 导入网格搜索法的函数
    from sklearn.grid_search import GridSearchCV
    # 选择不同的参数
    k_options = list(range(1,12))
    parameters = {'n_neighbors':k_options}
    # 搜索不同的K值
    grid_kn = GridSearchCV(estimator = KNeighborsClassifier(), param_grid = parameters, cv=10, scoring='accuracy', verbose=0, n_jobs=2)
    grid_kn.fit(X_train, y_train)
    print(grid_kn)
    # 结果输出
    grid_kn.grid_scores_, grid_kn.best_params_, grid_kn.best_score_  
    

    estimator接受一个指定的模型,param_grid指定模型需要搜索的参数列表对象,cv为x重交叉验证,scoring指定模型评估的度量值


    第一部分为11种K值下的平均准确率(因为是10重交叉验证),第二部分选择了最佳的K值,第三部分是当为最佳K值时,模型的最佳平均准确率

    评估模型,对于预测的连续变量为MSE和RMSE;对于预测的分类变量为混淆矩阵中的准确率,ROC曲线下的面积AUC,K-S值

    # 导入模型评估模块
    from sklearn import metrics
    # 预测测试集
    grid_kn_pred = grid_kn.predict(X_test)
    print(pd.crosstab(grid_kn_pred, y_test))
    
    # 模型得分
    print('模型在训练集上的准确率%f' %grid_kn.score(X_train,y_train))
    print('模型在测试集上的准确率%f' %grid_kn.score(X_test,y_test))
    
    # 绘制ROC曲线
    fpr, tpr, _ = metrics.roc_curve(y_test, grid_kn.predict_proba(X_test)[:,1])
    plt.plot(fpr, tpr, linestyle = 'solid', color = 'red')
    plt.stackplot(fpr, tpr, color = 'steelblue')
    plt.plot([0,1],[0,1], linestyle = 'dashed', color = 'black')
    plt.text(0.6,0.4,'AUC=%.3f' % metrics.auc(fpr,tpr), fontdict = dict(size = 18))
    plt.show()
    

    大于0.8,模型合理

    GBDT模型

    # 导入网格搜索法的函数
    from sklearn.grid_search import GridSearchCV
    # 导入GBDT模型的类
    from sklearn.ensemble import GradientBoostingClassifier
    from sklearn import ensemble
    # 运用网格搜索法选择梯度提升树的合理参数组合
    learning_rate = [0.01,0.05,0.1] #模型的学习速率
    n_estimators = [100,300,500] #生成的基础决策树的个数
    max_depth = [3,5,7,9] #每个基础决策树的最大深度
    params = {'learning_rate':learning_rate,'n_estimators':n_estimators,'max_depth':max_depth}
    gbdt_grid = GridSearchCV(estimator = ensemble.GradientBoostingClassifier(),
                             param_grid= params, scoring = 'roc_auc', cv = 10, n_jobs = 4)
    gbdt_grid.fit(X_train,y_train)
    # 返回参数的最佳组合和对应AUC值
    gbdt_grid.best_params_, gbdt_grid.best_score_
    

    最佳模型学习率为0.05,生成的基础决策树个数为300棵,并且每颗基础决策树的最大深度为5,这样的组合可以使GBDT模型的平均准确率达到93.02%

    # 导入模型评估模块
    from sklearn import metrics
    # 预测测试集
    gbdt_grid_pred = gbdt_grid.predict(X_test)
    print(pd.crosstab(gbdt_grid_pred, y_test))
    
    # 模型得分
    print('模型在训练集上的准确率%f' %gbdt_grid.score(X_train,y_train))
    print('模型在测试集上的准确率%f' %gbdt_grid.score(X_test,y_test))
    
    # 绘制ROC曲线
    fpr, tpr, _ = metrics.roc_curve(y_test, gbdt_grid.predict_proba(X_test)[:,1])
    plt.plot(fpr, tpr, linestyle = 'solid', color = 'red')
    plt.stackplot(fpr, tpr, color = 'steelblue')
    plt.plot([0,1],[0,1], linestyle = 'dashed', color = 'black')
    plt.text(0.6,0.4,'AUC=%.3f' % metrics.auc(fpr,tpr), fontdict = dict(size = 18))
    plt.show()
    

    模型在训练集上的准确率达到94%,在测试集上的准确率也达到92%,AUC的值高达0.921,远远超过0.8。

    集成算法GBDT的表现比KNN要优秀,因为这是基于多棵决策树进行投票的优点。

    总之,无论是KNN还是GBDT,都可以通过网格搜索法找到各自的最佳模型参数,而且这些最佳参数的组合一般都会使得模型比较优秀和健壮、

    在纵向比较默认参数的模型和网格搜索后的最佳参数模型,后者一般比较好;在横向比较单一模型和集成模型,后者也一般表现更优秀。

    相关文章

      网友评论

          本文标题:用KNN和GBDT判断美国某区域居民年收入

          本文链接:https://www.haomeiwen.com/subject/ovwduqtx.html