Pandas对象介绍
在基础的层面上,Pandas对象可以被认为是NumPy结构化数组的增强版本,在Pandas中行和列是由标签标识的而不是简单的整型索引。如我们在本章的课程中所见,Pandas在基本的数据结构上提供了许多有用的工具,方法和功能,但是所有的这些方法工具要求我们理解这些结构是什么样的。因此,在我们继续前进之前,让我们介绍Pandas的基本数据结构:Series
, DataFrame
和Index
。
使用标准的NumPy和Pandas导入来开始我们的代码阶段。
import numpy as np
import pandas as pd
Pandas Series 对象
Pandas Series 是带有索引数据的一维数组。它可以由列表或数组创建,例子如下:
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
然而,正如我们将看到的,Pandas的Series比它所模拟的一维NumPy更通用,更灵活。
Series作为广义的NumPy 数组
就我们目前所见而言,好像看起来,Series对象基本上可以和NumPy的一维数组可以互换。但关键的不同在于index的表达: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['b']
0.5
我们甚至可以使用非连续索引:
data = pd.Series([0.25, 0.5, 0.75, 1.0],
index=[2, 5, 3, 7])
data
2 0.25
5 0.50
3 0.75
7 1.00
dtype: float64
data[5]
0.5
Series 作为特殊的字典
用这种方式,你可以认为Pandas Series有点像特殊化的字典。字典是可以映射任意键到任意值集合的结构,而Series是映射类型化的值到类型化值集合的结构。这种类型化是重要的:正如NumPy数组中特定类型的编译代码是它比Python列表的某些操作快一样,Pandas Series的类型信息使它比Python字典的某些操作更高效。
population_dict = {'California': 38332521,
'Texas': 26448193,
'New York': 19651127,
'Florida': 19552860,
'Illinois': 12882135}
population = pd.Series(population_dict)
population
California 38332521
Florida 19552860
Illinois 12882135
New York 19651127
Texas 26448193
dtype: int64
默认情况下,Series的索引由排序的键所创建。在这里,典型的字典风格操作也可以被执行:
population['California']
38332521
然而,与字典不同,Series也支持数组风格的操作,例如切片:
population['California':'Illinois']
California 38332521
Florida 19552860
Illinois 12882135
dtype: int64
我们会在Data Indexing and Selection.中讨论Pandas的索引和切片的一些技巧。
构建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可以是标量,它被复制与索引的数量相对应:
pd.Series(5, index=[100, 200, 300])
100 5
200 5
300 5
dtype: int64
data可以是字典,默认的index是排序后的字典键值
pd.Series({2:'a', 1:'b', 3:'c'})
1 b
2 a
3 c
dtype: object
在每种情况下,如果期待不同的结果,都可以明确的设置索引:
pd.Series({2:'a', 1:'b', 3:'c'}, index=[3, 2])
3 c
2 a
dtype: object
注意在这个例子中,Series只是显示了明确定义了键值的内容。
Pandas 的DataFrame对象
Pandas中下一个基本结构是DataFrame。如同我们在前面章节讨论的Series对象一样,DataFrame可以被认为是通用化的NumPy数组或特殊化了的python字典。我们现在来看一下这些不同的观点。
DataFrame 作为广义的NumPy 数组
如果将Series类比于带有灵活索引的一维数组,那么DataFrame就类似于带有灵活行索引和列索引的二维数组。正如你可能认为二维数组是整齐排列的一维列序列一样,你可能认为DataFrame是整齐排列的Series对象序列。这里,‘整齐’的意思是指它们共享同样的索引。
作为说明,我们来构造一个新的Series列出前面例子中五个州的面积:
area_dict = {'California': 423967, 'Texas': 695662, 'New York': 141297,
'Florida': 170312, 'Illinois': 149995}
area = pd.Series(area_dict)
area
California 423967
Florida 170312
Illinois 149995
New York 141297
Texas 695662
dtype: int64
现在我们有area Series以及之前的population Series,我们可以使用字典来构造出一个单独的二维对象用以包那些信息
states = pd.DataFrame({'population': population,
'area': area})
states
area population
California 423967 38332521
Florida 170312 19552860
Illinois 149995 12882135
New York 141297 19651127
Texas 695662 26448193
与Series对象相似,DataFrame有index属性用来访问索引标识
states.index
Index(['California', 'Florida', 'Illinois', 'New York', 'Texas'], dtype='object')
另外,DataFrame有columns属性,它是一个保存列标识的index对象:
states.columns
Index(['area', 'population'], dtype='object')
因此DataFrame可以被认为是广义的二维NumPy数组,在这里可以使用广义的行索引和列索引来访问数据。
DataFrame作为特殊的字典
类似的,我们也可以认为DataFrame是一种特殊的字典。字典映射键到值,DataFrame映射一个列名字到一个Series类型的列数据。例如,请求‘area’属性返回包含面积信息的Series对象
states['area']
California 423967
Florida 170312
Illinois 149995
New York 141297
Texas 695662
Name: area, dtype: int64
注意这里潜在的困惑点:在一个二维NumPy数组中,data[0]返回的是第一行。对于一个DataFrame,data['col0']将会返回第一列。正因如此,可能认为DataFrame是广义的字典比认为是广义的数组会好些,尽管两种理解方式都是有用的。我们将会探索更多灵活的DataFrame索引方式在Data Indexing and Selection.
构建DataFrame对象
有好几种方式用来构建Pandas DataFrame。这里举几个例子:
通过单独的Series对象生成DataFrame
DateFrame是Series对象的集合,一个单列的DataFrame可以由一个独立的Series对象生成:
pd.DataFrame(population, columns=['population'])
population
California 38332521
Florida 19552860
Illinois 12882135
New York 19651127
Texas 26448193
通过字典列表生成DataFrame
任何字典列表都能制成DataFrame。我们使用列表解析来创建一些数据:
data = [{'a': i, 'b': 2 * i}
for i in range(3)]
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
通过Serie对象字典生成DataFrame
如我们前面见到的,DataFrame也可以通过Series对象字典来构造:
pd.DataFrame({'population': population,
'area': area})
通过二维NumPy数组
通过一组二维数组数据,我们可以创建任何指定的列和索引名称的DataFrame。如果选择忽略,列和索引都将是整数:
pd.DataFrame(np.random.rand(3, 2),
columns=['foo', 'bar'],
index=['a', 'b', 'c'])
foo bar
a 0.865257 0.213169
b 0.442759 0.108267
c 0.047110 0.905718
通过NumPy结构化数组
我们在Structured Data: NumPy's Structured Arrays.会覆盖结构化数组内容。Pandas的DataFrame操作非常像结构数组,并且可以直接从结构数组来创建:
A = np.zeros(3, dtype=[('A', 'i8'), ('B', 'f8')])
A
array([(0, 0.0), (0, 0.0), (0, 0.0)],
dtype=[('A', '<i8'), ('B', '<f8')])
pd.DataFrame(A)
A B
0 0 0.0
1 0 0.0
2 0 0.0
Pandas Index对象
我们已经看到Series和DataFrame对象都包含一个显式索引,使您可以引用和修改数据。Index对象本身是一个有趣的数据结构,它可以被认为是一个不可变数组或一个有序集合(技术上讲是多集合,因为Index对象可以包含重复的值)。这些观点在Index对象可用的操作中有一些有趣的结果。作为一个简单的例子,让我们从整数列表来构建Index
ind = pd.Index([2, 3, 5, 7, 11])
ind
Int64Index([2, 3, 5, 7, 11], dtype='int64')
index 作为不可变数组
Index在很多方面就像一个数组。例如,我们能使用标准的python索引符号来获取值或切片:
ind[1]
3
ind[::2]
Int64Index([2, 5, 11], dtype='int64')
Index对象也由许多属性与NumPy数组相似:
print(ind.size, ind.shape, ind.ndim, ind.dtype)
5 (5,) 1 int64
Index对象和NumPy数组间的一个区别是索引是不可变的,它们不能通过正常的手段进行修改:
ind[1] = 0
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-34-40e631c82e8a> in <module>()
----> 1 ind[1] = 0
/Users/jakevdp/anaconda/lib/python3.5/site-packages/pandas/indexes/base.py in __setitem__(self, key, value)
1243
1244 def __setitem__(self, key, value):
-> 1245 raise TypeError("Index does not support mutable operations")
1246
1247 def __getitem__(self, key):
TypeError: Index does not support mutable operations
这种不可变性使在多个DataFrames和数组间共享索引更加安全,没有潜在的索引被不经意修改的副作用。
index作为有序集合
Pandas对象被设计为便于如跨数据集的连接那样的操作,这些操作依赖集合计算的许多方面。Index对象遵循许多python内置set数据结构的约定,因此交集,并集,差集和其他组合可以以相似的方法计算:
indA = pd.Index([1, 3, 5, 7, 9])
indB = pd.Index([2, 3, 5, 7, 11])
indA & indB # intersection 交集
Int64Index([3, 5, 7], dtype='int64')
indA | indB # union 合并
Int64Index([1, 2, 3, 5, 7, 9, 11], dtype='int64')
indA ^ indB # symmetric difference 差集
Int64Index([1, 2, 9, 11], dtype='int64')
这些操作也可以通过对象方法来实现,例如indA.intersection(indB).