kaggle入门titanic分析

人人皆知的kaggle泰坦尼克号灾难分析,入门机器学习的一个很好的案例,在学完python后也试着做了一下分析。按照数据认知——数据探索性可视化分析——数据特征处理——模型建立——输出结果

一、数据认知
首先是导入数据,查看数据情况:

# -*- coding:utf-8 -*-
import pandas as pd
import numpy as np
from pylab import *
#更改matplotlib的字体中文显示
from matplotlib.font_manager import _rebuild
mpl.rcParams['font.family'] = ['Arial Unicode MS']
mpl.rcParams['axes.unicode_minus'] = False
train_df= pd.read_csv('/Users/shaling/Downloads/titanic/train.csv')
print(train_df.info())
print(train_df.describe())

结果如下:891个记录,其中Age和Cabin、Embarked缺少数据;平均年龄29岁,还是蛮年轻的

RangeIndex: 891 entries, 0 to 890
Data columns (total 12 columns):
PassengerId    891 non-null int64
Survived       891 non-null int64
Pclass         891 non-null int64
Name           891 non-null object
Sex            891 non-null object
Age            714 non-null float64
SibSp          891 non-null int64
Parch          891 non-null int64
Ticket         891 non-null object
Fare           891 non-null float64
Cabin          204 non-null object
Embarked       889 non-null object
       PassengerId    Survived      Pclass         Age       SibSp  \
count   891.000000  891.000000  891.000000  714.000000  891.000000   
mean    446.000000    0.383838    2.308642   29.699118    0.523008   
std     257.353842    0.486592    0.836071   14.526497    1.102743   
min       1.000000    0.000000    1.000000    0.420000    0.000000   
25%     223.500000    0.000000    2.000000   20.125000    0.000000   
50%     446.000000    0.000000    3.000000   28.000000    0.000000   
75%     668.500000    1.000000    3.000000   38.000000    1.000000   
max     891.000000    1.000000    3.000000   80.000000    8.000000   

            Parch        Fare  
count  891.000000  891.000000  
mean     0.381594   32.204208  
std      0.806057   49.693429  
min      0.000000    0.000000  
25%      0.000000    7.910400  
50%      0.000000   14.454200  
75%      0.000000   31.000000  
max      6.000000  512.329200  

二、数据探索性可视化分析

import matplotlib.pyplot as plt
fig = plt.figure()
plt.subplot2grid((2, 2), (0, 0))

# 生存人数柱状图
ax1 = train_df.Survived.value_counts().plot.bar(ylim=[0, 700])
plt.xticks(rotation=0)
ax1.set_title('生存分布')
plt.xlabel(u'获救情况(1为获救)')
plt.ylabel(u'获救人数')
plt.text(0 - 0.15, train_df.Survived.value_counts()[
         0] + 10, train_df.Survived.value_counts()[0])
plt.text(1 - 0.15, train_df.Survived.value_counts()[
         1] + 10, train_df.Survived.value_counts()[1])

# 乘客等级分布
plt.subplot2grid((2, 2), (0, 1))
ax2 = train_df.Pclass.value_counts().plot(kind='bar', ylim=[0, 700])
ax2.set_title('乘客船票等级分布')
plt.xlabel(u'乘客等级')
plt.ylabel(u'人数')
pc1 = list(train_df.Pclass.value_counts())
x = range((len(pc1)))
for i, j in zip(list(x), pc1):
    plt.text(i - 0.2, j + 10, j)

# 不同等级船票的年龄密度分布
plt.subplot2grid((2, 2), (1, 0), colspan=2)
train_df.Age[train_df.Pclass == 1].plot(kind='kde')
train_df.Age[train_df.Pclass == 2].plot(kind='kde')
train_df.Age[train_df.Pclass == 3].plot(kind='kde')
plt.title('各等级船票年龄密度分布')
plt.xlabel('年龄')
plt.ylabel('密度')
plt.legend((u'头等舱', u'一等舱', u'二等舱'), loc='best')
plt.tight_layout()

