Pandas库
Pandas库是一个专门用于数据分析的开源Python库,有Series(序列)和DataFrame(数据框)这两种数据结构。
9 排序和排位次
另外一种使用索引机制的基础操作是排序(sorting)。对数据进行排序通常为必要操作,因此简化它的实现非常重要。pandas的sort_index()函数返回一个跟原对象元素相同但顺序不同的新对象。
首先看一下Series对象各项的排序方法。要排序的索引只有一列,因此操作很简单。
>>> ser = pd.Series([5,0,3,8,4], index=['red','blue','yellow','white','green'])
>>> ser
red 5
blue 0
yellow 3
white 8
green 4
dtype: int64
>>> ser.sort_index()
blue 0
green 4
red 5
white 8
yellow 3
dtype: int64
输出结果中,各元素按照以字母表顺序升序排列(A~Z)的标签进行排序。这是默认的排序方法,但若指定ascending选项,将其值置为False,则可按照降序排列。
>>> ser.sort_index(ascending=False)
yellow 3
white 8
red 5
green 4
blue 0
dtype: int64
对于DataFrame对象,可分别对两条轴中的任意一条进行排序。如果要根据索引对行进行排序,可依旧使用sort_index()函数,不用指定参数,前面已经讲过;如果要按列进行排序,则需要指定axis选项,其值为1。
>>> frame = pd.DataFrame(np.arange(16).reshape((4,4)),
... index=['red','blue','yellow','white'],
... columns=['ball','pen','pencil','paper'])
>>> frame
ball pen pencil paper
red 0 1 2 3
blue 4 5 6 7
yellow 8 9 10 11
white 12 13 14 15
>>> frame.sort_index()
ball pen pencil paper
blue 4 5 6 7
red 0 1 2 3
white 12 13 14 15
yellow 8 9 10 11
>>> frame.sort_index(axis=1)
ball paper pen pencil
red 0 3 1 2
blue 4 7 5 6
yellow 8 11 9 10
white 12 15 13 14
至此已经讲解了根据索引进行排序的方法。但往往还需要对数据结构中的元素进行排序,对于这个问题,Series和DataFrame对象有所不同,要区别对待。
对Series对象排序,使用order()函数。
>>> ser.order()
blue 0
yellow 3
green 4
red 5
white 8
dtype: int64
对DataFrame对象排序,使用前面用过的sort_index()函数,只不过要用by选项指定根据哪一列进行排序。
>>> frame.sort_index(by='pen')
ball pen pencil paper
red 0 1 2 3
blue 4 5 6 7
yellow 8 9 10 11
white 12 13 14 15
如果要基于两列或更多的列进行排序,则把这些列的名称放到数组中,赋给by选项。
>>> frame.sort_index(by=['pen','pencil'])
ball pen pencil paper
red 0 1 2 3
blue 4 5 6 7
yellow 8 9 10 11
white 12 13 14 15
排位次操作(ranking)跟排序操作紧密相关,该操作为序列的每个元素安排一个位次(初始值为1,依次加1),位次越靠前,所使用的数值越小。
>>> ser.rank()
red 4
blue 1
yellow 2
white 5
green 3
dtype: float64
还可以把数据在数据结构中的顺序(没有进行排序操作)作为它的位次。只要使用method选项把first赋给它即可。
>>> ser.rank(method='first')
red 4
blue 1
yellow 2
white 5
green 3
dtype: float64
默认位次使用升序。要按照降序排列,就把ascending选项的值置为False。
>>> ser.rank(ascending=False)
red 2
blue 5
yellow 4
white 1
green 3
dtype: float64
10 相关性和协方差
相关性(correlation)和协方差(covariance)是两个重要的统计量,pandas计算这两个量的函数分别是corr()和cov()。这两个量的计算通常涉及两个Series对象。
>>> seq2 = pd.Seri
es([3,4,3,4,5,4,3,2],['2006','2007','2008','2009','2010','2011','2012','2013'])
>>> seq = pd.Seri
es([1,2,3,4,4,3,2,1],['2006','2007','2008','2009','2010','2011','2012','2013'])
>>> seq.corr(seq2)
0.77459666924148329
>>> seq.cov(seq2)
0.8571428571428571
另外一种情况是,计算单个DataFrame对象的相关性和协方差,返回两个新DataFrame对象形式的矩阵。
>>> frame2 = DataFrame([[1,4,3,6],[4,5,6,1],[3,3,1,5],[4,1,6,4]],
... index=['red','blue','yellow','white'],
... columns=['ball','pen','pencil','paper'])
>>> frame2
ball pen pencil paper
red 1 4 3 6
blue 4 5 6 1
yellow 3 3 1 5
white 4 1 6 4
>>> frame2.corr()
ball pen pencil paper
ball 1.000000 -0.276026 0.577350 -0.763763
pen -0.276026 1.000000 -0.079682 -0.361403
pencil 0.577350 -0.079682 1.000000 -0.692935
paper -0.763763 -0.361403 -0.692935 1.000000
>>> frame2.cov()
ball pen pencil paper
ball 2.000000 -0.666667 2.000000 -2.333333
pen -0.666667 2.916667 -0.333333 -1.333333
pencil 2.000000 -0.333333 6.000000 -3.666667
paper -2.333333 -1.333333 -3.666667 4.666667
用corrwith()方法可以计算DataFrame对象的列或行与Series对象或其他DataFrame对象元素两两之间的相关性。
>>> serred 0
blue 1
yellow 2
white 3
green 9
dtype: float64
>>> frame2.corrwith(ser)
ball 0.730297
pen -0.831522
pencil 0.210819
paper -0.119523
dtype: float64
>>> frame2.corrwith(frame)
ball 0.730297
pen -0.831522
pencil 0.210819
paper -0.119523
dtype: float64
11 NaN数据
补上缺失的数值很容易,它们在数据结构中用NaN来表示,以便于识别。在数据分析过程中,有些元素在某个数据结构中没有定义。
pandas意在更好地管理这种可能出现的情况。缺失值的处理方法,这样许多问题就可以避免。比如,pandas库在计算各种描述性统计量时,其实没有考虑NaN值。
11.1 为元素赋NaN值
有时需要为数据结构中的元素赋NaN值,这时用NumPy的np.NaN(或np.nan)即可。
>>> ser = pd.Series([0,1,2,np.NaN,9], index=['red','blue','yellow','white','green'])
>>> ser
red 0
blue 1
yellow 2
white NaN
green 9
dtype: float64
>>> ser['white'] = None
>>> ser
red 0
blue 1
yellow 2
white NaN
green 9
dtype: float64
11.2 过滤NaN
数据分析过程中,有几种去除NaN的方法。然而,若要人工逐一删除NaN元素很麻烦,也很不安全,因为无法确保删除了所有的NaN。而dropna()函数可以解决这个问题。
>>> ser.dropna()
red 0
blue 1
yellow 2
green 9
dtype: float64
另外一种方法是,用notnull()函数作为选取元素的条件,实现直接过滤。
>>> ser[ser.notnull()]
red 0
blue 1
yellow 2
green 9
dtype: float64
DataFrame处理起来要稍微复杂点。如果对这类对象使用dropna()方法,只要行或列有一个NaN元素,该行或列的全部元素都会被删除。
>>> frame3 = pd.DataFrame([[6,np.nan,6],[np.nan,np.nan,np.nan],[2,np.nan,5]],
... index = ['blue','green','red'],
... columns = ['ball','mug','pen'])
>>> frame3
ball mug pen
blue 6 NaN 6
green NaN NaN NaN
red 2 NaN 5
>>> frame3.dropna()
Empty DataFrame
Columns: [ball, mug, pen]
Index: []
因此,为了避免删除整行或整列,需使用how选项,指定其值为all,告知dropna()函数只删除所有元素均为NaN的行或列。
>>> frame3.dropna(how='all')
ball mug pen
blue 6 NaN 6
red 2 NaN 5
11.3 为NaN元素填充其他值
删除NaN元素,可能会删除跟数据分析相关的其他数据,所以与其冒着风险去过滤NaN元素,不如用其他数值替代NaN。fillna()函数能够满足大多数需要。这个函数以替换NaN的元素作为参数。所有NaN可以替换为同一个元素,如下所示:
>>> frame3.fillna(0)
ball mug pen
blue 6 0 6
green 0 0 0
red 2 0 5
或者,若要将不同列的NaN替换为不同的元素,依次指定列名称及要替换成的元素即可。
>>> frame3.fillna({'ball':1,'mug':0,'pen':99})
ball mug pen
blue 6 0 6
green 1 0 99
red 2 0 5
12 等级索引和分级
等级索引(hierarchical indexing)是pandas的一个重要功能,单条轴可以有多级索引。可以像操作两维结构那样处理多维数据。
举个简单的例子:创建包含两列索引的Series对象,也就是说,创建一个包含两层的数据结构。
>>> mser = pd.Series(np.random.rand(8),
... index=[['white','white','white','blue','blue','red','red','red'],
... ['up','down','right','up','down','up','down','left']])
>>> mser
white up 0.461689
down 0.643121
right 0.956163
blue up 0.728021
down 0.813079
red up 0.536433
down 0.606161
left 0.996686
dtype: float64
>>> mser.index
MultiIndex(levels=[[u'blue', u'red', u'white'], [u'down', u'left', u'right', u'up']],
labels=[[2, 2, 2, 0, 0, 1, 1, 1], [3, 0, 2, 3, 0, 3, 0, 1]])
通过指定等级索引,二级元素的选取操作得以简化。
可以选取第一列索引中某一索引项的元素,使用最经典的做法即可:
>>> mser['white']
up 0.461689
down 0.643121
right 0.956163
dtype: float64
或者像下面这样,选取第二列索引中某一索引项的元素:
>>> mser[:,'up']
white 0.461689
blue 0.728021
red 0.536433
dtype: float64
若要选取某一特定的元素,指定两个索引即可
>>> mser['white','up']
0.46168915430531676
等级索引在调整数据形状和进行基于组的操作(比如创建数据透视表)方面起着非常重要的作用。可以使用unstack()函数调整DataFrame中的数据。这个函数把使用的等级索引Series对象转换为一个简单的DataFrame对象,其中把第二列索引转换为相应的列。
>>> mser.unstack()
down left right up
blue 0.813079 NaN NaN 0.728021
red 0.606161 0.996686 NaN 0.536433
white 0.643121 NaN 0.956163 0.461689
如果想进行逆操作,把DataFrame对象转换为Series对象,可使用stack()函数。
>>> frame
ball pen pencil paper
red 0 1 2 3
blue 4 5 6 7
yellow 8 9 10 11
white 12 13 14 15
>>> frame.stack()
red ball 0
pen 1
pencil 2
paper 3
blue ball 4
pen 5
pencil 6
paper 7
yellow ball 8
pen 9
pencil 10
paper 11
white ball 12
pen 13
pencil 14
paper 15
dtype: int32
对于DataFrame对象,可以为它的行和列都定义等级索引。声明DataFrame对象时,为index选项和columns选项分别指定一个元素为数组的数组。
>>> mframe = pd.DataFrame(np.random.randn(16).reshape(4,4),
... index=[['white','white','red','red'], ['up','down','up','down']],
... columns=[['pen','pen','paper','paper'],[1,2,1,2]])
>>> mframe
pen paper
1 2 1 2
white up -1.964055 1.312100 -0.914750 -0.941930
down -1.886825 1.700858 -1.060846 -0.197669
red up -1.561761 1.225509 -0.244772 0.345843
down 2.668155 0.528971 -1.633708 0.921735
12.1 重新调整顺序和为层级排序
有时,需要调整某一条轴上各层级的顺序或者调整某一层中各元素的顺序。
swaplevel()函数以要互换位置的两个层级的名称为参数,返回交换位置后的一个新对象,其中各元素的顺序保持不变。
>>> mframe.columns.names = ['objects','id']
>>> mframe.index.names = ['colors','status']
>>> mframe
objects pen paper
id 1 2 1 2
colors status
white up -1.964055 1.312100 -0.914750 -0.941930
down -1.886825 1.700858 -1.060846 -0.197669
red up -1.561761 1.225509 -0.244772 0.345843
down 2.668155 0.528971 -1.633708 0.921735
>>> mframe.swaplevel('colors','status')
objects pen paper
id 1 2 1 2
status colors
up white -1.964055 1.312100 -0.914750 -0.941930
down white -1.886825 1.700858 -1.060846 -0.197669
up red -1.561761 1.225509 -0.244772 0.345843
down red 2.668155 0.528971 -1.633708 0.921735
而sortlevel()函数只根据一个层级对数据排序。
>>> mframe.sortlevel('colors')
objects pen paper
id 1 2 1 2
colors status
red down 2.668155 0.528971 -1.633708 0.921735
up -1.561761 1.225509 -0.244772 0.345843
white down -1.886825 1.700858 -1.060846 -0.197669
up -1.964055 1.312100 -0.914750 -0.941930
12.2 按层级统计数据
DataFrame或Series对象的很多描述性和概括统计量都有level选项,可用它指定要获取哪个层级的描述性和概括统计量。
想对行一层级进行统计,把层级的名称赋给level选项即可。
>>> mframe.sum(level='colors')
objects pen paper
id 1 2 1 2
colors
red 1.106394 1.754480 -1.878480 1.267578
white -3.850881 3.012959 -1.975596 -1.139599
若想对某一层级的列进行统计,例如id,则需要把axis选项的值设置为1,把第二条轴作为参数。
>>> mframe.sum(level='id', axis=1)
id 1 2
colors status
white up -2.878806 0.370170
down -2.947672 1.503189
red up -1.806532 1.571352
down 1.034447 1.450706