--------韦访 20181101
1、概述
这一讲我们来学习机器学习中常用的一个库sklearn,用sklearn中的线性回归、逻辑回归和随机森林来预测泰坦尼克号人员获救情况。
2、安装sklearn
第一步,先安装sklearn库,如果还没安装sklearn库的话,使用下面的命令进行安装,
sudo pip install -U scikit-learn
再执行下面的命令安装可视化工具,
sudo pip install pydotplus
sudo apt-get install graphviz
Sklearn的官网是:http://scikit-learn.org/stable/
上面有很多例子和示例代码,不过都是英文的,
3、泰坦尼克号数据分析
先来分析一下泰坦尼克号的数据,先来看一下数据结构,
如上图所示,泰坦数据供有12列,下面从左往右解释这些数据的含义,
PassengerId:乘客ID,其实也可以理解为行号,每一行代表一个乘客的数据,ID是唯一标示,没有重复的。
Survived:是否幸存下来,1表示幸存,0表示不幸。
Pclass:船舱等级,分为1、2、3等,根据后面对应的票价,1等舱是最贵的。
Name:乘客名字。
Sex:性别,male是男性,female是女性。
Age:年龄。
SibSp:兄弟姐妹、配偶在泰坦船上的数量。
Parch:父母、小孩在泰坦船上的数量。
Ticket:船票号。
Fare:船票价。
Cabin:仓号,很多乘客都没有这个数据。
Embarked:表示乘客在哪个港口上船的,共3个港口,分别为S、C、Q。
泰坦尼克号一共有1309个乘客的数据,我们将其分为训练数据titanic_train.csv和测试数据test.csv,其中,训练数据有891个,测试数据有418个。
4、数据预处理
第一列数据PassengerId,这个对是否获救没有什么影响的,舍弃。
第二列数据Survived表示乘客是否获救,我们来看看是否有哪些乘客的这个数据是空的。
import pandas as pd
def is_null_count(data, key):
col_datas = data[key]
is_null_data = col_datas[pd.isnull(col_datas)]
return len(is_null_data)
taitan_data = pd.read_csv('titanic_train.csv')
print(is_null_count(taitan_data, 'Survived'))
运行结果,
0
说明这列数据都没有空缺。
第三列数据Pclass,表示船舱等级,看看是否完整,
import pandas as pd
def is_null_count(data, key):
col_datas = data[key]
is_null_data = col_datas[pd.isnull(col_datas)]
return len(is_null_data)
taitan_data = pd.read_csv('titanic_train.csv')
print(is_null_count(taitan_data, 'Pclass'))
print(taitan_data['Pclass'].unique())
运行结果:
没有缺失数据,并且船舱等级只有1、2、3等。
第四列数据Name,表示乘客名,名字也能影响获救情况吗?暂时不管。
第五列数据Sex,表示性别,这里都是用male和female来表示男女,但是这样的表示不方便我们处理,所以,对这列数据进行一下预处理,male用1表示,female用0表示,先借鉴上面的方法查看数据的完整性。然后,使用下面的代码对数据进行转换,
import pandas as pd
def is_null_count(data, key):
col_datas = data[key]
is_null_data = col_datas[pd.isnull(col_datas)]
return len(is_null_data)
taitan_data = pd.read_csv('titanic_train.csv')
# print(is_null_count(taitan_data, 'Survived'))
print(is_null_count(taitan_data, 'Sex'))
print(taitan_data['Sex'].unique())
taitan_data.loc[taitan_data['Sex'] == 'male', 'Sex'] = 1
taitan_data.loc[taitan_data['Sex'] == 'female', 'Sex'] = 0
print(taitan_data['Sex'])
运行结果,
第六列数据Age,表示年龄,也先来看看数据完整性,
import pandas as pd
def is_null_count(data, key):
col_datas = data[key]
is_null_data = col_datas[pd.isnull(col_datas)]
return len(is_null_data)
taitan_data = pd.read_csv('titanic_train.csv')
print(is_null_count(taitan_data, 'Age'))
运行结果:
177
我靠,缺失177个,占比19.86%,这种情况下,要么对这列数据完全舍弃,要么为缺失的数据进行填充,我们能用的特征本来就少,那么,就用年龄的均值进行填充好了。代码如下,
import pandas as pd
def is_null_count(data, key):
col_datas = data[key]
is_null_data = col_datas[pd.isnull(col_datas)]
return len(is_null_data)
taitan_data = pd.read_csv('titanic_train.csv')
is_null_age = pd.isnull(taitan_data['Age'])
taitan_data['Age'] = taitan_data['Age'].fillna(taitan_data['Age'].mean())
print(is_null_count(taitan_data, 'Age'))
print(taitan_data[is_null_age]['Age'])
运行结果:
第七列数据SibSp,表示兄弟姐妹、配偶在泰坦船上的数量。
第八列数据Parch,表示父母、小孩在泰坦船上的数量,
老规矩先看看数据是否有缺失,
import pandas as pd
def is_null_count(data, key):
col_datas = data[key]
is_null_data = col_datas[pd.isnull(col_datas)]
return len(is_null_data)
taitan_data = pd.read_csv('titanic_train.csv')
print(is_null_count(taitan_data, 'SibSp'))
print(is_null_count(taitan_data, 'Parch'))
运行结果:
0
0
第九列数据Ticket,表示船票号,难道还有幸运数字一说吗?
第十列数据Fare,票价,这个其实跟船舱等级有点挂钩的,票价贵等级自然也贵。
第十一列数据Cabin,仓号,看看缺失数量的占比,
import pandas as pd
def is_null_count(data, key):
col_datas = data[key]
is_null_data = col_datas[pd.isnull(col_datas)]
return len(is_null_data)
taitan_data = pd.read_csv('titanic_train.csv')
print(float(is_null_count(taitan_data, 'Cabin'))/len(taitan_data['SibSp']))
运行结果:
0.771043771044
缺失了77.1%,这个占比太大了,所以对于这种缺失太多的数据,宁可舍弃也不要使用。
第十二列数据Embarked,在哪个港口上船。分别为S、C、Q。我们也用0、1、2代替,
import pandas as pd
def is_null_count(data, key):
col_datas = data[key]
is_null_data = col_datas[pd.isnull(col_datas)]
return len(is_null_data)
taitan_data = pd.read_csv('titanic_train.csv')
taitan_data.loc[taitan_data['Embarked'] == 'S', 'Embarked'] = 0
taitan_data.loc[taitan_data['Embarked'] == 'C', 'Embarked'] = 1
taitan_data.loc[taitan_data['Embarked'] == 'Q', 'Embarked'] = 2
print(taitan_data['Embarked'])
print(is_null_count(taitan_data, 'Embarked'))
运行结果:
Traceback (most recent call last):
File "/home/insounds905/rookie/opencv/demo/unit5/demo1.py", line 9, in <module>
taitan_data.loc[taitan_data['Embarked'] == 'S', 'Embarked'] = 0
File "/usr/lib/python2.7/dist-packages/pandas/core/ops.py", line 576, in wrapper
res[mask] = masker
File "/usr/lib/python2.7/dist-packages/pandas/core/series.py", line 635, in __setitem__
self.where(~key, value, inplace=True)
File "/usr/lib/python2.7/dist-packages/pandas/core/generic.py", line 3026, in where
cond = -(cond.fillna(True).astype(bool))
File "/usr/lib/python2.7/dist-packages/pandas/core/series.py", line 1001, in __neg__
arr = operator.neg(self.values)
TypeError: The numpy boolean negative, the `-` operator, is not supported, use the `~` operator or the logical_not function instead.
Process finished with exit code 1
出错了,提示说要用~来代替-,那我们试试,修改/usr/lib/python2.7/dist-packages/pandas/core/generic.py文件的第3026行,将-改为~,再运行,
有两个数据是缺失的,那么,哪个数据最多我们就填充哪个。先来看哪个只最多,使用下面代码查看,
print(taitan_data['Embarked'].value_counts())
运行结果:
S 644
C 168
Q 77
可以看到,S最多,那么对缺失数据填充S好了。
import pandas as pd
def is_null_count(data, key):
col_datas = data[key]
is_null_data = col_datas[pd.isnull(col_datas)]
return len(is_null_data)
taitan_data = pd.read_csv('titanic_train.csv')
print(taitan_data['Embarked'].value_counts())
taitan_data['Embarked'] = taitan_data['Embarked'].fillna('S')
taitan_data.loc[taitan_data['Embarked'] == 'S', 'Embarked'] = 0
taitan_data.loc[taitan_data['Embarked'] == 'C', 'Embarked'] = 1
taitan_data.loc[taitan_data['Embarked'] == 'Q', 'Embarked'] = 2
print(taitan_data['Embarked'])
print(is_null_count(taitan_data, 'Embarked'))
运行结果:
到这里,数据预处理完成,接下来分别使用线性回归、逻辑回归、随机森林来进行预测。
5、交叉验证
在将模型之前,先将一个概念-----交叉验证
假设我们有一堆数据,一般都是将其分为train数据和test数据,train数据用来训练模型,test数据用来测试模型的。如下图所示,
交叉验证呢,就是再将train数据分成几份,比如分成3份,分别为A、B、C,然后,先用A、B作为train数据,C作为test数据;再用A、C作为train数据,B作为test数据;再用B、C作为train数据,A作为test数据。最后,记录每次训练的误差Ei,取其平均值作为总误差。模型如下图所示,
交叉验证有很多种,我这里以k折交叉验证法作为例子,说明一下sklearn中怎么使用,
代码如下,
from sklearn.model_selection import KFold
import numpy as np
X = np.array([[1, 2], [3, 4], [5, 6], [7, 8], [5, 6], [7, 8], [5, 6], [7, 8], [5, 6], [7, 8], [5, 6], [7, 8]])
y = np.array([9, 10, 11, 12, 9, 10, 11, 12, 9, 10, 9, 10])
kf = KFold(n_splits=4)
kf.get_n_splits(X)
print(kf)
for train_index, test_index in kf.split(X):
print("TRAIN:", train_index, "TEST:", test_index)
运行结果,
从运行结果可以看出,其将数据分成了4等份,然后,每一等分都会成为test数据集,而且,如果它是test数据集时,不会同时为train数据集。
6、使用线性回归预测
首先,导入sklearn的线性回归模块和k折交叉验证模块,
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold
然后,使用第四步的代码进行数据预处理,这里就不贴代码了,
接着,选择使用哪些特征,对线性回归和交叉验证进行一些设置,
#使用到的特征
predictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"]
#线性回归
alg = LinearRegression()
#k折交叉验证,将数据分为3段
kf = KFold(n_splits=3)
然后,开始训练和评估模型,
#开始训练和评估模型
predictions = []
for train, test in kf.split(taitan_data):
train_predictors = (taitan_data[predictors].iloc[train,:])
# print(train_predictors)
# print('---------')
train_target = taitan_data["Survived"].iloc[train]
# print(train_target)
alg.fit(train_predictors, train_target)
test_predictions = alg.predict(taitan_data[predictors].iloc[test,:])
predictions.append(test_predictions)
最后,输出评估结果,
#输出评估结果
predictions = np.concatenate(predictions, axis=0)
predictions[predictions > .5] = 1
predictions[predictions <=.5] = 0
accuracy = sum(predictions[predictions == taitan_data["Survived"]]) / len(predictions)
print accuracy
运行结果,
0.2615039281705948
才0.26? 会不会是选择的分界点有问题?那么,从0到1,每次提升0.1,作为分界点试试看?代码如下,
#输出评估结果
predictions = np.concatenate(predictions, axis=0)
for i in np.arange(0, 1, 0.1):
tmp = predictions.copy()
tmp[tmp > i] = 1
tmp[tmp <= i] = 0
accuracy = sum(tmp[tmp == taitan_data["Survived"]]) / len(tmp)
print(i)
print accuracy
print('---------')
运行结果,
准确率也不是很高,我去百度拿别人的代码来运行,也是一样,不知道为什么这么低?哪位大神知道的话能否告知?谢谢。虽然这个预测结果不理想,完整的代码还是要贴一下,代码如下,
#encoding:utf-8
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold
import pandas as pd
import numpy as np
#检测数据为空的个数
def is_null_count(data, key):
col_datas = data[key]
is_null_data = col_datas[pd.isnull(col_datas)]
return len(is_null_data)
#读取csv文件
taitan_data = pd.read_csv('titanic_train.csv')
#查看获救数据完整性
# print(is_null_count(taitan_data, 'Survived'))
#查看船舱等级数据完整性
# print(is_null_count(taitan_data, 'Pclass'))
# print(taitan_data['Pclass'].unique())
#查看性别的数据完整性,并将male转成1,female转成0
# print(is_null_count(taitan_data, 'Sex'))
# print(taitan_data['Sex'].unique())
taitan_data.loc[taitan_data['Sex'] == 'male', 'Sex'] = 0
taitan_data.loc[taitan_data['Sex'] == 'female', 'Sex'] = 1
# print(taitan_data['Sex'])
#查看年龄的数据完整性,对于空缺的年龄,使用平均年龄填充
is_null_age = pd.isnull(taitan_data['Age'])
taitan_data['Age'] = taitan_data['Age'].fillna(taitan_data['Age'].median())
# print(is_null_count(taitan_data, 'Age'))
# print(taitan_data[is_null_age]['Age'])
#查看SibSp和Parch的数据完整性
# print(is_null_count(taitan_data, 'SibSp'))
# print(is_null_count(taitan_data, 'Parch'))
#查看缺失数据占比
# print(float(is_null_count(taitan_data, 'Cabin'))/len(taitan_data['SibSp']))
#上船港口,将S、C、Q分别用0、1、2表示
# print(taitan_data['Embarked'].value_counts())
taitan_data['Embarked'] = taitan_data['Embarked'].fillna('S')
taitan_data.loc[taitan_data['Embarked'] == 'S', 'Embarked'] = 0
taitan_data.loc[taitan_data['Embarked'] == 'C', 'Embarked'] = 1
taitan_data.loc[taitan_data['Embarked'] == 'Q', 'Embarked'] = 2
# print(taitan_data['Embarked'])
# print(is_null_count(taitan_data, 'Embarked'))
#使用到的特征
predictors = ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked"]
#线性回归
alg = LinearRegression()
#k折交叉验证,将数据分为3段
kf = KFold(n_splits=3)
#开始训练和评估模型
predictions = []
for train, test in kf.split(taitan_data):
train_predictors = (taitan_data[predictors].iloc[train,:])
# print(train_predictors)
# print('---------')
train_target = taitan_data["Survived"].iloc[train]
# print(train_target)
alg.fit(train_predictors, train_target)
test_predictions = alg.predict(taitan_data[predictors].iloc[test,:])
predictions.append(test_predictions)
#输出评估结果
predictions = np.concatenate(predictions, axis=0)
for i in np.arange(0, 1, 0.1):
tmp = predictions.copy()
tmp[tmp > i] = 1
tmp[tmp <= i] = 0
accuracy = sum(tmp[tmp == taitan_data["Survived"]]) / len(tmp)
print(i)
print accuracy
print('---------')
7、使用逻辑回归预测
既然线性回归预测的准确率很低,那用逻辑回归呢?
逻辑回归的代码也很简单,上面数据预处理的代码就不重复写了,直接给主代码,
#逻辑回归
from sklearn.model_selection import cross_val_score
from sklearn.linear_model import LogisticRegression
alg = LogisticRegression(random_state=1)
scores = cross_val_score(alg, taitan_data[predictors], taitan_data["Survived"], cv=3)
print(scores.mean())
运行结果,
0.7878787878787877
准确率为0.78多,比上面的线性回归好很多了。
8、使用随机森林预测
使用sklearn实现随机森林也很简单,
#随机森林
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import KFold
from sklearn.ensemble import RandomForestClassifier
alg = RandomForestClassifier(random_state=1, n_estimators=10, min_samples_split=2, min_samples_leaf=1)
kf = KFold(n_splits=3, random_state=1)
scores = cross_val_score(alg, taitan_data[predictors], taitan_data["Survived"], cv=kf)
print(scores.mean())
运行结果:
0.7856341189674523
跟逻辑回归的准确率差不多?别急,上面建森林时,只用了10棵树,我们将树的数量加到200棵,且将min_samples_split改为10看看,
#随机森林
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import KFold
from sklearn.ensemble import RandomForestClassifier
alg = RandomForestClassifier(random_state=1, n_estimators=200, min_samples_split=10, min_samples_leaf=1)
kf = KFold(n_splits=3, random_state=1)
scores = cross_val_score(alg, taitan_data[predictors], taitan_data["Survived"], cv=kf)
print(scores.mean())
运行结果,
0.8249158249158249
9、对比特征重要程度
#查看特征的重要性
from sklearn.feature_selection import SelectKBest, f_classif
import matplotlib.pyplot as plt
selector = SelectKBest(f_classif, k=5)
selector.fit(taitan_data[predictors], taitan_data["Survived"])
scores = -np.log10(selector.pvalues_)
plt.bar(range(len(predictors)), scores)
plt.xticks(range(len(predictors)), predictors, rotation='vertical')
plt.show()
运行结果,
可以看到,性别特征对获救的影响是最大的,其次是仓位,看过泰坦尼克号的电影也看到,老人和女人是优先获救的,那时候的人还是比较绅士的。