结果如下:
在这里插入图片描述
可以看到只有342人获救,占比38.38%,乘客的船票等级中3等最多,倒也符合有钱毕竟是少数的定理(穷啊穷),其次是一等,这里可以猜想船票等级高低应该会影响获救概率,年龄密度图中3等最年轻,一等最老,也符合财富分配的现实规律
我们一项一项来探索性看看各特征和获救之间的联系

# 不同等級船票对获救的影响
survide01 = train_df.Pclass[train_df.Survived == 0].value_counts()
survide02 = train_df.Pclass[train_df.Survived == 1].value_counts()
data_survie = pd.DataFrame({'unsurvie': survide01, 'survie': survide02})
data_survie.plot(kind='bar', stacked=True)
plt.title('不同等級船票对获救的影响')
plt.xlabel(u'各等级船舱')
plt.ylabel(u'人数')
plt.xticks(rotation=0)

在这里插入图片描述
看上去1等舱获救概率最高,3等最低,也符合现实逻辑,毕竟要让领导先走(滑稽)

再看性别,这一项肯定影响,因为女士毕竟有优先权嘛(名字暂且忽略,在我刚开始分析的逻辑里,名字应该是不影响的,除非暗含代表等级的信息或其他)

# 按性别查看获救情况
survie_sex01 = train_df.Survived[train_df.Sex == 'female'].value_counts(
)
survie_sex02 = train_df.Survived[train_df.Sex == 'male'].value_counts(
)
df_sex = pd.DataFrame({'女性': survie_sex01, '男性': survie_sex02})
df_sex.plot(kind='bar', stacked=True)
plt.xlabel('是否获救')
plt.ylabel('获救人数')
plt.title('不同性别获救情况')
plt.xticks([0,1],['未获救','获救'],rotation=0)

在这里插入图片描述
果然获救里女性占比非常高,性别是一个重要特征

再看年龄,按道理也应该是小孩和老人优先

#查看年龄与获救率的关系
survie_age01=train_df.Age[train_df.Survived==0]
survie_age02=train_df.Age[train_df.Survived==1]
fig=plt.figure()
plt.hist(survie_age01,bins=10,color='#85E6FF',edgecolor='black')
plt.hist(survie_age02,color='#FFD589',alpha=0.6,bins=10)
plt.legend(['未获救','获救'])
plt.title('年龄分布与获救率的关系')

在这里插入图片描述
可以看到年龄在10岁以下的获救概率相当高,并随着年龄增大降低,年龄也是一个重要特征

下一个是亲属人数

# 亲属人数对获救的影响
fig=plt.figure(figsize=(10,8))
ax1=fig.add_subplot(121)
p_s01=train_df.Parch[train_df.Survived==0].value_counts()
p_s02=train_df.Parch[train_df.Survived==1].value_counts()
df_ps=pd.DataFrame({'unsurvie':p_s01,'survie':p_s02})
df_ps.fillna(0,inplace=True)
df_ps.plot(kind='bar',ax=ax1,title='Parch人数对获救的影响')

ax2=fig.add_subplot(122)
ss01=train_df.SibSp[train_df.Survived==0].value_counts()
ss02=train_df.SibSp[train_df.Survived==1].value_counts()
df_ss=pd.DataFrame({'unsurvie':ss01,'survie':ss02})
df_ss.fillna(0,inplace=True)
df_ss.plot(kind='bar',ax=ax2,title='SibSp人数对获救的影响')

在这里插入图片描述
表明无论是Parch还是SibSp,在1人时获救概率都是最高的,为0的时候获救概率低,这里可以考虑延伸出一个特征,就是Parch或SibSp为1时,逻辑或许是一个人带着小孩,所以获救可能性提高?

Ticket信息也暂且忽略,实在活过于杂乱,不知道如何分析

Cabin缺失值很多,可以用是否有Cabin值来判断与获救之间是否联系(我原本以为是没有什么卵关系的,结果打脸==)
好吧,事实表明有船票号的貌似获救概率大一点

