The text is released under the CC-BY-NC-ND license, and code is released under the MIT license. If you find this content useful, please consider supporting the work by buying the book!
从实现的层面讲,Pandas 对象可以被认为是 NumPy 结构化数组的增强版本,其中行和列用标签而不是整数索引来标识。正如我们将在本章中所看到的,Pandas 在基本数据结构之上提供了大量工具、方法和功能,但是几乎所有内容都需要你理解其所使用的基础结构是什么样子的。因此,我们进一步讨论额外的功能时,先介绍 Pandas 的三个基本数据结构:Series
,DataFrame
和 Index
。
At the very basic level, Pandas objects can be thought of as enhanced versions of NumPy structured arrays in which the rows and columns are identified with labels rather than simple integer indices.
As we will see during the course of this chapter, Pandas provides a host of useful tools, methods, and functionality on top of the basic data structures, but nearly everything that follows will require an understanding of what these structures are.
Thus, before we go any further, let's introduce these three fundamental Pandas data structures: the Series
, DataFrame
, and Index
.
在开始本节之前,我们先引入 NumPy 与 Pandas:
We will start our code sessions with the standard NumPy and Pandas imports:
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 数组:
As we see in the output, the Series
wraps both a sequence of values and a sequence of indices, which we can access with the values
and index
attributes.
The values
are simply a familiar NumPy array:
data.values
array([ 0.25, 0.5 , 0.75, 1. ])
而 index
则是一个类似于数组的 pd.Index
,我们稍后会对其进行讨论。
The index
is an array-like object of type pd.Index
, which we'll discuss in more detail momentarily.
data.index
RangeIndex(start=0, stop=4, step=1)
和 NumPy 类似,Series
中的数据也可以通过 Python 中的方括号表示法的方式进行访问:
Like with a NumPy array, data can be accessed by the associated index via the familiar Python square-bracket notation:
data[1]
0.5
data[1:3]
1 0.50 2 0.75 dtype: float64
不过后面我们将会看到 Series
要比 NumPy 的一维数组要灵活的多,通用的多。
As we will see, though, the Pandas Series
is much more general and flexible than the one-dimensional NumPy array that it emulates.
到目前为止,Series
基本和 NumPy 的一维数组基本上是一样的。而它们本质区别在于索引:NumPy 数组包含一个隐式声明的整数索引,而 Pandas Series
具有显式声明的索引。
From what we've seen so far, it may look like the Series
object is basically interchangeable with a one-dimensional NumPy array.
The essential difference is the presence of the index: while the Numpy Array has an implicitly defined integer index used to access the values, the Pandas Series
has an explicitly defined index associated with the values.
其显式索引为 Series
提供了额外的特性。首先,其索引不一定是整数,可是是其他任意类型。比如,我们可以用字符串作为索引:
This explicit index definition gives the Series
object additional capabilities. For example, the index need not be an integer, but can consist of values of any desired type.
For example, if we wish, we can use strings as an index:
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
然后就可以像这样来访问数据了:
And the item access works as expected:
data['b']
0.5
我们甚至可以用不连续的数值来定义索引:
We can even use non-contiguous or non-sequential indices:
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
当做一种专用的 Python 字典。字典是一个包含任意类型键值对的存储结构,Series
则将一个特定类型的键与特定类型的值建立了映射。
在这里“特定类型”是非常关键的:就像 NumPy 数组通过固定类型使得它比某些操作 Python 列表更有快一样,一个固定类型的 Pandas Series
使得它更比 Python 字典对于某些操作更有快。
In this way, you can think of a Pandas Series
a bit like a specialization of a Python dictionary.
A dictionary is a structure that maps arbitrary keys to a set of arbitrary values, and a Series
is a structure which maps typed keys to a set of typed values.
This typing is important: just as the type-specific compiled code behind a NumPy array makes it more efficient than a Python list for certain operations, the type information of a Pandas Series
makes it much more efficient than Python dictionaries for certain operations.
Series
作为字典的隐喻在我们看到其可以通过一个字典创建的时候显得更加明确了:
The Series
-as-dictionary analogy can be made even more clear by constructing a Series
object directly from a Python dictionary:
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
默认按照排序后的键的顺序创建索引,其支持类似于字典风格的访问方式:
By default, a Series
will be created where the index is drawn from the sorted keys.
From here, typical dictionary-style item access can be performed:
population['California']
38332521
但是和字典不同 Series
支持数组那样的切片操作:
Unlike a dictionary, though, the Series
also supports array-style operations such as slicing:
population['California':'Illinois']
California 38332521 Florida 19552860 Illinois 12882135 dtype: int64
我们会在 数据索引与数据筛选 介绍更多有关 Pandas 索引与切片的内容。
We'll discuss some of the quirks of Pandas indexing and slicing in Data Indexing and Selection.
我们已经看到了几个创建 Series
的方法,它们差不多都是下面这种方式的不同版本而已:
We've already seen a few ways of constructing a Pandas Series
from scratch; all of them are some version of the following:
>>> pd.Series(data, index=index)
其中 index
是可选的,data
则可以支持多种类型。比如 data
可以是 list 也可以是 NumPy 数组,这时候其 index
就是默认的整数序列:
where index
is an optional argument, and data
can be one of many entities.
For example, data
can be a list or NumPy array, in which case index
defaults to an integer sequence:
pd.Series([2, 4, 6])
0 2 1 4 2 6 dtype: int64
data
也可以是一个标量,表示多个重复的值:
data
can be a scalar, which is repeated to fill the specified index:
pd.Series(5, index=[100, 200, 300])
100 5 200 5 300 5 dtype: int64
data
也可以是字典,其中 index
就是排序后的字典键:
data
can be a dictionary, in which index
defaults to the sorted dictionary keys:
pd.Series({2:'a', 1:'b', 3:'c'})
1 b 2 a 3 c dtype: object
当然,以上的情况里,索引也可以按照其他的方式显式的定义:
In each case, the index can be explicitly set if a different result is preferred:
pd.Series({2:'a', 1:'b', 3:'c'}, index=[3, 2])
3 c 2 a dtype: object
可以看到这里 Series
之后按照显式声明的索引创建:
Notice that in this case, the Series
is populated only with the explicitly identified keys.
下一个重要的基础结构是 DataFrame
。和 Series
类似,DataFrame
可以被认为是一个泛化版本的 NumPy 数组或者是一个特殊的 Python 字典。我们会从这两个视角出发对其进行讨论。
The next fundamental structure in Pandas is the DataFrame
.
Like the Series
object discussed in the previous section, the DataFrame
can be thought of either as a generalization of a NumPy array, or as a specialization of a Python dictionary.
We'll now take a look at each of these perspectives.
如果 Series
是一个包含灵活索引的一维数组,那么 DataFrame
就是一个既有行索引又有列索引的二维数组。如果你把二维数组理解为是一组有顺序又对齐的一维列,那么 DataFrame
就像是一组有顺序又对齐的 Series
。这里,“对齐”是指它们共用同一个索引。
If a Series
is an analog of a one-dimensional array with flexible indices, a DataFrame
is an analog of a two-dimensional array with both flexible row indices and flexible column names.
Just as you might think of a two-dimensional array as an ordered sequence of aligned one-dimensional columns, you can think of a DataFrame
as a sequence of aligned Series
objects.
Here, by "aligned" we mean that they share the same index.
为了说明这种类比,在这里我们创建一个新的包含上面提到的五个州的 Series
:
To demonstrate this, let's first construct a new Series
listing the area of each of the five states discussed in the previous section:
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
和之前创建的 population
一起,我们以字典的形式创建一个包含 area
和 population
的 DataFrame:
Now that we have this along with the population
Series from before, we can use a dictionary to construct a single two-dimensional object containing this information:
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
属性:
Like the Series
object, the DataFrame
has an index
attribute that gives access to the index labels:
states.index
Index([u'California', u'Florida', u'Illinois', u'New York', u'Texas'], dtype='object')
DataFrame
还包含一个 columns
属性,相当于列索引:
Additionally, the DataFrame
has a columns
attribute, which is an Index
object holding the column labels:
states.columns
Index([u'area', u'population'], dtype='object')
这么来看,我们就可以认为 DataFrame
就是一个包含了行索引与列索引的泛化版本的 NumPy 二维数组。
Thus the DataFrame
can be thought of as a generalization of a two-dimensional NumPy array, where both the rows and columns have a generalized index for accessing the data.
和以上的思路类似,字典将一个键映射给一个值,而 DataFrame
将一个列映射给一个 Series
。比如当我们想要获取 'area'
属性,DataFrame
会返回一个包含面积信息的 Series
:
Similarly, we can also think of a DataFrame
as a specialization of a dictionary.
Where a dictionary maps a key to a value, a DataFrame
maps a column name to a Series
of column data.
For example, asking for the 'area'
attribute returns the Series
object containing the areas we saw earlier:
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
索引的内容。
Notice the potential point of confusion here: in a two-dimesnional NumPy array, data[0]
will return the first row. For a DataFrame
, data['col0']
will return the first column.
Because of this, it is probably better to think about DataFrame
s as generalized dictionaries rather than generalized arrays, though both ways of looking at the situation can be useful.
We'll explore more flexible means of indexing DataFrame
s in Data Indexing and Selection.
pd.DataFrame(population, columns=['population'])
population | |
---|---|
California | 38332521 |
Florida | 19552860 |
Illinois | 12882135 |
New York | 19651127 |
Texas | 26448193 |
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
("not a number")填充:
Even if some keys in the dictionary are missing, Pandas will fill them in with NaN
(i.e., "not a number") values:
pd.DataFrame([{'a': 1, 'b': 2}, {'b': 3, 'c': 4}])
a | b | c | |
---|---|---|---|
0 | 1.0 | 2 | NaN |
1 | NaN | 3 | 4.0 |
pd.DataFrame({'population': population,
'area': area})
area | population | |
---|---|---|
California | 423967 | 38332521 |
Florida | 170312 | 19552860 |
Illinois | 149995 | 12882135 |
New York | 141297 | 19651127 |
Texas | 695662 | 26448193 |
pd.DataFrame(np.random.rand(3, 2),
columns=['foo', 'bar'],
index=['a', 'b', 'c'])
foo | bar | |
---|---|---|
a | 0.926006 | 0.850672 |
b | 0.137581 | 0.985609 |
c | 0.646509 | 0.329154 |
在结构化数据:NumPy 的结构化数组中我们介绍了结构化数组。DataFrame
和结构化数组非常类似,同时我们也可以用一个结构化数组创建 DataFrame
:
We covered structured arrays in Structured Data: NumPy's Structured Arrays.
A Pandas DataFrame
operates much like a structured array, and can be created directly from one:
A = np.zeros(3, dtype=[('A', 'i8'), ('B', 'f8')])
A
array([(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 |
Index
对象¶我们已经看到在 Series
和 DataFrame
中都包含一个默认的 index 用于数据的访问与操作。Index
对象本身可以被认为是一个只读数组或者是有序集合(实际上是一个可重复集合,因为 Index
可以包含重复值)。对 Index
不同观点也包含了不同的操作方式。
首先我们创建一个整型索引:
We have seen here that both the Series
and DataFrame
objects contain an explicit index that lets you reference and modify data.
This Index
object is an interesting structure in itself, and it can be thought of either as an immutable array or as an ordered set (technically a multi-set, as Index
objects may contain repeated values).
Those views have some interesting consequences in the operations available on Index
objects.
As a simple example, let's construct an Index
from a list of integers:
ind = pd.Index([2, 3, 5, 7, 11])
ind
Int64Index([2, 3, 5, 7, 11], dtype='int64')
ind[1]
3
ind[::2]
Int64Index([2, 5, 11], dtype='int64')
Index
对象也包含 NumPy 数组的很多属性:
Index
objects also have many of the attributes familiar from NumPy arrays:
print(ind.size, ind.shape, ind.ndim, ind.dtype)
(5, (5,), 1, dtype('int64'))
Index
与 NumPy 数组有一点区别,其内容是只读的,它们不能通过常见的方法进行修改:
One difference between Index
objects and NumPy arrays is that indices are immutable–that is, they cannot be modified via the normal means:
ind[1] = 0
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-31-40e631c82e8a> in <module>() ----> 1 ind[1] = 0 /usr/local/lib/python2.7/site-packages/pandas/indexes/base.pyc in __setitem__(self, key, value) 1402 1403 def __setitem__(self, key, value): -> 1404 raise TypeError("Index does not support mutable operations") 1405 1406 def __getitem__(self, key): TypeError: Index does not support mutable operations
其只读的特性使其在作为 DataFrame
的索引时不会被一些操作意外的修改。
This immutability makes it safer to share indices between multiple DataFrame
s and arrays, without the potential for side effects from inadvertent index modification.
Pandas 对象可以支持数据集合之间的链接(join),这会涉及很多集合操作。Index
遵循了很多 Python 内建 set
的规约,yin'ci
Pandas objects are designed to facilitate operations such as joins across datasets, which depend on many aspects of set arithmetic.
The Index
object follows many of the conventions used by Python's built-in set
data structure, so that unions, intersections, differences, and other combinations can be computed in a familiar way:
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')
These operations may also be accessed via object methods, for example indA.intersection(indB)
.