决策树与随机森林应用

2,049 阅读15分钟

一、实验目的及要求

1)实验目的

  • 了解决策树的工作机制;
  • 掌握决策树模型及模型改进流程;
  • 熟悉并建立随机森林分类模型。

2)实验要求

  • 根据实验题目编写好源程序;
  • 对上机操作过程中可能出现的问题预先分析,确定调试步骤和测试方法;
  • 输入一定数量的测试数据,对运行结果进行分析;
  • 上机实验后, 认真写出实验报告,对上机中出现的问题进行分析、总结。

二、实验环境(工具、配置等)

  • 硬件要求:计算机一台;
  • 软件要求:Mac操作系统,本实验在jupyter notebook上进行开发。

三、实验内容(实验方案、实验步骤、设计思路等)

1)实验方案

  • 学习并跟随着蓝桥云课完成此次试验;
  • 在实验中构建Adult数据集决策树,加载数据后对数据进行处理,经过数据处理,创建一个决策树分类器;
  • 对决策树参数进行调整,并且明白这些参数的意义,通过多次建模,训练提高精确度;
  • 建立随机森林分类模型,利用scikit-learn提供的随机森林算法建立相应的分类预测模型。

2)实验步骤

  • 模型定义:

    • 打开蓝桥实验楼的实验环境;
    • 导入实验所需要的模块。
  • 数据处理和加载:

    • 加载数据集;
    • 根据实验要求创建一个示例数据集并划分训练数据和测试数据;
    • 使用scikit-learn提供的方法绘制一棵决策树;
    • 对数据进行必要的清洗,将目标数值转换为0,1二元数值,将Age修复为整数类型;
    • 对数据进行一些预处理,区分数据集中的类别和连续特征,并对缺失数据进行填充,对类别特征进行独热编码;
    • 创建零值特征进行补齐测试数据。
  • 训练模型:

    • 根据实验内容实现香农熵计算函数entrop();
    • 实现信息增益的计算函数information_gain(root,left,right);
    • 实现基于信息增益划分函数best_feature_to_split;
    • 通过递归调用划分函数实现一个简单的树构建决策,并输出每一步的熵变化;
    • 构建Adult数据集决策树,加载并读取该数据集;
    • 使用训练数据创建一个决策树分类器;
    • 使用GridSearchCV网格搜索对决策树进行调参并返回最佳参数,输出精确度;
    • 建立随机森立分类模型,构建RandomForestClassifier随机森林分类器。
  • 训练过程可视化;

  • 测试,并多次修改。

3)设计思路:

  • 首先,对于第一个示例来了解简单决策树的工作机制,需要自己创建示例数据集,指定一些训练和测试数据,然后根据训练数据集理解基于信息熵的决策树,利用scikit-learn提供的方法绘制决策树。

  • 接下来,在计算熵和信息增益的时候,需要实现香农熵函数、信息增益的计算函数和基于信息增益划分函数,再实现一个简单的树构建策略,并输出每一步的熵变化,这对了解决策树的生成也很有帮助。

  • 接下来,构建Adult数据决策树。这是一个比较完整的例子,在加载数据集后需要对数据进行一系列的处理,将目标值转换为0,1二元数值,将浮点类型处理为整型,对缺失数据进行填充等等预处理操作。

  • 将数据处理完后使用训练数据创建一个决策树分类器,再对决策树模型进行参数的调整,查看在测试集上的准确度。

  • 最后建立随机森林分类模型,利用scikit-learn提供的随机森林算法建立相应的分类预测模型,并输出在测试集上的分类预测模型。

四、实验结果

简单示例练习

  1. 创建示例数据集
# 创建示例数据集,并对数据类别进行独热编码
def create_df(dic, feature_list):
    out = pd.DataFrame(dic)
    out = pd.concat([out, pd.get_dummies(out[feature_list])], axis=1)
    out.drop(feature_list, axis=1, inplace=True)
    return out
# 保证独热编码后的特征在训练和测试数据中同时存在
def intersect_features(train, test):
    common_feat = list(set(train.keys()) & set(test.keys()))
    return train[common_feat], test[common_feat]

  1. 定义特征
