练习:泰坦尼克号获救数据预测,预测人员获救与哪些因素有关。
1. 读取数据
import pandas as pd
df = pd.read_csv('titanic_train.csv')
print(df.head())
print(df.columns)
print(df.isnull().sum())
print(df.describe())
运行结果如图1所示:
从图中可以看出数据大小为891 * 12,列名有['PassengerId', 'Survived', 'Pclass', 'Name', 'Sex', 'Age', 'SibSp', 'Parch', 'Ticket','Fare', 'Cabin', 'Embarked'],有些列的数据存在着缺失,我们先看看每一列代表什么意思。
'PassengerId':乘客的ID
'Survived':乘客是否获救,0表示未获救,1表示获救
'Pclass':乘客舱位等级
'Name':乘客名字
'Sex':乘客性别
'Age':乘客年龄
'SibSp':乘客兄弟姐妹数
'Parch':乘客携带的老人以及小孩数量
'Ticket':乘客船票编号
'Fare':乘客当前船票价格
'Cabin':缺失值太多,忽略
'Embarked':乘客上船地点,分别有C、S、Q
2.数据预处理
- 从图1看出数据中'Age'列和'Carbin'列存在缺失值,所以我们先要对'Age'列进行填充,这里使用均值填充,而'Carbin'列在分析的时候可以忽视它。
df['Age'] = df['Age'].fillna(df['Age'].median())
print(df.describe())
运行结果如图2所示:
从图2可以看出,'Age'列缺失的数据已经被填充好了。
- 观察'Sex'这一列,发现里面的数据是male和female,分别代表男性和女性,在机器学习上无法识别str,我们需要将str转换为0,1,即male用0表示,female用1表示。
print(df['Sex'].unique())
df.loc[df['Sex'] == 'male','Sex'] = 0
df.loc[df['Sex'] == 'female','Sex'] = 1
print(df['Sex'].unique())
运行结果如图3所示:
从图3可以看出,已经将male和female转换为0和1.
- 观察Embarked这一列,发现里面的数据是C、Q、S,所以也要和上面的一样,进行数据类型转换。这里将S用0表示,C用1表示,Q用2表示。同时发现Embarked列也存在着缺失值,所以需要对缺失值进行填充,先统计C、Q、S中出现频率最高的,然后将出现频率最高的填入缺失值。
print(df['Embarked'].unique())
print(dict(df['Embarked'].value_counts()))
df['Embarked'] = df['Embarked'].fillna('S')
df.loc[df['Embarked'] == 'S','Embarked'] = 0
df.loc[df['Embarked'] == 'C','Embarked'] = 1
df.loc[df['Embarked'] == 'Q','Embarked'] = 2
print(df['Embarked'].unique())
运行结果如图4所示:
从图4中可以看出S的频率最高,所以这里讲S填入缺失值,经过处理后的数据变为0、1、2。
3.模型建立
- 用线性回归预测
# 利用线性回归预测
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold
# 设置标签
predictors = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
alg = LinearRegression()
kf = KFold(n_splits=3, random_state=1)
predictions = []
for train, test in kf.split(df[predictors]):
train_predictors = df[predictors].iloc[train, :]
train_target = df['Survived'].iloc[train]
alg.fit(train_predictors, train_target)
# 对测试集进行预测
test_predictions = alg.predict(df[predictors].iloc[test, :])
predictions.append(test_predictions)
predictions = np.concatenate(predictions, axis=0)
predictions[predictions > 0.5] = 1 # 大于0.5表示获救
predictions[predictions <= 0.5] = 0 # 小于0.5表示未获救
accuracy = len(predictions[predictions == df['Survived']]) / len(predictions)
print(accuracy)
运行结果为:0.7833894500561167
可见预测的准确率并不是很高。同时还要注意,由于版本的变换,KFold模块现在改为从model_selection中导入。还有,KFold的使用方法也改了,下面是之前版本的使用方式。
kf = KFold(df.shape[0], n_folds=3, random_state=1)
predictions = []
for train, test in kf:
train_predictors = (df[predictors].iloc[train, :]
- 用逻辑回归预测
from sklearn import model_selection
from sklearn.linear_model import LogisticRegression
predictors = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
alg_1 = LogisticRegression(random_state=1)
scores = model_selection.cross_val_score(alg_1, df[predictors], df['Survived'], cv=3)
print(scores.mean())
运行结果为:0.7878787878787877
可见预测的准确率之比线性回归高了那么一点点,但还是不高。在运行程序时发现了警告,警告内容如图5所示:
虽然警告信息并不影响代码运行,但输出窗口异常明显的几行红字提醒,总觉得不爽。
FutureWarning是语言或者库中将来可能改变的有关警告。
根据报警信息和参考相关文档,“Default will change from 'liblinear' to 'lbfgs' in 0.22.”,默认的solver参数在0.22版本中,将会由“liblinear”变为“lbfgs”,且指定solver参数可以消除该warning。
这是代码在发出警告,将来代码运行时如果没有及时关注到版本的问题,可能solver的参数会发生改变。所以,最安全的方法并不是通过ignore消除警告,而是指定一个solver参数。
解决办法:
# 版本问题,需要在后面添加 solver='liblinear',否则会有警告,虽然不影响运行
alg_1 = LogisticRegression(random_state=1, solver='liblinear')
另一种思路是:
# 这种方法可以消除任何警告信息
import warnings
warnings.filterwarnings("ignore")
- 随机森林模型进行预测
同时我们要考虑上述标签中,到底是哪个标签的权重对获救的概率影响高一点,哪个权重会使得获救的概率降低,这一点需要考虑。这里引进随机森林模型,能够综合的利用标签,降低过拟合的风险,提高预测的准确性。
# 使用随机森林模型
from sklearn import model_selection
from sklearn.ensemble import RandomForestClassifier
# 设置标签
predictors = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked']
# 通过随机森林进行预测,这里随机森林的参数只是随便设定的,具体参数需要建立一个随机森林模型
# n_estimators指树的个数,min_samples_split指内部节点再划分所需最小样本数,min_samples_leaf叶子节点最少样本数
alg_2 = RandomForestClassifier(random_state=1, n_estimators=50, min_samples_split=4, min_samples_leaf=2)
kf = model_selection.KFold(n_splits=3, random_state=1, shuffle=True)
scores_1 = model_selection.cross_val_score(alg_2, df[predictors], df['Survived'], cv=kf)
print(scores_1.mean())
运行结果为:0.8260381593714926
从图中的结果可以看出预测的准确率又比前面的高了一些,但还是不够。由于这里的随机森林模型是随便设定的,没有设定好参数,所以需要对模型进行优化,建立合适的树模型,优化数的个数和深度。
RandomForestClassifier用法参考:https://www.cnblogs.com/pinard/p/6160412.html
- 自己构造特征
前面我们还有一些标签没有使用上,在这里我们将剩下的一些标签给用上,将标签进行数字化处理。其中,构建一个FamilySize标签,它是由SibSp标签和Parch标签相加得到;构建一个NameLength标签,它是乘客的名字长度;再构建Title标签,它是每个乘客的称号,如Mr、Mrs、Miss、Dr...。然后将标签的数据转换为数字进行处理。
# 自己构建特征
df['FamilySize'] = df['SibSp'] + df['Parch'] # 兄弟姐妹和老人小孩
df['NameLength'] = df['Name'].apply(lambda x: len(x)) # 名字的长度
import re
# 每个人都有自己的身份的词,如Miss, Mr...
def get_title(name):
title_search = re.search(' ([A-Za-z]+)\.', name)
if title_search:
return title_search.group(1)
return ''
titles = df['Name'].apply(get_title)
print(pd.value_counts(titles))
# 将称号用数字表示
title_mapping = {'Mr': 1, 'Miss': 2, 'Mrs': 3, 'Master': 4, 'Dr': 5, 'Rev': 6, 'Col': 7, 'Major': 8, 'Mlle': 9,
'Countess': 10, 'Ms': 11, 'Lady': 12, 'Jonkheer': 13, 'Don': 14, 'Mme': 15, 'Capt': 16, 'Sir': 17}
for k, v in title_mapping.items():
titles[titles == k] = v
print(pd.value_counts(titles))
df['Title'] = titles
运行结果如图6、7所示:
至此,3个新的标签就已经构建好了,我们先将数据可视化,观察哪个标签所占的权重较高,然后使用随机森林模型进行预测。
from sklearn.feature_selection import SelectKBest, f_classif
predictors_new = ['Pclass', 'Sex', 'Age', 'SibSp', 'Parch', 'Fare', 'Embarked', 'FamilySize', 'NameLength', 'Title']
selector = SelectKBest(f_classif, k=5)
selector.fit(df[predictors_new], df['Survived'])
scores_2 = -np.log10(selector.pvalues_)
plt.bar(range(len(predictors_new)), scores_2)
plt.xticks(range(len(predictors_new)), predictors_new, rotation='vertical')
plt.show()
SelectKBest用法参考:https://www.jianshu.com/p/586ba8c96a3d
运行结果如图8所示:
通过上述图8我们选择5个最重要的标签,接下来用随机森林模型进行预测。
predictors_1 = ['Pclass', 'Sex', 'Fare', 'Title', 'NameLength']
alg_3 = RandomForestClassifier(random_state=1, n_estimators=50, min_samples_split=8, min_samples_leaf=4)
kf = model_selection.KFold(n_splits=3, random_state=1, shuffle=True)
scores_1 = model_selection.cross_val_score(alg_3, df[predictors_1], df['Survived'], cv=kf)
print(scores_1.mean())
运行结果为:0.8215488215488215
可见,预测的结果并没有多大的改变,可见随机森林的预测效果还不够好,看能否优化一下树结构。