Pandas库
Pandas库是一个专门用于数据分析的开源Python库,有Series(序列)和DataFrame(数据框)这两种数据结构。
1 Pandas简介
数据分析工作需要一个专门的库,它能够以最简单的方式提供数据处理、数据抽取和数据操作所需的全部工具,pandas正是为了满足这个需求。
以NumPy为基础,不仅使pandas能和其他大多数模块相兼容,而且还能借力NumPy模块在计算方面性能高的优势。
另外一个意义深远的决定是为数据分析专门设计了两种数据结构。实际情况是,pandas没有使用Python已有的内置数据结构,也没有使用其他库的数据结构,而是开发了两种新型的数据结构。这两种数据结构的设计初衷是用于关系型或带标签的数据。用它们管理与SQL关系数据库和Excel工作表具有类似特征的数据很方便。
2 安装
2.1 Anaconda安装
在终端输入以下命令:
conda list pandas
运行上述命令时的输出结果有异,请输入以下命令:
conda install pandas
Anaconda将会下载下面这些包。
Package | build
---------------------------|--------------------------------
pytz-2014.9 | py27_0 169 KB
requests-2.5.3 | py27_0 588 KB
six-1.9.0 | py27_0 16 KB
conda-3.9.1 | py27_0 206 KB
pip-6.0.8 | py27_0 1.6 MB
scipy-0.15.1 | np19py27_0 71.3 MB
pandas-0.15.2 | np19py27_0 4.3 MB
------------------------------------------------------------
2.2 用PyPI安装
还可以从PyPI安装pandas:
pip install pandas
2.3 在Linux系统的安装方法
如果用的是某一Linux发行版,且不打算使用打包好的Python发行版,可以像安装其他包那样安装pandas。
Debian和Ubuntu Linux系统:
sudo apt-get install python-pandas
2.4 用源代码安装
如果想通过编译源代码来安装pandas,请参考以下GitHub链接:http://github.com/pydata/pandas。
git clone git://github.com/pydata/pandas.git
cd pandas
python setup.py install
3 测试pandas是否安装成功
pandas库还提供一项功能,安装完成后,可运行测试,检查内部命令是否能够执行。
首先确保Python发行版安装了nose模块。如已安装,输入以下命令开始测试:
nosetests pandas
4 pandas之旅
解释数据结构和用来处理这些数据结构的相关函数/方法,不需要编写大段代码。
打开Python,逐条输入命令。这样就有机会熟悉本章依次讲解的单个函数和数据结构。
首先,在Python shell打开一段会话,导入pandas库。pandas的常用导入方法如下:
>>> import pandas as pd
>>> import numpy as np
再见到pd和np时,分别指的是与pandas和NumPy这两个库相关的对象或方法,使用下面这种方法导入pandas模块:
>>> from pandas import *
这样就无需用pd指定函数、对象或方法。,这种方法在Python社区看来是不太好的。
5 pandas数据结构简介
pandas的核心为两大数据结构,数据分析相关的所有事务都是围绕着这两种结构进行的:
Series
DataFrame
Series这类数据结构用于存储一个序列这样的一维数据,而DataFrame作为更复杂的数据结构,则用于存储多维数据。
虽然这些数据结构不能解决所有问题,但它们为大多数应用提供了有效和强大的工具。就简洁性而言,它们理解和使用起来都很简单。此外,很多更为复杂的数据结构都可以追溯到这两种结构。
两者的奇特之处是将Index(索引)对象和标签整合到自己的结构中,该特点使得这两种数据结构具有很强的可操作性。
5.1 Series对象
pandas库的Series对象用来表示一维数据结构,跟数组类似,但多了一些额外的功能。它的内部结构很简单,由两个相互关联的数组组成,其中主数组用来存放数据(NumPy任意类型数据)。主数组的每个元素都有一个与之相关联的标签,这些标签存储在另外一个叫作Index的数组中。
Series对象的结构
声明Series对象
调用Series()构造函数,把要存放在Series对象中的数据以数组形式传入,就能创建一个Series对象。
>>> s = pd.Series([12,-4,7,9])
>>> s
0 12
1 -4
2 7
3 9
dtype: int64
从Series的输出可以看到,左侧Index是一列标签,右侧是标签对应的元素。
声明Series时,若不指定标签,pandas默认使用从0开始依次递增的数值作为标签。这种情况下,标签与Series对象中元素的索引(在数组中的位置)一致。
然而,最好使用有意义的标签,用以区分和识别每个元素,而不用考虑元素插入到Series中的顺序。
因此,调用构造函数时,就需要指定index选项,把存放有标签的数组赋给它,其中标签为字符串类型。
>>> s = pd.Series([12,-4,7,9], index=['a','b','c','d'])
>>> s
a 12
b -4
c 7
d 9
dtype: int64
如果想分别查看组成Series对象的两个数组,可像下面这样调用它的两个属性:index(索引)和values(元素)。
>>> s.values
array([12, -4, 7, 9], dtype=int64)
>>> s.index
Index([u'a', u'b', u'c', u'd'], dtype='object')
选择内部元素
若想获取Series对象内部的元素,把它作为普通的NumPy数组,指定键即可。
>>> s[2]
7
或者,指定位于索引位置处的标签。
>>> s['b']
-4
跟从NumPy数组选择多个元素的方法相同,可像下面这样选取多项:
>>> s[0:2]
A 12
b -4
dtype: int64
这种情况甚至可以使用元素对应的标签,只不过要把标签放到数组中:
>>> s[['b','c']]
b -4
c 7
dtype: int64
为元素赋值
用索引或标签选取元素后进行赋值。
>>> s[1] = 0
>>> s
a 12
b 0
c 7
d 9
dtype: int64
>>> s['b'] = 1
>>> s
a 12
b 1
c 7
d 9
dtype: int64
用NumPy数组或其他Series对象定义新Series对象
可以用NumPy数组或现有的Series对象定义新的Series对象。
>>> arr = np.array([1,2,3,4])
>>> s3 = pd.Series(arr)
>>> s3
0 1
1 2
2 3
3 4
dtype: int32
>>> s4 = pd.Series(s)
>>> s4
a 12
b 4
c 7
d 9
dtype: int64
这样做时不要忘记新Series对象中的元素不是原NumPy数组或Series对象元素的副本,而是对它们的引用。 也就是说,这些对象是动态插入到新Series对象中。如改变原有对象元素的值,新Series对象中这些元素也会发生改变。
>>> s3
0 1
1 2
2 3
3 4
dtype: int32
>>> arr[2] = -2
>>> s3
0 1
1 2
2 -2
3 4
dtype: int32
筛选元素
pandas库的开发是以NumPy库为基础的,因此就数据结构而言,NumPy数组的多种操作方法得以扩展到Series对象中,其中就有根据条件筛选数据结构中的元素这一方法。
如要获取Series对象中所有大于8的元素,可使用以下代码:
>>> s[s > 8]
a 12
d 9
dtype: int64
Series对象运算和数学函数
适用于NumPy数组的运算符(+、-、*、/)或其他数学函数,也适用于Series对象。
至于运算符,直接用来编写算术表达式即可。
>>> s / 2
a 6.0
b -2.0
c 3.5
d 4.5
dtype: float64
然而,至于NumPy库的数学函数,必须指定它们的出处np,并把Series实例作为参数传入。
>>> np.log(s)
a 2.484907
b NaN
c 1.945910
d 2.197225
dtype: float64
Series对象的组成元素
Series对象往往包含重复的元素,统计元素重复出现的次数或判断一个元素是否在Series中。
声明一个包含多个重复元素的Series对象。
>>> serd = pd.Series([1,0,2,1,2,3], index=['white','white','blue','green','green','yellow'])
>>> serd
white 1
white 0
blue 2
green 1
green 2
yellow 3
dtype: int64
要弄清楚Series对象包含多少个不同的元素,可使用unique()函数。其返回结果为一个数组,包含Series去重后的元素,但顺序随意。
>>> serd.unique()
array([1, 0, 2, 3], dtype=int64)
跟unique()函数相似的另外一个函数是value_counts()函数,它不仅返回各个不同的元素,还计算每个元素在Series中的出现次数。
>>> serd.value_counts()
2 2
1 2
3 1
0 1
dtype: int64
isin()函数用来判断所属关系,也就是判断给定的一列元素是否包含在数据结构之中。isin()函数返回布尔值,可用于筛选Series或DataFrame列中的数据。
>>> serd.isin([0,3])
white False
white True
blue False
green False
green False
yellow True
dtype: bool
>>> serd[serd.isin([0,3])]
white 0
yellow 3
dtype: int64
NaN
求负数的对数,得到的返回结果为NaN(Not a Number,非数值)。数据结构中若字段为空或者不符合数字的定义时,用这个特定的值来表示。
一般来讲,NaN值表示数据有问题,必须对其进行处理,尤其是在数据分析时。从某些数据源抽取数据时遇到了问题,甚至是数据源缺失数据,往往就会产生这类数据。进一步来讲,计算负数的对数,执行计算或函数时抛出异常等特定情况,也可能产生这类数据。
尽管NaN值是数据有问题才产生的,然而在pandas中是可以定义这种类型的数据,并把它添加到Series等数据结构中的。创建数据结构时,可为数组中元素缺失的项输入np.NaN。
>>> s2 = pd.Series([5,-3,np.NaN,14])
>>> s2
0 5
1 -3
2 NaN
3 14
isnull()和notnull()函数用来识别没有对应元素的索引时非常好用。
>>> s2.isnull( )
0 False
1 False
2 True
3 False
dtype: bool
>>> s2.notnull( )
0 True
1 True
2 False
3 True
dtype: bool
上述两个函数返回两个由布尔值组成的Series对象,其元素值是True还是False取决于原Series对象的元素是否为NaN。如果是NaN,isnull()函数返回值为True;反之,如果不是NaN,notnull()函数返回值为True。这两个函数可用作筛选条件。
>>> s2[s2.notnull( )]
0 5
1 -3
3 14
dtype: float64
>>> s2[s2.isnull( )]
2 NaN
dtype: float64
Series用作字典
我们还可以把Series对象当作字典(dict,dictionary)对象来用。定义Series对象时,就可以利用这种相似性。事实上,我们可以用事先定义好的字典来创建Series对象。
>>> mydict = {'red': 2000, 'blue': 1000, 'yellow': 500, 'orange': 1000}
>>> myseries = pd.Series(mydict)
blue 1000
orange 1000
red 2000
yellow 500
dtype: int64
上述例子中,索引数组用字典的键来填充,每个索引所对应的元素为用作索引的键在字典中对应的值。还可以单独指定索引,pandas会控制字典的键和数组索引标签之间的相关性。如遇缺失值处,pandas就会为其添加NaN。
>>> colors = ['red','yellow','orange','blue','green']
>>> myseries = pd.Series(mydict, index=colors)
red 2000
yellow 500
orange 1000
blue 1000
green NaN
dtype: float64
Series对象之间的运算
见识过Series对象和标量之间的数学运算,Series对象之间也可以进行这类运算,甚至标签也可以参与运算。
Series这种数据结构在运算时有一大优点,它能够通过识别标签对齐不一致的数据。
在下面来求只有部分元素标签相同的两个Series对象之和。
>>> mydict2 = {'red':400,'yellow':1000,'black':700}
>>> myseries2 = pd.Series(mydict2)
>>> myseries + myseries2
black NaN
blue NaN
orange NaN
green NaN
red 2400
yellow 1500
dtype: float64
上述运算得到一个新Series对象,其中只对标签相同的元素求和。其他只属于任何一个Series对象的标签也被添加到新对象中,只不过它们的值均为NaN。
5.2 DataFrame对象
DataFrame这种列表式数据结构跟工作表(最常见的是Excel工作表)极为相似,其设计初衷是将Series的使用场景由一维扩展到多维。DataFrame由按一定顺序排列的多列数据组成,各列的数据类型可以有所不同(数值、字符串或布尔值等)。
DataFrame数据结构
Series对象的Index数组存放有每个元素的标签,而DataFrame对象则有所不同,它有两个索引数组。第一个数组与行相关,它与Series的索引数组极为相似。每个标签与标签所在行的所有元素相关联。而第二个数组包含一系列列标签,每个标签与一列数据相关联。
DataFrame还可以理解为一个由Series组成的字典,其中每一列的名称为字典的键,形成DataFrame的列的Series作为字典的值。进一步来说,每个Series的所有元素映射到叫作Index的标签数组。
定义DataFrame对象
新建DataFrame对象的最常用方法是传递一个dict对象给DataFrame()构造函数。dict对象以每一列的名称作为键,每个键都有一个数组作为值。
>>> data = {'color' : ['blue','green','yellow','red','white'],
'object' : ['ball','pen','pencil','paper','mug'],
'price' : [1.2,1.0,0.6,0.9,1.7]}
>>>frame = pd.DataFrame(data)
>>> frame
color object price
0 blue ball 1.2
1 green pen 1.0
2 yellow pencil 0.6
3 red paper 0.9
4 white mug 1.7
如果用来创建DataFrame对象的dict对象包含一些用不到的数据,
可以只选择自己感兴趣的。在DataFrame构造函数中,用columns选项指定需要的列即可。新建的DataFrame各列顺序与指定的列顺序一致,而与它们在字典中的顺序无关。
>>> frame2 = pd.DataFrame(data, columns=['object','price'])
>>> frame2
object price
0 ball 1.2
1 pen 1.0
2 pencil 0.6
3 paper 0.9
4 mug 1.7
DataFrame对象跟Series一样,如果Index数组没有明确指定标签,pandas也会自动为其添加一列从0开始的数值作为索引。如果想用标签作为DataFrame的索引,则要把标签放到数组中,赋给index选项。
frame2 = pd.DataFrame(data, index=[‘one’,‘two’,‘three’,‘four’,‘five’])
>>> frame2
color object price
one blue ball 1.2
two green pen 1.0
three yellow pencil 0.6
four red paper 0.9
five white mug 1.7
既已引入两个新选项index和columns,还可以想出一种定义DataFrame的新方法。不再使用dict对象,而是定义一个构造函数,指定三个参数,参数顺序如下:数据矩阵、index选项和columns选项。记得将存放有标签的数组赋给index选项,将存放有列名称的数组赋给columns选项。
要方便、快捷地创建包含数据的数组,可以使用np.arange(16).reshape((4,4))生成一个4×4型、包含数字0~15的矩阵。
>>> frame3 = pd.DataFrame(np.arange(16).reshape((4,4)),
... index=['red','blue','yellow','white'],
... columns=['ball','pen','pencil','paper'])
>>> frame3
ball pen pencil paper
red 0 1 2 3
blue 4 5 6 7
yellow 8 9 10 11
white 12 13 14 15
选取元素
如想知道DataFrame对象所有列的名称,在它上面调用columns属性即可。
类似地,要获取索引列表,调用index属性即可。
>>> frame.columns
Index([u'colors', u'object', u'price'], dtype='object')
>>> frame.index
Int64Index([0, 1, 2, 3, 4], dtype='int64')
如果要获取存储在数据结构中的元素,可以使用values属性获取所有元素。
>>> frame.values
array([['blue', 'ball', 1.2],
['green', 'pen', 1.0],
['yellow', 'pencil', 0.6],
['red', 'paper', 0.9],
['white', 'mug', 1.7]], dtype=object)
或者,如果想选择一列内容,把这一列的名称作为索引即可。
>>> frame['price']
0 1.2
1 1.0
2 0.6
3 0.9
4 1.7
Name: price, dtype: float64
如上所见,返回值为Series对象。另外一种方法是用列名称作为DataFrame实例的属性。
>>> frame.price
0 1.2
1 1.0
2 0.6
3 0.9
4 1.7
Name: price, dtype: float64
至于DataFrame中的行,用ix属性和行的索引值就能获取到。
>>> frame.ix[2]
color yellow
object pencil
price 0.6
Name: 2, dtype: object
返回结果同样是一个Series对象,其中列的名称已经变为索引数组的标签,而列中的元素变为Series的数据部分。
用一个数组指定多个索引值就能选取多行:
>>> frame.ix[[2,4]]
color object price
2 yellow pencil 0.6
4 white mug 1.7
若要从DataFrame抽取一部分,可以用索引值选择想要的行。事实上,可以把一行看作DataFrame的一部分,通过指定索引范围来选取,其中这一行的索引作为起始索引值(下面例子中的0),下一行的索引作为结束索引(下面例子中的1)。
>>> frame[0:1]
color object price
0 blue ball 1.2
返回结果为只包含一行数据的DataFrame对象。如需多行,必须扩展选择范围。
>>> frame[1:3]
color object price
1 green pen 1.0
2 yellow pencil 0.6
最后,如要获取存储在DataFrame中的一个元素,需要依次指定元素所在的列名称、行的索引值或标签。
>>> frame['object'][3]
'paper'
赋值
用index属性指定DataFrame结构中的索引数组,用columns属性指定包含列名称的行。还可以用name属性为这两个二级结构指定标签,便于识别。
>>> frame.index.name = 'id'; frame.columns.name = 'item'
>>> frame
item color object price
id
0 blue ball 1.2
1 green pen 1.0
2 yellow pencil 0.6
3 red paper 0.9
4 white mug 1.7
灵活程度非常高是pandas数据结构的一大优点。事实上,可以在任何层级修改它们的内部结构。例如,执行添加一列新元素这类常见的操作。
添加列的方法很简单,指定DataFrame实例新列的名称,为其赋值即可。
>>> frame['new'] = 12
>>> frame
colors object price new
0 blue ball 1.2 12
1 green pen 1.0 12
2 yellow pencil 0.6 12
3 red paper 0.9 12
4 white mug 1.7 12
从结果可以看出,DataFrame新增了名称为“new”的一列,它的各个元素均为12。
然而,如果想更新一列的内容,需要把一个数组赋给这一列。
>>> frame['new'] = [3.0,1.3,2.2,0.8,1.1]
>>> frame
color object price new
0 blue ball 1.2 3.0
1 green pen 1.0 1.3
2 yellow pencil 0.6 2.2
3 red paper 0.9 0.8
4 white mug 1.7 1.1
如果想更新某一列的全部数据,方法类似。例如借助np.arange()函数预先定义一个序列,用它更新某一列的所有元素。
为DataFrame的各列赋一个Series对象也可以创建DataFrame,例如使用np.arange()函数生成一个递增序列。
>>> ser = pd.Series(np.arange(5))
>>> ser
0 0
1 1
2 2
3 3
4 4
dtype: int32
>>> frame['new'] = ser
>>> frame
color object price new
0 blue ball 1.2 0
1 green pen 1.0 1
2 yellow pencil 0.6 2
3 red paper 0.9 3
4 white mug 1.7 4
最后,我们来看一下修改单个元素的方法:选择元素,为其赋新值即可。
>>> frame['price'][2] = 3.3
元素的所属关系
前面已经见过用isin()函数判断一组元素是否属于Series对象,其实该函数对DataFrame对象也适用。
>>> frame.isin([1.0,'pen'])
color object price
0 False False False
1 False True True
2 False False False
3 False False False
4 False False False
得到了一个只包含布尔值的DataFrame对象,其中只有满足从属关系之处元素为True。如把上述返回结果作为条件,将得到一个新DataFrame,其中只包含满足条件的元素。
>>> frame[frame.isin([1.0,'pen'])]
color object price
0 NaN NaN NaN
1 NaN pen 1
2 NaN NaN NaN
3 NaN NaN NaN
4 NaN NaN NaN
删除一列
如想删除一整列的所有数据,使用del命令。
>>> del frame['new']
>>> frame
colors object price
0 blue ball 1.2
1 green pen 1.0
2 yellow pencil 0.6
3 red paper 0.9
4 white mug 1.7
筛选
对于DataFrame对象,也可以通过指定条件筛选元素。例如想获取所有小于指定数字(比如12)的元素。
>>> frame[frame < 12]
ball pen pencil paper
red 0 1 2 3
blue 4 5 6 7
yellow 8 9 10 11
white NaN NaN NaN NaN
返回的DataFrame对象只包含所有小于12的数字,各元素的位置保持不变。其他不符合条件的元素被替换为NaN。
用嵌套字典生成DataFrame对象
嵌套字典是Python广泛使用的数据结构,示例如下:
nestdict = { 'red': { 2012: 22, 2013: 33 },
'white': { 2011: 13, 2012: 22; 2013: 16},
'blue': {2011: 17, 2012: 27; 2013: 18}}
直接将这种数据结构作为参数传递给DataFrame()构造函数,pandas就会将外部的键解释成列名称,将内部的键解释为用作索引的标签。
解释嵌套结构时,可能并非所有位置都有相应的元素存在。pandas会用NaN填补缺失的元素。
>>> nestdict = {'red':{2012: 22, 2013: 33},
... 'white':{2011: 13, 2012: 22, 2013: 16},
... 'blue': {2011: 17, 2012: 27, 2013: 18}}
>>> frame2 = pd.DataFrame(nestdict)
>>> frame2
blue red white
2011 17 NaN 13
2012 27 22 22
2013 18 33 16
DataFrame转置
处理列表数据时可能会用到转置操作(列变为行,行变为列)。pandas提供了一种很简单的转置方法。调用T属性就能得到DataFrame对象的转置形式。
>>> frame2.T
2011 2012 2013
blue 17 27 18
red NaN 22 33
white 13 22 16
5.3 Index对象
知道了Series、DataFrame对象以及它们的结构形式,对这些数据结构的特性也一定有所了解。事实上,它们在数据分析方面的大多数优秀特性都取决于完全整合到这些数据结构中的Index对象。
轴标签或其他用作轴名称的元数据就存储为Index对象。如何把存储多个标签的数组转化为Index对象:指定构造函数的index选项。
>>> ser = pd.Series([5,0,3,8,4], index=['red','blue','yellow','white','green'])
>>> ser.index
Index([u'red', u'blue', u'yellow', u'white', u'green'], dtype='object')
跟pandas数据结构(Series和DataFrame)中其他元素不同的是,**Index对象不可改变。声明后,它不能改变。**不同数据结构共用Index对象时,该特性能够保证它的安全。
每个Index对象都有很多方法和属性,需要知道它们所包含的值时,这些方法和属性非常有用。
Index对象的方法
Index对象提供了几种方法,可用来获取数据结构索引的相关信息。例如,idmin()和idmax()函数分别返回索引值最小和最大的元素。
>>> ser.idxmin()
'red'
>>> ser.idxmax()
'green'
含有重复标签的Index
到目前为止,见过的所有索引都是位于一个单独的数据结构中,且所有标签都是唯一的。虽然只有满足这个条件,很多函数才能运行,但是对pandas数据结构而言,这个条件并不是必需的。
举一个例子,定义一个含有重复标签的Series。
>>> serd = pd.Series(range(6), index=['white','white','blue','green','green','yellow'])
>>> serd
white 0
white 1
blue 2
green 3
green 4
yellow 5
dtype: int64
从数据结构中选取元素时,如果一个标签对应多个元素,我们得到的将是一个Series对象而不是单个元素。
>>> serd['white']
white 0
white 1
dtype: int64
以上逻辑适用于索引中存在重复项的DataFrame,其返回结果为DataFrame对象。
数据结构很小时,识别索引的重复项很容易,但随着数据结构逐渐增大以后,难度也在增加。pandas的Index对象还有is_unique属性。调用该属性,就可以知道数据结构(Series和DataFrame)中是否存在重复的索引项。
>>> serd.index.is_unique
False
>>> frame.index.is_unique
True