features = ['Looks', 'Alcoholic_beverage', 'Eloquence', 'Money_spent']

  1. 训练数据
df_train = {}
df_train['Looks'] = ['handsome', 'handsome', 'handsome', 'repulsive',
                     'repulsive', 'repulsive', 'handsome']
df_train['Alcoholic_beverage'] = [
    'yes', 'yes', 'no', 'no', 'yes', 'yes', 'yes']
df_train['Eloquence'] = ['high', 'low', 'average', 'average', 'low',
                         'high', 'average']
df_train['Money_spent'] = ['lots', 'little', 'lots', 'little', 'lots',
                           'lots', 'lots']
df_train['Will_go'] = LabelEncoder().fit_transform(
    ['+', '-', '+', '-', '-', '+', '+'])
df_train = create_df(df_train, features)
df_train

图片描述

图一 训练数据的结果图


  1. 测试数据
df_test = {}
df_test['Looks'] = ['handsome', 'handsome', 'repulsive']
df_test['Alcoholic_beverage'] = ['no', 'yes', 'yes']
df_test['Eloquence'] = ['average', 'high', 'average']
df_test['Money_spent'] = ['lots', 'little', 'lots']
df_test = create_df(df_test, features)
df_test
# 保证独热编码后的特征在训练和测试数据中同时存在
y = df_train['Will_go']
df_train, df_test = intersect_features(train=df_train, test=df_test)

图片描述

图二 测试集

图片描述

图三 训练集

图片描述

图四 测试集


  1. 绘制一颗基于信息熵的决策树。
dt = DecisionTreeClassifier(criterion='entropy', random_state=17)
dt.fit(df_train, y)
tree_str = export_graphviz(
    dt, feature_names=df_train.columns, out_file=None, filled=True)
graph = pydotplus.graph_from_dot_data(tree_str)
SVG(graph.create_svg())
**
图五 决策树
** - - - - ### 计算熵和信息增益 1. 分球,9蓝色,标签为1,11黄色,标签为0 ```python balls = [1 for i in range(9)] + [0 for i in range(11)] ``` - - - - 2. 将球分为两组 ```python # 数据分组 # 8 蓝色 和 5 黄色 balls_left = [1 for i in range(8)] + [0 for i in range(5)] # 1 蓝色 和 6 黄色 balls_right = [1 for i in range(1)] + [0 for i in range(6)] ```

图片描述

图六 球分组情况


  1. 实现香农熵计算函数entropy()
from math import log
def entropy(a_list):
    lst = list(a_list)
    size = len(lst) * 1.0
    entropy = 0
    set_elements = len(set(lst))
    if set_elements in [0, 1]:
        return 0
    for i in set(lst):
        occ = lst.count(i)
        entropy -= occ/size * log(occ/size, 2)
    return entropy

图片描述

图七 列表 ball_left 给出状态的熵

图片描述

图八 一个 6 面立方体等概率骰子的熵


  1. 实现信息增益的计算函数information_gain(root, left, right)
def information_gain(root, left, right):
    return entropy(root) - 1.0 * len(left) / len(root) * entropy(left) \
                         - 1.0 * len(right) / len(root) * entropy(right)

图片描述

图九 信息增益


  1. 实现基于信息增益划分函数best_feature_to_split
def best_feature_to_split(X, y):
    '''信息增益用于特征分割'''
    out = []
    for i in X.columns:
        out.append(information_gain(y, y[X[i] == 0], y[X[i] == 1]))
    return out

  1. 通过递归调用 best_feature_to_split 实现一个简单的树构建策略,并输出每一步的熵变化
def btree(X, y):
    clf = best_feature_to_split(X, y)
    param = clf.index(max(clf))
    ly = y[X.iloc[:, param] == 0]
    ry = y[X.iloc[:, param] == 1]
    print('Column_' + str(param) + ' N/Y?')
    print('Entropy: ', entropy(ly), entropy(ry))
    print('N count:', ly.count(), '/', 'Y count:', ry.count())
    if entropy(ly) != 0:
        left = X[X.iloc[:, param] == 0]
        btree(left, ly)
    if entropy(ry) != 0:
        right = X[X.iloc[:, param] == 1]
        btree(right, ry)

