Python Pandas数据处理入门(Kaggle Titanic竞赛数据)

Kaggle数据竞赛是学习ML的一种有效途径。对数据进行有效预处理是在做ML之前重要的一个步骤。本文翻译自Kaggle Titanic入门级竞赛的教程之一:

Getting Started with Pandas: Kaggle's Titanic Competition


通过使用编程语言,我们可以1)使用更加强大的的结构和方法,例如使用数组去存储和检索变量,2)编写可重复运行的脚本。

然而,你可能会认为使用Excel会更容易的理解数据。(当你想看某一列时,你只需要鼠标滚轮滑到那列即可)另一方面,你的统计学朋友告诉你使用R会更好,R有“data.frame”的概念。好吧,在本教程中,我们使用另一种方式来为数据和模型之间搭一座桥梁。

Python有一个很棒的包叫做Pandas,它可以让数据探索和数据清洗变得比操作数组更容易。而且它让你的代码更加易读。Pandas也有数据框架(dataFrame)的概念。最后,如果你去论坛中搜索其他教程的话,你会发现那些作者也用Pandas。

本教程与之前的两个有一些区别:没有粘性的可运行脚本,在数据页面没有样例.py文件。取而代之的是,本教程列出python命令行,这样,你可以自己学习一些方法,然后观察发生了什么。你甚至可以使用你自己感兴趣的方法。最后,命令的输出有时候会非常长,这里不会将之全部显示出来。

Ready?如果你已经安装了ipython或ipython notebook,启动它吧。

Numpy数组

先让我们复习一下我们的train.csv数据在python中是长什么样的。再次运行一下导入数据的脚本:

import csv as csv
import numpy as np

csv_file_object = csv.reader(open('../csv/train.csv', 'rb')) 
header = csv_file_object.next() 
data=[] 

for row in csv_file_object:
    data.append(row)
data = np.array(data)

现在输入print data

[['1' '0' '3' ..., '7.25' '' 'S']
 ['2' '1' '1' ..., '71.2833' 'C85' 'C']
 ['3' '1' '3' ..., '7.925' '' 'S']
 ..., 
 ['889' '0' '3' ..., '23.45' '' 'S']
 ['890' '1' '1' ..., '30' 'C148' 'C']
 ['891' '0' '3' ..., '7.75' '' 'Q']]

这些都是相似的。。。一个csv包能够读取的字符串数组。

观察Age列的前15行:data[0:15,5]

array(['22', '38', '26', '35', '35', '', '54', '2', '27', '14', '4', '58', '20', '39', '14'], 
 dtype='|S82')
很好,这条命令仅给出了年龄,而且它们仍为字符串。整个列的对象类型是什么呢?

type(data[0::,5])

numpy.ndarray

我们从数据中获取的任意的切片仍然是一个Numpy数组。现在我们看看我们能否计算游客年龄的均值。它们应该是浮点类型:

ages_onboard = data[0::,5].astype(np.float) 

ValueError: could not convert string to float:
额。看起来只对前几行管用,当处理第6行的缺失数据‘’时numpy就产生错误了。由此我们就必须使用python去过滤缺失值、转换到浮点型,再计算均值--但这不再简单了。那么我们使用Pandas再试一遍看看。

Pandas数据框架

我们需要做的第一件事情是import Pandas包。Pandas拥有自己的读写.csv文件的函数,因此我们不在需要csv包。我们创建一个新的对象‘df’来存储train.csv版本的pandas。

import pandas as pd
import numpy as np

# For .read_csv, always use header=0 when you know row 0 is the header row
df = pd.read_csv('train.csv', header=0)
看看出现了什么:

df

(...long list of stuff...!)
...
...
...
891 rows × 12 columns
这看起来没什么用。让我们仅看前几行:

df.head(3)

(a short list of stuff!)
...
3 rows × 12 columns
你注意到它有列名,边上还有行的序号。(注意,你也可以试试df.tail(3))现在,比较原始的数据数组,这个数据对象的类型是什么?

type(df)

pandas.core.frame.DataFrame
回顾之前使用csv包的结果,每个值都被读取为字符串类型。现在使用pandas自己的csv读取器的结果如何?

df.dtypes

