本节介绍 Pandas 数据结构的基础用法。下列代码创建上一节用过的示例数据对象:
In [1]: index = pd.date_range('1/1/2000', periods=8)
In [2]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
In [3]: df = pd.DataFrame(np.random.randn(8, 3), index=index,
...: columns=['A', 'B', 'C'])
...:
n=10
index=pd.date_range('20000101',periods=n)
print(index)
s=pd.Series(np.random.rand(8),index=list(string.ascii_letters[:8]))
# 这是一个方便用户从 Matlab 移植代码的函数,并包装了 random_sample。该函数采用元组来指定输出的大小,这与 numpy.zeros 和 numpy.ones 等其他 NumPy 函数一致。
# 创建给定形状的数组,并用 [0, 1) 上均匀分布的随机样本填充它。
print(s)
df=pd.DataFrame(np.random.rand(n,8),index=index,columns=list(string.ascii_letters[:8]))
print(df)
DatetimeIndex(['2000-01-01', '2000-01-02', '2000-01-03', '2000-01-04',
'2000-01-05', '2000-01-06', '2000-01-07', '2000-01-08',
'2000-01-09', '2000-01-10'],
dtype='datetime64[ns]', freq='D')
a 0.516861
b 0.220140
c 0.667483
d 0.550804
e 0.631757
f 0.494191
g 0.908128
h 0.544240
dtype: float64
a b c d e f \
2000-01-01 0.951364 0.165370 0.630935 0.898385 0.040457 0.846490
2000-01-02 0.636938 0.081687 0.899861 0.887537 0.427308 0.678841
2000-01-03 0.954679 0.590853 0.779015 0.758092 0.318983 0.797294
2000-01-04 0.000130 0.314111 0.346645 0.382747 0.644762 0.215349
2000-01-05 0.150452 0.720875 0.363832 0.954863 0.298944 0.880833
2000-01-06 0.006456 0.399737 0.081659 0.310611 0.495450 0.466368
2000-01-07 0.386115 0.415283 0.238122 0.993131 0.114366 0.098060
2000-01-08 0.325408 0.339976 0.122992 0.035576 0.910130 0.398590
2000-01-09 0.780130 0.928054 0.853599 0.879124 0.095143 0.117855
2000-01-10 0.764207 0.449446 0.367501 0.709571 0.872381 0.210814
g h
2000-01-01 0.893280 0.336661
2000-01-02 0.182542 0.554913
2000-01-03 0.470463 0.322261
2000-01-04 0.513242 0.928213
2000-01-05 0.772144 0.953689
2000-01-06 0.404961 0.624106
2000-01-07 0.414454 0.517201
2000-01-08 0.198384 0.800897
2000-01-09 0.132946 0.581366
2000-01-10 0.578434 0.481795
head()
open in new window 与 tail()
open in new window 用于快速预览 Series 与 DataFrame,默认显示 5 条数据,也可以指定显示数据的数量。
In [4]: long_series = pd.Series(np.random.randn(1000))
In [5]: long_series.head()
Out[5]:
0 -1.157892
1 -1.344312
2 0.844885
3 1.075770
4 -0.109050
dtype: float64
In [6]: long_series.tail(3)
Out[6]:
997 -0.289388
998 -1.020544
999 0.589993
dtype: float64
Pandas 可以通过多个属性访问元数据:
注意: 为属性赋值是安全的!
In [7]: df[:2]
Out[7]:
A B C
2000-01-01 -0.173215 0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929
In [8]: df.columns = [x.lower() for x in df.columns]
In [9]: df
Out[9]:
a b c
2000-01-01 -0.173215 0.119209 -1.044236
2000-01-02 -0.861849 -2.104569 -0.494929
2000-01-03 1.071804 0.721555 -0.706771
2000-01-04 -1.039575 0.271860 -0.424972
2000-01-05 0.567020 0.276232 -1.087401
2000-01-06 -0.673690 0.113648 -1.478427
2000-01-07 0.524988 0.404705 0.577046
2000-01-08 -1.715002 -1.039268 -0.370647
Pandas 对象(Index
open in new window, Series
open in new window, DataFrame
open in new window)相当于数组的容器,用于存储数据、执行计算。大部分类型的底层数组都是 numpy.ndarray
open in new window。不过,Pandas 与第三方支持库一般都会扩展 NumPy 类型系统,添加自定义数组(见数据类型open in new window)。
.array
属性用于提取 Index
open in new window 或 Series
open in new window 里的数据。
In [10]: s.array
Out[10]:
<PandasArray>
[ 0.4691122999071863, -0.2828633443286633, -1.5090585031735124,
-1.1356323710171934, 1.2121120250208506]
Length: 5, dtype: float64
In [11]: s.index.array
Out[11]:
<PandasArray>
['a', 'b', 'c', 'd', 'e']
Length: 5, dtype: object
array
open in new window 一般指 ExtensionArray
open in new window。至于什么是 ExtensionArray
open in new window 及 Pandas 为什么要用 ExtensionArray
open in new window 不是本节要说明的内容。更多信息请参阅数据类型open in new window。
提取 NumPy 数组,用 to_numpy()
open in new window 或 numpy.asarray()
。
In [12]: s.to_numpy()
Out[12]: array([ 0.4691, -0.2829, -1.5091, -1.1356, 1.2121])
In [13]: np.asarray(s)
Out[13]: array([ 0.4691, -0.2829, -1.5091, -1.1356, 1.2121])
Series
与 Index
的类型是 ExtensionArray
open in new window 时, to_numpy()
open in new window 会复制数据,并强制转换值。详情见数据类型open in new window。
to_numpy()
open in new window 可以控制 numpy.ndarray
open in new window 生成的数据类型。以带时区的 datetime 为例,NumPy 未提供时区信息的 datetime 数据类型,Pandas 则提供了两种表现形式:
Timestamp
open in new window 的 numpy.ndarray
open in new window,提供了正确的 tz
信息。datetime64[ns]
,这也是一种 numpy.ndarray
open in new window,值被转换为 UTC,但去掉了时区信息。时区信息可以用 dtype=object
保存。
In [14]: ser = pd.Series(pd.date_range('2000', periods=2, tz="CET"))
In [15]: ser.to_numpy(dtype=object)
Out[15]:
array([Timestamp('2000-01-01 00:00:00+0100', tz='CET', freq='D'),
Timestamp('2000-01-02 00:00:00+0100', tz='CET', freq='D')],
dtype=object)
或用 dtype='datetime64[ns]'
去除。
In [16]: ser.to_numpy(dtype="datetime64[ns]")
Out[16]:
array(['1999-12-31T23:00:00.000000000', '2000-01-01T23:00:00.000000000'],
dtype='datetime64[ns]')
提取 DataFrame
里的原数据稍微有点复杂。DataFrame 里所有列的数据类型都一样时,DataFrame.to_numpy()
open in new window 返回底层数据:
In [17]: df.to_numpy()
Out[17]:
array([[-0.1732, 0.1192, -1.0442],
[-0.8618, -2.1046, -0.4949],
[ 1.0718, 0.7216, -0.7068],
[-1.0396, 0.2719, -0.425 ],
[ 0.567 , 0.2762, -1.0874],
[-0.6737, 0.1136, -1.4784],
[ 0.525 , 0.4047, 0.577 ],
[-1.715 , -1.0393, -0.3706]])
DataFrame 为同构型数据时,Pandas 直接修改原始 ndarray
,所做修改会直接反应在数据结构里。对于异质型数据,即 DataFrame 列的数据类型不一样时,就不是这种操作模式了。与轴标签不同,不能为值的属性赋值。
注意
处理异质型数据时,输出结果 ndarray
的数据类型适用于涉及的各类数据。若 DataFrame 里包含字符串,输出结果的数据类型就是 object
。要是只有浮点数或整数,则输出结果的数据类型是浮点数。
以前,Pandas 推荐用 Series.values
open in new window 或 DataFrame.values
open in new window 从 Series 或 DataFrame 里提取数据。旧有代码库或在线教程里仍在用这种操作,但 Pandas 已改进了此功能,现在,推荐用 .array
或 to_numpy
提取数据,别再用 .values
了。.values
有以下几个缺点:
array
,还是返回 ExtensionArray
。而 Series.array
open in new window 则只返回 ExtensionArray
open in new window,且不会复制数据。Series.to_numpy()
open in new window 则返回 NumPy 数组,其代价是需要复制、并强制转换数据的值。DataFrame.values
open in new window 会复制数据,并将数据的值强制转换同一种数据类型,这是一种代价较高的操作。DataFrame.to_numpy()
open in new window 则返回 NumPy 数组,这种方式更清晰,也不会把 DataFrame 里的数据都当作一种类型。借助 numexpr
与 bottleneck
支持库,Pandas 可以加速特定类型的二进制数值与布尔操作。
处理大型数据集时,这两个支持库特别有用,加速效果也非常明显。 numexpr
使用智能分块、缓存与多核技术。bottleneck
是一组专属 cython 例程,处理含 nans
值的数组时,特别快。
请看下面这个例子(DataFrame
包含 100 列 X 10 万行数据):
操作 | 0.11.0版 (ms) | 旧版 (ms) | 提升比率 |
---|---|---|---|
df1 > df2 |
13.32 | 125.35 | 0.1063 |
df1 * df2 |
21.71 | 36.63 | 0.5928 |
df1 + df2 |
22.04 | 36.50 | 0.6039 |
强烈建议安装这两个支持库,更多信息,请参阅推荐支持库open in new window。
这两个支持库默认为启用状态,可用以下选项设置:
0.20.0 版新增。
pd.set_option('compute.use_bottleneck', False)
pd.set_option('compute.use_numexpr', False)
Pandas 数据结构之间执行二进制操作,要注意下列两个关键点:
这两个问题可以同时处理,但下面先介绍怎么分开处理。
DataFrame 支持 add()
open in new window、sub()
open in new window、mul()
open in new window、div()
open in new window 及 radd()
open in new window、rsub()
open in new window 等方法执行二进制操作。广播机制重点关注输入的 Series。通过 axis
关键字,匹配 index 或 columns 即可调用这些函数。
In [18]: df = pd.DataFrame({
....: 'one': pd.Series(np.random.randn(3), index=['a', 'b', 'c']),
....: 'two': pd.Series(np.random.randn(4), index=['a', 'b', 'c', 'd']),
....: 'three': pd.Series(np.random.randn(3), index=['b', 'c', 'd'])})
....:
In [19]: df
Out[19]:
one two three
a 1.394981 1.772517 NaN
b 0.343054 1.912123 -0.050390
c 0.695246 1.478369 1.227435
d NaN 0.279344 -0.613172
In [20]: row = df.iloc[1]
In [21]: column = df['two']
In [22]: df.sub(row, axis='columns')
Out[22]:
one two three
a 1.051928 -0.139606 NaN
b 0.000000 0.000000 0.000000
c 0.352192 -0.433754 1.277825
d NaN -1.632779 -0.562782
In [23]: df.sub(row, axis=1)
Out[23]:
one two three
a 1.051928 -0.139606 NaN
b 0.000000 0.000000 0.000000
c 0.352192 -0.433754 1.277825
d NaN -1.632779 -0.562782
In [24]: df.sub(column, axis='index')
Out[24]:
one two three
a -0.377535 0.0 NaN
b -1.569069 0.0 -1.962513
c -0.783123 0.0 -0.250933
d NaN 0.0 -0.892516
In [25]: df.sub(column, axis=0)
Out[25]:
one two three
a -0.377535 0.0 NaN
b -1.569069 0.0 -1.962513
c -0.783123 0.0 -0.250933
d NaN 0.0 -0.892516
pandas中iat和iloc都是用于按位置选择数据的方法,二者有些细微的区别:
iat是按整数位置选择单个元素,而iloc是按整数位置选择子集。
iat只能接受整数索引,而iloc可以接受slice对象进行切片。
iat的索引从0开始,iloc的索引从0或1开始取决于索引是否含有0。
例子:
import pandas as pd
df = pd.DataFrame([[1, 2], [3, 4]], columns=list('AB'), index=[0, 1])
# iat选择单个元素
df.iat[0,1] # 2
# iloc选择子集
df.iloc[0:1,0:1]
# A
# 0 1
# iloc支持切片
df.iloc[:,0:1]
# A
# 0 1
# 1 3
总结:
df.sub(column, axis=‘index’)中的axis参数指定进行计算的轴向。
在Pandas中,axis=0或axis='index’表示沿着行的方向进行计算,即对各列进行操作。
axis=1或axis='columns’表示沿着列的方向进行计算,即对各行进行操作。
例如:
import pandas as pd
df = pd.DataFrame([[1, 2], [3, 4]], columns=['A', 'B'])
# 沿行方向,以A列为基准,计算其他列与A列的差值
df.sub(df['A'], axis='index')
# 结果:
A B
0 0 1
1 0 1
# 沿列方向,以行0为基准,计算其他行与行0的差值
df.sub(df.iloc[0], axis='columns')
# 结果:
A B
0 0.0 0.0
1 2.0 2.0
所以在df.sub(column, axis=‘index’)中,表示以列column为基准,计算df中其他列与该列的差值。
axis参数在Pandas中非常常用,可以灵活地指定计算的方向。
还可以用 Series 对齐多层索引 DataFrame 的某一层级。
In [26]: dfmi = df.copy()
In [27]: dfmi.index = pd.MultiIndex.from_tuples([(1, 'a'), (1, 'b'),
....: (1, 'c'), (2, 'a')],
....: names=['first', 'second'])
....:
In [28]: dfmi.sub(column, axis=0, level='second')
Out[28]:
one two three
first second
1 a -0.377535 0.000000 NaN
b -1.569069 0.000000 -1.962513
c -0.783123 0.000000 -0.250933
2 a NaN -1.493173 -2.385688
Series 与 Index 还支持 divmod()
open in new window 内置函数,该函数同时执行向下取整除与模运算,返回两个与左侧类型相同的元组。示例如下:
In [29]: s = pd.Series(np.arange(10))
In [30]: s
Out[30]:
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
dtype: int64
In [31]: div, rem = divmod(s, 3)
In [32]: div
Out[32]:
0 0
1 0
2 0
3 1
4 1
5 1
6 2
7 2
8 2
9 3
dtype: int64
In [33]: rem
Out[33]:
0 0
1 1
2 2
3 0
4 1
5 2
6 0
7 1
8 2
9 0
dtype: int64
In [34]: idx = pd.Index(np.arange(10))
In [35]: idx
Out[35]: Int64Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')
In [36]: div, rem = divmod(idx, 3)
In [37]: div
Out[37]: Int64Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64')
In [38]: rem
Out[38]: Int64Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64')
s=pd.Series(np<