图片描述

图十 信息增益用于特征分割

图片描述

图十一 树构建


构建Adult 数据集决策树

  1. 加载人口收入普查数据集
data_train = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult_train.csv', sep=';')
data_test = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult_test.csv', sep=';')

  1. 人口收入普查数据集中的特征
  • Age – 连续数值特征
  • Workclass – 连续数值特征
  • fnlwgt – 连续数值特征
  • Education – 类别特征
  • Education_Num – 连续数值特征
  • Martial_Status – 类别特征
  • Occupation – 类别特征
  • Relationship – 类别特征
  • Race – 类别特征
  • Sex – 类别特征
  • Capital_Gain – 连续数值特征
  • Capital_Loss – 连续数值特征
  • Hours_per_week – 连续数值特征
  • Country – 类别特征
  • Target – 收入水平,二元分类目标值

  1. 对数据集进行一些必要的清洗。同时,将目标值转换为 0,1 二元数值:
# 移除测试集中的错误数据
data_test = data_test[(data_test['Target'] == ' >50K.')
                      | (data_test['Target'] == ' <=50K.')]
# 将目标编码为 0 和 1
data_train.loc[data_train['Target'] == ' <=50K', 'Target'] = 0
data_train.loc[data_train['Target'] == ' >50K', 'Target'] = 1
data_test.loc[data_test['Target'] == ' <=50K.', 'Target'] = 0
data_test.loc[data_test['Target'] == ' >50K.', 'Target'] = 1

图片描述

图十二 特征和目标值的各项统计指标

图片描述

图十三 训练数据集目标分布计数


  1. 绘制各项特征的关联分布图像:
fig = plt.figure(figsize=(25, 15))
cols = 5
rows = np.ceil(float(data_train.shape[1]) / cols)
for i, column in enumerate(data_train.columns):
    ax = fig.add_subplot(rows, cols, i + 1)
    ax.set_title(column)
    if data_train.dtypes[column] == np.object:
        data_train[column].value_counts().plot(kind="bar", axes=ax)
    else:
        data_train[column].hist(axes=ax)
        plt.xticks(rotation="vertical")
plt.subplots_adjust(hspace=0.7, wspace=0.2)

图片描述

图十四 各项特征的关联分布图像


  1. 将Age特征类型由object变为整形
data_test['Age'] = data_test['Age'].astype(int)

  1. 将测试数据中浮点类型特征全部处理成整数类型,以便与训练数据对应:
data_test['fnlwgt'] = data_test['fnlwgt'].astype(int)
data_test['Education_Num'] = data_test['Education_Num'].astype(int)
data_test['Capital_Gain'] = data_test['Capital_Gain'].astype(int)
data_test['Capital_Loss'] = data_test['Capital_Loss'].astype(int)
data_test['Hours_per_week'] = data_test['Hours_per_week'].astype(int)

  1. 从数据集中选择类别和连续特征变量:
categorical_columns = [c for c in data_train.columns
                       if data_train[c].dtype.name == 'object']
numerical_columns = [c for c in data_train.columns
                     if data_train[c].dtype.name != 'object']
print('categorical_columns:', categorical_columns)
print('numerical_columns:', numerical_columns)

  1. 对连续特征使用中位数对缺失数据进行填充,而类别特征则使用众数进行填充:
for c in categorical_columns:
    data_train[c].fillna(data_train[c].mode(), inplace=True)
    data_test[c].fillna(data_train[c].mode(), inplace=True)
for c in numerical_columns:
    data_train[c].fillna(data_train[c].median(), inplace=True)
    data_test[c].fillna(data_train[c].median(), inplace=True)

  1. 对类别特征进行独热编码,以保证数据集特征全部为数值类型方便后续传入模型:
data_train = pd.concat([data_train[numerical_columns],
                        pd.get_dummies(data_train[categorical_columns])], axis=1)