#是否有船票号对获救的影响
survide_Cabin = train_df.Survived[pd.notnull(train_df.Cabin)].value_counts()
survide_noCabin = train_df.Survived[pd.isnull(train_df.Cabin)].value_counts()
df3 = pd.DataFrame({'notnull': survide_Cabin, 'isnull': survide_noCabin}).transpose()
df3.plot(kind='bar', stacked=True)
plt.title('是否有船票号对获救的影响')
plt.xticks(x,['有船票','没有船票'],rotation=0)#用plt.xticks(['notnull','isnull'],['有船票','没有船票'],rotation=0)无效
plt.legend(('未获救', '获救'), loc='best')

在这里插入图片描述
再看看不同登船口是否影响获救

#不同港口登船的获救情况
E0 = train_df.Embarked[train_df.Survived == 0].value_counts()
E1 = train_df.Embarked[train_df.Survived == 1].value_counts()
df_em = pd.DataFrame({'unsurvived': E0, 'survied': E1})
df_em.plot(kind='bar', stacked=True)
plt.title('不同港口登船的获救情况')
plt.xticks(rotation=0)

在这里插入图片描述
貌似是C口获救概率高一点,为啥我也不知道,可能与在此口登陆的1等舱比例有关?
那就看看登陆口的仓位分布

# 登船口人数及船票等级分布
pc1 = train_df.Embarked[train_df.Pclass == 1].value_counts()
pc2 = train_df.Embarked[train_df.Pclass == 2].value_counts()
pc3 = train_df.Embarked[train_df.Pclass == 3].value_counts()
df_pc = pd.DataFrame({'一等舱': pc1, '二等舱': pc2, '三等舱': pc3}).stack().unstack()
df_pc.plot(kind='bar', stacked=True)
plt.xlabel('登船口')
plt.ylabel('人数')
plt.xticks(rotation=0)
plt.title('不同港口登船的仓位等级')

在这里插入图片描述
果不其然,C口明显一等舱占比高

三、数据特征处理
补充年龄缺失值,原本想用平均值一代了之,但是又觉得实在粗糙,就尝试用随机森林预测,并用交叉验证看看拟合度,结果array([ 0.32414037, 0.18749903, 0.18038975, 0.15348003, -0.03636364])),差的没法说,遂放弃。

# 补全缺失的年龄
from sklearn.ensemble import RandomForestRegressor #尝试用RF填充
def set_miss_age(df):
    # 把已有的数据值特征取出来放入RFRg
    df_age = df[['Age', 'Pclass', 'SibSp', 'Parch', 'Fare']]
    no_age = df_age[pd.isnull(df_age.Age)].values  # 缺少年龄的数据
    kown_age = df_age[pd.notnull(df_age.Age)].values  # 具有年龄的数据
    x = kown_age[:, 1:]
    y = kown_age[:, 0]
    rf = RandomForestRegressor(n_estimators=1100, n_jobs=-1, random_state=0)
    rf.fit(x, y)
    #看一下预测年龄的准确度
    from sklearn.model_selection import cross_val_score
    score_age=cross_val_score(rf,x,y,cv=5)
    return rf,score_age
print(set_miss_age(train_df))#年龄拟合度很差,放弃

低头思索,发现名字里都带有能够一定程度上反应年龄的称呼,Mr,Miss,Master等,所以就愉快的决定了尝试利用已知同样称呼的年龄平均值去填充未知年龄的同样称呼的缺失值,有那么一点绕= =,大概就是反正我们都被称为Mr,我年龄缺失,就拿已知的Mr年龄平均值用一下,总之过程如下:

