pandas是贯穿本书后续部分的主要工具。它所包含的数据结构和数据处理工具的设计使得在Python中进行数据清洗和分析非常快捷。pandas经常是和其他数值计算工具,比如NumPy和SciPy,以及数据可视化工具比如matplotlib一起使用的。pandas支持大部分NumPy语言风格的数组计算,尤其是数组函数以及没有for循环的各种数据处理。 尽管pandas采用了很过NumPy的代码风格,但最大的不同在于pandas是用来处理表格型或异质性数据的。而NumPy则相反,它更适合处理同质型的数值类型数据。 由于在2010年成为了开源项目,pandas已经逐渐成熟,成为一个在真实世界中广泛应用的大型类库。pandas的开发者社区已经有超过800个代码贡献者,他们帮助构建了项目,并将pandas应用到日常中去解决他们的数据难题。 贯穿本书的后续章节,我会使用下面的便捷方式导入pandas:
import pandas as pd
因此,无论何时,只要你在代码中看到pd.,它表示对pandas的引用。你还可以方便地从本地命名空间中导入Series和DataFrame,它们是常用的类:
from pandas import Series,DataFrame
pandas数据结构介绍
为了入门pandas,你需要熟悉两个常用的工具数据结构:Series和DataFrame。尽管他们并不能解决所有的问题,但它们为大多数应用提供了一个有效、易用的基础。
Series
Series是一个一维的数组型对象,它包含了一个值序列(与NumPy中的类型相似),并且包含了数据标签,称为索引(index)。最简单的序列可以仅仅由一个数组形成:
>> > import pandas as pd
>> > obj = pd.Series( [ 4,7,-5,3] )
>> > obj
0 4
1 7
2 -5
3 3
dtype: int64
交互式环境中Series的字符串表示,索引在左边,值在右边。由于我们不为数据指定索引,默认生成生成的索引是从0到N-1(N是数据的长度)。你可以通过values属性和index属性分别Series对象的值和索引
>> > obj.values
array( [ 4, 7, -5, 3] , dtype= int64)
>> > obj.index
RangeIndex( start= 0, stop= 4, step= 1)
>> > obj2 = pd.Series( [ 4,7,-5,3] ,index= [ 'd' ,'b' ,'a' ,'c' ] )
>> > obj2
d 4
b 7
a -5
c 3
dtype: int64
与NumPy的数组相比,你可以在数据中选择数据的时候使用标签来进行索引:
>> > obj2[ 'a' ]
-5
>> > obj2[ 'd' ] = 6
>> > 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,可以认为它是一个长度固定且有序的字典,因为它将索引值和数据值按位置配对。在你可能会使用字典的上下文中,也可以使用Series:
>> > sdata = { 'Ohio' :35000,'Texas' :7100,'Oregon' :16000,'Utah' :5000}
>> > obj3 = pd.Series( sdata)
>> > obj3
Ohio 35000
Texas 7100
Oregon 16000
Utah 5000
dtype: int64
当你把字典传递给Series构造函数时,产生的Series的索引将是排序好的字典键。你可以将字典键按照你所想要的顺序传递给构造函数,从而使生成的Series的索引顺序符合你的预期:
>> > states = [ 'California' ,'Ohio' ,'Oregon' ,'Texas' ]
>> > obj4 = pd.Series( sdata,index= states)
>> > obj4
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 7100.0
dtype: float64
上面的例子中,sdata中的三个值被放置在正确的位置,但是因为’California’没有出现在sdata的键中,它对应的值是NaN(not a number),这是pandas中标记缺失值或NA值的方式。因为’Utah’并不在states中,它被排除在结果对象外。 我们将持续使用术语”缺失“或”NA“来表示缺失数据。pandas中使用isnull和notnull函数检查缺失数据:
>> > pd.isnull( obj4)
California True
Ohio False
Oregon False
Texas False
dtype: bool
>> > pd.notnull( obj4)
California False
Ohio True
Oregon True
Texas True
dtype: bool
isnull和notnull也是Series的实例方法:
>> > obj4.isnull( )
California True
Ohio False
Oregon False
Texas False
dtype: bool
对于很多应用来说,在数学操作中自动对齐索引是Series的一个非常有用的特性:
>> > obj4.isnull( )
California True
Ohio False
Oregon False
Texas False
dtype: bool
>> > obj3
Ohio 35000
Texas 7100
Oregon 16000
Utah 5000
dtype: int64
>> > obj4
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 7100.0
dtype: float64
>> > obj3 + obj4
California NaN
Ohio 70000.0
Oregon 32000.0
Texas 14200.0
Utah NaN
dtype: float64
Series对象自身和其索引都有name属性,这个特性与pandas其他重要功能集成在一起:
>> > obj4.name = 'population'
>> > obj4.index.name = 'stata'
>> > obj4
stata
California NaN
Ohio 35000.0
Oregon 16000.0
Texas 7100.0
Name: population, dtype: float64
Series的索引可以通过按位置赋值的方式进行改变:
>> > obj
0 4
1 7
2 -5
3 3
dtype: int64
>> > obj.index = [ 'Bob' ,'Steve' ,'Jeff' ,'Ryan' ]
>> > obj
Bob 4
Steve 7
Jeff -5
Ryan 3
dtype: int64
DataFrame
DataFrame表示的是矩阵的数据表,它包含已排序的列集合,每一列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame既有行索引也有列索引,它可以被视为一个共享相同索引的Series的字典。在DataFrame中,数据被存储为一个以上的二维块,而不是列表、字典或其他一维数组的集合。 尽管DataFrame是二维的,但你可以利用分层索引在DataFrame中展现更高维度的数据。分层索引是pandas中一种更为高级的数据处理特性,我们将在第8章进行讨论。 有多种方式可以构建DataFrame,其中最常用的方式是利用包含等长度列表或NumPy数组的字典来形成DataFrame:
data = { 'state' :[ 'Ohio' ,'Ohio' ,'Ohio' ,'Nevada' ,'Nevada' ,'Nevada' ] ,
'year' :[ 2000,20001,2002,2001,2002,2003] ,
'pop' :[ 1.5,1.7,3.6,2.4,2.9,3.2] }
frame = pd.DataFrame( data)
产生的DataFrame会自动为Sereies分配索引,并且列会按照排序的顺序排列:
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
ju中展现的是一个对浏览器更为友好的HTML表格。 对于大型DataFrame,head方法将会只选出头部的五行:
如果你指定了列的顺序,DataFrame的列将会按照指定顺序排列: 如果你传的列不包含在字典中,将会在结果中出现缺失值: DataFrame中的一列,可以按字典型标记或属性那样检索为Series: 在IPython中,属性型连接(frame2.year)和列名的tab补全是非常方便的。frame2[colunm]对于任意列名均有效,但是frame2.column只在列名是有效的Python变量名时有效。 请注意,返回的Series与原DataFrame有相同的索引,且Series的name属性也会被合理地设置。 行也可以通过位置或特殊属性loc进行选取:
列的引用是可以修改的。例如,空的‘debt’列可以赋值为标量或值数组: 当你将列表或数组赋值给一个列时,值的长度必须和DataFrame的长度相匹配。如果你将Series赋值给一列时,Series的索引将会按照DataFrame的索引重新排列,并在空缺的地方填充缺失值: 如果被赋值的列并不存在,则会生成一个新的列。del关键字可以像在字典中那样对DataFrame删除列。 在del的例子中,我首先增加一列,这一列是布尔值,判断条件是state列是否为’ohio’: frame2.eastern的语法无法创建新的列。 del方法可以用于移除之前新建的列: 从DataFrame中选取的列是数据的视图,而不是拷贝。因此,对Series的修改会映射到DataFrame中。如果需要复制,则应当显示地使用Series的copy方法。 另一种常用的数据形式是包含字典的嵌套字典。 如果嵌套字典被赋值给DataFrame,pandas会将字典的键作为列,将内部字典作为行索引: 你可以将使用类似NumPy的语法对DataFrame进行转置操作(调换行和列): 内部字典的键被联合、排序后形成了结果的索引。如果已经显示指明索引的话,内部字典的键将不会被排序: 包含Series的字典也可以用于构造DataFrame: 如果DataFrame的索引和列拥有name属性,则这些name属性也会被显示: 和Series类似,DataFrame的values属性会将包含在DataFrame中的数据以二维ndarray的形式返回: 如果DataFrame的列是不同的dtypes,则values的dtype会自动选择适合所有列的类型:
索引对象
pandas中的索引对象是用于存储轴标签和其他元数据的(例如轴名称或标签)。在构建Series或DataFrame时,你所使用的任意数组或标签序列都可以在内部转换为索引对象: 索引对象是不可变的,因此用户是无法修改索引对象的: 不变性使得在多种数据结构中分享索引对象更为安全: 一些用户并不经常利用索引对象提供的功能,但是因为一些操作会产生包含索引化数据的结果,理解索引如何工作还是很重要的。 除了类似数组,索引对象也像一个固定大小的集合: 与Python集合不同,pandas索引对象可以包含重复标签: 根据重复标签进行筛选,会选取所有重复标签对应的数据。每个索引都有一些集合逻辑的方法和属性,这些方法和属性解决了关于它所包含的数据的其他问题。下表总结了这些方法和属性中常用的一部分。