data_test = pd.concat([data_test[numerical_columns],
                       pd.get_dummies(data_test[categorical_columns])], axis=1)
set(data_train.columns) - set(data_test.columns)
data_train.shape, data_test.shape
'''
独热编码之后发现测试数据中没有 Holland,为了与训练数据对应,这里需要创建零值特征进行补齐。
'''
data_test['Country_ Holand-Netherlands'] = 0
set(data_train.columns) - set(data_test.columns)

  1. 去除target特性
X_train = data_train.drop(['Target'], axis=1)
y_train = data_train['Target']

X_test = data_test.drop(['Target'], axis=1)
y_test = data_test['Target']

寻找决策树参数模型

  1. 使用默认参数,max_depth=3, random_state=17:
tree = DecisionTreeClassifier(max_depth=3, random_state=17)
tree.fit(X_train, y_train)
tree_predictions = tree.predict(X_test)
accuracy_score(y_test, tree_predictions)

图片描述

图十五 使用默认参数准确率


一些重要重要参数


  • criterion Criterion这个参数正是用来决定不纯度的计算方法的。

sklearn提供了两种选择:

1)输入“entropy”, 使用信息熵(Entropy)

2)输入“gini”,使用基尼系数(Gini Impurity)

注意, 当使用信息熵时, sklearn实际计算的是基于信息熵的信息增益(Information Gain), 即父节点的信息熵和子节点的信息熵之差. 比起基尼系数, 信息熵对不纯度更加敏感, 对不纯度的惩罚最强. 但是在实际使用中,信息熵和基尼系数的效果基本相同. 信息熵的计算比基尼系数缓慢一些, 因为基尼系数的计算不涉及对数. 另外, 因为信息熵对不纯度更加敏感, 所以信息熵作为指标时, 决策树的生长会更加“精细”, 因此对于高维数据或者噪音很多的数据, 信息熵很容易过拟合, 基尼系数在这种情况下效果往往比较好. 当模型拟合程度不足的时候, 即当模型在训练集和测试集上都表 现不太好的时候, 使用信息熵. 当然, 这些不是绝对的。


  • random_state & splitter

random_state用来设置分枝中的随机模式的参数, 默认None, 在高维度时随机性会表现更明显,低维度的数据 (比如鸢尾花数据集), 随机性几乎不会显现. 输入任意整数, 会一直长出同一棵树, 让模型稳定下来. splitter也是用来控制决策树中的随机选项的, 有两种输入值, 输入”best", 决策树在分枝时虽然随机, 但是还是会优先选择更重要的特征进行分枝(重要性可以通过属性feature_importances_查看), 输入“random”, 决策树在 分枝时会更加随机, 树会因为含有更多的不必要信息而更深更大,并因这些不必要信息而降低对训练集的拟合. 这也是防止过拟合的一种方式. 当你预测到你的模型会过拟合, 用这两个参数来帮助你降低树建成之后过拟合的可能性. 当然, 树一旦建成,我们依然是使用剪枝参数来防止过拟合.


剪枝参数

不加限制的情况下, 一棵决策树会生长到衡量不纯度的指标最优, 或者没有更多的特征可用为止. 这样的决策树往往会过拟合, 这就是说, 它会在训练集上表现很好, 在测试集上却表现糟糕. 我们收集的样本数据不可能和整体的状况完全一致, 因此当一棵决策树对训练数据有了过于优秀的解释性, 它找出的规则必然包含了训练样本中的噪声, 并使它对未知数据的拟合程度不足。

为了让决策树有更好的泛化性, 我们要对决策树进行剪枝. 剪枝策略对决策树的影响巨大, 正确的剪枝策略是优化决策树算法的核心. sklearn为我们提供了不同的剪枝策略:


  • max_depth: 限制树的最大深度, 超过设定深度的树枝全部剪掉. 这是用得最广泛的剪枝参数, 在高维度低样本量时非常有效. 决策树多生长一层, 对样本量的需求会增加一倍, 所以限制树深度能够有效地限制过拟合. 在集成算法中也非常实用. 实际使用时, 建议从=3开始尝试, 看看拟合的效果再决定是否增加设定深度。

  • min_samples_leaf & min_samples_split