#考虑使用姓名的称呼中的平均值来判断缺失年龄
def set_age_byname(df):
	df['call']=df.Name.apply(lambda x:x.split(',')[1].split('.')[0].replace(' ',''))#提取姓名称呼
	lost_age=list(set(df.call[df.Age.isnull()]))#缺失年龄所对应的称呼
	for i in lost_age:
		if df[(df.Age.notnull())&(df.call==i)].empty:
			df.loc[(df.Age.isnull())&(df.call==i),'Age']=int(df.Age.mean())#如果缺失年龄的类型没有参考的年龄则填充所有已知平均值
		else:
			df.loc[(df.Age.isnull())&(df.call==i),'Age']=int(df.Age[(df.Age.notnull())&(df.call==i)].mean())#如果缺失年龄的类型有参考的年龄则填充参考类型的平均值
	return df
train_df=set_age_byname(train_df)

将是否有船票特征化

#将是否有船票特征化
def set_carbin(df):
    df.loc[(pd.notnull(df.Cabin)), 'Cabin'] = 'YES'
    df.loc[(pd.isnull(df.Cabin)), 'Cabin'] = 'NO'
    return df
train_df = set_carbin(train_df)

填充缺失Embarked值

#补充embarked缺失值
train_df['Embarked']=train_df['Embarked'].fillna('S')#以众数补充

类别因子特征化

# 类目特征因子化
cabin_dummies = pd.get_dummies(train_df.Cabin, prefix='Cabin')
Sex_dummies = pd.get_dummies(train_df.Sex, prefix='Sex')
Embarked_dummies = pd.get_dummies(train_df.Embarked, prefix='Embarked')
Pclass_dummies = pd.get_dummies(train_df.Pclass, prefix='Pclass')
train_df = pd.concat([train_df, Pclass_dummies, cabin_dummies,Sex_dummies, Embarked_dummies], axis=1)

然后就是根据之前的探索分析加入年龄小于10岁和Parch或SibSp为1人列

#增加child,和亲属为1人的特征值(根据之前分析亲属为1人时存活率最高,年龄小于10岁时存活率高)
def f_age(x):
	if x<10:
		return 1
	else:
		return 0
train_df['child']=train_df.apply(lambda x:f_age(x.Age),axis=1)#增加10岁以下为child列

def f_parch(x,y):
	if x+y >0 and x+y<3:
		return 1
	else:
		return 0
train_df['relative']=train_df.apply(lambda x:f_parch(x.Parch,x.SibSp),axis=1)#增加所有亲属为1的列

然后就是Age值和Fare值标准化

# age,fare数值标准化
from sklearn import preprocessing
sclaer = preprocessing.StandardScaler()
# 这里series要转行成narray,但是此处标准化是对列进行,在进行转换时要用reshape(-1,1),而不是reshape(1,-1)
def feature_af(df,*a):
	for i in a:
		sclaerd = sclaer.fit(df[i].values.reshape(-1, 1))
		df[i+'_scalerd'] = sclaerd.transform(df[i].values.reshape(-1, 1))
	return df
train_df=feature_af(train_df,'Age','Fare')

三、数据建模
这里选用逻辑回归

#逻辑回归建模
from sklearn import linear_model
# 提取所有特征值
trainfeauter_df = train_df.filter(regex='Survived|Pclass_.*|Sex_.*|Age_.*|Fare_.*|Cabin_.*|child|Embarked_.*|relative|SibSp|Parch')
train_np = trainfeauter_df.values
y_survie = train_np[:, 0]
x_survie = train_np[:, 1:]
stf = linear_model.LogisticRegression(C=1.0, penalty='l1', n_jobs=-1, tol=1e-6)
stf.fit(x_survie, y_survie)
print(trainfeauter_df.info())

#查看现有模型性能及系数
from sklearn.model_selection import cross_val_score,train_test_split
score_stf=cross_val_score(stf,x_survie,y_survie,cv=10).mean()
print(score_stf)
table_feature=pd.DataFrame({'feature':list(trainfeauter_df.columns[1:]),'coef':list(stf.coef_.T)})
print(table_feature)

结果如下,拟合度0.809,各参数也基本符合之前探索,只是觉得C口这个居然没有关系有点诧异

0.8092217114969923
         feature                    coef
