重建索引
考虑一个最简单的Series对象:
import pandas as pd
obj = pd.Series([4.5,7.2,-5.3,3.6],index = ['d','b','a','c'])
# d 4.5
# b 7.2
# a -5.3
# c 3.6
# dtype: float64
我们可以使用reindex方法来改变其索引,数据会按照新索引来排序,若某个索引值之前不存在,则会引入缺失值:
obj2 = obj.reindex(['a','b','c','d','e'])
# a -5.3
# b 7.2
# c 3.6
# d 4.5
# e NaN
# dtype: float64
这里并不会改变原Series的索引
对于DataFrame,使用reindex可以同时改变行索引和列索引,在更改列索引的时候需要显示的指定:
frame2 = frame.reindex(['a','b','c','d'])
#默认更改行索引
# O T C
# 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
states = ['T','U','C']
frame3 = frame.reindex(columns = states)
#更改列索引,发现之前有一列不见了
# T U C
# a 1 NaN 2
# c 4 NaN 5
# d 7 NaN 8
轴向上删除条目
使用drop方法可以返回一个删除r若干行(列)后的新对象,首先是Series类型:
obj = pd.Series(np.arange(5.),index = ['a','b','c','d','e'])
new_obj = obj.drop('c')
# a 0.0
# b 1.0
# d 3.0
# e 4.0
# dtype: float64
对DataFrame对象使用drop:
data = pd.DataFrame(np.arange(16).reshape((4,4)),
index = ['O','C','U','NY'],
columns = [1,2,3,4])
#初始化一个DataFrame对象
# 1 2 3 4
# O 0 1 2 3
# C 4 5 6 7
# U 8 9 10 11
# NY 12 13 14 15
data_new = data.drop(['C','O'])
#根据行标签删除值(默认,axis = 0)
# 1 2 3 4
# U 8 9 10 11
# NY 12 13 14 15
data_new = data.drop([1,2],axis= 1 )
#根据列标签删除值
# 3 4
# O 2 3
# C 6 7
# U 10 11
# NY 14 15
我们在使用drop方法时,可以指定inplace参数,当其为真时则会直接在原对象上修改而不返回新对象:
new_data = data.drop([1,2],axis = 1,inplace = True)
print(new_data)
#None
print(data)
# 3 4
# O 2 3
# C 6 7
# U 10 11
# NY 14 15
索引、选择与过滤
Series的索引与Numpy的索引类似,但是Series的索引可以不仅仅是整数,也可以是字符。访问Series内部的元素即可以使用我们定义的标签,也可以使用默认标签:
obj = pd.Series(np.arange(4.),index = ['a','b','c','d'])
obj['b']
#1.0
obj[1]
#1.0
obj[['b','a','d']]
# b 1.0
# a 0.0
# d 3.0
# dtype: float64
obj[[1,3]]
# b 1.0
# d 3.0
# dtype: float64
obj[obj<2]
# a 0.0
# b 1.0
# dtype: float64
我们注意到,部分结果返回的是数值,而部分结果返回的是Series对象
使用默认的索引进行切片时是不包含尾部的,而使用自定义的索引切片时时包含尾部的:
obj[1:3]
# b 1.0
# c 2.0
# dtype: float64
obj['b':'d']
# b 1.0
# c 2.0
# d 3.0
# dtype: float64
使用以上方法修改值的时候原Series中的值也会发生改变,因为其返回的是数据的视图而不是拷贝
对于DataFrame可以根据以下方式选择若干列:
data = pd.DataFrame(np.arange(16).reshape((4,4)),
index = ['O','C','U','NY'],
columns = ['one','two','three','four'])
data['two']
# O 1
# C 5
# U 9
# NY 13
# Name: two, dtype: int32
data[['three','one']]
# three one
# O 2 0
# C 6 4
# U 10 8
# NY 14 12
可以使用以下方式方便的对DataFrame行进行选取:
data[:2]
# one two three four
# O 0 1 2 3
# C 4 5 6 7
DataFrame也支持布尔值切片与数据选择:
data[data['three']>5]
#删选出所有three大于5的行
# one two three four
# C 4 5 6 7
# U 8 9 10 11
# NY 12 13 14 15
data[data<5]=0
#让DataFrame中的所有<5的数值变为0
# one two three four
# O 0 0 0 0
# C 0 5 6 7
# U 8 9 10 11
# NY 12 13 14 15
以上方法是对DataFrame行选取或者列选取的方法,当需要选取DataFrame的特定行、特定列时,则需要用到loc和iloc方法。
loc方法可以使用我们自定义的标签来完成单行单列、单行多列、多行单列和多行多列的选择,单行多列的选择例子如下:
data = pd.DataFrame(np.arange(16).reshape((4,4)),
index = ['O','C','U','NY'],
columns = ['one','two','three','four'])
# one two three four
# O 0 1 2 3
# C 4 5 6 7
# U 8 9 10 11
# NY 12 13 14 15
data.loc['C',['two','three']]
# two 5
# three 6
# Name: C, dtype: int32
值得注意的是,当选取的是单行单列时返回的是一个值,当选取的是单行多列或者多行单列的时候返回的是一个Series对象,当选取的是多行多列时返回的是DataFrame对象
我们也可以使用整数标签(默认)配合iloc进行数据的选择:
data.iloc[2,[3,0,1]]
# four 11
# one 8
# two 9
# Name: U, dtype: int32
data.iloc[2]
# one 8
# two 9
# three 10
# four 11
# Name: U, dtype: int32
data.iloc[[1,2],[3,0,1]]
# 3 4
# O 2 3
# C 6 7
# U 10 11
# NY 14 15
loc和iloc还可以进行切片:
data.loc[:'U','two']
# O 1
# C 5
# U 9
# Name: two, dtype: int32
data.iloc[:,:3][data.three>5]
# one two three
# C 4 5 6
# U 8 9 10
# NY 12 13 14
data索引选项
类型 | 描述 |
---|---|
df[val] | 从DataFrame中选择单列或者列序列;特殊情况的遍历:布尔数组(过滤行),切片(切片行)或布尔值DataFrame |
df.loc[val] | 根据标签选择DataFrtame的单行或者多行 |
df.loc[:,val] | 根据标签选择单列或者多列 |
df.loc[vak1,val2] | 同时选择行和列的一部分 |
df.iloc[where] | 根据整数位置选择单行或者多行 |
df.iloc[:,where] | 根据整数位置选择单列或者多列 |
df.iloc[where_i,where_j] | 根据整数位置选择行和列 |
df.iat[I,j] | 根据行列整数位置选择单个标量值 |
reindex 方法 | 通过标签选择行和列 |
get_value,set_value方法 | 根据行和列的标签设置单个值 |
算数和数据对齐
现在有两个不完全相同标签的Series对象:
ser1 = pd.Series([7.3,-2.5,3.5,1.5],index = ['a','b','c','d'])
# a 7.3
# b -2.5
# c 3.5
# d 1.5
# dtype: float64
ser2 = pd.Series([-2.1,3.6,-1.5,4,3.1],index = ['a','c','e','f','g'])
# a -2.1
# c 3.6
# e -1.5
# f 4.0
# g 3.1
# dtype: float64
现在将这两个对象相加:
print(ser1+ser2)
# a 5.2
# b NaN
# c 7.1
# d NaN
# e NaN
# f NaN
# g NaN
# dtype: float64
在没有交叠的标签位置上,内部数据对齐会产生缺失值,缺失值会在后续的算术操作上产生影响。在DataFrame情况中行和列上都会进行对其。如果将两个行或者列完全不同的DataFrame相加则会得到一个全空的结果。
在两个不同的索引化对象之间进行算术操作时,如果想要将确实的值替换为一个指定的数,可以在其中一个对象上使用add方法,并且将这个指定的数作为fill_value参数传入:
df1 = pd.DataFrame(np.arange(12.).reshape((3,4)),
columns = list('abcd'))
# 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 = pd.DataFrame(np.arange(20.).reshape((4,5)),
columns=list('abcde'))
# a b c d e
# 0 0.0 1.0 2.0 3.0 4.0
# 1 5.0 6.0 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
print(df1.add(df2,fill_value = 0))
# a b c d e
# 0 0.0 2.0 4.0 6.0 4.0
# 1 9.0 11.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
灵活算数方法
方法 | 描述 |
---|---|
add,radd | 加法(+) |
sub,rsub | 减法(-) |
div,rdiv | 除法(/) |
floordiv,rfloordiv | 整除(//) |
mul,rmul | 乘法(*) |
pow,rpow | 幂乘方(**) |
上面这些方法中每一个都有一个以r开头的副本,这些副本的方法参数是翻转的。因此以下语句是等价的:
1/df1
df1.rdiv(1)
现在考虑DataFrame和Series之间的操作:
frame = pd.DataFrame(np.arange(12.).reshape((4,3)),
columns = list('abc'),
index = ['U','O','T','Or'])
# a b c
# U 0.0 1.0 2.0
# O 3.0 4.0 5.0
# T 6.0 7.0 8.0
# Or 9.0 10.0 11.0
series = frame.iloc[0]
# a 0.0
# b 1.0
# c 2.0
# Name: U, dtype: float64
print(frame - series)
# a b c
# U 0.0 0.0 0.0
# O 3.0 3.0 3.0
# T 6.0 6.0 6.0
# Or 9.0 9.0 9.0
正常情况下,DataFrame和Series的数学操作中会将Series的索引和DataFrame的列进行匹配,并广播的各行。如果一个索引不在DataFrame中,也不再Series的索引中,则对象会重建索引并形成联合。
在列上进行广播的方法如下:
frame = pd.DataFrame(np.arange(12.).reshape((4,3)),
columns = list('abc'),
index = ['U','O','T','Or'])
# a b c
# U 0.0 1.0 2.0
# O 3.0 4.0 5.0
# T 6.0 7.0 8.0
# Or 9.0 10.0 11.0
series = frame['a']
# U 0.0
# O 3.0
# T 6.0
# Or 9.0
# Name: a, dtype: float64
print(frame.sub(series,axis = 'index'))
# U 0.0 1.0 2.0
# O 0.0 1.0 2.0
# T 0.0 1.0 2.0
# Or 0.0 1.0 2.0
函数应用和映射
Numpy中的通用函数也可以对pandas中的对象使用:
frame = pd.DataFrame(np.random.randn(4,3),
columns = list('abc'),
index = ['U','O','T','Or'])
# a b c
# U -1.868928 0.952929 0.619027
# O -0.752540 1.711835 -1.064560
# T 1.180530 0.507043 -0.633658
# Or 0.160631 -1.668812 0.027523
np.abs(frame)
# a b c
# U 1.868928 0.952929 0.619027
# O 0.752540 1.711835 1.064560
# T 1.180530 0.507043 0.633658
# Or 0.160631 1.668812 0.027523
使用DataFrame的apply方法可以自定义一个函数,然后将这个函数应用到行或者列上,应用在列上的例子如下:
frame = pd.DataFrame(np.arange(12.).reshape((4,3)),
columns = list('abc'),
index = ['U','O','T','Or'])
# a b c
# U 0.0 1.0 2.0
# O 3.0 4.0 5.0
# T 6.0 7.0 8.0
# Or 9.0 10.0 11.0
f = lambda x: x.max() - x.min()
#定义一个匿名函数,作用是返回每列的最大值与最小值之差
print(frame.apply(f))
# a 9.0
# b 9.0
# c 9.0
如果需要应用在行上,需要指定axis的参数:
print(frame.apply(f,axis = 'columns'))
# U 2.0
# O 2.0
# T 2.0
# Or 2.0
# dtype: float64
传递给apply的函数不一定需要返回一个标量,也可以返回一个Series:
def func(x):
return pd.Series([x.min(),x.max()],index = ['max','min'])
print(frame.apply(func))
# a b c
# max 0.0 1.0 2.0
# min 9.0 10.0 11.0
如果想对DataFrame进行逐元素运算,可以使用applymap方法:
f = lambda x: x+2
print(frame.applymap(f))
# a b c
# U 2.0 3.0 4.0
# O 5.0 6.0 7.0
# T 8.0 9.0 10.0
# Or 11.0 12.0 13.0
对于Series,则使用map方法:
print(frame['c'].map(f))
# U 4.0
# O 7.0
# T 10.0
# Or 13.0
# Name: c, dtype: float64
排序和排名
对于Series和DataFrame可以使用sort_index方法按索引进行重新排序,对于Series:
series = pd.Series(np.arange(4),index = list('bcda'))
print(series)
# b 0
# c 1
# d 2
# a 3
# dtype: int32
print(series.sort_index())
# a 3
# b 0
# c 1
# d 2
# dtype: int32
对于DataFrame可以使用axis参数选择在行或者列上进行排序,并且可以指定ascending的值来改变升降序(默认为True,升序):
frame = pd.DataFrame(np.arange(8).reshape((2,4)),
index = ['three','one'],
columns= list('dcba'))
# d c b a
# three 0 1 2 3
# one 4 5 6 7
frame.sort_index(axis = 1)
# a b c d
# three 3 2 1 0
# one 7 6 5 4
frame.sort_index(ascending=True)
# d c b a
# one 4 5 6 7
# three 0 1 2 3
另外如果含有缺失值则会默认放至尾部。
也可以使用sort_values方法按列使用元素值进行排序,可以使用by指定列:
frame = pd.DataFrame({'b':[4,7,-3,2],'a':[0,1,0,1]})
# b a
# 0 4 0
# 1 7 1
# 2 -3 0
# 3 2 1
frame.sort_values(by = 'b')
#按b列中的值进行排序
# b a
# 2 -3 0
# 3 2 1
# 0 4 0
# 1 7 1
frame.sort_values(by = ['a','b'])
#x先使用b中的值排序,在使用a中的值进行排序
# b a
# 2 -3 0
# 0 4 0
# 3 2 1
# 1 7 1
另外使用Series和DataFrame对象的rank方法可以得到各元素按大小排列得到的名次(默认为最小的数名次为1):
obj = pd.Series([7,-5,7,4,2,0,4])
# 0 7
# 1 -5
# 2 7
# 3 4
# 4 2
# 5 0
# 6 4
# dtype: int64
obj.rank()
# 0 6.5
# 1 1.0
# 2 6.5
# 3 4.5
# 4 3.0
# 5 2.0
# 6 4.5
# dtype: float64
我们注意到第三个元素是第4小的,但是名次是4.5,这是由于还有一个元素也是4,即产生了平级关系,默认方法是取排名的平均值(一个第四名一个第五名,(4+5)/2 = 4.5,如果有3个4,那就是(4+5+6)/3 = 5)。若想要打破平级关系,则可以指定参数method:
print(obj.rank(method = 'first'))
# 0 6.0
# 1 1.0
# 2 7.0
# 3 4.0
# 4 3.0
# 5 2.0
# 6 5.0
# dtype: float64
first指的是如果有平级元素,则按最开始的索引大小排序,索引在前的排序在前。当然我们还可以指定ascending参数来让它从大到小排列。
对于DataFrame的rank方法和Series类似,但是需要使用axis选择行或者列。
排名中打破平级的方法 | 描述 |
---|---|
'average | 默认:在每个组中平均分配排名 |
‘min’ | 对整个数组使用最小排名 |
‘max’ | 对整个数组使用最大排名 |
‘first’ | 按照值在数据中出现的次序排名 |
‘dense’ | 类似于’min’,但是组间排名总是增加1,而不是一个组中相等的元素数量 |
含有重复标签的索引
对于一个含有重复索引的Series对象,选择重复的标签进行索引会返回一个序列,可以使用is_unique来判断索引是否唯一:
series = pd.Series(np.arange(4),index = list('aabb'))
# a 0
# a 1
# b 2
# b 3
# dtype: int32
print(series['a'])
# a 0
# a 1
# dtype: int32
print(series.index.is_unique)
# False
对于DataFrame类似:
df = pd.DataFrame(np.arange(12).reshape((4,3)),index = list('aabb'))
# 0 1 2
# a 0 1 2
# a 3 4 5
# b 6 7 8
# b 9 10 11
print(df.loc['b'])
# 0 1 2
# b 6 7 8
# b 9 10 11
print(df.index.is_unique)
# False
print(df.columns.is_unique)
# True