Pandas对象简介
可以把Pandas看成增强版的NumPy结构化数组,行列都不再只是简单的整数索引,还可以带上标签。Pandas有三个基本数据结构:Series、DataFrame、Index。
Series
Pandas的Series对象是一个带索引数据构成的一维数组。可以用一个数组创建Series对象:
>>> import numpy as np
>>> import pandas as pd
>>> data = pd.Series([0.25, 0.5, 0.75, 1.0])
>>> data
0 0.25
1 0.50
2 0.75
3 1.00
dtype: float64
从上面的结果中,你会发现Series对象将一组数据和一组索引绑定在一起,我们可以通过Values属性和index属性获取数据。values属性返回的结果与NumPy数组类似:
>>> data.values
array([0.25, 0.5 , 0.75, 1. ])
index属性返回的结果是一个类型为pd.Index的类数组对象。
>>> data.index
RangeIndex(start=0, stop=4, step=1)
和NumPy数组一样,数据可以通过Python的中括号索引标签获取:
>>> data[1]
0.5
>>> data[1:3]
1 0.50
2 0.75
dtype: float64
1.Series是通用的NumPy数组
到目前为止,你可能觉得Series对象和一维NumPy数组基本可以等价交换,但两者的本质差异其实是索引:NumPy数组通过隐式定义的整数索引获取数值,而Pandas的Series对象用一种显示定义的索引与数值关联。
显示索引的定义让Series对象拥有了更强的能力。例如,索引不再仅仅是整数,还可以是任何想要的类型。如果需要,完全可以用字符串定义索引:
>>> data = pd.Series([0.25, 0.5, 0.75, 1.0], index=['a', 'b', 'c', 'd'])
>>> data
a 0.25
b 0.50
c 0.75
d 1.00
dtype: float64
也可以使用不连续的索引:
>>> data = pd.Series([0.25, 0.5, 0.75, 1.0], index=[2, 3, 5, 7])
>>> data
2 0.25
3 0.50
5 0.75
7 1.00
dtype: float64
2.Series是特殊的字典
你可以把Pandas的Series对象看成一种特殊的Python字典。字典是一种将任意键映射到一组任意值的数据结构,而Series对象其实是一种将类型键映射到一组类型值得数据结构。
>>> population_dict = {
'California': 38332521,
'Texas': 26448193,
'New York': 19651127,
'Florida': 19552860,
'Illinois': 12882135}
>>> population = pd.Series(population_dict)
>>> population
California 38332521
Texas 26448193
New York 19651127
Florida 19552860
Illinois 12882135
dtype: int64
和字典不同,Series对象还支持数组形式的操作,比如切片:
>>> population['California':'New York']
California 38332521
Texas 26448193
New York 19651127
dtype: int64
3.创建Series对象
我们已将见过几种创建Pandas的Series对象的方法,都是像这样的形式:
>>> pd.Series(data, index=index)
其中,index是一个可选参数,data参数支持各种数据类型。例如data可以是列表或者NumPy数组,这时index默认值为整数数列:
>>> pd.Series([2, 4, 6])
0 2
1 4
2 6
dtype: int64
data也可以是一个标量,创建Series对象会重复填充到每一个索引上:
>>> pd.Series(5)
0 5
dtype: int64
>>> pd.Series(5, index=[100, 200, 300])
100 5
200 5
300 5
dtype: int64
data还可以是一个字典:
>>> pd.Series({
2:'a', 1:'b', 3:'c'})
2 a
1 b
3 c
dtype: object
DataFrame
和Series一样,DataFrame既可以作为一个通用性NumPy数组,也可以看做特殊的Python字典。
1.DataFrame是通用的NumPy数组
如果将Series类比作带灵活索引的一维数组,那么DataFrame就可以看做是一种既有灵活的行索引,又有灵活列索引的二维数组。
>>> population
California 38332521
Texas 26448193
New York 19651127
Florida 19552860
Illinois 12882135
dtype: int64
>>> area_dict = {
'California': 423967, 'Texas': 695662, 'New York': 141297,
'Florida': 170312, 'Illinois': 149995}
>>> area = pd.Series(area_dict)
>>> area
California 423967
Texas 695662
New York 141297
Florida 170312
Illinois 149995
dtype: int64
>>> # 用一个字典创建一个包含这些信息的二维对象
>>> states = pd.DataFrame({
'population':population, 'area':area})
>>> states
population area
California 38332521 423967
Texas 26448193 695662
New York 19651127 141297
Florida 19552860 170312
Illinois 12882135 149995
DataFrame的index属性和columns属性
>>> states.index
Index(['California', 'Texas', 'New York', 'Florida', 'Illinois'], dtype='object')
>>> states.columns
Index(['population', 'area'], dtype='object')
DataFrame可以看做一种通用的NumPy二维数组,它的行列都可以通过索引获取。
2.DataFrame是特殊字典
与Series类似,我们也可以把DataFrame看成一种特殊的字典。字典是一个键映射一个值,而DataFrame是一列映射一个Series的数据。
>>> states['area']
California 423967
Texas 695662
New York 141297
Florida 170312
Illinois 149995
Name: area, dtype: int64
需要注意的是,在NumPy的二维数组里,data[0]返回第一行;而在DataFrame中,data[‘col0’]返回第一列。
3.创建DataFrame对象
(1)通过单个Series对象创建。DataFrame是一组Series对象的集合,可以用单个Series创建一个单列的DataFrame:
>>> population
California 38332521
Texas 26448193
New York 19651127
Florida 19552860
Illinois 12882135
dtype: int64
>>> pd.DataFrame(population, columns=['population'])
population
California 38332521
Texas 26448193
New York 19651127
Florida 19552860
Illinois 12882135
(2)通过字典列表创建。任何元素是字典的列表都可以变成DataFrame:
>>> data = [{
'a':i, 'b':2 * i}for i in range(3)]
>>> data
[{
'a': 0, 'b': 0}, {
'a': 1, 'b': 2}, {
'a': 2, 'b': 4}]
>>> pd.DataFrame(data)
a b
0 0 0
1 1 2
2 2 4
即使字典中有些键不在,Pandas也会用缺失值NaN(不是数字)来表示:
>>> pd.DataFrame([{
'a':1, 'b':2},{
'b':3, 'c':4}])
a b c
0 1.0 2 NaN
1 NaN 3 4.0
(3)通过Series对象字典创建。就像之前见过的那样,DataFrame也可以用一个由Series对象构成的字典创建:
>>> pd.DataFrame({
'population':population, 'area':area})
population area
California 38332521 423967
Texas 26448193 695662
New York 19651127 141297
Florida 19552860 170312
Illinois 12882135 149995
(4)通过NumPy二维数组创建。假如有一个二维数组,就可以创建一个可以指定行列索引值得DataFrame。如果不指定行列索引值,那么行列默认都是整数索引值:
pd.DataFrame(np.random.rand(3, 2), columns=['foo', 'bar'], index=['a', 'b', 'c'])
foo bar
a 0.086317 0.664006
b 0.582919 0.211217
c 0.341268 0.032053
(5)通过NumPy结构化数组创建
以后补充
数据取值与选择
Series数据选择方法
1.将Series看做字典
>>> data = pd.Series([0.25, 0.5, 0.75, 1.0], index=['a', 'b', 'c', 'd'])
>>> data
a 0.25
b 0.50
c 0.75
d 1.00
dtype: float64
>>> data['b']
0.5
我们还可以用Python字典的表达式和方法来检测键、索引和值:
>>> 'a' in data
True
>>> data.keys()
Index(['a', 'b', 'c', 'd'], dtype='object')
>>> list(data.items())
[('a', 0.25), ('b', 0.5), ('c', 0.75), ('d', 1.0)]
>>> data.values()
Traceback (most recent call last):
File "<pyshell#82>", line 1, in <module>
data.values()
TypeError: 'numpy.ndarray' object is not callable
>>> data.values
array([0.25, 0.5 , 0.75, 1. ])
Series对象可以用字典语法调整数据,就像你可以通过增加新的键扩展字典一样,你也可以通过增加新的索引扩展Series:
>>> data['e'] = 1.25
>>> data
a 0.25
b 0.50
c 0.75
d 1.00
e 1.25
dtype: float64
2.将Series看做一维数组
Series不仅有着和字典一样的接口,而且还具备和NumPy数组一样的数组数据选择功能,包括索引、掩码、花俏的索引等操作:
>>> data
a 0.25
b 0.50
c 0.75
d 1.00
e 1.25
dtype: float64
>>> # 将显示索引作为切片
>>> data['a':'c']
a 0.25
b 0.50
c 0.75
dtype: float64
>>> # 将隐式索引作为切片
>>> data[0:2]
a 0.25
b 0.50
dtype: float64
>>> # 掩码
>>> data[(data > 0.3) & (data < 0.8)]
b 0.50
c 0.75
dtype: float64
>>> # 花俏的索引
>>> data[['a', 'e']]
a 0.25
e 1.25
dtype: float64
在以上所有示例中,切片是绝大部分混乱之源。需要注意的是,当使用显示索引(即data[‘a’:‘c’])做切片时,结果包含最后一个索引;而当使用隐式索引(即data[0:2])做切片时,结果不包含最后一个索引
3.索引器:loc、iloc和ix
这些切片和取值的习惯用法经常会造成混乱。例如,如果你的Series是显示整数索引,那么data[1]这样的取值操作会使用显式索引,而data[1:3]这样的切片操作却会使用隐式索引。
>>> data = pd.Series(['a', 'b', 'c'], index=[1, 3, 5])
>>> data
1 a
3 b
5 c
dtype: object
>>> # 取值操作是显式索引
>>> data[1]
'a'
>>> # 切片操作是隐式索引
>>> data[1:2]
3 b
dtype: object
由于整数索引很容易造成混淆,所以Pandas提供了一些索引器(indexer)属性来作为取值的方法。
第一种索引器是loc属性,表示取值和切片都是显示的:
>>> data
1 a
3 b
5 c
dtype: object
>>> data.loc[1]
'a'
>>> data.loc[1:3]
1 a
3 b
dtype: object
第二种是iloc属性,表示取值和切片都是Python形式的隐式索引:
>>> data
1 a
3 b
5 c
dtype: object
>>> data.iloc[1]
'b'
>>> data.iloc[1:3]
3 b
5 c
dtype: object
第三种取值属性是ix,它是前两种索引器的混合形式,之后会提到。
Python代码设计原则之一是“显式优于隐式”。使用loc和iloc可以让代码更容易维护,可读性更高。特别是在处理整数索引的对象时,我强烈推荐使用着两种索引器,它们既可以让代码阅读和理解起来更加容易,也能避免因误用索引/切片而产生的小bug。
DataFrame数据选择方法
1.将DataFrame看做字典
>>> area = pd.Series({
'California': 423967, 'Texas': 695662, 'New York': 141297, 'Florida': 170312, 'Illinois': 149995})
>>> pop = pd.Series({
'California': 38332521, 'Texas': 26448193, 'New York': 19651127, 'Florida': 19552860, 'Illinois': 12882135})
>>> data = pd.DataFrame({
'area':area, 'pop':pop})
>>> data
area pop
California 423967 38332521
Texas 695662 26448193
New York 141297 19651127
Florida 170312 19552860
Illinois 149995 12882135
两个Series分别构成DataFrame的一列,可以通过对列名进行字典形式的取值来获取数据:
>>> data['area']
California 423967
Texas 695662
New York 141297
Florida 170312
Illinois 149995
Name: area, dtype: int64
对于字符串形式的列名,可以采用属性形式:
>>> data.area
California 423967
Texas 695662
New York 141297
Florida 170312
Illinois 149995
Name: area, dtype: int64
对同一个对象进行属性形式与字典形式获取的列数据,结果是相同的:
>>> data.area is data['area']
True
如果列名不是纯字符串,或者列名与DataFrame的方法同名,那么就不能用属性索引。
和Sries一样可以用字典形式的语法调整对象,比如增加一列:
>>> data['density'] = data['pop'] / data['area']
>>> data
area pop density
California 423967 38332521 90.413926
Texas 695662 26448193 38.018740
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763
2.将DataFrame看做二维数组
用values属性按行查看数组数据:
>>> data
area pop density
California 423967 38332521 90.413926
Texas 695662 26448193 38.018740
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763
>>> data.values
array([[4.23967000e+05, 3.83325210e+07, 9.04139261e+01],
[6.95662000e+05, 2.64481930e+07, 3.80187404e+01],
[1.41297000e+05, 1.96511270e+07, 1.39076746e+02],
[1.70312000e+05, 1.95528600e+07, 1.14806121e+02],
[1.49995000e+05, 1.28821350e+07, 8.58837628e+01]])
可以对DataFrame进行转置:
>>> data
area pop density
California 423967 38332521 90.413926
Texas 695662 26448193 38.018740
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763
>>> data.T
California Texas New York Florida Illinois
area 4.239670e+05 6.956620e+05 1.412970e+05 1.703120e+05 1.499950e+05
pop 3.833252e+07 2.644819e+07 1.965113e+07 1.955286e+07 1.288214e+07
density 9.041393e+01 3.801874e+01 1.390767e+02 1.148061e+02 8.588376e+01
通过字典形式对列进行取值显然会限制我们把DataFrame作为NumPy数组可以获得的能力,尤其当我们在DataFrame数组中使用单个行索引获取一行数据时:
>>> data.values[0]
array([4.23967000e+05, 3.83325210e+07, 9.04139261e+01])
>>> # 而获取一列数据就需要向DataFrame传递单个列索引
>>> data['area']
California 423967
Texas 695662
New York 141297
Florida 170312
Illinois 149995
Name: area, dtype: int64
因此,在进行数组形式的取值时,我们需要用另一种方法——前面介绍过的Pandas索引器loc、iloc和lx了。通过iloc索引器,我们可以像对待NumPy数组一样索引Pandas的底层数组,DataFrame的行列标签会自动保留在结果中:
>>> data
area pop density
California 423967 38332521 90.413926
Texas 695662 26448193 38.018740
New York 141297 19651127 139.076746
Florida 170312 19552860 114.806121
Illinois 149995 12882135 85.883763
>>> data.iloc[:3, :2]
area pop
California 423967 38332521
Texas 695662 26448193
New York 141297 19651127
>>> data.loc[:'Illinois', :'pop']
area pop
California 423967 38332521
Texas 695662 26448193
New York 141297 19651127
Florida 170312 19552860
Illinois 149995 12882135
使用ix索引器可以实现一种混合效果:
>>> data.ix[:3, :'pop']
area pop
California 423967 38332521
Texas 695662 26448193
New York 141297 19651127
任何用于处理NumPy形式数据的方法都可以用于这些索引器。例如,在loc中结合使用掩码和花俏的索引方法:
>>> data.loc[data.density > 100, ['pop', 'density']]
pop density
New York 19651127 139.076746
Florida 19552860 114.806121
任何一种取值方法都可以用于调整数据,这一点和NumPy相同:
>>> data.iloc[0, 2] = 90
>>> data
area pop density
California 423967 38332521 90.000000
Texas 695662 26448193 38.01874