min_samples_leaf限定, 一个节点在分枝后的每个子节点都必须包含至少min_samples_leaf个训练样本, 否则分枝就不会发生; 或者, 分枝会朝着满足每个子节点都包含min_samples_leaf个样本的方向去发生.

一般搭配max_depth使用, 在回归树中有神奇的效果, 可以让模型变得更加平滑. 这个参数的数量设置得太小会引 起过拟合, 设置得太大就会阻止模型学习数据. 一般来说, 建议从=5开始使用. 如果叶节点中含有的样本量变化很大, 建议输入浮点数作为样本量的百分比来使用. 同时, 这个参数可以保证每个叶子的最小尺寸, 可以在回归问题 中避免低方差, 过拟合的叶子节点出现. 对于类别不多的分类问题, =1通常就是最佳选择. min_samples_split限定, 一个节点必须要包含至少min_samples_split个训练样本, 这个节点才允许被分枝, 否则分枝就不会发生.


  • max_features & min_impurity_decrease

max_features限制分枝时考虑的特征个数, 超过限制个数的特征都会被舍弃. 和max_depth异曲同工, max_features是用来限制高维度数据的过拟合的剪枝参数, 但其方法比较暴力, 是直接限制可以使用的特征数量而强行使决策树停下的参数, 在不知道决策树中的各个特征的重要性的情况下, 强行设定这个参数可能会导致模型学习不足. 如果希望通过降维的方式防止过拟合, 建议使用PCA, ICA或者特征选择模块中的降维算法.


  1. 使用 GridSearchCV 网格搜索对决策树进行调参并返回最佳参数:
tree_params = {'max_depth': range(8, 11)}
locally_best_tree = GridSearchCV(DecisionTreeClassifier(random_state=17),
                                 tree_params, cv=5)
locally_best_tree.fit(X_train, y_train)
print("Best params:", locally_best_tree.best_params_)
print("Best cross validaton score", locally_best_tree.best_score_)

图片描述

图十六 搜索结果


  1. 使用上面找到的最佳参数,再次测试:
tuned_tree = DecisionTreeClassifier(max_depth=9, random_state=17)
tuned_tree.fit(X_train, y_train)
tuned_tree_predictions = tuned_tree.predict(X_test)
accuracy_score(y_test, tuned_tree_predictions)

图片描述

图十七 调参后测试结果


建立随机森林分类模型

  1. 建立随机森林分类模型并预测:
rf = RandomForestClassifier(n_estimators=100, random_state=17)
rf.fit(X_train, y_train)
forest_predictions = rf.predict(X_test)
accuracy_score(y_test,forest_predictions)

图片描述

图十八 随机森林分类模型测试结果


遇到的问题及解决的方法

  • 问题:对于决策树算法的内核不够了解,并且对于决策树中能够使用的各种参数也不了解。

  • 解决方案:通过网络自学并且总结知识点。我了解到决策树是一种自上而下,对样本数据进行树形分类的过程,由节点和有向边组成。节点分为内部节点和叶节点,每个内部节点表示一个特征或属性,叶节点表示类别,边代表划分的条件。从顶部节点开始,所有样本聚在一起,经过根节点的划分,样本被分到不同的子节点中,再根据子节点的特征进一步划分,直至所有样本都被归到某个类别。构建决策树就是一个递归的选择内部节点,计算划分条件的边,最后到达叶子节点的过程。并且对sklearn库中的决策树中的模型进行了一定的了解,并在本次试验中多次改变参数值完成了本次试验。


五、附源程序

import warnings
import pydotplus
from io import StringIO
from IPython.display import SVG
from sklearn.metrics import accuracy_score
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier, export_graphviz
from sklearn import preprocessing
from sklearn.model_selection import GridSearchCV, cross_val_score

