泰坦尼克号预测生存可能性
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函数,这个函数说白了就是通过遍历的方式,找到算法某个参数中比较好的值,在以后的论文中一定能用到。