0          SibSp   [-0.5055749360612298]
1          Parch   [-0.2324543480815097]
2       Pclass_1    [0.3163060430651191]
3       Pclass_2                   [0.0]
4       Pclass_3   [-1.0500482980848262]
5       Cabin_NO   [-0.1492679145618098]
6      Cabin_YES    [0.5926646317175299]
7     Sex_female    [1.8894251365330756]
8       Sex_male   [-0.7827157986964006]
9     Embarked_C                   [0.0]
10    Embarked_Q                   [0.0]
11    Embarked_S   [-0.3337015299987719]
12         child    [1.6525186173174804]
13      relative    [0.4626813043649717]
14   Age_scalerd  [-0.30876093679311006]
15  Fare_scalerd   [0.14129150188979916]

四、输出结果

接着就是处理预测数据,先看看整体情况

test_df = pd.read_csv('/Users/shaling/Downloads/titanic/test.csv')
print(test_df.info())

结果:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 418 entries, 0 to 417
Data columns (total 11 columns):
PassengerId    418 non-null int64
Pclass         418 non-null int64
Name           418 non-null object
Sex            418 non-null object
Age            332 non-null float64
SibSp          418 non-null int64
Parch          418 non-null int64
Ticket         418 non-null object
Fare           417 non-null float64
Cabin          91 non-null object
Embarked       418 non-null object
dtypes: float64(2), int64(4), object(5)
memory usage: 36.0+ KB

按照训练数据一样处理预测数据,

# 补充缺失年龄
test_df=set_age_byname(test_df)
#将是否有船票特征化
test_df=set_carbin(test_df)
# 填充缺失fare值
test_df.loc[test_df.Fare.isnull(), 'Fare'] = test_df.Fare.mean()

# 类目因子化
cabin_dummies = pd.get_dummies(test_df.Cabin, prefix='Cabin')
Sex_dummies = pd.get_dummies(test_df.Sex, prefix='Sex')
Embarked_dummies = pd.get_dummies(test_df.Embarked, prefix='Embarked')
Pclass_dummies = pd.get_dummies(test_df.Pclass, prefix='Pclass')
test_df = pd.concat([test_df, Pclass_dummies, cabin_dummies,Sex_dummies, Embarked_dummies], axis=1)

#增加relative为1特征值
test_df['child']=test_df.apply(lambda x:f_age(x.Age),axis=1)#增加10岁以下为child列
test_df['relative']=test_df.apply(lambda x:f_parch(x.Parch,x.SibSp),axis=1)

#Age,Fare数据标准化
test_df=feature_af(test_df,'Age','Fare')

接着便是最后的预测过程了

# 提取测试所有特征值
testfeature_df=test_df.filter(regex='Pclass_.*|Sex_.*|Age_.*|Fare_.*|Cabin_.*|child|Embarked_.*|relative|SibSp|Parch')

stf.fit(x_survie,y_survie)
reslut_teststf=stf.predict(testfeature_df)
reslut_dfstf=pd.DataFrame({'PassengerId':test_df.PassengerId,'Survived':reslut_teststf.astype(np.int32)})
reslut_dfstf.to_csv('/Users/shaling/Downloads/titanic/logistic_regression_reslutstf.csv',index=False)

这个时候提交上去大概也只有0.7799好像(实在想不起来这次提交的是多少了,因为刚开始做的时候不知道哪里出错,试了好几个特征,提交了好几次,便不知道这一版是多少了==)

心想也太低了,便再研究,如下:

  1. 既然利用了称呼预测年龄,那么将称呼算一个特征,看看是否影响
#称呼的获救影响
call01=train_df.call[train_df.Survived==1].value_counts()
call02=train_df.call[train_df.Survived==0].value_counts()
df_cs=pd.DataFrame({'survived':call01,'unsurvived':call02})
df_cs.plot(kind='bar')
plt.xticks(rotation=0)
plt.xlabel('称呼')
plt.title('称呼对获救影响')
plt.show()

