pandas含有使数据清洗和分析⼯作变得更快更简单的数据结构和操作⼯具。
pandas经常和其它⼯具⼀同使⽤,如数值计算⼯具NumPy和SciPy,分析库statsmodels和scikit-learn,和数据可视化库matplotlib。pandas是基于NumPy数组构建的,特别是基于数组的函数和不使⽤for循环的数据处理。
pandas与NumPy的不同:pandas采⽤了⼤量的NumPy编码⻛格,不同是pandas是专⻔为处理表格和混杂数据设计的。⽽NumPy更适合处理统⼀的数值数组数据。
从2010年pandas开源以来,pandas逐渐成⻓为⼀个⾮常⼤的库,应⽤于许多真实案例。开发者社区已经有了800个独⽴的贡献者。
pandas导⼊约定:import pandas as pd
Series和DataFrame⽤的次数多,可将其引⼊本地命名空间中会更⽅便:
from pandas import Series, DataFram
一、pandas的数据结构介绍
两个主要数据结构:Series和DataFrame。虽不能解决所有问题,但它们为⼤多数应⽤提供了⼀种可靠的、易于使⽤的基础。
1、Series
Series是⼀种类似于⼀维数组的对象,它由⼀组数据(各种NumPy数据类型)以及⼀组与之相关的数据标签(即索引)组成。仅由⼀组数据即可产⽣最简单的Series:
obj = pd.Series([4, 7, -5, 3])
obj # 输出如下:包含元素索引、元素,以及数据类型
0 4
1 7
2 -5
3 3
dtype: int64
Series的字符串表现形式为:索引在左边,值在右边。未指定索引,于是会⾃动创建⼀个0到N-1(N为数据的⻓度)的整数型索引。可通过Series 的values和index属性获取其数组表示形式和索引对象:
obj.values # 输出:array([ 4, 7, -5, 3], dtype=int64)
obj.index # 输出:RangeIndex(start=0, stop=4, step=1),(like range(4))
通常,希望所创建的Series带有⼀个可以对各个数据点进⾏标记的索引:
obj2 = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c']) # 指定行索引
obj2 # 输出如下:
d 4
b 7
a -5
c 3
dtype: int64
obj2.index # 输出:Index(['d', 'b', 'a', 'c'], dtype='object')
与普通NumPy数组相⽐,你可以通过索引的⽅式选取Series中的单个或⼀组值:
obj2['a'] # 输出:-5
obj2['d'] = 6 # 设定索引'd'的值
obj2[['c', 'a', 'd']] # 输出:(注意参数是列表)
c 3
a -5
d 6
dtype: int64
['c', 'a', 'd']是索引列表,即使它包含的是字符串⽽不是整数。
使⽤NumPy函数或类似NumPy的运算(如根据布尔型数组进⾏过滤、标量乘法、应⽤数学函数等)都会保留索引值的链接:
obj2[obj2 > 0] # 输出:(布尔运算)
d 6
b 7
c 3
dtype: int64
obj2 * 2 # 输出:(标量乘法)
d 12
b 14
a -10
c 6
dtype: int64
np.exp(obj2) # 输出:(运用于数学函数)
d 403.428793
b 1096.633158
a 0.006738
c 20.085537
dtype: float64
还可以将Series看成是⼀个定⻓的有序字典,因为它是索引值到数据值的⼀个映射。它可以⽤在许多原本需要字典参数的函数中:
'b' in obj2 # 输出:True
'e' in obj2 # 输出:False
如果数据被存放在⼀个Python字典中,也可以直接通过这个字典来创建Series:
sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
obj3 = pd.Series(sdata)
obj3 # 输出:
Ohio 35000
Texas 71000
Oregon 16000
Utah 5000
dtype: int64
如果只传⼊⼀个字典,则结果Series中的索引就是原字典的键(有序排列)。你可以传⼊排好序的字典的键以改变顺序:
states = ['California', 'Ohio', 'Oregon', 'Texas']
obj4 = pd.Series(sdata, index=states)
obj4 # 输出:
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
dtype: float64
在这个例⼦中,sdata中跟states索引相匹配的那3个值会被找出来并放到相应的位置上,但由于"California"所对应的sdata值找不到,所以其结果就为NaN(即“⾮数字”(not a number),在pandas中,它⽤于表示缺失或NA值)。因为‘Utah’不在states中,它被从结果中除去。
使⽤缺失(missing)或NA表示缺失数据。pandas的isnull和notnull函数可⽤于检测缺失数据:
pd.isnull(obj4) # 检测obj4中哪些数据缺失
California True
Ohio False
Oregon False
Texas False
dtype: bool
pd.notnull(obj4) # 检测obj4中哪些数据不缺失
California False
Ohio True
Oregon True
Texas True
dtype: bool
Series也有类似的实例⽅法:
obj4.isnull() # 检测obj4中哪些数据缺失,obj4.notnull()方法检测数据不缺失
California True
Ohio False
Oregon False
Texas False
dtype: bool
对于许多应⽤⽽⾔,Series最重要的⼀个功能是,它会根据运算的索引标签⾃动对⻬数据:
obj3 # 输出:
Ohio 35000
Texas 71000
Oregon 16000
Utah 5000
dtype: int64
obj4 # 输出:
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
dtype: float64
obj3 + obj4 # 输出:
California NaN
Ohio 70000.0
Oregon 32000.0
Texas 142000.0
Utah NaN
dtype: float64
该功能类似于数据库的join的操作
Series对象本身及其索引都有⼀个name属性,该属性跟pandas其他的关键功能关系⾮常密切:
obj4.name = 'population' # 设置Series对象本身的name属性
obj4.index.name = 'state' # 设置Series对象索引的name属性
obj4 # 输出如下:
state
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 71000.0
Name: population, dtype: float64
Series的索引可以通过赋值的⽅式就地修改:
obj # obj的原始索引及数据
0 4
1 7
2 -5
3 3
dtype: int64
obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan'] # 修改obj的索引
obj # obj修改后的索引及数据,输出如下所示:
Bob 4
Steve 7
Jeff -5
Ryan 3
dtype: int64
2、DataFrame
DataFrame是⼀个表格型的数据结构,它含有⼀组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame既有⾏索引也有列索引,它可以被看做由Series组成的字典(共⽤同⼀个索引)。DataFrame中的数据是以⼀个或多个⼆维块存放的(⽽不是列表、字典或别的⼀维数据结构)。
虽然DataFrame是以⼆维结构保存数据的,仍可以将其表示为更⾼维度的数据(层次化索引的表格型结构,这是pandas中许多⾼级数据处理功能的关键要素)。
创建DataFrame的办法很多,最常⽤的⼀种是直接传⼊⼀个由等⻓列表或NumPy数组组成的字典:
data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
'year': [2000, 2001, 2002, 2001, 2002, 2003],
'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
frame = pd.DataFrame(data)
结果DataFrame会⾃动加上索引(跟Series⼀样),且全部列会被有序排列:
frame # 输出如下:
state year pop
0 Ohio 2000 1.5
1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9
5 Nevada 2003 3.2
使⽤Jupyter notebook,pandas DataFrame对象会以对浏览器友好的HTML表格的⽅式呈现。
对于特别⼤的DataFrame,head⽅法会选取前五⾏:
frame.head() # 输出如下:
state year pop
0 Ohio 2000 1.5
1 Ohio 2001 1.7
2 Ohio 2002 3.6
3 Nevada 2001 2.4
4 Nevada 2002 2.9
如果指定了列序列,则DataFrame的列就会按照指定顺序进⾏排列:
pd.DataFrame(data, columns=['year', 'state', 'pop'])
year state pop
0 2000 Ohio 1.5
1 2001 Ohio 1.7
2 2002 Ohio 3.6
3 2001 Nevada 2.4
4 2002 Nevada 2.9
5 2003 Nevada 3.2
如果传⼊的列在数据中找不到,就会在结果中产⽣缺失值:
frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
index=['one', 'two', 'three', 'four','five', 'six'])
frame2 # 输出如下:
year state pop debt
one 2000 Ohio 1.5 NaN
two 2001 Ohio 1.7 NaN
three 2002 Ohio 3.6 NaN
four 2001 Nevada 2.4 NaN
five 2002 Nevada 2.9 NaN
six 2003 Nevada 3.2 NaN
frame2.columns # 输出: Index(['year', 'state', 'pop', 'debt'], dtype='object')
通过类似字典标记的⽅式或属性的⽅式,可以将DataFrame的列获取为⼀个Series:
frame2['state'] # 通过字典的方式获取列
one Ohio
two Ohio
three Ohio
four Nevada
five Nevada
six Nevada
Name: state, dtype: object
frame2.year # 通过属性的方式获取
one 2000
two 2001
three 2002
four 2001
five 2002
six 2003
Name: year, dtype: int64
注意:IPython提供了类似属性的访问(即frame2.year)和tab补全。frame2[column]适⽤于任何列的名,但是frame2.column只有在列名是⼀个合理的Python变量名时才适⽤。返回的Series拥有原DataFrame相同的索引,且其name属性也已经被相应地设置好了。
⾏也可以通过位置或名称的⽅式进⾏获取,⽐如⽤loc属性:
frame2.loc['three'] # 输出如下:
year 2002
state Ohio
pop 3.6
debt NaN
Name: three, dtype: object
列可以通过赋值的⽅式进⾏修改。例如,可以给那个空的"debt"列赋上⼀个标量值或⼀组值:
frame2['debt'] = 16.5 # 赋值一个标量值
frame2 # 输出如下:
year state pop debt
one 2000 Ohio 1.5 16.5
two 2001 Ohio 1.7 16.5
three 2002 Ohio 3.6 16.5
four 2001 Nevada 2.4 16.5
five 2002 Nevada 2.9 16.5
six 2003 Nevada 3.2 16.5
frame2['debt'] = np.arange(6.) # 赋值一组值
frame2 # 输出如下:
year state pop debt
one 2000 Ohio 1.5 0.0
two 2001 Ohio 1.7 1.0
three 2002 Ohio 3.6 2.0
four 2001 Nevada 2.4 3.0
five 2002 Nevada 2.9 4.0
six 2003 Nevada 3.2 5.0
将列表或数组赋值给某个列时,其⻓度必须跟DataFrame的⻓度相匹配。如果赋值的是⼀个Series,就会精确匹配DataFrame的索引,所有的空位都将被填上缺失值:例如:
val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2['debt'] = val # 精确匹配索引
frame2 # 输出如下:
year state pop debt
one 2000 Ohio 1.5 NaN
two 2001 Ohio 1.7 -1.2
three 2002 Ohio 3.6 NaN
four 2001 Nevada 2.4 1.5
five 2002 Nevada 2.9 -1.7
six 2003 Nevada 3.2 NaN
为不存在的列赋值会创建出⼀个新列。关键字del⽤于删除列。作为del的例⼦,我先添加⼀个新的布尔值的列,判断state是否为'Ohio':
frame2['eastern'] = frame2.state == 'Ohio'
frame2 # 输出如下:
year state pop debt eastern
one 2000 Ohio 1.5 NaN True
two 2001 Ohio 1.7 -1.2 True
three 2002 Ohio 3.6 NaN True
four 2001 Nevada 2.4 -1.5 False
five 2002 Nevada 2.9 -1.7 False
six 2003 Nevada 3.2 NaN False
注意:不能⽤frame2.eastern创建新的列。
del⽅法可以⽤来删除这列:
del frame2['eastern']
frame2.columns # 输出:Index(['year', 'state', 'pop', 'debt'], dtype='object')
注意:通过索引⽅式返回的列只是相应数据的视图⽽已,并不是副本。因此,对返回的Series所做的任何就地修改全都会反映到源DataFrame上。通过Series的copy⽅法即可指定复制列。
另⼀种常⻅的数据形式是嵌套字典:
pop = {'Nevada': {2001: 2.4, 2002: 2.9}, 'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
如果嵌套字典传给DataFrame,pandas就会被解释为:外层字典的键作为列索引,内层键则作为⾏索引:
frame3 = pd.DataFrame(pop) # 将字典pop转换为DataFrame
frame3 # 输出:
Nevada Ohio
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
也可以使⽤类似NumPy数组的⽅法,对DataFrame进⾏转置(交换⾏和列):
frame3.T
2000 2001 2002
Nevada NaN 2.4 2.9
Ohio 1.5 1.7 3.6
内层字典的键会被合并、排序以形成最终的索引。如果明确指定了索引,则不会这样:
pd.DataFrame(pop, index=[2001, 2002, 2003]) # 要报错:AttributeError: 'list' object has no attribute 'astype'
frame55 = pd.DataFrame(pop) # 先将pop数据转换为DataFrame数据后可指定索引
pd.DataFrame(frame55, index=[2001, 2002, 2003]) # 这样指定索引排序不会报错
Nevada Ohio
2001 2.4 1.7
2002 2.9 3.6
2003 NaN NaN
由Series组成的字典差不多也是⼀样的⽤法:
pdata = {'Ohio': frame3['Ohio'][:-1], 'Nevada': frame3['Nevada'][:2]} # 注意:依次是列标签,行标签
pd.DataFrame(pdata) # 输出如下:(这里使用:pd.DataFrame(pdata, index=[2001,2000]),不会报错)
Ohio Nevada
2000 1.5 NaN
2001 1.7 2.4
下面列出了DataFrame构造函数所能接受的各种数据(表5-1):
如果设置了DataFrame的index和columns的name属性,则这些信息也会被显示出来:
frame3.index.name = 'year'; frame3.columns.name = 'state'
frame3 # 输出如下:
state Nevada Ohio
year
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
跟Series⼀样,values属性也会以⼆维ndarray的形式返回DataFrame中的数据:
frame3.values # 输出如下:
array([[nan, 1.5],
[2.4, 1.7],
[2.9, 3.6]])
如果DataFrame各列的数据类型不同,则值数组的dtype就会选⽤能兼容所有列的数据类型:
frame2.values # 输出如下:
array([[2000, 'Ohio', 1.5, nan],
[2001, 'Ohio', 1.7, -1.2],
[2002, 'Ohio', 3.6, nan],
[2001, 'Nevada', 2.4, -1.5],
[2002, 'Nevada', 2.9, -1.7],
[2003, 'Nevada', 3.2, nan]], dtype=object)
1、索引对象
pandas的索引对象负责管理轴标签和其他元数据(⽐如轴名称等)。构建Series或DataFrame时,所⽤到的任何数组或其他序列的标签都会被转换成⼀个Index:
obj = pd.Series(range(3), index=['a', 'b', 'c'])
index = obj.index # 将obj的索引赋值给另一个变量
index # 输出:Index(['a', 'b', 'c'], dtype='object')
index[1:] # 输出:Index(['b', 'c'], dtype='object')
Index对象是不可变的,因此⽤户不能对其进⾏修改:
index[1] = 'd' # 提示错误:TypeError: Index does not support mutable operations
不可变可以使Index对象在多个数据结构之间安全共享:
labels = pd.Index(np.arange(3)) # 创建一个索引
labels # 输出:Int64Index([0, 1, 2], dtype='int64')
obj2 = pd.Series([1.5, -2.5, 0], index=labels) # 创建对象时指定索引
obj2 # 输出可以看出索引是创建时指定的索引
0 1.5
1 -2.5
2 0.0
dtype: float64
obj2.index is labels # 输出:True(验证是否是同一个索引)
虽然Index功能不经常使用,但一些操作会生成包含索引化的数据,理解它的工作原理很重要。
除了类似于数组,Index的功能也类似⼀个固定⼤⼩的集合:
frame3 # 先看下面前的frame3的输出:
state Nevada Ohio
year
2000 NaN 1.5
2001 2.4 1.7
2002 2.9 3.6
'Ohio' in frame3.columns # 输出:True(判断是否在列索引)
2003 in frame3.index # 输出:False(判断是否在行索引)
与python的集合不同,pandas的Index可以包含重复的标签:
dup_labels = pd.Index(['foo', 'foo', 'bar', 'bar']) # 包含重复索引
dup_labels # 输出:Index(['foo', 'foo', 'bar', 'bar'], dtype='object')
选择重复的标签,会显示所有的结果。
每个索引都有⼀些⽅法和属性,它们可⽤于设置逻辑并回答有关该索引所包含的数据的常⻅问题。下面列出了Index的方法和属性(表5-2)
二、基本功能
下面介绍操作Series和DataFrame中的数据的基本⼿段。
1、重新索引(reindex)
pandas对象的⼀个重要⽅法是reindex,其作⽤是创建⼀个新对象,它的数据符合新的索引。例:
obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b','a', 'c'])
obj # 输出:
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 # 输出:
a -5.3
b 7.2
c 3.6
d 4.5
e NaN
dtype: float64
对于时间序列这样的有序数据,重新索引时可能需要做⼀些插值处理。method选项即可达到此⽬的,例如,使⽤ffill可以实现前向值填充:
obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
obj3 # 输出:
0 blue
2 purple
4 yellow
dtype: object
obj3.reindex(range(6), method='ffill') # 输出如下
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 # 输出:
Ohio Texas California
a 0 1 2
c 3 4 5
d 6 7 8
frame2 = frame.reindex(['a', 'b', 'c', 'd']) # 只传递一个序列,修改行索引
frame2 # 输出
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) # 指定columns关键字,修改列索引
Texas Utah California
a 1 NaN 2
c 4 NaN 5
d 7 NaN 8
下面列出了reindex函数的各参数及说明(表5-3)
2、丢弃指定轴上的项
丢弃某条轴上的⼀个或多个项很简单,只要有⼀个索引数组或列表即可。由于需要执⾏⼀些数据整理和集合逻辑,所以drop⽅法返回的是⼀个在指定轴上删除了指定值的新对象:
obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e']) # 假设有这个Series序列
obj # 输出如下
a 0.0
b 1.0
c 2.0
d 3.0
e 4.0
dtype: float64
new_obj = obj.drop('c') # 删除传入的值,并得到新的Index
new_obj
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
obj.drop(['d', 'c']) # 删除传入的值,注意参数是列表
a 0.0
b 1.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 # 输出如下:
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
⽤标签序列调⽤drop会从⾏标签(axis 0)删除值:
data.drop(['Colorado', 'Ohio']) # 输出如下:
one two three four
Utah 8 9 10 11
New York 12 13 14 15
通过传递axis=1或axis='columns'可以删除列的值:
data.drop('two', axis=1) # 输出如下:
one three four
Ohio 0 2 3
Colorado 4 6 7
Utah 8 10 11
New York 12 14 15
data.drop(['two', 'four'], axis='columns') # 输出如下:
one three
Ohio 0 2
Colorado 4 6
Utah 8 10
New York 12 14
许多函数,如drop,会修改Series或DataFrame的⼤⼩或形状,可就地修改对象,不会返回新的对象:
obj.drop('c', inplace=True) # ⼩⼼使⽤inplace,它会销毁所有被删除的数据。
obj # 输出如下:
a 0.0
b 1.0
d 3.0
e 4.0
dtype: float64
3、索引、选取和过滤
Series索引(obj[...])的⼯作⽅式类似于NumPy数组的索引,只不过Series的索引值不只是整数。例如:
obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
obj # 输出如下:
a 0.0
b 1.0
c 2.0
d 3.0
dtype: float64
obj['b'] # 输出:1.0
obj[1] # 输出:1.0
obj[2:4] # 输出如下:
c 2.0
d 3.0
dtype: float64
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
利⽤标签的切⽚运算与普通的Python切⽚运算不同,其末端是包含的:
obj['b':'c'] # 输出如下:
b 1.0
c 2.0
dtype: float64
⽤切⽚可以对Series的相应部分进⾏设置:
obj['b':'c'] = 5
obj # 输出如下:
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 # 输出如下:
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['two'] # 输出如下
Ohio 1
Colorado 5
Utah 9
New York 13
Name: two, dtype: int32
data[['three', 'one']] # 输出如下:
three one
Ohio 2 0
Colorado 6 4
Utah 10 8
New York 14 12
data[:2] # 输出如下:(选取前2行)
one two three four
Ohio 0 1 2 3
Colorado 4 5 6 7
data[data['three'] > 5] # 输出如下:(选取满足条件的行)
one two three four
Colorado 4 5 6 7
Utah 8 9 10 11
New York 12 13 14 15
选取⾏的语法data[:2]⼗分⽅便。向[ ]传递单⼀的元素或列表,就可选择列。
另⼀种⽤法是通过布尔型DataFrame进⾏索引:⽐如下⾯这个由标量⽐较运算得出的
data < 5 # 输出如下:
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
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⼆维数组的语法很像。
4、⽤loc和iloc进⾏选取
DataFrame⾏标签索引,引⼊特殊的标签运算符loc和iloc。它们可以像⽤类似NumPy的标记,使⽤轴标签(loc)或整数索引(iloc),从DataFrame选择⾏和列的⼦集。先看⼀个初步示例,通过标签选择⼀⾏和多列:
data.loc['Colorado', ['two', 'three']] # 选取'Colorado'行,和 ['two', 'three']列,输出如下:
two 5
three 6
Name: Colorado, dtype: int32
然后⽤iloc和整数进⾏选取:
data.iloc[2, [3, 0, 1]] # 输出如下:
four 11
one 8
two 9
Name: Utah, dtype: int32
data.iloc[2] # 输出如下:(选取第三行数据)
one 8
two 9
three 10
four 11
Name: Utah, dtype: int32
data.iloc[[1, 2], [3, 0, 1]] # 输出如下:(选取多行和多列)
four one two
Colorado 7 0 5
Utah 11 8 9
这两个索引函数也适⽤于⼀个标签或多个标签的切⽚:
data.loc[:'Utah', 'two'] # 输出如下:
Ohio 0
Colorado 5
Utah 9
Name: two, dtype: int32
data.iloc[:, :3][data.three > 5] # 输出如下:(选取满足多种条件的数据)
one two three
Colorado 0 5 6
Utah 8 9 10
New York 12 13 14
在pandas中,有多个⽅法可以选取和重新组合数据。当然还有更多的方法进行层级化索引。
DataFrame的索引选项如下所示(表5-4):
5、整数索引
pandas整数索引与Python内置的列表和元组的索引语法不同。
例如,你可能不认为下⾯的代码会出错:
ser = pd.Series(np.arange(3.))
ser # 输出如下:
0 0.0
1 1.0
2 2.0
dtype: float64
ser[-1] # 索引会报错,KeyError: -1,因为轴索引含有整数
ser有包含0,1,2的索引,但是引⼊⽤户想要的东⻄(基于标签或位置的索引)很难:
另外,对于⾮整数索引,不会产⽣歧义:
ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c'])
ser2[-1] # 输出:2.0
为了进⾏统⼀,如果轴索引含有整数,数据选取总会使⽤标签。为了更准确,请使⽤loc(标签)或iloc(整数):
ser[:1] # 整数索引,注意看与下面2个索引的区别,输出如下:
0 0.0
dtype: float64
ser.loc[:1] # 标签索引,输出如下:
0 0.0
1 1.0
dtype: float64
ser.iloc[:1] # 整数索引,输出如下:
0 0.0
dtype: float64
6、算术运算和数据对⻬
pandas最重要的⼀个功能是,它可以对不同索引的对象进⾏算术运算。在将对象相加时,如果存在不同的索引对,则结果的索引就是该索引对的并集。对于有数据库经验的话,这就像在索引标签上进⾏⾃动外连接。看⼀个简单的例⼦:
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 # 输出如下:
a 7.3
c -2.5
d 3.4
e 1.5
dtype: float64
s2 # 输出如下:
a -2.1
c 3.6
e -1.5
f 4.0
g 3.1
dtype: float64
s1 + s2 # 输出如下:
a 5.2
c 1.1
d NaN
e 0.0
f NaN
g NaN
dtype: float64
⾃动的数据对⻬操作在不重叠的索引处引⼊了NA值。缺失值会在算术运算过程中传播。
对于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 # 输出如下:
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 # 输出如下:
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
把它们相加后将会返回⼀个新的DataFrame,其索引和列为原来那两个DataFrame的并集:
df1 + df2 # 输出如下:
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对象相加,没有共⽤的列或⾏标签,结果都会是空
df1 = pd.DataFrame({'A': [1, 2]})
df2 = pd.DataFrame({'B': [3, 4]})
df1 # 输出如下:
A
0 1
1 2
df2 # 输出如下:
B
0 3
1 4
df1 - df2 # 输出如下:(没有共⽤的列或⾏标签,结果都会是空)
A B
0 NaN NaN
1 NaN NaN
7、在算术⽅法中填充值
在对不同索引的对象进⾏算术运算时,你可能希望当⼀个对象中某个轴标签在另⼀个对象中找不到时填充⼀个特殊值(⽐如0):
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 # 输出如下:
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 # 输出如下:
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
将它们相加时,没有重叠的位置就会产⽣NA值:
df1 + df2 # 输出如下:
a b c d e
0 0.0 2.0 4.0 6.0 NaN
1 9.0 NaN 13.0 15.0 NaN
2 18.0 20.0 22.0 24.0 NaN
3 NaN NaN NaN NaN NaN
使⽤df1的add⽅法,传⼊df2以及⼀个fill_value参数:
df1.add(df2, fill_value=0) # 输出如下:(参数fill_value指定缺失的话用替代)
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
下面列出了Series和DataFrame的算术⽅法。它们每个都有⼀个副本,以字⺟r开头,
它会翻转参数。因此这两个语句是等价的:
1 / df1 # 输出如下:
a b c d
0 inf 1.000000 0.500000 0.333333
1 0.250000 0.200000 0.166667 0.142857
2 0.125000 0.111111 0.100000 0.090909
df1.rdiv(1) # 输出如下:
a b c d
0 inf 1.000000 0.500000 0.333333
1 0.250000 0.200000 0.166667 0.142857
2 0.125000 0.111111 0.100000 0.090909
灵活的算术⽅法(Series和DataFrame的算术⽅法)(表5-5):
与此类似,在对Series或DataFrame重新索引时,也可以指定⼀个填充值:
df1.reindex(columns=df2.columns, fill_value=0) # 输出如下:
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
8、DataFrame和Series之间的运算
DataFrame和Series之间算术运算也是有明确规定的。先来看⼀个例⼦,计算⼀个⼆维数组与其某⾏之间的差:
arr = np.arange(12.).reshape((3, 4))
arr # 输出如下:
array([[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.],
[ 8., 9., 10., 11.]])
arr[0] # 输出:array([0., 1., 2., 3.])
arr - arr[0] # 输出如下:
array([[0., 0., 0., 0.],
[4., 4., 4., 4.],
[8., 8., 8., 8.]])
当从arr减去arr[0],每⼀⾏都会执⾏这个操作。这就叫做⼴播(broadcasting)。DataFrame和Series之间的运算差不多也是如此:
frame = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
series = frame.iloc[0]
frame # 输出如下:
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
series # 输出如下:
b 0.0
d 1.0
e 2.0
Name: Utah, dtype: float64
默认情况下,DataFrame和Series之间的算术运算会将Series的索引匹配到DataFrame的列,然后沿着⾏⼀直向下⼴播:
frame - series # 输出如下:
b d e
Utah 0.0 0.0 0.0
Ohio 3.0 3.0 3.0
Texas 6.0 6.0 6.0
Oregon 9.0 9.0 9.0
如果某个索引值在DataFrame的列或Series的索引中找不到,则参与运算的两个对象就会被重新索引以形成并集:
series2 = pd.Series(range(3), index=['b', 'e', 'f'])
frame + series2 # 输出如下:
b d e f
Utah 0.0 NaN 3.0 NaN
Ohio 3.0 NaN 6.0 NaN
Texas 6.0 NaN 9.0 NaN
Oregon 9.0 NaN 12.0 NaN
如果你希望匹配⾏且在列上⼴播,则必须使⽤算术运算⽅法。例如:
frame # 输出如下:
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
series3 # 输出如下:
Utah 1.0
Ohio 4.0
Texas 7.0
Oregon 10.0
Name: d, dtype: float64
frame.sub(series3, axis='index') # 输出如下:
b d e
Utah -1.0 0.0 1.0
Ohio -1.0 0.0 1.0
Texas -1.0 0.0 1.0
Oregon -1.0 0.0 1.0
传⼊的轴号就是希望匹配的轴。在本例中,我们的⽬的是匹配DataFrame的⾏索引(axis='index' or axis=0)并进⾏⼴播。
9、函数应⽤和映射
NumPy的ufuncs(元素级数组⽅法)也可⽤于操作pandas对象:
frame = pd.DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah', 'Ohio', 'Texas', 'Oregon'])
frame # 输出如下:
b d e
Utah -0.238758 -1.228157 -0.433499
Ohio 0.281959 0.229944 1.908042
Texas -1.180401 -0.234310 -0.264257
Oregon 0.635323 -1.248893 1.319811
np.abs(frame) # 输出如下:
b d e
Utah 0.238758 1.228157 0.433499
Ohio 0.281959 0.229944 1.908042
Texas 1.180401 0.234310 0.264257
Oregon 0.635323 1.248893 1.319811
另⼀个常⻅的操作是,将函数应⽤到由各列或⾏所形成的⼀维数组上。DataFrame的apply⽅法即可实现此功能:
f = lambda x: x.max() - x.min()
frame.apply(f) # 输出如下:
b 1.815723
d 1.478837
e 2.341541
dtype: float64
这⾥的函数f,计算了⼀个Series的最⼤和虽⼩的差,在frane的每列都执⾏了⼀次。结果是⼀个Series,使⽤frame的列作为索引。
如果传递axis='columns'到apply,这个函数会在每⾏执⾏:
frame.apply(f, axis='columns') # 输出如下:
Utah 0.989399
Ohio 1.678098
Texas 0.946090
Oregon 2.568704
dtype: float64
许多常⻅的数组统计功能都被实现成DataFrame的⽅法(如sum和mean),因此⽆需使⽤apply⽅法。传递到apply的函数不是必须返回⼀个标量,还可以返回由多个值组成的Series:
def f(x):
return pd.Series([x.min(), x.max()], index=['min', 'max'])
frame.apply(f) # 输出如下:
b d e
min -1.180401 -1.248893 -0.433499
max 0.635323 0.229944 1.908042
元素级的Python函数也是可以⽤的。假如你想得到frame中各个浮点值的格式化字符串,使⽤applymap即可:
format = lambda x: '%.2f' % x
frame.applymap(format) # 输出如下:
b d e
Utah -0.24 -1.23 -0.43
Ohio 0.28 0.23 1.91
Texas -1.18 -0.23 -0.26
Oregon 0.64 -1.25 1.32
之所以叫做applymap,是因为Series有⼀个⽤于应⽤元素级函数的map⽅法:
frame['e'].map(format) # 输出如下:
Utah -0.43
Ohio 1.91
Texas -0.26
Oregon 1.32
Name: e, dtype: object
10、排序和排名
根据条件对数据集排序(sorting)也是⼀种重要的内置运算。要对⾏或列索引进⾏排序(按字典顺序),可使⽤sort_index⽅法,它将返回⼀个已排序的新对象:
obj = pd.Series(range(4), index=['d', 'a', 'b', 'c'])
obj.sort_index() # 输出如下:
a 1
b 2
c 3
d 0
dtype: int64
对于DataFrame,则可以根据任意⼀个轴上的索引进⾏排序:
frame = pd.DataFrame(np.arange(8).reshape((2, 4)), index=['three', 'one'], columns=['d', 'a', 'b', 'c'])
frame.sort_index() # 输出如下:(行索引排序)
d a b c
one 4 5 6 7
three 0 1 2 3
frame.sort_index(axis=1) # 输出如下:(指定参数axis=1按列标签排序)
a b c d
three 1 2 3 0
one 5 6 7 4
数据默认是按升序排序的,但也可以降序排序:
frame.sort_index(axis=1, ascending=False) # 输出如下:(列标签降序排序)
d c b a
three 0 3 2 1
one 4 7 6 5
若要按值对Series进⾏排序,可使⽤其sort_values⽅法:
obj = pd.Series([4, 7, -3, 2])
obj.sort_values() # 输出如下:
2 -3
3 2
0 4
1 7
dtype: int64
在排序时,任何缺失值默认都会被放到Series的末尾:
obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
obj.sort_values() # 输出如下:
4 -3.0
5 2.0
0 4.0
2 7.0
1 NaN
3 NaN
dtype: float64
当排序⼀个DataFrame时,你可能希望根据⼀个或多个列中的值进⾏排序。将⼀个或多个列的名字传递给sort_values的by选项即可达到该⽬的:
frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 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']) # 输出如下:(参数是列表)
b a
2 -3 0
0 4 0
3 2 1
1 7 1
排名会从1开始⼀直到数组中有效数据的数量。接下来介绍Series和DataFrame的rank⽅法。
默认情况下,rank是通过“为各组分配⼀个平均排名”的⽅式破坏平级关系的:
obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
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
也可以根据值在原数据中出现的顺序给出排名:
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
这⾥,条⽬0和2没有使⽤平均排名6.5,它们被设成了6和7,因为数据中标签0位于标签2的前⾯。
也可以按降序进⾏排名:
obj.rank(ascending=False, method='max') # 输出如下:
0 2.0
1 7.0
2 2.0
3 4.0
4 5.0
5 6.0
6 4.0
dtype: float64
DataFrame可以在⾏或列上计算排名:
frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1], 'c': [-2, 5, 8, -2.5]})
frame # 输出如下:
b a c
0 4.3 0 -2.0
1 7.0 1 5.0
2 -3.0 0 8.0
3 2.0 1 -2.5
frame.rank(axis='columns') # 输出如下:
b a c
0 3.0 2.0 1.0
1 3.0 1.0 2.0
2 1.0 2.0 3.0
3 3.0 2.0 1.0
下面是所有⽤于破坏平级关系的method选项(排名时⽤于破坏平级关系的⽅法)(表5-6)
11、带有重复标签的轴索引
pandas函数(如reindex)都要求标签唯⼀,但这并不是强制性的。下面是带有重复索引值的Series:
obj = pd.Series(range(5), index=['a', 'a', 'b', 'b', 'c'])
obj # 输出如下:
a 0
a 1
b 2
b 3
c 4
dtype: int64
索引的is_unique属性可以告诉你它的值是否是唯⼀的:
obj.index.is_unique # 输出:False
对于带有重复值的索引,数据选取的⾏为将会有些不同。如果某个索引对应多个值,则返回⼀个Series;⽽对应单个值的,则返回⼀个标量值:
obj['a'] # 输出如下:(返回的是Series)
a 0
a 1
dtype: int64
obj['c'] # 输出: 4(返回的是一个标量)
这样会使代码变复杂,因为索引的输出类型会根据标签是否有重复发⽣变化。
对DataFrame的⾏进⾏索引时也是如此:
df = pd.DataFrame(np.random.randn(4, 3), index=['a', 'a', 'b', 'b'])
df # 输出如下:
0 1 2
a 1.280512 1.843114 -0.818365
a 0.919565 -0.214873 0.862256
b 1.367134 0.730995 -0.427160
b -1.763069 0.130508 0.133941
df.loc['b'] # 输出如下:
0 1 2
b 1.367134 0.730995 -0.427160
b -1.763069 0.130508 0.133941
三、汇总和计算描述统计
pandas对象拥有⼀组常⽤的数学和统计⽅法。⼤部分都属于约简和汇总统计,⽤于从Series中提取单个值(如sum或mean)或从DataFrame的⾏或列中提取⼀个Series。跟对应的NumPy数组⽅法相⽐,它们都是基于没有缺失数据的假设⽽构建的。看⼀个简单的DataFrame:
df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5], [np.nan, np.nan], [0.75, -1.3]], index=['a', 'b', 'c', 'd'], columns=['one', 'two'])
df # 输出如下:
one two
a 1.40 NaN
b 7.10 -4.5
c NaN NaN
d 0.75 -1.3
调⽤DataFrame的sum⽅法将会返回⼀个含有列的和的Series:
df.sum() # 输出如下:
one 9.25
two -5.80
dtype: float64
传⼊axis='columns'或axis=1将会按⾏进⾏求和运算:
df.sum(axis=1) # 输出如下:
a 1.40
b 2.60
c 0.00
d -0.55
dtype: float64
NA值会⾃动被排除,除⾮整个切⽚(这⾥指的是⾏或列)都是NA。通过skipna选项可以禁⽤该功能:
df.mean(axis='columns', skipna=False) # 输出如下:(排除有NA值的行)
a NaN
b 1.300
c NaN
d -0.275
dtype: float64
下面列出了约简⽅法的常⽤选项(表5-7):
有些⽅法(如idxmin和idxmax)返回的是间接统计(⽐如达到最⼩值或最⼤值的索引):
df.idxmax() # 输出如下:
one b
two d
dtype: object
另⼀些⽅法则是累计型的:
df.cumsum() # 输出如下:(按列累加)
one two
a 1.40 NaN
b 8.50 -4.5
c NaN NaN
d 9.25 -5.8
还有⼀种⽅法,它既不是约简型也不是累计型。describe就是⼀个例⼦,它⽤于⼀次性产⽣多个汇总统计:
df.describe() # 输出如下:
one two
count 3.000000 2.000000
mean 3.083333 -2.900000
std 3.493685 2.262742
min 0.750000 -4.500000
25% 1.075000 -3.700000
50% 1.400000 -2.900000
75% 4.250000 -2.100000
max 7.100000 -1.300000
对于⾮数值型数据,describe会产⽣另外⼀种汇总统计:
obj = pd.Series(['a', 'a', 'b', 'c'] * 4)
obj.describe() # 输出如下:
count 16
unique 3
top a
freq 8
dtype: object
下面列出了所有与描述统计相关的⽅法(描述和汇总统计)(表5-8):
1、相关系数与协⽅差
有些汇总统计(如相关系数和协⽅差)是通过参数对计算出来的。我们来看⼏个DataFrame,它们的数据来⾃Yahoo!Finance的股票价格和成交量,使⽤的是pandas-datareader包(可以⽤conda或pip安装):
conda install pandas-datareader
使⽤pandas_datareader模块下载了⼀些股票数据:
all_data = {ticker: web.get_data_yahoo(ticker) for ticker in ['AAPL', 'IBM', 'MSFT', 'GOOG']}
price = pd.DataFrame({ticker: data['Adj Close'] for ticker, data in all_data.items()})
volume = pd.DataFrame({ticker: data['Volume'] for ticker, data in all_data.items()})
注:Yahoo被Verizon收购,上面代码可能运行不成功。目前测试是成功的。参阅pandas-datareader⽂档,可以学习最新的功能。
returns = price.pct_change()
returns.tail() # 输出如下:
AAPL IBM MSFT GOOG
Date
2018-11-23 -0.025399 -0.011639 -0.000388 -0.013232
2018-11-26 0.013524 0.020224 0.032987 0.024163
2018-11-27 -0.002176 0.003931 0.006293 -0.004015
2018-11-28 0.038453 0.024744 0.037148 0.040042
2018-11-29 -0.007682 -0.012358 -0.008369 0.001906
Series的corr⽅法⽤于计算两个Series中重叠的、⾮NA的、按索引对⻬的值的相关系数。与此类似,cov⽤于计算协⽅差:
returns['MSFT'].corr(returns['IBM']) # 输出:0.4764567156320382
returns['MSFT'].cov(returns['IBM']) # 输出:8.31764255718186e-05
因为MSTF是⼀个合理的Python属性,我们还可以⽤更简洁的语法选择列:
returns.MSFT.corr(returns.IBM) # 输出:0.4764567156320382
DataFrame的corr和cov⽅法将以DataFrame的形式分别返回完整的相关系数或协⽅差矩阵:
returns.corr() # 输出如下:
AAPL IBM MSFT GOOG
AAPL 1.000000 0.363002 0.429684 0.441674
IBM 0.363002 1.000000 0.476457 0.398712
MSFT 0.429684 0.476457 1.000000 0.524992
GOOG 0.441674 0.398712 0.524992 1.000000
returns.cov() # 输出如下:
AAPL IBM MSFT GOOG
AAPL 0.000262 0.000071 0.000100 0.000110
IBM 0.000071 0.000148 0.000083 0.000074
MSFT 0.000100 0.000083 0.000206 0.000116
GOOG 0.000110 0.000074 0.000116 0.000235
利⽤DataFrame的corrwith⽅法,可以计算其列或⾏跟另⼀个Series或DataFrame之间的相关系数。传⼊⼀个Series将会返回⼀个相关系数值Series(针对各列进⾏计算):
returns.corrwith(returns.IBM) # 输出如下:
AAPL 0.363002
IBM 1.000000
MSFT 0.476457
GOOG 0.398712
dtype: float64
传⼊⼀个DataFrame则会计算按列名配对的相关系数。这⾥,计算百分⽐变化与成交量的相关系数:
returns.corrwith(volume) # 输出如下:
AAPL -0.060845
IBM -0.177534
MSFT -0.087247
GOOG -0.016870
dtype: float64
传⼊axis='columns'即可按⾏进⾏计算。⽆论如何,在计算相关系数之前,所有的数据项都会按标签对⻬。
2、唯⼀值、值计数以及成员资格
还有⼀类⽅法可以从⼀维Series的值中抽取信息。看下⾯的例⼦:
obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])
uniques = obj.unique()
uniques # 输出:array(['c', 'a', 'd', 'b'], dtype=object)
返回的唯⼀值是未排序的,如果需要的话,可以对结果再次进⾏排序(uniques.sort())。相似的,value_counts⽤于计算⼀个Series中各值出现的频率:
obj.value_counts() # 输出如下:
a 3
c 3
b 2
d 1
dtype: int64
为了便于查看,结果Series是按值频率降序排列的。value_counts还是⼀个顶级pandas⽅法,可⽤于任何数组或序列:
pd.value_counts(obj.values, sort=False) # 输出如下:
c 3
a 3
d 1
b 2
dtype: int64
isin⽤于判断⽮量化集合的成员资格,可⽤于过滤Series中或DataFrame列中数据的⼦集:
obj # 输出如下:
0 c
1 a
2 d
3 a
4 a
5 b
6 b
7 c
8 c
dtype: object
mask = obj.isin(['b', 'c'])
mask # 输出如下:
0 True
1 False
2 False
3 False
4 False
5 True
6 True
7 True
8 True
dtype: bool
obj[mask] # 输出如下:
0 c
5 b
6 b
7 c
8 c
dtype: object
与isin类似的是Index.get_indexer⽅法,它可以给你⼀个索引数组,从可能包含重复值的数组到另⼀个不同值的数组:
to_match = pd.Series(['c', 'a', 'b', 'b', 'c', 'a'])
unique_vals = pd.Series(['c', 'b', 'a'])
# 将to_match的索引设置为unique_vals的索引
pd.Index(unique_vals).get_indexer(to_match) # 输出:array([0, 2, 1, 1, 0, 2], dtype=int64)
表5-9 唯⼀值、值计数、成员资格⽅法(这⼏个⽅法的⼀些参考信息)
有时,可能希望得到DataFrame中多个相关列的⼀张柱状图。例如:
data = pd.DataFrame({'Qu1': [1, 3, 4, 3, 4], 'Qu2': [2, 3, 1, 2, 3], 'Qu3': [1, 5, 2, 4, 4]})
将pandas.value_counts传给该DataFrame的apply函数,就会出现:
result = data.apply(pd.value_counts).fillna(0)
result # 输出如下:
Qu1 Qu2 Qu3
1 1.0 1.0 1.0
2 0.0 2.0 1.0
3 2.0 2.0 0.0
4 2.0 0.0 2.0
5 0.0 0.0 1.0
这⾥,结果中的⾏标签是所有列的唯⼀值。这些值是每个列中这些值的相应计数。