【Chapter 5.2】Pandas的基本功能
【5.2 Essential Functionality(主要功能)】
接下来介绍pandas中的一些主要功能,这里只介绍一些经常用到的。
1 Reindexing(重新索引)
pandas对象的一个重要方法是reindex,其作用是创建一个新对象,它的数据符合新的索引。看下面的例子:
obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
obj
Out[104]:
d 4.5
b 7.2
a -5.3
c 3.6
dtype: float64
用该Series的reindex将会根据新索引进行重排。如果某个索引值当前不存在,就引入缺失值:
obj2 = obj.reindex(['a','b','c','d','e'])
obj2
Out[106]:
a -5.3
b 7.2
c 3.6
d 4.5
e NaN
dtype: float64
对于时间序列这样的有序数据,重新索引时可能需要做一些插值处理。method选项即可达到此目的,例如,使用ffill可以实现前向值填充:
In [95]: obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
In [96]: obj3
Out[96]:
0 blue
2 purple
4 yellow
dtype: object
In [97]: obj3.reindex(range(6), method='ffill')
Out[97]:
0 blue
1 blue
2 purple
3 purple
4 yellow
5 yellow
dtype: object
借助DataFrame,reindex可以修改(行)索引和列。只传递一个序列时,会重新索引结果的行:
frame = pd.DataFrame(np.arange(9).reshape((3,3)),index=['a','c','d'],columns=['Ohio', 'Texas', 'California'])
frame
Out[113]:
Ohio Texas California
a 0 1 2
c 3 4 5
d 6 7 8
frame2 = frame.reindex(['a','b','c','d'])
frame2
Out[115]:
Ohio Texas California
a 0.0 1.0 2.0
b NaN NaN NaN
c 3.0 4.0 5.0
d 6.0 7.0 8.0
列可以用columns关键字重新索引:
states = ['Texas','Utah','California']
frame.reindex(columns=states)
Out[118]:
Texas Utah California
a 1 NaN 2
c 4 NaN 5
d 7 NaN 8
表5-3列出了reindex函数的各参数及说明。
还可以使用loc更简洁的reindex:
frame.loc[['a', 'b', 'c', 'd'], states]
Texas Utah California
a 1.0 NaN 2.0
b NaN NaN NaN
c 4.0 NaN 5.0
d 7.0 NaN 8.0
2. Dropping Entries from an Axis (按轴删除记录)
丢弃某条轴上的一个或多个项很简单,只要有一个索引数组或列表即可。由于需要执行一些数据整理和集合逻辑,所以drop回返回一个新的object,并删去你制定的axis的值:
obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
obj
Out[122]:
a 0.0
b 1.0
c 2.0
d 3.0
e 4.0
dtype: float64
new_obj = obj.drop('c')
new_obj
Out[124]:
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
对于DataFrame,可以删除任意轴上的索引值。为了演示,先新建一个DataFrame例子:
data = pd.DataFrame(np.arange(16).reshape(4, 4),
index=['Ohio', 'Colorado', 'Utah', 'New York'],
columns=['one', 'two', 'three', 'four'])
data
Out[127]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
行处理:如果a sequence of labels(一个标签序列)来调用drop,会删去row labels(axis 0):
data.drop(['Colorado', 'Ohio'])
Out[128]:
one two three four
Utah 8 9 10 11
New York 12 13 14 15
列处理:drop列的话,设定axis=1或axis='columns':
data.drop('two', axis=1)
Out[129]:
one three four
Ohio 0 2 3
Colorado 4 6 7
Utah 8 10 11
New York 12 14 15
data.drop('two', axis='columns')
Out[130]:
one three four
Ohio 0 2 3
Colorado 4 6 7
Utah 8 10 11
New York 12 14 15
drop也可以不返回一个新的object,而是直接更改series or dataframe in-place:
obj.drop('c', inplace=True)
obj
Out[132]:
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
3. Indexing, Selection, and Filtering(索引,选择,过滤)
series indexing(obj[...]) 相当于numpy的array indexing, 只不过Series的索引值不只是整数 :
obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
obj
Out[137]:
a 0.0
b 1.0
c 2.0
d 3.0
dtype: float64
obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
obj
Out[137]:
a 0.0
b 1.0
c 2.0
d 3.0
dtype: float64
obj['b']
Out[138]: 1.0
obj[1]
Out[139]: 1.0
obj[2:4]
Out[140]:
c 2.0
d 3.0
dtype: float64
obj[0]
Out[141]: 0.0
obj[['b', 'a', 'd']]
Out[142]:
b 1.0
a 0.0
d 3.0
dtype: float64
obj[[1, 3]]
Out[143]:
b 1.0
d 3.0
dtype: float64
obj[obj < 2]
Out[144]:
a 0.0
b 1.0
dtype: float64
用label来slicing(切片)的时候,和python的切片不一样的在于,会包括尾节点:
obj['b':'d']
Out[146]:
b 1.0
c 2.0
d 3.0
dtype: float64
用切片可以对Series的相应部分进行设置:
obj['b':'c']=5
obj
Out[151]:
a 0.0
b 5.0
c 5.0
d 3.0
dtype: float64
用一个值或序列对DataFrame进行索引其实就是获取一个或多个列:
data = pd.DataFrame(np.arange(16).reshape((4, 4)),
index=['Ohio', 'Colorado', 'Utah', 'New York'],
columns=['one', 'two', 'three', 'four'])
data
Out[153]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
In [130]: data['two']
Out[130]:
Ohio 1
Colorado 5
Utah 9
New York 13
Name: two, dtype: int64
In [131]: data[['three', 'one']]
Out[131]:
three one
Ohio 2 0
Colorado 6 4
Utah 10 8
New York 14 12
选取行的语法data[:2]十分方便。向[ ]传递单一的元素或列表,就可选择列。
另一种用法是通过布尔型DataFrame(比如下面这个由标量比较运算得出的)进行索引:
data
Out[155]:
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
data<5
Out[156]:
one two three four
Ohio True True True True
Colorado True False False False
Utah False False False False
New York False False False False
data[data<5] = 0
data
Out[158]:
one two three four
Ohio 0 0 0 0
Colorado 0 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
这使得DataFrame的语法与NumPy二维数组的语法很像。
用loc和iloc来选择
对于DataFrame的行的标签索引,我引入了特殊的标签运算符loc和iloc。它们可以让你用类似NumPy的标记,使用轴标签(loc)或整数索引(iloc),从DataFrame选择行和列的子集。
作为一个初步示例,让我们通过标签选择一行和多列:
data.loc['Colorado',['two','three']]
Out[159]:
two 5
three 6
Name: Colorado, dtype: int32
然后用iloc和整数进行选取:
data.iloc[2, [3, 0, 1]]
Out[161]:
four 11
one 8
two 9
Name: Utah, dtype: int32
data.iloc[2]#行
Out[162]:
one 8
two 9
three 10
four 11
Name: Utah, dtype: int32
data.iloc[[1, 2], [3, 0, 1]]
Out[163]:
four one two
Colorado 7 0 5
Utah 11 8 9
这两个索引函数也适用于一个标签或多个标签的切片:
In [141]: data.loc[:'Utah', 'two']
Out[141]:
Ohio 0
Colorado 5
Utah 9
Name: two, dtype: int64
In [142]: data.iloc[:, :3][data.three > 5]
Out[142]:
one two three
Colorado 0 5 6
Utah 8 9 10
New York 12 13 14
所以,在pandas中,有多个方法可以选取和重新组合数据。对于DataFrame,表5-4进行了总结。后面会看到,还有更多的方法进行层级化索引。
注意:当设计padnas的时候,作者发现frame[:, col]这样的语法是比较冗长的,因为这是会被经常用到的一个功能。作者把一些indexing的功能(lable or integer)集成在了ix这个方法上。实际中,因为这种label和integer都可以用的方式很方便,于是pandas team设计了loc和ilco来实现label-based和integer-based indexing.
虽然ix indexing依然存在,但是已经过时,不推荐使用。
4. Integer Indexes(整数索引)
一些新手再用integer来index的时候,总是会被绊倒。因为这种方法和python用于list和tuple的indexing方法不同。
比如,你不希望下面的代码出现error:
ser = pd.Series(np.arange(3.))
ser
ser[-1]
这里,pandas可以勉强进行整数索引,但是会导致小bug。我们有包含0,1,2的索引,但是引入用户想要的东西(基于标签或位置的索引)很难:
In [144]: ser
Out[144]:
0 0.0
1 1.0
2 2.0
dtype: float64
另外,对于非整数索引,不会产生歧义:
In [145]: ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c'])
In [146]: ser2[-1]
Out[146]: 2.0
为了保持连贯性,如果axis index里包含integer,那么选择数据的时候,就会是label-orented. 为了更精确地选择,使用loc
(for label)或ilco
(for integers):
In [147]: ser[:1]
Out[147]:
0 0.0
dtype: float64
In [148]: ser.loc[:1]
Out[148]:
0 0.0
1 1.0
dtype: float64
In [149]: ser.iloc[:1]
Out[149]:
0 0.0
dtype: float64
5. 算数和数据对齐
pandas一个有用的feature就是,不同index的obejct之间的算数计算。如果两个object相加,但他们各自的index并不相同,最后结果得到的index是这两个index的合集:
s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])
s2 = pd.Series([2.1, 3.6, -1.5, 4, 3.1], index=['a', 'c', 'e', 'f', 'g'])
s1,s2
Out[171]:
(a 7.3
c -2.5
d 3.4
e 1.5
dtype: float64, a 2.1
c 3.6
e -1.5
f 4.0
g 3.1
dtype: float64)
s1 + s3
s1 + s2
Out[173]:
a 9.4
c 1.1
d NaN
e 0.0
f NaN
g NaN
dtype: float64
这种数据对齐的方式(internal data alignment)引入了很多缺失值在没有陈赫的位置上。这些缺失值会被用在之后的算数计算中。
在DataFrame中,数据对齐同时发生在行和列上:
df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),
index=['Ohio', 'Texas', 'Colorado'])
df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
df1
Out[177]:
b c d
Ohio 0.0 1.0 2.0
Texas 3.0 4.0 5.0
Colorado 6.0 7.0 8.0
df2
Out[178]:
b d e
Utah 0.0 1.0 2.0
Ohio 3.0 4.0 5.0
Texas 6.0 7.0 8.0
Oregon 9.0 10.0 11.0
df1 + df2
Out[179]:
b c d e
Colorado NaN NaN NaN NaN
Ohio 3.0 NaN 6.0 NaN
Oregon NaN NaN NaN NaN
Texas 9.0 NaN 12.0 NaN
Utah NaN NaN NaN NaN
因为'c'和'e'列都不在两个DataFrame里,所有全是缺失值。对于行,即使有相同的,但列不一样的话也会是缺失值。
如果DataFrame对象相加,没有共用的列或行标签,结果都会是空:
In [160]: df1 = pd.DataFrame({'A': [1, 2]})
In [161]: df2 = pd.DataFrame({'B': [3, 4]})
In [162]: df1
Out[162]:
A
0 1
1 2
In [163]: df2
Out[163]:
B
0 3
1 4
In [164]: df1 - df2
Out[164]:
A B
0 NaN NaN
1 NaN NaN
在算术方法中填充值
在对不同索引的对象进行算术运算时,你可能希望当一个对象中某个轴标签在另一个对象中找不到时填充一个特殊值(比如0):
df1 = pd.DataFrame({'A': [1, 2]})
df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)),
columns=list('abcd'))
df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)),
columns=list('abcde'))
df2.loc[1, 'b'] = np.nan
df1
Out[182]:
a b c d
0 0.0 1.0 2.0 3.0
1 4.0 5.0 6.0 7.0
2 8.0 9.0 10.0 11.0
df2
Out[183]:
a b c d e
0 0.0 1.0 2.0 3.0 4.0
1 5.0 NaN 7.0 8.0 9.0
2 10.0 11.0 12.0 13.0 14.0
3 15.0 16.0 17.0 18.0 19.0
使用fill_value:
df1.add(df2, fill_value=0)
Out[184]:
a b c d e
0 0.0 2.0 4.0 6.0 4.0
1 9.0 5.0 13.0 15.0 9.0
2 18.0 20.0 22.0 24.0 14.0
3 15.0 16.0 17.0 18.0 19.0
下表中就有很多这样灵活的算数方法:
img与此类似,在对Series或DataFrame重新索引时,也可以指定一个填充值:
In [174]: df1.reindex(columns=df2.columns, fill_value=0)
Out[174]:
a b c d e
0 0.0 1.0 2.0 3.0 0
1 4.0 5.0 6.0 7.0 0
2 8.0 9.0 10.0 11.0 0
6. Function Application and Mapping (函数应用和映射)
numpy的ufuncs(element-wise数组方法)也能用在pandas的object上:
frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'),
index=['Utah', 'Ohio', 'Texas', 'Oregon'])
frame
Out[186]:
b d e
Utah 0.810787 -0.033742 0.453338
Ohio 1.943699 -1.217804 0.986678
Texas -0.755921 -1.377585 0.380717
Oregon -1.696865 0.232021 1.687488
np.abs(frame)
Out[187]:
b d e
Utah 0.810787 0.033742 0.453338
Ohio 1.943699 1.217804 0.986678
Texas 0.755921 1.377585 0.380717
Oregon 1.696865 0.232021 1.687488
另一个常用的操作是把一个用在一维数组上的函数,应用在一行或一列上。要用到DataFrame中的apply函数:
f = lambda x: x.max() - x.min()
frame.apply(f)
Out[188]:
b 3.640564
d 1.609605
e 1.306771
dtype: float64
这里函数f,计算的是一个series中最大值和最小值的差,在frame中的每一列,这个函数被调用一次。作为结果的series,它的index就是frame的column。
如果你传入axis='column'
用于apply,那么函数会被用在每一行:
frame.apply(f, axis='columns')
Out[189]:
Utah 0.844529
Ohio 3.161503
Texas 1.758302
Oregon 3.384353
dtype: float64
像是sum, mean这样的数组统计方法,DataFrame中已经集成了,所以没必要用apply。
apply不会返回标量,只会返回一个含有多个值的series:
def f(x):
return pd.Series([x.min(), x.max()], index=['min', 'max'])
frame
Out[191]:
b d e
Utah 0.810787 -0.033742 0.453338
Ohio 1.943699 -1.217804 0.986678
Texas -0.755921 -1.377585 0.380717
Oregon -1.696865 0.232021 1.687488
frame.apply(f)
Out[192]:
b d e
min -1.696865 -1.377585 0.380717
max 1.943699 0.232021 1.687488
element-wise的python函数也能用。假设想要格式化frame中的浮点数,变为string。可以用apply map:
format = lambda x: '%.2f' % x
frame.applymap(format)
Out[195]:
b d e
Utah 0.81 -0.03 0.45
Ohio 1.94 -1.22 0.99
Texas -0.76 -1.38 0.38
Oregon -1.70 0.23 1.69
applymap的做法是,series有一个map函数,能用来实现element-wise函数:
frame['e'].map(format)
Out[196]:
Utah 0.45
Ohio 0.99
Texas 0.38
Oregon 1.69
Name: e, dtype: object
7. Sorting and Ranking (排序)
按row或column index来排序的话,可以用sort_index方法,会返回一个新的object:
obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])
obj
Out[200]:
d 0
a 1
b 2
c 3
dtype: int64
obj.sort_index()
Out[201]:
a 1
b 2
c 3
d 0
dtype: int64
在DataFrame,可以用index或其他axis来排序:
frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
index=['three', 'one'],
columns=['d', 'a', 'b', 'c'])
frame
Out[202]:
d a b c
three 0 1 2 3
one 4 5 6 7
frame.sort_index(axis=1)#升序
Out[203]:
a b c d
three 1 2 3 0
one 5 6 7 4
frame.sort_index(axis=1, ascending=False) #降序
Out[204]:
d c b a
three 0 3 2 1
one 4 7 6 5
通过值来排序,用sort_values方法:
obj = pd.Series([4, 7, -3, 2])
obj
Out[209]:
0 4
1 7
2 -3
3 2
dtype: int64
obj.sort_values()
Out[210]:
2 -3
3 2
0 4
1 7
dtype: int64
缺失值会被排在最后:
obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
obj.sort_values()
Out[211]:
4 -3.0
5 2.0
0 4.0
2 7.0
1 NaN
3 NaN
dtype: float64
对于一个DataFrame,可以用一列或多列作为sort keys。这样的话,只需要把一列多多列的名字导入到sort_values即可:
frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 1]})
frame
Out[213]:
a b
0 0 4
1 1 7
2 0 -3
3 1 2
frame.sort_values(by='b')
Out[214]:
a b
2 0 -3
3 1 2
0 0 4
1 1 7
多列排序的话,传入一个list of names:
frame.sort_values(by='b')
Out[214]:
a b
2 0 -3
3 1 2
0 0 4
1 1 7
ranking(排名)是给有效的数据分配数字。rank方法能用于series和DataFrame,rank方法默认会给每个group一个mean rank(平均排名)。rank 表示在这个数在原来的Series中排第几名,有相同的数,取其排名平均(默认)作为值:
obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
obj
Out[217]:
0 7
1 -5
2 7
3 4
4 2
5 0
6 4
dtype: int64
obj.sort_values()
Out[218]:
1 -5
5 0
4 2
3 4
6 4
0 7
2 7
dtype: int64
obj.rank()
Out[219]:
0 6.5
1 1.0
2 6.5
3 4.5
4 3.0
5 2.0
6 4.5
dtype: float64
在obj中,4和4的排名是第4名和第五名,取平均得4.5。7和7的排名分别是第六名和第七名,则其排名取平均得6.5。
rank也可以根据数据被观测到的顺序来设定:
obj
Out[221]:
0 7
1 -5
2 7
3 4
4 2
5 0
6 4
dtype: int64
obj.rank(method='first')
Out[222]:
0 6.0
1 1.0
2 7.0
3 4.0
4 3.0
5 2.0
6 5.0
dtype: float64
这里没有给0和2(指两个数字7)赋予average rank 6.5,而是给第一个看到的7(label 0)设置rank为6,第二个看到的7(label 2)设置rank为7。
也可以设置降序:
obj.rank(ascending=False, method='max')
Out[223]:
0 2.0
1 7.0
2 2.0
3 4.0
4 5.0
5 6.0
6 4.0
dtype: float64
dataframe 可以根据行或列来计算rank:
frame = pd.DataFrame({'b': [4.3, 7, -3, 2],
'a': [0, 1, 0, 1],
'c': [-2, 5, 8, -2.5]})
frame
Out[224]:
a b c
0 0 4.3 -2.0
1 1 7.0 5.0
2 0 -3.0 8.0
3 1 2.0 -2.5
frame.rank(axis='columns') # columns表示列与列之间的排序(即每一行里数据间的排序)
Out[225]:
a b c
0 2.0 3.0 1.0
1 1.0 3.0 2.0
2 2.0 1.0 3.0
3 2.0 3.0 1.0
img
8. 带有重复标签的轴索引
直到目前为止,我所介绍的所有范例都有着唯一的轴标签(索引值)。虽然许多pandas函数(如reindex)都要求标签唯一,但这并不是强制性的。我们来看看下面这个简单的带有重复索引值的Series:
In [222]: obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
In [223]: obj
Out[223]:
a 0
a 1
b 2
b 3
c 4
dtype: int64
索引的is_unique属性可以告诉你它的值是否是唯一的:
In [224]: obj.index.is_unique
Out[224]: False
对于带有重复值的索引,数据选取的行为将会有些不同。如果某个索引对应多个值,则返回一个Series;而对应单个值的,则返回一个标量值:
In [225]: obj['a']
Out[225]:
a 0
a 1
dtype: int64
In [226]: obj['c']
Out[226]: 4
这样会使代码变复杂,因为索引的输出类型会根据标签是否有重复发生变化。
对DataFrame的行进行索引时也是如此:
In [227]: df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])
In [228]: df
Out[228]:
0 1 2
a 0.274992 0.228913 1.352917
a 0.886429 -2.001637 -0.371843
b 1.669025 -0.438570 -0.539741
b 0.476985 3.248944 -1.021228
In [229]: df.loc['b']
Out[229]:
0 1 2
b 1.669025 -0.438570 -0.539741
b 0.476985 3.248944 -1.021228