PassengerId  int64
Survived     int64
Pclass       int64
Name        object
Sex         object
Age        float64
SibSp        int64
Parch        int64
Ticket      object
Fare       float64
Cabin       object
Embarked    object
dtype: object
Pandas能够探测到数值类型。因此我们已经有一些存为整数的值了。当它检测到双精度点的时候,它会自动将其转化为float类型。这里有两个更加实用的命令:

df.info()


Int64Index: 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)
这里有好多有用的信息!你可以直观的知道这里有891项(行),大部分变量都是完整的(891为non-null),但除了Age、Cabin、Embarked——这些列在某处都有空值。现在试试:

df.describe()

      PassengerId    Survived      Pclass         Age  ...
count  891.000000  891.000000  891.000000  714.000000  ...
mean   446.000000    0.383838    2.308642   29.699118  ... 
std    257.353842    0.486592    0.836071   14.526497  ...
min      1.000000    0.000000    1.000000    0.420000  ...
25%    223.500000    0.000000    2.000000   20.125000  ...
50%    446.000000    0.000000    3.000000   28.000000  ... 
75%    668.500000    1.000000    3.000000   38.000000  ...
max    891.000000    1.000000    3.000000   80.000000  ...

这个同样有用:pandas列出了所有数值列,而且快速地算出了均值、标准差、最小和最大值。非常方便!但要注意的是:我们知道在Age中有很多缺失值,pandas是怎么处理它们的呢?它必须遗留下很多空值。那么如果我们查询“Titanic上年龄均值”,我们需要告诫我们是如何得到这个数字的。

数据整理

任何数据分析中都有一个数据清洗的步骤。Pandas让数据过滤、制造、剔除、填充和替换都变得非常简单。下面我们学习pandas对特定列操作的语法。

引用与过滤

让我们看一下Age列的前10行。在pandas中是这样的

df['Age'][0:10]

0 22
1 38
2 26
3 35
4 35
5 NaN
6 54
7 2
8 27
9 14
Name: Age, dtype: float64
-->试试可替换的语法:df.Age[0:10]

-->不要数下标,你现在能显示Cabin列了吗?

与之前一样,让我们看看这个对象的类型:

type(df['Age'])

pandas.core.series.Series
一个单列既不是numpy的数组类型,也非pandas数据框架——而是一个特定的pandas对象,叫做数据序列(series)

我们如果想得到均值:

df['Age'].mean()

29.69911764705882
这和df.describe()中描述的是一样的。

-->看看你能不能获得Age的中值。

我们需要做的下一件事情就是观察dataframe中特定的子集。Pandas使之写起来非常方便。a[list]的形式即可:

df[['Sex','Pclass','Age']]

  Sex    Pclass Age
0 male   3      22.0
1 female 1      38.0
2 female 3      26.0
3 female 1      35.0
4 male   3      35.0
5 male   3       NaN
  ...    ...    ...
  ...    ...    ...

[891 rows x 3 columns]
如果我们手动研究数据的话,过滤数据是另一个十分重要的工具。.describe()命令已经告诉我们最大年龄是80。老人在数据集中是什么样的?

df[df['Age']>60]

(a medium list of stuff!)
...
...
22 rows × 12 columns
如果你对这些游客的性别和Pclass和他们是否幸存感兴趣的话:
df[df['Age']>60][['Sex','Pclass','Age','Survived']]

    Sex    Pclass Age   Survived
33  male   2      66.0  0
54  male   1      65.0  0
96  male   1      71.0  0
116 male   3      70.5  0
170 male   1      61.0  0
252 male   1      62.0  0
275 female 1      63.0  1
280 male   3      65.0  0
    ...    ...    ...   ...
    ...    ...    ...   ...
[22 rows x 4 columns]
现在是时候研究所有的这些缺失的Age值,如果我们希望使用更加先进的算法,我们就要解决这些值。过滤缺失值,使用:

df[df['Age'].isnull()][['Sex', 'Pclass', 'Age']]

(a long list of stuff!)
...
...
...
177 rows × 3 columns
这里我们仅仅显示了177行,但用同样的语法我们可以对其进行操作。

连接多个标准也是非常有用的(使用&)。让我们计算一个每个类别中男性的数量:

for i in range(1,4):
    print i, len(df[ (df['Sex'] == 'male') & (df['Pclass'] == i) ])
1 122
2 108
3 347
在我们完成初始的手动研究之前,先看看另一个非常方便的pandas函数,它可以为任一列绘制直方图。pandas直方图函数比起matplotlib/pylab而言比较方便,输入以下代码:

import pylab as P
df['Age'].hist()
P.show()


在.hist()的括号中,你可以指定一些明确的选项。在你调用它之前,你也可以明确的去掉那些缺失值:

df['Age'].dropna().hist(bins=16, range=(0,80), alpha = .5)
P.show()


清洗数据
Ok,现在我们对语法已经较为熟悉了,我们下一步要做的就是将dataframe中的值转化到机器学习中的样子。首先,我们很难使用字符串‘male’和‘female’来分析数据。因此先让我们练习用三种方式转换他们--前两个为了好玩,后一个比较实用。我们将把转换后的数据变成新的一列,所以原始Sex列不会改变。

在Pandas中,增加一列非常简单:

df['Gender'] = 4
显示一些.head()行看看我们刚刚做了什么。现在让我们把它的之变为Sex的首字母大写形式:

df['Gender'] = df['Sex'].map( lambda x: x[0].upper() )
lambda x 是python中内建的匿名函数。记住,x[0]返回任何字符串的首个字符。

看看现在.head()行变成什么样了?

当然我们需要的是一个二元整数来表示男性和女性,就和Survived列一样。为了与其一致,我们让Gender列映射到0,1。我们有先分析女性的先例,因此我们决定female=0,male=1.

df['Gender'] = df['Sex'].map( {'female': 0, 'male': 1} ).astype(int)
看看现在.head()行又变成什么样了?

-->你能对Embarked值做相似的事吗?

因为大部分机器学习需要一个完整的特征列,现在我们需要处理Age中缺失的数据。如果我们随便猜测数据填进去,这就相当于想模型中加入了一些噪音,但如果我们猜测是有根据的,一些值是接近历史事实的,那么模型的效果必然是比随机猜测要好。我们知道已知数据的均值是29.6991176——我们应该把空值替换为这个值。但是不是中值会更好呢?(会减少70-,80-的影响)Age直方图确实看起来是倾斜的。这些是你在Kaggle比赛中应当做的决定之一。

现在,让我们的决定更加复杂,我们希望使用游客类别来划分填入的年龄值。同时决定使用中值会更好。让我们建立另一个参照表存储这些值:

median_ages = np.zeros((2,3))
median_ages
生成:

array([[ 0., 0., 0.],
       [ 0., 0., 0.]])
计算数组:

for i in range(0, 2):
    for j in range(0, 3):
        median_ages[i,j] = df[(df['Gender'] == i) & \
                              (df['Pclass'] == j+1)]['Age'].dropna().median()
 
median_ages
生成:

array([[ 35. , 28. , 21.5],
       [ 40. , 30. , 25. ]])
我们可以直接向Age列填入这些值。但为了区分哪些地方是缺失的,我们还是新建一列AgeFill比较好。

做一个Age的拷贝:

df['AgeFill'] = df['Age']

df.head()
看看,Age列值为空, 我们关注的几列 现在变成什么样了:

df[ df['Age'].isnull() ][['Gender','Pclass','Age','AgeFill']].head(10)
   Gender Pclass Age AgeFill
5  1      3      NaN     NaN
17 1      2      NaN     NaN
19 0      3      NaN     NaN
26 1      3      NaN     NaN
28 0      3      NaN     NaN
29 1      3      NaN     NaN
31 0      1      NaN     NaN
32 0      3      NaN     NaN
36 1      3      NaN     NaN
42 1      3      NaN     NaN
用median_ages中的值填到AgeFill列中。

for i in range(0, 2):
    for j in range(0, 3):
        df.loc[ (df.Age.isnull()) & (df.Gender == i) & (df.Pclass == j+1),\
                'AgeFill'] = median_ages[i,j]
看看我们之前观察的前10行变成什么样了:

df[ df['Age'].isnull() ][['Gender','Pclass','Age','AgeFill']].head(10)
   Gender Pclass Age AgeFill
