import pandas as pd
import numpy as np
前言
-
本文重点关注Pandas的实际用法,而不是理论基础
-
本文面向新手,欢迎讨论
-
Pandas建立在NumPy上,可以把它们看成增强版的 NumPy 结构化数组,为数据处理提供了基于NumPy且超越NumPy的大量功能
-
本文介绍Pandas提供的几种基本数据结构
Series:带显式定义索引的数组
-
普通的列表或numpy ndarray的索引是隐式定义的,即对于列表
a = [10, 20, 30, 40]
,显然,隐式定义的索引1, 2, 3, 4
分别与[10, 20, 30, 40]的元素相对应 -
但Series的索引可以显式定义
-
从下面的例子可以发现,Series像一个NumPy数组,又像一个字典
x = pd.Series([10, 20, 30, 40])
x # 索引与对象相互绑定
0 10
1 20
2 30
3 40
dtype: int64
print(
x.values,
x.index,
sep = '\n'
)
[10 20 30 40]
RangeIndex(start=0, stop=4, step=1)
y = pd.Series(
[1, 2, 3, 4],
index=['a', 'b', 'c', 'd']
)
y
a 1
b 2
c 3
d 4
dtype: int64
y['c']
3
my_dict = {
'a': 0,
'b': 1
}
y = pd.Series(my_dict)
y
a 0
b 1
dtype: int64
- 创建Series的其它方法
DataFrame:更高级的Series
- 如果将 Series 类比为带灵活索引的一维数组,那么 DataFrame 就可以看作是一种既有灵活的行索引,又有灵活列索引的二维数组
y
a 0
b 1
dtype: int64
x = {'a' : 10, 'b' : 20}
x = pd.Series(x)
x
a 10
b 20
dtype: int64
z1 = pd.DataFrame(
data = [x, y],
index = ['x', 'y']
)
z1
a | b | |
---|---|---|
x | 10 | 20 |
y | 0 | 1 |
z = pd.DataFrame({
'x': x,
'y': y
})
z
# 注意区分上面两种构造DataFrame的方式
x | y | |
---|---|---|
a | 10 | 0 |
b | 20 | 1 |
print(
z1.index,
z.index,
sep = '\n'
)
Index(['x', 'y'], dtype='object')
Index(['a', 'b'], dtype='object')
print(
z1.columns,
z.columns,
sep = '\n'
)
Index(['a', 'b'], dtype='object')
Index(['x', 'y'], dtype='object')
-
如何理解上面两种构造方式的差异?
-
pd.DataFrame
的函数原型:pd.DataFrame(data=None, index=None, columns=None, dtype=None, copy=False)
-
根据上面的测试可知,DataFrame的标签是行标签。
-
首先可以这么理解:Series是带标签的一维数组,即每个标签只对应于一个数据块;而DataFrame是带标签的二维数组,每个标签都可以对应于多个数据块,且每个数据块都可以有自带标签。
-
其次,考察上面的案例。
-
z1的构造本质上是指明了data和index参数,而data指示每一个标签(行标签)对应的数据块(一行),采用列表的方式将指定列表元素为行。
-
z的构造其实是
z = pd.DataFrame(data = {'x': x, 'y': y})
,没有指示index参数。这里是指定每一个标签(行标签)对应的数据块(一行)为一个字典。采用字典不会指定字典元素为行,而是将字典整体(整个数据块)作为一行
注意,这里x y本质上是series
z
x | y | |
---|---|---|
a | 10 | 0 |
b | 20 | 1 |
z['x']['a']
# 列索引优先
10
-
列优先是很少见的,因此一般不要把DataFrame看作一种二维数组,而应当看作一种多维字典
-
这种字典是每个key映射一个Series,Series作为一个value。
-
这很有趣,在前面讨论Series时,核心概念在index上,是index和value绑定,因此一般把Series看作一个动态指定index的数组。而DataFrame不是,它的核心概念在于字典的key上,具体而言就是其列索引
-
因此,上面展示的DataFrame的index本质上不是它的index,而是它内部存储的Series的index。它的列索引(key)才是核心概念
-
这样,对于上面构造z和z1两种不同的结果又有了新的理解
-
我们再看下构造过程:
z = pd.DataFrame({
'x': x,
'y': y
})
z1 = pd.DataFrame(
data = [x, y],
index = ['x', 'y']
)
z
x | y | |
---|---|---|
a | 10 | 0 |
b | 20 | 1 |
z1
a | b | |
---|---|---|
x | 10 | 20 |
y | 0 | 1 |
-
z将DataFrame的data域用字典指定,根据上述的理解,自然列标签就应当是字典中的两个key,分别对应于两个Series,行标签本质上是各个Series的index
-
z1将data域用列表指定,可以理解为每个data都是这样一个列表
[x, y]
(这样一个列表被视为一个Series) -
更推荐将DataFrame视为字典而不是二维列表,故这里更推荐z的初始化方法,更直观
-
综上,我们将Series视为动态指定标签的列表,DataFrame视为Series的集合
-
创建DataFrame的其它方法:
Index对象
- 在某种意义上,Index对象可以看作不可变的NumPy数组。除了修改值以外,许多基于数组的操作(如切片等)都可以完成
ind = pd.Index([i for i in range(5, 1, -1)])
ind
Int64Index([5, 4, 3, 2], dtype='int64')
ind[1] = 2
# 报错:Index does not support mutable operations
- 更推荐的是将Index当作一个集合来用,Index支持许多常规的集合运算
indA = pd.Index([i for i in range(1, 10, 1)])
indB = pd.Index([i for i in range(6, 15, 1)])
print(
indA,
indB,
sep = '\n'
)
Int64Index([1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')
Int64Index([6, 7, 8, 9, 10, 11, 12, 13, 14], dtype='int64')
indA & indB
Int64Index([6, 7, 8, 9], dtype='int64')
indA | indB
Int64Index([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], dtype='int64')
indA ^ indB
Int64Index([1, 2, 3, 4, 5, 10, 11, 12, 13, 14], dtype='int64')