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的变量信息分别如下:

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
----------------------------

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最高的一个模型提交。

你可能感兴趣的:(Python基础和数据分析)