5  1      3      NaN    25.0
17 1      2      NaN    30.0
19 0      3      NaN    21.5
26 1      3      NaN    25.0
28 0      3      NaN    21.5
29 1      3      NaN    25.0
31 0      1      NaN    35.0
32 0      3      NaN    21.5
36 1      3      NaN    25.0
42 1      3      NaN    25.0
这证实了我们想要做的已经完成了。

让我们在创建一个记录原始Age为缺失的特征:

df['AgeIsNull'] = pd.isnull(df.Age).astype(int)
现在,我们有3个新的数值列:Gender、AgeFill、AgeIsNull。你可以用df.describe()看看整个dataframe的统计信息。

特征工程
我们可以通过一些简单数学运算创建一些其他的特征。我们知道Parch是游客船上父母或子女的数量,SibSp是游客兄弟姐妹或配偶的数量,我们可以将之结合为新的特征FamilySize:

df['FamilySize'] = df['SibSp'] + df['Parch']
如果我们认为对机器学习有益,我们甚至可以人造出一些特征。例如,我们知道Pclass和Age对是否幸存有很大影响。一种人造特征的方式是将两者相乘,这个特征放大了第三类游客和老年乘客的值。这两个都很难存活,因此这个理论应该是有用的。

df['Age*Class'] = df.AgeFill * df.Pclass
我们可以绘制一些直方图来更好地理解这些新的特征。

最后准备
我们现在已经有几乎适用于机器学习的数据了。但大部分基础的ML技术都无法在字符串上工作,在python中它们也要求数据为数组类型——在sklearn包中,数据要求为数组,而不是pandas的dataframe。因此最后我们要做两件事:1)决定我们需要留下那些列,2)将pandas.DataFrame转化回numpy.array。

在pandas中你可以用.info()方法查看列的类型。或者直接输入:

df.dtypes
PassengerId   int64
Survived      int64
Pclass        int64
Name         object
Sex          object
Age         float64
SibSp         int64
Parch         int64
Ticket       object
Fare        float64
Cabin        object
Embarked     object
Gender        int64
AgeFill     float64
AgeIsNull     int64
FamilySize    int64
Age*Class   float64
dtype: object
让dtype仅显示object类型的列:

df.dtypes[df.dtypes.map(lambda x: x=='object')]
Name        object
Sex         object
Ticket      object
Cabin       object
Embarked    object
dtype: object
(你可能已经将‘Embarked’类转换过了)

下一步是去掉我们不用的这些列:

df = df.drop(['Name', 'Sex', 'Ticket', 'Cabin', 'Embarked'], axis=1) 
因为我们已经创建了新的无空值的AgeFill列,我们也可以将'Age'删除。

df = df.drop(['Age'], axis=1)
去除有 缺失值的 任意行的命令为:

df = df.dropna()
但要注意,.drop()会移除哪怕只有一列是空值的行。

现在我们有一个干净且紧凑的数据集了。

最终的步骤是将其转化回Numpy数组。使用Pandas.values方法可以轻松做到:

train_data = df.values
train_data
array([[ 1. , 0. , 3. , ..., 0. , 1. , 66. ],
       [ 2. , 1. , 1. , ..., 0. , 1. , 38. ],
       [ 3. , 1. , 3. , ..., 0. , 0. , 78. ],
       ..., 
       [ 889. , 0. , 3. , ..., 1. , 3. , 64.5],
       [ 890. , 1. , 1. , ..., 0. , 0. , 26. ],
       [ 891. , 0. , 3. , ..., 0. , 0. , 96. ]])
与原始的数组比较:

data
 array([['1' '0' '3' ..., '7.25' '' 'S']
        ['2' '1' '1' ..., '71.2833' 'C85' 'C']
        ['3' '1' '3' ..., '7.925' '' 'S']
        ..., 
        ['889' '0' '3' ..., '23.45' '' 'S']
        ['890' '1' '1' ..., '30' 'C148' 'C']
        ['891' '0' '3' ..., '7.75' '' 'Q']], 
      dtype='|S82')


本文原文:点击打开链接

数据下载:train.scv

你可能感兴趣的:(Python Pandas数据处理入门(Kaggle Titanic竞赛数据))