import collections
from sklearn.preprocessing import LabelEncoder
import pandas as pd
import numpy as np
import seaborn as sns
from matplotlib import pyplot as plt
%matplotlib inline
plt.rcParams['figure.figsize'] = (10, 8)
warnings.filterwarnings('ignore')
def create_df(dic, feature_list):
    out = pd.DataFrame(dic)
    out = pd.concat([out, pd.get_dummies(out[feature_list])], axis=1)
    out.drop(feature_list, axis=1, inplace=True)
    return out

def intersect_features(train, test):
    common_feat = list(set(train.keys()) & set(test.keys()))
    return train[common_feat], test[common_feat]
features = ['Looks', 'Alcoholic_beverage', 'Eloquence', 'Money_spent']
df_train = {}
df_train['Looks'] = ['handsome', 'handsome', 'handsome', 'repulsive',                     'repulsive', 'repulsive', 'handsome']
df_train['Alcoholic_beverage'] = [    'yes', 'yes', 'no', 'no', 'yes', 'yes', 'yes']
df_train['Eloquence'] = ['high', 'low', 'average', 'average', 'low',                         'high', 'average']
df_train['Money_spent'] = ['lots', 'little', 'lots', 'little', 'lots',                           'lots', 'lots']
df_train['Will_go'] = LabelEncoder().fit_transform(
    ['+', '-', '+', '-', '-', '+', '+'])

df_train = create_df(df_train, features)
df_train
df_test = {}
df_test['Looks'] = ['handsome', 'handsome', 'repulsive']
df_test['Alcoholic_beverage'] = ['no', 'yes', 'yes']
df_test['Eloquence'] = ['average', 'high', 'average']
df_test['Money_spent'] = ['lots', 'little', 'lots']
df_test = create_df(df_test, features)
df_test
y = df_train['Will_go']
df_train, df_test = intersect_features(train=df_train, test=df_test)
df_train
df_test
dt = DecisionTreeClassifier(criterion='entropy', random_state=17)
dt.fit(df_train, y)
tree_str = export_graphviz(
    dt, feature_names=df_train.columns, out_file=None, filled=True)
graph = pydotplus.graph_from_dot_data(tree_str)
SVG(graph.create_svg())
balls = [1 for i in range(9)] + [0 for i in range(11)]
balls_left = [1 for i in range(8)] + [0 for i in range(5)]
balls_right = [1 for i in range(1)] + [0 for i in range(6)]
from math import log


def entropy(a_list):
    lst = list(a_list)
    size = len(lst) * 1.0
    entropy = 0
    set_elements = len(set(lst))
    if set_elements in [0, 1]:
        return 0
    for i in set(lst):
        occ = lst.count(i)
        entropy -= occ/size * log(occ/size, 2)
    return entropy
entropy(balls_left) 
entropy([1, 2, 3, 4, 5, 6])
def information_gain(root, left, right):
    ''' root - 初始数据, left and right - 分组数据'''
    return entropy(root) - 1.0 * len(left) / len(root) * entropy(left) \
                         - 1.0 * len(right) / len(root) * entropy(right)
information_gain(balls, balls_left, balls_right)
def best_feature_to_split(X, y):
    '''信息增益用于特征分割'''
    out = []
    for i in X.columns:
        out.append(information_gain(y, y[X[i] == 0], y[X[i] == 1]))
    return out
def btree(X, y):
    clf = best_feature_to_split(X, y)
    param = clf.index(max(clf))
    ly = y[X.iloc[:, param] == 0]
    ry = y[X.iloc[:, param] == 1]
    print('Column_' + str(param) + ' N/Y?')
    print('Entropy: ', entropy(ly), entropy(ry))
    print('N count:', ly.count(), '/', 'Y count:', ry.count())
    if entropy(ly) != 0:
        left = X[X.iloc[:, param] == 0]
        btree(left, ly)
    if entropy(ry) != 0:
        right = X[X.iloc[:, param] == 1]
        btree(right, ry)
best_feature_to_split(df_train, y)
btree(df_train, y)
### 构建 Adult 数据集决策树
data_train = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult_train.csv', sep=';')
data_train.tail()
data_test = pd.read_csv(
    'https://labfile.oss.aliyuncs.com/courses/1283/adult_test.csv', sep=';')
