泰坦尼克号预测生存可能性

import pandas as pd
data = pd.read_csv("train.csv")
#确定每一列,或者叫每一个series下,是否有空数据
columns_name = data.columns.tolist()
columns_name_true_false={}
print(type(columns_name))
print(columns_name[0])
for i in columns_name:
    lenth = len(data[data[i].isnull()==True])
    columns_name_true_false[i]=lenth
print(columns_name_true_false)
#确定每一列,或者叫每一个series下,是否有空数据
#输出发现,'Age'、'Cabin'和'Embarked'有空数据,下一步要填补这些空数据。

输出:

{'PassengerId': 0, 'Survived': 0, 'Pclass': 0, 'Name': 0, 'Sex': 0, 'Age': 177, 'SibSp': 0, 'Parch': 0, 'Ticket': 0, 'Fare': 0, 'Cabin': 687, 'Embarked': 2}

下面要处理这些空数据。

#处理'Embarked',计算'Embarked'中C、S、Q出现的次数,用出现次数多的填补空位置。
Embarked_list=['S','C','Q']
Embarked_list_times = {}
for i in Embarked_list:
    times = len(data[data['Embarked']==i])
    Embarked_list_times[i]=times
print(Embarked_list_times)
#处理'Embarked',计算'Embarked'中C、S、Q出现的次数,用出现次数多的填补空位置。

输出:

{'S': 644, 'C': 168, 'Q': 77}

发现‘S’的次数最高,那就用S填补这些空位置。

data.fillna(axis=0,value='S',inplace=True)

'Cabin'缺失值太多,舍掉这个特征。

data.drop('Cabin',axis =1,inplace=True)

下边处理Age空缺的问题,一般可以用中值和均值填补空缺位置。这里两种情况都试一下,看下结果有什么差异。

data1 = data['Age'].dropna(axis=0)
zhongshu = data1.median()
data['Age'].fillna(value=zhongshu,inplace=True) #用中值弥补空缺位置
#data['Age'].fillna(value=data1.mean(),inplace=True)   #用均值弥补空缺位置
data['Sex']下和data['Embarked']有字符串,将字符串转成对应的数字,方便预测。其实这里转成数字不太合适,用one_hot比较合适,后边有时间用one_hot做一下。
#data['Sex']一列下有男女,将男女转为0和1
data['Sex'].replace('male',0,inplace=True)
data['Sex'].replace('female',1,inplace=True)
#data['Sex']一列下有男女,将男女转为0和1

#同上,将data['Embarked']一列'S','C','Q'转成0、1、2
data['Embarked'].replace('S',0,inplace=True)
data['Embarked'].replace('C',1,inplace=True)
data['Embarked'].replace('Q',2,inplace=True)
#同上,将data['Embarked']一列'S','C','Q'转成0、1、2

数据预处理完毕,下边用线性回归模型预测。

#下一步,利用回归预测
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import KFold
alg = LinearRegression()
kf = KFold(3,shuffle=False)
data_list=['Pclass','Sex','Age','SibSp','Parch','Fare','Embarked']  #提取有效的特征,name这类的暂时没用
predict_values=[]
for train,test in kf.split(data):
    #alg.fit(data.iloc[train],)
    data_train = data[data_list].iloc[train]
    data_train_label = data['Survived'].iloc[train]
    alg.fit(data_train,data_train_label) #fit(x,y) x是分割的训练集的数据部分,y是训练集的标签部分
    predict_value = alg.predict(data[data_list].iloc[test])#利用刚训练好的模型,预测测试集部分
    #注意预测的结果也就是predict_value是一个list,内容是一个获救的概率。
    predict_values.append(predict_value)#最终的predict_values是一个二维list,行数为3
predict_values = [i for item in predict_values for i in item]
#将predict_values由二维转一维,需要注意的是,前边正则交叉时,test部分是按照顺序来的,也就是第一次分割是[0-296],第二次是[297-594],第三次是[595-981]
#这是能够转成一维的前提,因为后边要用predict_values和data['Survived']对比,所以数据一一对应是前提
prediction=[]  #将概率转成0或1
for i in predict_values:
    if i>0.5:
        prediction.append(1)
    else:
        prediction.append(0)
j = 0#j是预测成功的数量
for i in range(len(prediction)):
    if prediction[i]==data['Survived'].tolist()[i]:
        j = j+1
print('percent: {:.6%}'.format(j/len(prediction)))

输出:percent: 78.338945%

如果用均值填补Age空缺部分,输出是:78.563412%,差不多。

利用逻辑回归解这个问题:

把上边注释掉

kf = KFold(3,shuffle=False)
lr = LogisticRegression(solver='liblinear')
data_list=['Pclass','Sex','Age','SibSp','Parch','Fare','Embarked']
predict_values=[]
for train,test in kf.split(data):
    data_train = data[data_list].iloc[train]
    data_train_label = data['Survived'].iloc[train]
    lr.fit(data_train,data_train_label) #fit(x,y) x是分割的训练集的数据部分,y是训练集的标签部分
    predict_value = lr.predict(data[data_list].iloc[test])#利用刚训练好的模型,预测测试集部分
    predict_values.append(predict_value)
print('-------')
predict_values = [i for item in predict_values for i in item]
j = 0#j是预测成功的数量
for i in range(len(predict_values)):
    if predict_values[i]==data['Survived'].tolist()[i]:
        j = j+1