在这里插入图片描述
结果显示称呼为Miss、Master和Mrs获救概率高,Mr获救概率低,故加入称呼特征,这里为好处理,将所有不是Miss、Master、Mr、Mrs的其他类归为other类,因为数量太少

#加入称呼因子化列
def call_dum(x):
    if x=='Mr':
        return 'Mr'
    elif x=='Miss':
            return 'Miss'
    elif x=='Mrs':
            return 'Mrs'
    elif x=='Master':
        return 'Master'
    else:
        return 'other'
train_df['dumscall']=train_df.call.apply(lambda x:call_dum(x))
calldums=pd.get_dummies(train_df['dumscall'],prefix='call')
train_df=pd.concat([train_df,calldums],axis=1)
  1. 发现船票号好像有些事重复的,会不会重复的代表一家人?便看了一下家庭姓名是一样的,船票号又是一样的会不会要么一起死,要么一起生?,研究完发现好像也不是的,过程如下:
#增加家属人数及家族姓名列:
train_df['familysize']=train_df['Parch']+train_df['SibSp']
train_df['familyname']=train_df.Name.apply(lambda x: x.split(',')[0].replace(' ',''))
t=train_df[train_df.Ticket.duplicated(keep=False)]
t1=t[t.familyname.duplicated(keep=False)&t.familysize>0]
ticket01=t1.Ticket[t1.Survived==0].value_counts()
ticket02=t1.Ticket[t1.Survived==1].value_counts()
ticket_df=pd.DataFrame({'survived':ticket01,'unsurvived':ticket02})
ticket_df.plot(kind='bar',figsize=(100,80))
plt.xticks(rotation=0)

在这里插入图片描述
家族名相同船票也一样的很多,所有有些看不清,结果就是并不是同船票同姓的人一起生一起死,还是有生有死。

再想同船票号的有影响么,画图看看

#船票有同号的生存率
train_df['shareticket']=np.where(train_df.Ticket.duplicated(keep=False),'shared','unshared')
st01=train_df.shareticket[train_df.Survived==0].value_counts()
st02=train_df.shareticket[train_df.Survived==1].value_counts()
df_st=pd.DataFrame({'survived':st02,'unsurvived':st01}).transpose()
df_st.plot(kind='bar',stacked=True)
plt.xticks(rotation=0)
plt.title('所持船票号是否出现重复对生存影响')
plt.xlabel('是否共享船票号')
plt.ylabel('人数')

结果
在这里插入图片描述
貌似所持船票号有重复的生存概率高,好吧,算一个,加入

shareticket=pd.get_dummies(train_df.shareticket,prefix='ticket')
  1. 第三个其实是看的大佬的分析,就是按理女性生存率要高,那么我们看看那些是女性但是又死亡的了是个什么情况,发现但凡是女性死了,那么与这个女性相同的姓名且拥有相同的船票号且亲属又是大于1人的全部都死了(应该还是家庭团灭),只有一个家庭例外,生存下来的是一个不到1岁的婴儿,故加入女性家庭的死亡特征(实际上在加入这一特征后公分达到了0.8,模型拟合度也达到了0.87)
#添加女性死亡的家庭成员所有死亡特征
def feature_familydead(df):
    dead_df=train_df[train_df.Survived==0]
    tf=dead_df[(dead_df['Sex']=='female')&(dead_df['familysize']>=1)][['familyname','Ticket']]
    df['deadfamily']=np.where(df['Ticket'].isin(tf.Ticket)&df['familyname'].isin(tf.familyname)&((df.Age>=1)),1,0)
    return df
feature_familydead(train_df)

加入以上称呼、是否船票重复,女性家庭死亡特征后再次预测,公分达到了0.8,虽然后来我又折腾了男性生存特征等等,总之SVM和逻辑回归模型拟合度可以达到0.9,但是提交上去公分始终是0.8038或者0.808,不折腾了,到此为止,还要再研究一下各个行业的业务模型,用户行为啊,信贷风控等。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值