data_test.tail()
# 移除测试集中的错误数据
data_test = data_test[(data_test['Target'] == ' >50K.')
                      | (data_test['Target'] == ' <=50K.')]

# 将目标编码为 0 和 1
data_train.loc[data_train['Target'] == ' <=50K', 'Target'] = 0
data_train.loc[data_train['Target'] == ' >50K', 'Target'] = 1
data_test.loc[data_test['Target'] == ' <=50K.', 'Target'] = 0
data_test.loc[data_test['Target'] == ' >50K.', 'Target'] = 1
data_test.describe(include='all').T
data_train['Target'].value_counts()
fig = plt.figure(figsize=(25, 15))
cols = 5
rows = np.ceil(float(data_train.shape[1]) / cols)
for i, column in enumerate(data_train.columns):
    ax = fig.add_subplot(rows, cols, i + 1)
    ax.set_title(column)
    if data_train.dtypes[column] == np.object:
        data_train[column].value_counts().plot(kind="bar", axes=ax)
    else:
        data_train[column].hist(axes=ax)
        plt.xticks(rotation="vertical")
plt.subplots_adjust(hspace=0.7, wspace=0.2)
data_train.dtypes
data_test.dtypes
data_test['Age'] = data_test['Age'].astype(int)
data_test['fnlwgt'] = data_test['fnlwgt'].astype(int)
data_test['Education_Num'] = data_test['Education_Num'].astype(int)
data_test['Capital_Gain'] = data_test['Capital_Gain'].astype(int)
data_test['Capital_Loss'] = data_test['Capital_Loss'].astype(int)
data_test['Hours_per_week'] = data_test['Hours_per_week'].astype(int)
categorical_columns = [c for c in data_train.columns
                       if data_train[c].dtype.name == 'object']
numerical_columns = [c for c in data_train.columns
                     if data_train[c].dtype.name != 'object']

print('categorical_columns:', categorical_columns)
print('numerical_columns:', numerical_columns)
for c in categorical_columns:
    data_train[c].fillna(data_train[c].mode(), inplace=True)
    data_test[c].fillna(data_train[c].mode(), inplace=True)

for c in numerical_columns:
    data_train[c].fillna(data_train[c].median(), inplace=True)
    data_test[c].fillna(data_train[c].median(), inplace=True)
data_train = pd.concat([data_train[numerical_columns],
                        pd.get_dummies(data_train[categorical_columns])], axis=1)

data_test = pd.concat([data_test[numerical_columns],
                       pd.get_dummies(data_test[categorical_columns])], axis=1)
set(data_train.columns) - set(data_test.columns)
data_train.shape, data_test.shape
data_test['Country_ Holand-Netherlands'] = 0
set(data_train.columns) - set(data_test.columns)
data_train.head(2)
data_test.head(2)
X_train = data_train.drop(['Target'], axis=1)
y_train = data_train['Target']

X_test = data_test.drop(['Target'], axis=1)
y_test = data_test['Target']
tree = DecisionTreeClassifier(max_depth=3, random_state=17)
tree.fit(X_train, y_train)
tree_predictions = tree.predict(X_test)
accuracy_score(y_test, tree_predictions)
tree_params = {'max_depth': range(8, 11)}

locally_best_tree = GridSearchCV(DecisionTreeClassifier(random_state=17),
                                 tree_params, cv=5)

locally_best_tree.fit(X_train, y_train)
print("Best params:", locally_best_tree.best_params_)
print("Best cross validaton score", locally_best_tree.best_score_)
tuned_tree = DecisionTreeClassifier(max_depth=9, random_state=17)
tuned_tree.fit(X_train, y_train)
tuned_tree_predictions = tuned_tree.predict(X_test)
accuracy_score(y_test, tuned_tree_predictions)
rf = RandomForestClassifier(n_estimators=100, random_state=17)
rf.fit(X_train, y_train)
forest_predictions = rf.predict(X_test) 
accuracy_score(y_test,forest_predictions)