print('percent: {:.6%}'.format(j/len(predict_values)))

输出:percent: 79.124579%。相比简单的线性回归,有所提高。

用随机森林再次预测,把上边代码注释掉。

#下边是用随机森林预测结果
from sklearn.model_selection import cross_val_score
#cross_val_score用来做交叉验证,新版本的sklenrn中是在model_selection模块下
from sklearn.ensemble import RandomForestClassifier #RandomForestClassifier是随机森林函数
data_list=['Pclass','Sex','Age','SibSp','Parch','Fare','Embarked']
kf = KFold(3,shuffle=False)
lr = RandomForestClassifier(n_estimators=10,min_samples_split=2,min_samples_leaf=1)
#n_estimators是树的数量,min_samples_split是当样本节点数小于2时,不再继续划分,min_samples_leaf是需要在叶子结点上的最小样本数量:
score = cross_val_score(estimator=lr,X=data[data_list],y=data['Survived'],cv=kf)
print(score.mean())
#上边是用随机森林预测结果

调节RandomForestClassifier()里的参数min_samples_split,min_samples_leaf可以提高预测准确率,可以防止过拟合现象。

例如:lr = RandomForestClassifier(n_estimators=200,min_samples_split=10,min_samples_leaf=2)

输出:0.8215488215488215

下边,对特征进行分析,找出对结果影响比较高的特征。用SelectKBest。额外加了3个特征,名字长度、称呼、家庭成员数量。

from sklearn.feature_selection import SelectKBest,f_classif
import re #提取姓名的中间部分,需要用到正则表达式
data_name=[]  #data_name用来放所有人名的中间的称呼
for i in data['Name'].tolist():
    name = re.search(r' (\w+)',i).group(1)
    #这是正则表达式的用法re.search().group()返回的才是提取到的内容。group()的话提取到的内容带空格,group(1)是去掉空格的部分,也就是group(0)是空格
    data_name.append(name)
data["Name_middle"] = data_name
print(data["Name_middle"].value_counts())  #可以查看不同的称呼内容以及出现的次数
data_name_list=[]
for i in data_name:  #把所有出现过的称呼组成一个列表,放入data_name_list中,也就相当于data_name去重之后的内容
    if i not in data_name_list:
        data_name_list.append(i)
data_name_list_number=list(range(0,len(data_name_list)))
name_dic = dict(zip(data_name_list,data_name_list_number))
#将所有称呼进行编码,组成一个字典,key是称呼,value是编码
for i,j in name_dic.items(): #将data["Name_middle"]的称呼转为对应的编码
    data["Name_middle"].replace(i,j,inplace=True)
print(data["Name_middle"].value_counts())  #检查下,发现转换前后没有问题,转换成功
data['family_nubmer'] =data['SibSp']+data['Parch']
name_length_list=[] #计算所有人的姓名的长度
for i in data['Name'].tolist():
    name_length_list.append(len(i))
data['name_length'] =name_length_list
data_list=['Pclass','Sex','Age','SibSp','Parch','Fare','Embarked',
          'name_length',"Name_middle",'family_nubmer']
selector = SelectKBest(f_classif) #SelectKBest新版sklearn不用写常数项参数。
selector.fit(data[data_list],data['Survived'])
print("pvalues_:",selector.pvalues_)  # p-values 越小,置信度越高,特征越重要
print("scores_:",selector.scores_)  # scores_得分越高,特征越重要
print(type(selector.scores_))

输出:

pvalues_: [2.53704739e-25 1.40606613e-69 5.27606885e-02 2.92243929e-01
 1.47992454e-02 6.12018934e-15 1.40831242e-03 2.02679507e-24
 1.81233819e-04 6.19891122e-01]
scores_: [1.15031272e+02 3.72405724e+02 3.76152805e+00 1.11057220e+00
 5.96346384e+00 6.30307642e+01 1.02593551e+01 1.10388690e+02
 1.41353649e+01 2.46193112e-01]

 

下面画柱状图分析下:

 

#画柱状图,将pvalues_和scores_都反应到一张图上。
name_list = data_list
num_list = pvalues_list
num_list2 = selector.scores_
x = list(range(len(num_list)))
total_width, n = 0.8, 2
width = total_width / n
plt.bar(x, num_list, width=width, label='pvalues', fc='b')
for i in range(len(x)):
    x[i] += width
plt.bar(x, num_list2, width=width, label='scores', tick_label=name_list, fc='g')
plt.xticks(rotation=90)
plt.legend()
plt.show()

输出:

 

 发现:Pclass、Sex、Fare、name_length影响最大。下边,应该利用这些特征,应用在集成算法上,继续计算生存率。集成算法现在还没有学到,大体上是说,把很多算法融合在一起,每一个算法计算一个生存率,再求平均值。这种集成算法应用比较广,再很多比赛里用这样的算法可以提高预测精度。但是比较耗费时间。这是集成算法中的一类。这些请参考https://blog.csdn.net/wp_python/article/details/79815191。

另外,在写完结束的到时候,发现有一篇类似的文章,https://www.cnblogs.com/my-love-is-python/p/9514546.html。和唐宇迪的写的几乎是一样的,代码比较简洁。

无意中看到了GridSearchCV函数,这个函数说白了就是通过遍历的方式,找到算法某个参数中比较好的值,在以后的论文中一定能用到。

posted @ 2020-08-15 15:07  理工—王栋轩  阅读(231)  评论(0编辑  收藏  举报