Python+pandas数据分析:用python实现Kaggle的Titanic数据分析例子

一、在数据处理方向上,R语言相比,python更接近编程语言,先学习pandas包的内容,之后再学习sklearn包运用;

二、引用kaggle上面的入门例子,Titanic的数据学习,是kaggle网站上分享的代码,我基本上是将它翻译过来了,原网址: https://www.kaggle.com/omarelgabry/titanic/a-journey-through-titanic python版本:我装的是anaconda,里面有了各种包,但是没有seaborn包,使用pip install seaborn 安装或者conda install seaborn。

1、大致思路很明显: ①对每个变量进行数据探索,然后将其作图,然后又变量被舍弃,比如Embarked,有变量被拆分,比如性别Female和male,有变量被合并之类的。 ②对于train集合和test集合都是一起对变量做取舍,方便建模使用。 ③用不同的模型拟合train集合,然后会给不同的模型打分,提交打分最高的那个模型,这需要sklearn包来实现,它自带了很多算法。

2、我觉得最难的是: ①有一个统计学的底蕴,能够对数据充满敏感,对于数据分布有一定的感知能力,这就需要对各种数据分布非常了解。 ②在数据字段操作的时候,要同时操作train和test两个集合,以便在建模时候的变量准确。 ③对于sklearn中各个模型和模型的使用都有一定的了解。

3、在我实习时,数据处理用R来写的,在查看每个变量的分布之前,有对数据进行很粗糙的处理,比如: _集中度、缺失度、因子水平、iv值 _ ,这些变量如果达不到一定的硬性标准,脚本会自动删除这些变量,不需要查看变量的分布就可以做到。 我觉得这个可以作为数据处理的标准化流程之一,一定要谨记!!! 三、代码: 1、数据准备和加载: ①首先引入包

    from pandas import DataFrame
  import numpy as np
  import matplotlib.pyplot as plt
  import seaborn as sns
  sns.set_style('whitegrid')
  from sklearn.linear_model import LogisticRegression
  from sklearn.ensemble importRandomForestClassifier
    引入了pandas中的DataFrame对象,numpy包,matplotlib包,seaborn包,还有sklearn包的逻辑回归模型和随机森林分类器,sklearn中的子包和文档需要仔细研究、、

②加载并查看数据:

    titanic_df=pd.read_csv('C:\\Datemining\\tatannic\\train.csv', dtype={"Age": np.float64}, )
  test_df=pd.read_csv('C:\\Datemining\\tatannic\\test.csv', dtype={"Age": np.float64}, )
  titanic_df.head()
  titanic_df.info()
  print("----------------------------")
  test_df.info()
  titanic_df = titanic_df.drop(['PassengerId','Name','Ticket'], axis=1)
  test_df   = test_df.drop(['Name','Ticket'], axis=1)
    用pandas.read_csv()方法读入本地的csv格式的train和test文件(R语言写法为read.csv()),读入后数据的类型默认是DataFrame,用DataFrame.head()方法得到它的前几行,用DataFrame.info()方法得到它的列变量信息。
  将train的'PassengerId','Name','Ticket'这三列删除,test删除'Name','Ticket'列。
  train和test的变量信息分别如下:
    <class 'pandas.core.frame.DataFrame'>
  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
  dtypes: float64(2), int64(5), object(5)
  memory usage: 66.2+ KB
  ----------------------------
  <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: 27.8+ KB

用DataFrame.shape属性可以查看数据框的大小,删除了三列变量后train的大小是一个891×9的数据框。

    titanic_df.shape
  (891, 9)

2、变量探索: ①‘Embarked’字段,我觉得翻译过来应该称“乘船类型” 数据框引用某一列有两种方式,一个是用.引用,一个是[ ]引用,个人倾向于第一个,更像面向对象的引用方式。(如果是R语言,用dataframe$Embarked引用) 统计得到train数据中Embarked非空值,用count()函数,可以看出Embarked有两个空值

    titanic_df.Embarked.count()
  Out[16]: 889
  或者:
  titanic_df['Embarked'].count()
  Out[19]: 889

可以查看第哪几个记录是空值:

    titanic_df.Embarked[titanic_df.Embarked.isnull()]
  Out[8]:
  61     NaN
  829   NaN
  Name: Embarked, dtype: object

isnull() 会返回一个值是boolean的Series序列,在引用时候返回值是ture的值(R语言会写成 titanic_df$Embarked[which(titanic_df$Embarked==NA)] 可以看到61和829条记录是空值,只有两个是空。 可以用groupby方法大致统计一下,Embarked的取值情况:

    titanic_df.groupby('Embarked').Survived.count()
    >Out[13]: 
    Embarked
    C    168
    Q     77
    S    646
    Name: Survived, dtype: int64
    可以看出Embarked取值只有3个值,每个值对应的人数有统计量,发现基本上大部分取值都是'S',即顾客。因此将两个空值用出现次数最多的'S'来填补
    (如果是数值int类型,并且确实率在可接受范围内(<20%)可以用均值、中位数来填补)
    titanic_df["Embarked"] = titanic_df["Embarked"].fillna("S")
    把Embarked这个变量画出来,看一下它的大致分布和样子,初步判断应该做怎样的筛选:
    sns.catplot('Embarked','Survived',data=titanic_df,height=3,aspect=3)
    使用了seaborn中的factorplot函数,产看它的文档发现factorplot()函数的图像的kind参数可以设为:violin、strip、bar、box。可以下去试一下,个人觉得kind='violin'是最容易看出它的分布和人数情况,也是最有用的!
    下面这句话定义了一个fig图和它对应的三个子图axis1,axis2,axis3,
    fig, (axis1,axis2,axis3) = plt.subplots(1,3,figsize=(15,5))

可以将它拆分为四句话:

    fig=plt.figure(figsize=(15,5))
    axis1=fig.add_subplot(1,3,1)
    axis2=fig.add_subplot(1,3,2)
    axis2=fig.add_subplot(1,3,3)

第一种写法更精炼! 延伸一下,在使用matplotlib的pyplot的figure定义图像时候,如果统一定义坐标轴的名称用plt的属性,例如:

    import matplotlib.pyplot as plt
    fig=plt.figure(figsize=(10,5))
    fig.title('testdata')
    fig.xlabel('age')
    fig.ylabel('survived')
    plt.show()
    如果单独定义每个头像的坐标轴和title:
    axis1=fig.add_subplot(131)
    axis1.set_xlabel('age')
    axis1.set_ylabel('survived')
    temp1.plot()
    temp1是一个DataFrame,只要plot()就默认为占用这个axis1

经过初步统计和factorplot函数的画图后,大致对Embarked这个变量的分布情况有了一定的了解,因为定义了一个fig里面有三个子图,接下来就是对3个子图的填充:

    sns.countplot(x='Embarked', data=titanic_df, ax=axis1)
    
    sns.countplot(x='Survived', hue="Embarked", data=titanic_df, order=[1,0], ax=axis2)

上面两行分别填充fig的前两个图,axis1对Embarked的三个取值进行统计,axis2对Survived的取值做统计; 下面是构造了一个embark_perc数据框计算它的三个取值的均值。

    embark_perc = titanic_df[['Embarked','Survived"]].groupby(['Embarked'],as_index=False).mean()
    sns.barplot(x='Embarked', y='Survived', data=embark_perc,order=['S','C','Q'],ax=axis3)

titanic_df[['Embarked','Survived"]].groupby(['Embarked'] 相当于sql语句中的groupby函数,mean()函数对它计算均值后,生成了一个数据框DataFrame。(图就不上传了)

使用pd.get_dummies()方法得到Embarked这个变量的指标,然后train和test分别右连接这个统计指标表,会产生三个新的变量:‘S’,‘C’,‘Q’。 可能连接后’S’变量贡献度太小,删除’S’这个变量,保留’C’,‘Q’,而且很显然这两列的值都是0或1的标签。

    embark_dummies_titanic  = pd.get_dummies(titanic_df['Embarked'])
    embark_dummies_titanic.drop(['S'], axis=1, inplace=True)
    
    embark_dummies_test  = pd.get_dummies(test_df['Embarked'])
    embark_dummies_test.drop(['S'], axis=1, inplace=True)
    
    titanic_df = titanic_df.join(embark_dummies_titanic)
    test_df    = test_df.join(embark_dummies_test)
    
    titanic_df.drop(['Embarked'], axis=1,inplace=True)
    test_df.drop(['Embarked'], axis=1,inplace=True)

将原来的Emabrked这个变量删除:

    titanic_df.drop(['Embarked'], axis=1,inplace=True)
    test_df.drop(['Embarked'], axis=1,inplace=True)

注:删除时候,要对train和test数据都做一样的处理。不能忘了、、

①‘Fare’字段:票价 如果变量是数值类型,可以用describe方法查看统计特性:

    test_df.Fare.describe()
    >Out[7]: 
    count    417.000000
    mean      35.627188
    std       55.907576
    min        0.000000
    25%        7.895800
    50%       14.454200
    75%       31.500000
    max      512.329200
    Name: Fare, dtype: float64
    
    test_df.shape
    >Out[5]: (418, 10)
    
    test_df.Fare.count()
    >Out[6]: 417

发现test数据中有一个Fare变量是空值,用fillna()方法填充中值:

    test_df["Fare"].fillna(test_df["Fare"].median(), inplace=True)

数据处理转换,将float转换成int类型:

    titanic_df['Fare'] = titanic_df['Fare'].astype(int)
    test_df['Fare']    = test_df['Fare'].astype(int)

分别得到Fare变量对应的幸存和没有幸存的记录,(这种引用很像R语言中的which()函数):

    fare_not_survived = titanic_df["Fare"][titanic_df["Survived"] == 0]
    fare_survived     = titanic_df["Fare"][titanic_df["Survived"] == 1]

转换成数据框DataFrame,并作图出来:

    avgerage_fare = DataFrame([fare_not_survived.mean(), fare_survived.mean()])
    std_fare      = DataFrame([fare_not_survived.std(), fare_survived.std()])
    频率直方图:
    titanic_df['Fare'].plot(kind='hist', figsize=(10,3),bins=100, xlim=(0,50))

注:直接调用plot()也是一种简单画图方法,与matplotlib.pyplot中面向对象画图一样,需要研究、、

    avgerage_fare.index.names = std_fare.index.names = ["Survived"]
    avgerage_fare.plot(yerr=std_fare,kind='bar',legend=False)

③Age变量:年龄 面向对象画图,两个图,分别设置title:

    fig, (axis1,axis2) = plt.subplots(1,2,figsize=(15,4))
    axis1.set_title('Original Age values - Titanic')
    axis2.set_title('New Age values - Titanic')

分别得到train和test年龄的平均数,方差,空值的数量:

    average_age_titanic   = titanic_df["Age"].mean()
    std_age_titanic       = titanic_df["Age"].std()
    count_nan_age_titanic = titanic_df["Age"].isnull().sum()
    
    average_age_test   = test_df["Age"].mean()
    std_age_test       = test_df["Age"].std()
    count_nan_age_test = test_df["Age"].isnull().sum()

随机制造count_nan_age_test 个年龄,然后填充到空值中(这个方法需要谨记,很有用!!!用到的是numpy包的可迭代序列):

    rand_1 = np.random.randint(average_age_titanic - std_age_titanic, average_age_titanic + std_age_titanic, size = count_nan_age_titanic)
    rand_2 = np.random.randint(average_age_test - std_age_test, average_age_test + std_age_test, size = count_nan_age_test)
    
    titanic_df['Age'].dropna().astype(int).hist(bins=70, ax=axis1)
    
    titanic_df["Age"][np.isnan(titanic_df["Age"])] = rand_1
    test_df["Age"][np.isnan(test_df["Age"])] = rand_2

用下面这段代码替换上面的代码不行,提示是Series不能hash:

    rand_1 = Series(np.random.randint(average_age_titanic - std_age_titanic, average_age_titanic + std_age_titanic, size = count_nan_age_titanic))
    rand_2 = Series(np.random.randint(average_age_test - std_age_test, average_age_test + std_age_test, size = count_nan_age_test))
    
    titanic_df["Age"][titanic_df["Age"].isnull()]=rand_1
    test_df["Age"][test_df["Age"].isnull]=rand_2

将年龄转换为int:

    titanic_df['Age'] = titanic_df['Age'].astype(int)
    test_df['Age']    = test_df['Age'].astype(int)

作图:

    titanic_df['Age'].hist(bins=70, ax=axis1)
    titanic_df['Age'].hist(bins=70, ax=axis2)

继续作图,seaborn的FaceGrid()方法,需要查一下、

    facet = sns.FacetGrid(titanic_df, hue="Survived",aspect=4)
    facet.map(sns.kdeplot,'Age',shade= True)
    facet.set(xlim=(0, titanic_df['Age'].max()))
    facet.add_legend()
    
    每个年龄的存活率:
    fig, axis1 = plt.subplots(1,1,figsize=(18,4))
    average_age = titanic_df[["Age", "Survived"]].groupby(['Age'],as_index=False).mean()
    sns.barplot(x='Age', y='Survived', data=average_age)

④Cabin变量:船舱号

     titanic_df.shape
    >Out[4]: (891, 12)
    
    titanic_df.Cabin.count()
    >Out[5]: 204
    
    titanic_df.drop("Cabin",axis=1,inplace=True)
    test_df.drop("Cabin",axis=1,inplace=True)

可以看到,总共891个记录,只有204个记录是非空的,而且它是一个字符型的,所以这个变量被删除了。

⑤整合Parch和SibSp变量(不太明白这两个变量的中文意思…) 将Parch和SibSp变量整合为一个Famliy变量,作为一个取值为0和1的标签变量。

    titanic_df.Parch.describe()
    >Out[15]:
    count    891.000000
    mean       0.381594
    std        0.806057
    min        0.000000
    25%        0.000000
    50%        0.000000
    75%        0.000000
    max        6.000000
    Name: Parch, dtype: float64
    
    titanic_df.Parch[titanic_df.Parch!=0].count()
    >Out[14]: 213
    
    titanic_df.SibSp[titanic_df.SibSp!=0].count()
    >Out[17]: 283
    可以发现,两者只有极少数不是0值,故:
    titanic_df['Family'] =  titanic_df["Parch"] + titanic_df["SibSp"]
    titanic_df['Family'].loc[titanic_df['Family'] > 0] = 1
    titanic_df['Family'].loc[titanic_df['Family'] == 0] = 0
    
    test_df['Family'] =  test_df["Parch"] + test_df["SibSp"]
    test_df['Family'].loc[test_df['Family'] > 0] = 1
    test_df['Family'].loc[test_df['Family'] == 0] = 0
    删除这两列:
    titanic_df = titanic_df.drop(['SibSp','Parch'], axis=1)
    test_df    = test_df.drop(['SibSp','Parch'], axis=1)
    画图:
    fig, (axis1,axis2) = plt.subplots(1,2,sharex=True,figsize=(10,5))
    sns.countplot(x='Family', data=titanic_df, order=[1,0], ax=axis1)
    
    计算幸存的人平均有几个家人:
    family_perc = titanic_df[["Family", "Survived"]].groupby(['Family'],as_index=False).mean()
    
    sns.barplot(x='Family', y='Survived', data=family_perc, order=[1,0], ax=axis2)
    axis1.set_xticklabels(["With Family","Alone"], rotation=0)

seaborn里的countplot()方法和barplot()方法。

⑥整合变量Sex: 定义一个函数来判断age是否超过16岁,对其进行分类,小于16岁分类为’child’,大于16岁保留性别:

    def get_person(passenger):
        age,sex = passenger
        return 'child' if age < 16 else sex
    新建的变量名为Person:,用apply()函数对train和test数据分类(像R中的apply):
    
    titanic_df['Person'] = titanic_df[['Age','Sex']].apply(get_person,axis=1)
    test_df['Person']    = test_df[['Age','Sex']].apply(get_person,axis=1)
    删除Sex变量
    titanic_df.drop(['Sex'],axis=1,inplace=True)
    test_df.drop(['Sex'],axis=1,inplace=True)
    
    达到变量的指标,并将它们的columns换成'Child','Female','Male'
    person_dummies_titanic  = pd.get_dummies(titanic_df['Person'])
    person_dummies_titanic.columns = ['Child','Female','Male']
    person_dummies_titanic.drop(['Male'], axis=1, inplace=True)
    
    person_dummies_test  = pd.get_dummies(test_df['Person'])
    person_dummies_test.columns = ['Child','Female','Male']
    person_dummies_test.drop(['Male'], axis=1, inplace=True)
    
    titanic_df = titanic_df.join(person_dummies_titanic)
    test_df    = test_df.join(person_dummies_test)
    
    定义图,作图:
    fig, (axis1,axis2) = plt.subplots(1,2,figsize=(10,5))
    
    sns.countplot(x='Person', data=titanic_df, ax=axis1)
    
    person_perc = titanic_df[["Person", "Survived"]].groupby(['Person'],as_index=False).mean()
    sns.barplot(x='Person', y='Survived', data=person_perc, ax=axis2, order=['male','female','child'])
    效果不明显,最后删了它:
    titanic_df.drop(['Person'],axis=1,inplace=True)
    test_df.drop(['Person'],axis=1,inplace=True)

⑦Pclass变量:等级

    sns.factorplot('Pclass','Survived',order=[1,2,3], data=titanic_df,size=5)
    
    # create dummy variables for Pclass column, & drop 3rd class as it has the lowest average of survived passengers
    将Pclass的三个取值做成标签,并删除train和test中的class_3变量,因为它的幸存率太低了!
    
    pclass_dummies_titanic  = pd.get_dummies(titanic_df['Pclass'])
    pclass_dummies_titanic.columns = ['Class_1','Class_2','Class_3']
    pclass_dummies_titanic.drop(['Class_3'], axis=1, inplace=True)
    
    pclass_dummies_test  = pd.get_dummies(test_df['Pclass'])
    pclass_dummies_test.columns = ['Class_1','Class_2','Class_3']
    pclass_dummies_test.drop(['Class_3'], axis=1, inplace=True)
    
    titanic_df.drop(['Pclass'],axis=1,inplace=True)
    test_df.drop(['Pclass'],axis=1,inplace=True)
    
    titanic_df = titanic_df.join(pclass_dummies_titanic)
    test_df    = test_df.join(pclass_dummies_test)

⑧数据探索结束,将train和test的变量化为统一:

    X_train = titanic_df.drop("Survived",axis=1)
    Y_train = titanic_df["Survived"]
    X_test  = test_df.drop("PassengerId",axis=1).copy()

三、建立模型 此时应用sklearn包了: ①用逻辑回归去拟合X_train和Y_train,然后用logreg.predict()函数去预测X_test的数据,最后用拟合的结果去给模型打分!

    logreg = LogisticRegression()
    
    logreg.fit(X_train, Y_train)
    
    Y_pred = logreg.predict(X_test)
    
    logreg.score(X_train, Y_train)

②随机森林:

    random_forest = RandomForestClassifier(n_estimators=100)
    
    random_forest.fit(X_train, Y_train)
    
    Y_pred = random_forest.predict(X_test)
    
    random_forest.score(X_train, Y_train)

四、相关分析(不懂!!):

    coeff_df = DataFrame(titanic_df.columns.delete(0))
    coeff_df.columns = ['Features']
    coeff_df["Coefficient Estimate"] = pd.Series(logreg.coef_[0])

五、生成csv,提交文件: 先构造一个数据框DataFrame

    submission = pd.DataFrame({
            "PassengerId": test_df["PassengerId"],
            "Survived": Y_pred
        })

再将其写成一个csv文件:

    submission.to_csv('titanic.csv', index=False)

至于,加标签后的准确率就是你排名高低的依据了,如果变量控制的好,无效变量少,那么你的得分率肯定是很高的,如果你建立模型的时候无效的变量 很多,那拟合出的结果肯定不理想,同时,你也需要比较各种不同模型,选择一个建立模型后的score最高的一个模型提交。

 

posted on 2021-06-29 15:29  BabyGo000  阅读(523)  评论(0)    收藏  举报