年
官方文档中文版 PDF(最全)
2022 Pandas
介绍
Pandas
本文档由Pandas中文社区公众号整理 ,完整版内容,进入公众号后台回复“Pandas官方文档最新”
是一个Python包,它提供了快速、灵活、有表现力的数据结构。设计这些数据结构的目的,是为了更加简单、更加符合直觉地操作相
互关联的或带有标签属性的数据。pandas的目标是成为Python分析实用的、现实世界产生的数据的基本高级构成要素。另外,pandas还有更
广阔的目标:成为所有语言最有力、最方便的数据操作和分析工具。现在,pandas已经很好地走在奔向这个目标的路上。
pandas适合处理多种类型的数据:
表格数据,每列表示不同的类。例如SQL表格或者Excel表格。
排序的或未排序的时间序列数据。
行和列各有含义的矩阵数据。
其他形式的观测数据集或统计数据集。数据放入pandas数据结构时不必带有标签。
pandas特有的两个数据结构, Series 和 DataFrame ,可以应对大部分金融、统计、社会科学和很多工程领域的典型使用场景。对于R用
户来说, DataFrame 在提供了R的 data.frame 提供的所有特性之外,还提供了更多的特性。pandas构建在Numpy之上,目的是在科学计
算环境中更好地集成第三方库。
Pandas表现的比较好的方面:
轻松处理浮点型缺失数据(用NaN表示)和非浮点型缺失数据。
数据大小可变:DataFrame和更高维的对象的列可以插入和删除。
自动地、清晰地数据规整:数据对象会被自动地规整到一组标签集,或者,使用者可以简单地忽略标签,在计算时,让Series和
DataFrame自动规整数据。
强大而灵活的分组方法,在数据聚合和转换中,更好地对数据集进行分割、应用和组合。
更容易地将粗糙的、带有不同索引的Python和Numpy数据转换成DataFrame。
基于标签的切片、索引和子集构造。
基于直觉的数据连接(merge和join操作)。
方便的数据变形(reshape和pivot操作)。
多级轴标签(每个标记可能有多个轴标签)。
强壮的用来加载Flat文件(csv或逗号分割的文件)、Excel文件、数据库文件的IO工具,超快的HDF5格式数据的加载和保存。
处理时间序列的特殊功能:日期范围生成和频率转换,移动窗口统计,移动窗口线性回归,日期移位和滞后等等。
pandas的许多设计原则都是为了解决在使用其他语言时或在科学研究中常见的问题。数据科学家的工作一般分为三部分:改写和清理数据、分
析建模、组织结果呈现。pandas是这些工作的理想工具。
pandas其他的特点:
pandas很快。在Cython代码中,许多底层算法被广泛地调整过。但是,就像其他任何东西一样,泛化通常会牺牲性能。所以,如果你关
注你的应用程序的一个特性,你可以创建更快的专门工具。
pandas依赖statsmodels,也因为此,pandas在Python统计计算生态系统中占有重要的位置。
pandas被广泛地应用在金融领域。
注:本文档默认读者对Numpy有大致的了解。如果你没有使用过Numpy,请移步:learning about Numpy。
pandas
第二章 安装
对大部分使用者来说,安装pandas最简单的方式是使用Anaconda(pandas默认包含在内)。Anaconda是Python的一个跨平台的数据分析和
科学计算发行版。大家可以进入Pandas中文社区微信公众号,回复“anaconda” 直接获取anaconda下载版本。
2.1
舍弃Python2.7计划
核心开发组计划在2020年1月1日之后停止支持Python2.7。在NumPy的计划中,所有在2018年12月31日前发布的pandas版本都将支持
。
2018年12月31日发布的版本将会是最后一个支持Python2的版本,发布出来的包可以持续的从PyPi和conda中获得。
2019年1月1日起,所有的发布版本只支持Python3。
如果想在2018年12月31日之后持续的支持Python2.7(打补丁或提供资金支持),请联系问题讨论区的维护人员。
更多的信息,请查阅:Python3声明和移植到Python3指南。
Python
Python2
2.2
支持的Python版本
官方Python2.5, 3.5 和 3.6版本。
2.3
安装pandas
使用Anaconda安装pandas
对没有经验的使用者来说,安装pandas和相关的Numpy、Scipy会有点困难。
不止安装pandas,也包括安装Python、Scipy、IPython、Numpy、Matplotlib等流行的包,最简单的方式是使用Anaconda,Python的一个跨
平台(Linux,Mac OS X,Windows)的数据分析和科学计算发行版。
执行安装程序之后,不用安装其他的东西,也不用安装其他的依赖程序,使用者就可以使用pandas和Scipy程序栈。
这里是安装Anaconda的方法。
Anaconda的包完全列表(随Anaconda一起安装)在这里。
使用Anaconda的另一个好处是,你不必使用管理员权限安装。Anaconda可以安装在home文件夹下,后面想删除的话,直接删除这个文件夹
即可。
2.3.1
使用Miniconda安装pandas
上面介绍了作为Anaconda的一部分安装pandas的方法。但是,使用Anaconda安装pandas,意味着你要安装它附带的上百个包,下载几百兆
大小的安装包。
如果想控制安装哪些包,或者带宽有限,使用Miniconda来安装是个不错的选择。
Conda是Anaconda的跨平台、跟语言无关的包管理工具(扮演pip和虚拟环境结合的角色)。
Miniconda允许创建最小的自包含Python安装程序,然后使用Conda命令安装附加的包。
首先,安装Conda,下载并运行Miniconda即可完成上面的步骤。Miniconda的安装包在这里。
下一步,为conda创建一个新的conda环境(类似于虚拟环境,不过允许精确指定Python和库集合的版本)。在终端窗口运行下面的命令:
2.3.2
conda create ‑n name_of_my_env python
这个命令会创建一个最小的运行环境,该环境中只安装了Python。进入该运行环境中,运行:
source activate name_of_my_env
用户运行:
windows
activate name_of_my_env
最后,安装pandas:
conda install pandas
安装特定的pandas版本:
conda install pandas=0.20.3
安装其他的包,比如IPython:
conda install ipython
安装完整的Anaconda发行版:
conda install anaconda
如果你需要的包conda不提供,可以先安装pip,再安装相应的包:
conda install pip
pip install django
从PyPi安装pandas(使用pip)
使用pip从PyPi安装pandas:
2.3.3
pip install pandas
使用ActivePython安装
ActivePython的安装说明可以从这里获得。2.7版和3.5版包含pandas
2.3.4
用Linux发行版的包管理器安装pandas
下面的表格中列出了通过Linux发行版的包管理器安装Python3的pandas的方法,如果想要安装Python2的pandas,把包的名称替换成pythonpandas。
2.3.5
发行版
版本
下载地址
安装方法
Debian
稳定版 官方Debian仓库 sudo apt-get install python3-pandas
Debian & Ubuntu 非稳定版(最新版本)
NeuroDebian sudo apt-get install python3-pandas
Ubuntu
稳定版 官方Ubuntu仓库 sudo apt-get install python3-pandas
OpenSuse
zypper install python3-pandas
稳定版 OpenSuse仓库
Fedora
dnf install python3-pandas
稳定版 官方Fedora仓库
Centos/RHEL
yum install python3-pandas
稳定版
EPEL仓库
Linux
不过,Linux发行版提供的包的版本通常会滞后一些,所以,安装最新版的pandas,建议使用 pip 或 conda 。
源码安装
查看 文档 ,获取从git源码安的pandas的完整说明。如果想要为pandas贡献代码,查看 创建开发者环境 。
2.3.6
运行测试套件
pandas提供了详尽的单元测试,截止到撰写本文时,覆盖了97%的代码。运行这些单元测试,核实所有的地方都能正常工作,所有的软硬件
依赖都已安装。确保你已安装pytest,运行:
2.4
import pandas as pd pd.test() running: pytest --skip-slow --skip-network C:⧵Users⧵TP⧵Anaconda3⧵envs⧵py36⧵lib⧵site-packages⧵pandas
============================= test session starts ============================= platform win32 -- Python 3.6.2, pytest3.2.1, py-1.4.34, pluggy-0.4.0 rootdir: C:⧵Users⧵TP⧵Documents⧵Python⧵pandasdev⧵pandas, inifile: setup.cfg collected 12145 items / 3
skipped
..................................................................S...... ........S................................................................ .........................................................................
==================== 12130 passed, 12 skipped in 368.339 seconds =====================
依赖
2.5
:
或更高版本
或更高版本
: 或更高版本
setuptools 24.2.0
Numpy 1.9.0
python-dateutil 2.5.0
pytz
:
推荐依赖
numexpr:加速某些数值操作。numexpr使用多核、智能分块和缓存来达到大幅加速的目的。如果安装,需要2.4.6或更高版本。
bottleneck:加速某些nan类型的计算。bottleneck使用指定的cython程序来实现大幅加速。如果安装,需要1.0.0或更高版本。
注意:强烈建议安装这些库,因为他们实现了很大的加速,尤其在处理大数据集时。
2.5.1
可选依赖
Cython:只有创建开发者版本时才必要。0.24或更高版本。
Scipy:各种统计函数需要。0.14.0或更高版本。
xarray:操作大于2维数组时需要,转换 Panels 到 xarray 对象时需要。0.7.0或更高版本。
PyTables:处理 HDF5‑based 存储时需要。3.2.1或更高版本,建议3.2.1或更高版本。
Feather Format:处理 feather‑based 存储时需要。0.3.1或更高版本。
Apache Parquet,pyarrow(0.4.1以上版本)或fastparquet(0.0.6以上版本),处理 parquet‑based 存储时需要。snappy和brotli用来
支持压缩操作。
SQLAlchemy:支持SQL数据库。建议0.8.1或更高版本。SQLAlchemy之外,还需要数据库驱动。可以从SQLAlchemy文档中查看不同的
SQL方言的驱动,常见的有:
psycopg2:PostgreSQL
pymysql:MySQL
SQLite:SQLite(Python默认包含在标准库中)
matplotlib:绘图。1.4.3或更高版本。
Excel IO:
xlrd/xlwt:Excel读(xlrd)写(xlwt)
openpyxl:openpyxl 2.4.0或更高版本,xlrd 0.9.0以上版本,用来写.xlsx文件。
XlsxWriter:写Excel的可选库。
Jinja2:HTML模板引擎。
s3fs:Amazon S3。0.0.7或以上版本。
blosc:msgpack使用blosc压缩。
qtpy(需要安装PyQt或PySide),PyQt5,PyQt4,pygtk,xsel和xclip中的一个:read_clipboard()时需要。大部分Linux发行版会默认安
装xclip和(或)xsel。
pandas-gbq:Google BigQuery IO。
Backports.lzma: 读写csv文件中xz压缩的DataFrame。只在Python2时需要,Python3默认安装。
使用 read_html() 时,必须安装以下的库组合中的一个。0.23.0版本中有修改。注意:如果使用BeautifulSoup4,至少需要4.2.1版
本。
BeautifulSoup4和html5lib(任一html5lib版本)
BeautifulSoup4和lxml
BeautifulSoup4,html5lib和lxml
只是lxml。不建议采用这个方法,原因见这里。
警告:
如果安装了BeautifulSoup4,必须安装lxml或html5lib或者两个都安装。只安装BeautifulSoup4,read_html()不能正常工作。
强烈建议阅读HTML表格解析陷阱,解释了安装的相关问题和上面的库的使用方法。
可能需要安装老版本的BeautifulSoup。4.2.1,4.1.3和4.0.2版本已经确定可以在Ubuntu和Debian的32位和64位版本上运行。
注意:
如果系统支持apt-get,可以这么安装lxml:
2.5.2
sudo apt‑get build‑dep python‑lxml
这可以防止以后出现更多的头疼问题。
如果不安装可选依赖,很多特性使用不了,因此,建议安装这些依赖。Anaconda、ActivePython(2.7版或3.5版)或Enthought Canopy是不
错的选择。
第三章 概览
是一个基于BSD协议的开源库,为Python提供高性能、易用的数据结构和数据分析工具。 4 5 pandas由以下元素构成: 6
带标签的数组数据结构,主要是Series和DataFrame。 7
索引对象既可以简单的轴索引也可以多级或多层轴索引。 8
完整的分组引擎用来聚合、转换数据集。 9
日期段生成器(date_range)和个性化的日期偏移实现自定义的频率。 10
IO工具:从flat文件(CSV,逗号分隔,Excel 2003)加载表格数据,从更快的更有效率的Pytables和HDF5格式保存、加载pandas对象。
11
标准数据结构的节约内存的“稀疏”版本来存储主要是确实数据和主要是常数(一些定值)的数据。 12
移动窗口统计(滚动平均,滚动标准差等等)。 13 14 #### 4.1 数据结构 15 16 | 维度 | 名称 | 描述 | 17 | ---- | --------- | --------------------------------------------------------- | 18 | 1 | Series | 一维的带标签的单一类型数组 | 19 | 2 | DataFrame | 一般是二维的带标签的,大小可
变的,列可能是不同类的表格结构 | 20 21 ##### 4.1.1 为什么有不止一个数据结构? 22 23 理解pandas的数据结构的最好方式是,将其
看成低维数据的灵活容器。例如,DataFrame是Series的容器,Series是标量的容器。我们希望能够使用类似于字典的方式从这些容器中
插入和删除对象。 24 25 另外,我们希望那些考虑到时间序列和截面数据集的典型方向的公共API函数拥有合理的默认行为。使用
ndarrays存储2维或3维数据时,使用者的一个任务是在写函数时必须考虑数据集的方向;轴或多或少是等价的(当C连续性或Fortran连续
性关系到性能时除外)。在pandas中,轴是为了给数据提供更多的语义含义,比如,对于一个特定的数据集,可能存在一种“正确”的方式
来定位数据。因此,我们的目标是,减少在编写数据转换的下游函数时的脑力劳动。 26 27 例如,对于数据框(DataFrame),从语义上
理解index(行)和columns(列)比理解0轴和1轴更容易。因此,沿着列迭代数据框的代码会更有可读性: 28
pandas
29
for col in df.columns:
30
series = df[col]
31
# do something with series
32
易变性和复制数据 34 所有的pandas数据结构都是值可变的(包含的值可以改变),但大小并不总是可变。一个Series的长度
是不可变的,但是,DataFrame中可以插入列。不过,绝大多数方法会生成新的对象,并且输入数据不受影响。总的来说,我们喜欢在合
适的地方保持不变。 35 36 #### 4.3 获得帮助 37 pandas的问题讨论区:Github Issue Tracker。如果是一般问题,可以在Stack
Overflow提问。 38 39 #### 4.4 社区 40 现在,一群来自全世界的志趣相投的人组成的社区正在支持pandas。他们付出了宝贵的时间和
精力,使pandas的开源成为可能。感谢所有的贡献者。 41 42 如果想要为pandas贡献代码,请访问这里。
33 #### 4.2
分钟了解pandas
10
本章是对pandas的简单介绍,适合初学者。详细的介绍请查阅Cookbook。
首先,我们使用下面的方式引入本章所需要的Python包:
In [1]: import pandas as pd
In [2]: import numpy as np
In [3]: import matplotlib.pyplot as plt
创建对象
详细介绍,请查阅数据结构介绍。
传入一组值的列表,创建一个 Series ,pandas会自动创建默认的整型索引:
5.1
In [4]: s = pd.Series([1,3,5,np.nan,6,8])
In [5]: s
Out[5]:
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64
传入NumPy数组、时间索引和标签列,创建 DataFrame :
In [6]: dates = pd.date_range('20130101', periods=6)
In [7]: dates
Out[7]:
DatetimeIndex(['2013‑01‑01', '2013‑01‑02', '2013‑01‑03', '2013‑01‑04',
'2013‑01‑05', '2013‑01‑06'],
dtype='datetime64[ns]', freq='D')
In [8]: df = pd.DataFrame(np.random.randn(6,4), index=dates, columns=list('ABCD'))
mpy
In [9]: df
Out[9]:
A
B
C
D
2013‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
2013‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
2013‑01‑04 0.721555 ‑0.706771 ‑1.039575 0.271860
2013‑01‑05 ‑0.424972 0.567020 0.276232 ‑1.087401
2013‑01‑06 ‑0.673690 0.113648 ‑1.478427 0.524988
数组)、索引(时间序列)和列(带有标签的列表)
#
传入数据(Nu
传入一个可以转换成类似序列的对象的字典,创建 DataFrame :
In [10]: df2 = pd.DataFrame({ 'A' : 1.,
....:
'B' : pd.Timestamp('20130102'),
....:
'C' : pd.Series(1,index=list(range(4)),dtype='float32'),
....:
'D' : np.array([3] * 4,dtype='int32'),
....:
'E' : pd.Categorical(["test","train","test","train"]),
....:
'F' : 'foo' })
....:
In [11]: df2
Out[11]:
A
B
C D
E
F
0 1.0 2013‑01‑02 1.0 3 test foo
1 1.0 2013‑01‑02 1.0 3 train foo
2 1.0 2013‑01‑02 1.0 3 test foo
3 1.0 2013‑01‑02 1.0 3 train foo
DataFrame
的列的类型不同:
In [12]: df2.dtypes
Out[12]:
A float64
B datetime64[ns]
C float32
D int32
E category
F object
dtype: object
如果使用 IPython ,tab键可以实现列名和公共属性的自动补全。下面是可以自动补全的属性的子集:
In [13]: df2.<TAB>
df2.A
df2.abs
df2.add
df2.add_prefix
df2.add_suffix
df2.align
df2.all
df2.any
df2.append
df2.apply
df2.applymap
df2.D
df2.bool
df2.boxplot
df2.C
df2.clip
df2.clip_lower
df2.clip_upper
df2.columns
df2.combine
df2.combine_first
df2.compound
df2.consolidate
可以看到,A,B,C,D都可以使用tab补全。实际上,E也可以,为简洁起见,省略了。
查看数据
详细介绍,查看基本函数。
查看头尾:
5.2
In [14]: df.head()
Out[14]:
A
B
C
D
2013‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
2013‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
2013‑01‑04 0.721555 ‑0.706771 ‑1.039575 0.271860
2013‑01‑05 ‑0.424972 0.567020 0.276232 ‑1.087401
In [15]: df.tail(3)
A
B
C
D
2013‑01‑04 0.721555 ‑0.706771 ‑1.039575 0.271860
2013‑01‑05 ‑0.424972 0.567020 0.276232 ‑1.087401
2013‑01‑06 ‑0.673690 0.113648 ‑1.478427 0.524988
查看索引、列和底层的NumPy数据:
In [16]: df.index
Out[16]:
DatetimeIndex(['2013‑01‑01', '2013‑01‑02', '2013‑01‑03', '2013‑01‑04',
'2013‑01‑05', '2013‑01‑06'],
dtype='datetime64[ns]', freq='D')
In [17]: df.columns
Index(['A', 'B', 'C', 'D'], dtype='object')
In [18]: df.values
array([[ 0.4691, ‑0.2829, ‑1.5091, ‑1.1356],
[ 1.2121, ‑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 ]])
可以快速查看统计信息:
describe()
译者注:统计信息默认包括:样本数量(count)、均值(mean)、标准差(std)、最小值(min)、下四分位数(25%)、
中位数(50%)、上四分位数(75%)、最大值(max)。
In [19]: df.describe()
Out[19]:
A
B
C
D
count 6.000000 6.000000 6.000000 6.000000
mean 0.073711 ‑0.431125 ‑0.687758 ‑0.233103
std
0.843157 0.922818 0.779887 0.973118
min ‑0.861849 ‑2.104569 ‑1.509059 ‑1.135632
25% ‑0.611510 ‑0.600794 ‑1.368714 ‑1.076610
50%
0.022070 ‑0.228039 ‑0.767252 ‑0.386188
75%
0.658444 0.041933 ‑0.034326 0.461706
max
1.212112 0.567020 0.276232 1.071804
转置(行列互换):
In [20]: df.T
Out[20]:
2013‑01‑01 2013‑01‑02 2013‑01‑03 2013‑01‑04 2013‑01‑05 2013‑01‑06
A
0.469112
1.212112 ‑0.861849
0.721555 ‑0.424972 ‑0.673690
B
‑0.282863 ‑0.173215 ‑2.104569 ‑0.706771
0.567020
0.113648
C
‑1.509059
0.119209 ‑0.494929 ‑1.039575
0.276232 ‑1.478427
D
‑1.135632 ‑1.044236
1.071804
0.271860 ‑1.087401
0.524988
按照某个轴排序(轴标签的词法顺序):
In [21]: df.sort_index(axis=1, ascending=False) #
Out[21]:
D
C
B
A
2013‑01‑01 ‑1.135632 ‑1.509059 ‑0.282863 0.469112
2013‑01‑02 ‑1.044236 0.119209 ‑0.173215 1.212112
2013‑01‑03 1.071804 ‑0.494929 ‑2.104569 ‑0.861849
2013‑01‑04 0.271860 ‑1.039575 ‑0.706771 0.721555
2013‑01‑05 ‑1.087401 0.276232 0.567020 ‑0.424972
2013‑01‑06 0.524988 ‑1.478427 0.113648 ‑0.673690
倒序
按照数据排序:
In [22]: df.sort_values(by='B')
Out[22]:
A
B
C
D
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
2013‑01‑04 0.721555 ‑0.706771 ‑1.039575 0.271860
2013‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
2013‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2013‑01‑06 ‑0.673690 0.113648 ‑1.478427 0.524988
2013‑01‑05 ‑0.424972 0.567020 0.276232 ‑1.087401
5.3
选取
注:虽然Python或NumPy选择和设置数据的方法更加符合直觉,在交互性工作中也能起作用,但是在生产代码中,我们建议
使用pandas特有的访问数据的方法:.at,.iat,.loc,.iloc和.ix。
详细介绍,查看文档:索引和选择数据 和 多级/高级索引
获取数据
选择一列,生成一个Series(等价于df.A):
5.3.1
In [23]: df['A']
Out[23]:
2013‑01‑01 0.469112
2013‑01‑02 1.212112
2013‑01‑03 ‑0.861849
2013‑01‑04 0.721555
2013‑01‑05 ‑0.424972
2013‑01‑06 ‑0.673690
Freq: D, Name: A, dtype: float64
使用[]选择数据,返回行切片(若干行):
In [24]: df[0:3]
Out[24]:
A
B
C
D
2013‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
2013‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
In [25]: df['20130102':'20130104']
A
B
C
D
2013‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
2013‑01‑04 0.721555 ‑0.706771 ‑1.039575 0.271860
按标签选择数据
详细介绍,请查阅:按标签选择数据
按标签获取一个横截面(一行):
5.3.2
In [26]: df.loc[dates[0]]
Out[26]:
A 0.469112
B ‑0.282863
C ‑1.509059
D ‑1.135632
Name: 2013‑01‑01 00:00:00, dtype: float64
选择多个轴(多行多列):
In [27]: df.loc[:,['A','B']]
Out[27]:
A
B
2013‑01‑01 0.469112 ‑0.282863
2013‑01‑02 1.212112 ‑0.173215
2013‑01‑03 ‑0.861849 ‑2.104569
2013‑01‑04 0.721555 ‑0.706771
2013‑01‑05 ‑0.424972 0.567020
2013‑01‑06 ‑0.673690 0.113648
按标签切片,包含端点:
In [28]: df.loc['20130102':'20130104',['A','B']]
Out[28]:
A
B
2013‑01‑02 1.212112 ‑0.173215
2013‑01‑03 ‑0.861849 ‑2.104569
2013‑01‑04 0.721555 ‑0.706771
减少返回对象的维度:
In [29]: df.loc['20130102',['A','B']]
Out[29]:
A 1.212112
B ‑0.173215
Name: 2013‑01‑02 00:00:00, dtype: float64
获取标量数据:
In [30]: df.loc[dates[0],'A']
Out[30]: 0.46911229990718628
快速访问标量数据(等价于 .loc ):
In [31]: df.at[dates[0],'A']
Out[31]: 0.46911229990718628
按位置选择数据
详细介绍:按位置选择数据
传入代表位置(第几行或第几列)的整数:
5.3.3
In [32]: df.iloc[3]
Out[32]:
A 0.721555
B ‑0.706771
C ‑1.039575
D 0.271860
Name: 2013‑01‑04 00:00:00, dtype: float64
切片(译者注:不要使用 [[3:5],[0:2]] ),类似于Python和NumPy的方式:
In [33]: df.iloc[3:5,0:2]
Out[33]:
A
B
2013‑01‑04 0.721555 ‑0.706771
2013‑01‑05 ‑0.424972 0.567020
枚举(译者注:不是切片),类似于Python和Numpy的方式:
In [34]: df.iloc[[1,2,4],[0,2]]
Out[34]:
A
C
2013‑01‑02 1.212112 0.119209
2013‑01‑03 ‑0.861849 ‑0.494929
2013‑01‑05 ‑0.424972 0.276232
明确地指定行:
In [35]: df.iloc[1:3,:]
Out[35]:
A
B
C
D
2013‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
明确地指定列:
In [36]: df.iloc[:,1:3]
Out[36]:
B
C
2013‑01‑01 ‑0.282863 ‑1.509059
2013‑01‑02 ‑0.173215 0.119209
2013‑01‑03 ‑2.104569 ‑0.494929
2013‑01‑04 ‑0.706771 ‑1.039575
2013‑01‑05 0.567020 0.276232
2013‑01‑06 0.113648 ‑1.478427
获取特定行特定列的数据:
In [37]: df.iloc[1,1]
Out[37]: ‑0.17321464905330858
更快地获取特定行特定列的数据(等价于.iloc):
In [38]: df.iat[1,1]
Out[38]: ‑0.17321464905330858
布尔索引
使用某一列的数据来选择整个DataFrame的数据:
In [39]: df[df.A > 0] # 返回A列数据大于0的所有行。
5.3.4
Out[39]:
A
B
C
D
2013‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
2013‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2013‑01‑04 0.721555 ‑0.706771 ‑1.039575 0.271860
是否满足一个布尔条件:
In [40]: df[df > 0]
Out[40]:
A
B
C
D
2013‑01‑01 0.469112
NaN
NaN
NaN
2013‑01‑02 1.212112
NaN 0.119209
NaN
2013‑01‑03
NaN
NaN
NaN 1.071804
2013‑01‑04 0.721555
NaN
NaN 0.271860
2013‑01‑05
NaN 0.567020 0.276232
NaN
2013‑01‑06
NaN 0.113648
NaN 0.524988
使用 isin() 方法过滤数据:
In [41]: df2 = df.copy()
In [42]: df2['E'] = ['one', 'one','two','three','four','three']
In [43]: df2
Out[43]:
A
B
C
D
E
2013‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
one
2013‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
one
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
two
2013‑01‑04 0.721555 ‑0.706771 ‑1.039575 0.271860 three
2013‑01‑05 ‑0.424972 0.567020 0.276232 ‑1.087401 four
2013‑01‑06 ‑0.673690 0.113648 ‑1.478427 0.524988 three
In [44]: df2[df2['E'].isin(['two','four'])]
A
B
C
D
E
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804 two
2013‑01‑05 ‑0.424972 0.567020 0.276232 ‑1.087401 four
赋值
设置一个新的列,自动按索引将数据对齐:
5.3.5
In [45]: s1 = pd.Series([1,2,3,4,5,6], index=pd.date_range('20130102', periods=6))
In [46]: s1
Out[46]:
2013‑01‑02 1
2013‑01‑03 2
2013‑01‑04 3
2013‑01‑05 4
2013‑01‑06 5
2013‑01‑07 6
Freq: D, dtype: int64
In [47]: df['F'] = s1
通过标签设置新的值:
In [48]: df.at[dates[0],'A'] = 0
通过位置设置新的值:
In [49]: df.iat[0,1] = 0
通过NumPy数组设置新的值:
In [50]: df.loc[:,'D'] = np.array([5] * len(df))
上一条的赋值操作的结果:
In [51]: df
Out[51]:
A
B
C D
F
2013‑01‑01 0.000000 0.000000 ‑1.509059 5 NaN
2013‑01‑02 1.212112 ‑0.173215 0.119209 5 1.0
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 5 2.0
2013‑01‑04 0.721555 ‑0.706771 ‑1.039575 5 3.0
2013‑01‑05 ‑0.424972 0.567020 0.276232 5 4.0
2013‑01‑06 ‑0.673690 0.113648 ‑1.478427 5 5.0
通过 where 操作设置新的值:
In [52]: df2 = df.copy()
In [53]: df2[df2 > 0] = ‑df2
In [54]: df2
Out[54]:
A
B
C D
F
2013‑01‑01 0.000000 0.000000 ‑1.509059 ‑5 NaN
2013‑01‑02 ‑1.212112 ‑0.173215 ‑0.119209 ‑5 ‑1.0
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 ‑5 ‑2.0
2013‑01‑04 ‑0.721555 ‑0.706771 ‑1.039575 ‑5 ‑3.0
2013‑01‑05 ‑0.424972 ‑0.567020 ‑0.276232 ‑5 ‑4.0
2013‑01‑06 ‑0.673690 ‑0.113648 ‑1.478427 ‑5 ‑5.0
缺失数据
pandas通常用 np.nan 来代表缺失数据,缺失数据默认不参与数值计算。详细介绍,请看缺失数据。
重建索引可以在某个特定的轴上改变、添加或删除索引,返回数据的拷贝。
5.4
In [55]: df1 = df.reindex(index=dates[0:4], columns=list(df.columns) + ['E'])
In [56]: df1.loc[dates[0]:dates[1],'E'] = 1
In [57]: df1
Out[57]:
A
B
C D
F
E
2013‑01‑01 0.000000 0.000000 ‑1.509059 5 NaN 1.0
2013‑01‑02 1.212112 ‑0.173215 0.119209 5 1.0 1.0
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 5 2.0 NaN
2013‑01‑04 0.721555 ‑0.706771 ‑1.039575 5 3.0 NaN
丢弃含有缺失数据的行:
In [58]: df1.dropna(how='any')
Out[58]:
A
B
C D
F
E
2013‑01‑02 1.212112 ‑0.173215 0.119209 5 1.0 1.0
填充缺失数据:
In [59]: df1.fillna(value=5)
Out[59]:
A
B
C D
F
E
2013‑01‑01 0.000000 0.000000 ‑1.509059 5 5.0 1.0
2013‑01‑02 1.212112 ‑0.173215 0.119209 5 1.0 1.0
2013‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 5 2.0 5.0
2013‑01‑04 0.721555 ‑0.706771 ‑1.039575 5 3.0 5.0
获取值为nan的布尔掩码:
In [60]: pd.isna(df1)
Out[60]:
A
B
C
D
F
E
2013‑01‑01 False False False False True False
2013‑01‑02 False False False False False False
2013‑01‑03 False False False False False True
2013‑01‑04 False False False False False True
操作
详细介绍,请查阅:基本的二元操作。
5.5
统计
统计运算一般排除缺失值。
描述统计:
5.5.1
In [61]: df.mean()
Out[61]:
A ‑0.004474
B ‑0.383981
C ‑0.687758
D 5.000000
F 3.000000
dtype: float64
另一个轴上的同样操作:
In [62]: df.mean(1)
Out[62]:
2013‑01‑01 0.872735
2013‑01‑02 1.431621
2013‑01‑03 0.707731
2013‑01‑04 1.395042
2013‑01‑05 1.883656
2013‑01‑06 1.592306
Freq: D, dtype: float64
对不同维度、需要对齐的对象进行运算。pandas自动沿着指定的维度广播:
In [63]: s = pd.Series([1,3,5,np.nan,6,8], index=dates).shift(2)
In [64]: s
Out[64]:
2013‑01‑01 NaN
2013‑01‑02 NaN
2013‑01‑03 1.0
2013‑01‑04 3.0
2013‑01‑05 5.0
2013‑01‑06 NaN
Freq: D, dtype: float64
In [65]: df.sub(s, axis='index')
A
B
C
D
F
2013‑01‑01
NaN
NaN
NaN NaN NaN
2013‑01‑02
NaN
NaN
NaN NaN NaN
2013‑01‑03 ‑1.861849 ‑3.104569 ‑1.494929 4.0 1.0
2013‑01‑04 ‑2.278445 ‑3.706771 ‑4.039575 2.0 0.0
2013‑01‑05 ‑5.424972 ‑4.432980 ‑4.723768 0.0 ‑1.0
2013‑01‑06
NaN
NaN
NaN NaN NaN
调用
对数据调用函数
5.5.2
In [66]: df.apply(np.cumsum)
Out[66]:
A
B
C D
F
2013‑01‑01 0.000000 0.000000 ‑1.509059 5 NaN
2013‑01‑02 1.212112 ‑0.173215 ‑1.389850 10 1.0
2013‑01‑03 0.350263 ‑2.277784 ‑1.884779 15 3.0
2013‑01‑04 1.071818 ‑2.984555 ‑2.924354 20 6.0
2013‑01‑05 0.646846 ‑2.417535 ‑2.648122 25 10.0
2013‑01‑06 ‑0.026844 ‑2.303886 ‑4.126549 30 15.0
In [67]: df.apply(lambda x: x.max() ‑ x.min())
A 2.073961
B 2.671590
C 1.785291
D 0.000000
F 4.000000
dtype: float64
直方图(频率)
详细介绍,请查阅:直方图与离散化。
5.5.3
In [68]: s = pd.Series(np.random.randint(0, 7, size=10))
In [69]: s
Out[69]:
0 4
1 2
2 1
3 2
4 6
5 4
6 4
7 6
8 4
9 4
dtype: int64
In [70]: s.value_counts()
Out[70]:
4 5
6 2
2 2
1 1
dtype: int64
字符串方法
Series的str属性中,有一个处理字符串的方法集合,这个方法集合可以很方便地操作数组的每个元素,就像下面的代码片段一样。注意,模式
匹配默认使用正则表达式。更多细节,查阅:向量化字符串方法。
5.5.4
In [71]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
In [72]: s.str.lower()
Out[72]:
0 a
1 b
2 c
3 aaba
4 baca
5 NaN
6 caba
7 dog
8 cat
dtype: object
5.6
合并
(纵向连接)
pandas提供了大量合并 Series 、 DataFrame 和 Panel 对象的方法,这些方法利用合并或连接操作时的索引或代数关系的集合逻辑来操
作。
详细介绍,查阅:合并。
使用concat()纵向连接:
5.6.1 Concat
In [73]: df = pd.DataFrame(np.random.randn(10, 4))
In [74]: df
Out[74]:
0
1
2
3
0 ‑0.548702 1.467327 ‑1.015962 ‑0.483075
1 1.637550 ‑1.217659 ‑0.291519 ‑1.745505
2 ‑0.263952 0.991460 ‑0.919069 0.266046
3 ‑0.709661 1.669052 1.037882 ‑1.705775
4 ‑0.919854 ‑0.042379 1.247642 ‑0.009920
5 0.290213 0.495767 0.362949 1.548106
6 ‑1.131345 ‑0.089329 0.337863 ‑0.945867
7 ‑0.932132 1.956030 0.017587 ‑0.016692
8 ‑0.575247 0.254161 ‑1.143704 0.215897
9 1.193555 ‑0.077118 ‑0.408530 ‑0.862495
# break it into pieces
In [75]: pieces = [df[:3], df[3:7], df[7:]]
In [76]: pd.concat(pieces)
Out[76]:
0
1
2
3
0 ‑0.548702 1.467327 ‑1.015962 ‑0.483075
1 1.637550 ‑1.217659 ‑0.291519 ‑1.745505
2 ‑0.263952 0.991460 ‑0.919069 0.266046
3 ‑0.709661 1.669052 1.037882 ‑1.705775
4 ‑0.919854 ‑0.042379 1.247642 ‑0.009920
5 0.290213 0.495767 0.362949 1.548106
6 ‑1.131345 ‑0.089329 0.337863 ‑0.945867
7 ‑0.932132 1.956030 0.017587 ‑0.016692
8 ‑0.575247 0.254161 ‑1.143704 0.215897
9 1.193555 ‑0.077118 ‑0.408530 ‑0.862495
(横向连接)
SQL风格的合并。详细介绍,查阅:数据库风格的连接
5.6.2 Join
In [77]: left = pd.DataFrame({'key': ['foo', 'foo'], 'lval': [1, 2]})
In [78]: right = pd.DataFrame({'key': ['foo', 'foo'], 'rval': [4, 5]})
In [79]: left
Out[79]:
key lval
0 foo
1
1 foo
2
In [80]: right
Out[80]:
key rval
0 foo
4
1 foo
5
In [81]: pd.merge(left, right, on='key')
Out[81]
key lval rval
0 foo
1
4
1 foo
1
5
2 foo
2
4
3 foo
2
5
另一个示例:
In [82]: left = pd.DataFrame({'key': ['foo', 'bar'], 'lval': [1, 2]})
In [83]: right = pd.DataFrame({'key': ['foo', 'bar'], 'rval': [4, 5]})
In [84]: left
Out[84]:
key lval
0 foo
1
1 bar
2
In [85]: right
Out[85]:
key rval
0 foo
4
1 bar
5
In [86]: pd.merge(left, right, on='key')
Out[86]
key lval rval
0 foo
1
4
1 bar
2
5
(追加)
为DataFrame添加行,详细介绍,查阅:追加。
5.6.3 Append
In [87]: df = pd.DataFrame(np.random.randn(8, 4), columns=['A','B','C','D'])
In [88]: df
Out[88]:
A
B
C
D
0 1.346061 1.511763 1.627081 ‑0.990582
1 ‑0.441652 1.211526 0.268520 0.024580
2 ‑1.577585 0.396823 ‑0.105381 ‑0.532532
3 1.453749 1.208843 ‑0.080952 ‑0.264610
4 ‑0.727965 ‑0.589346 0.339969 ‑0.693205
5 ‑0.339355 0.593616 0.884345 1.591431
6 0.141809 0.220390 0.435589 0.192451
7 ‑0.096701 0.803351 1.715071 ‑0.708758
In [89]: s = df.iloc[3]
In [90]: df.append(s, ignore_index=True)
Out[90]:
A
B
C
D
0 1.346061 1.511763 1.627081 ‑0.990582
1 ‑0.441652 1.211526 0.268520 0.024580
2 ‑1.577585 0.396823 ‑0.105381 ‑0.532532
3 1.453749 1.208843 ‑0.080952 ‑0.264610
4 ‑0.727965 ‑0.589346 0.339969 ‑0.693205
5 ‑0.339355 0.593616 0.884345 1.591431
6 0.141809 0.220390 0.435589 0.192451
7 ‑0.096701 0.803351 1.715071 ‑0.708758
8 1.453749 1.208843 ‑0.080952 ‑0.264610
5.7
分组
指的是下面三个操作中的一个或多个:
基于某些条件,将数据 分解 为一些组。
分别在每组数据上 调用 某个函数。
合并 结果。
详细介绍,查阅:分组。
group by
pyhton
In [91]: df = pd.DataFrame({'A' : ['foo', 'bar', 'foo', 'bar',
....:
'foo', 'bar', 'foo', 'foo'],
....:
'B' : ['one', 'one', 'two', 'three',
....:
'two', 'two', 'one', 'three'],
....:
'C' : np.random.randn(8),
....:
'D' : np.random.randn(8)})
....:
In [92]: df
Out[92]:
A
B
C
D
0 foo
one ‑1.202872 ‑0.055224
1 bar
one ‑1.814470 2.395985
2 foo
two 1.018601 1.552825
3 bar three ‑0.595447 0.166599
4 foo
two 1.395433 0.047609
5 bar
two ‑0.392670 ‑0.136473
6 foo
one 0.007207 ‑0.561757
7 foo three 1.928123 ‑1.623033
分组,然后执行sum()函数。
In [93]: df.groupby('A').sum()
Out[93]:
C
D
A
bar ‑2.802588 2.42611
foo 3.146492 ‑0.63958
对多列分组,形成多级索引,然后执行 sum 函数:
In [94]: df.groupby(['A','B']).sum()
Out[94]:
C
D
A
B
bar one
‑1.814470 2.395985
three ‑0.595447 0.166599
two
‑0.392670 ‑0.136473
foo one
‑1.195665 ‑0.616981
three 1.928123 ‑1.623033
two
2.414034 1.600434
重塑
详细介绍,查阅:多级索引 和 重塑。
5.8
5.8.1 Stack
In [95]: tuples = list(zip(*[['bar', 'bar', 'baz', 'baz',
....:
'foo', 'foo', 'qux', 'qux'],
....:
['one', 'two', 'one', 'two',
....:
'one', 'two', 'one', 'two']]))
....:
In [96]: index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
In [97]: df = pd.DataFrame(np.random.randn(8, 2), index=index, columns=['A', 'B'])
In [98]: df2 = df[:4]
In [99]: df2
Out[99]:
A
B
first second
bar
one
0.029399 ‑0.542108
two
0.282696 ‑0.087302
baz
one
‑1.575170 1.771208
two
0.816482 1.100230
调用stack()方法,压缩 DataFrame 的列的级别。
In [100]: stacked = df2.stack()
In [101]: stacked
Out[101]:
first second
bar
one
A 0.029399
B ‑0.542108
two
A 0.282696
B ‑0.087302
baz
one
A ‑1.575170
B 1.771208
two
A 0.816482
B 1.100230
dtype: float64
stack
缩。
之后的DataFrame或Series的index对象是MultiIndex。stack()方法的逆方法是unstack(), unstack() 方法默认将最后一级索引 解压
In [102]: stacked.unstack()
Out[102]:
A
B
first second
bar
one
0.029399 ‑0.542108
two
0.282696 ‑0.087302
baz
one
‑1.575170 1.771208
two
0.816482 1.100230
In [103]: stacked.unstack(1)
second
one
two
first
bar
A 0.029399 0.282696
B ‑0.542108 ‑0.087302
baz
A ‑1.575170 0.816482
B 1.771208 1.100230
In [104]: stacked.unstack(0)
first
bar
baz
second
one
A 0.029399 ‑1.575170
B ‑0.542108 1.771208
two
A 0.282696 0.816482
B ‑0.087302 1.100230
透视表
详细介绍,查阅:透视表。
5.8.2
In [105]: df = pd.DataFrame({'A' : ['one', 'one', 'two', 'three'] * 3,
.....:
'B' : ['A', 'B', 'C'] * 4,
.....:
'C' : ['foo', 'foo', 'foo', 'bar', 'bar', 'bar'] * 2,
.....:
'D' : np.random.randn(12),
.....:
'E' : np.random.randn(12)})
.....:
In [106]: df
Out[106]:
A B
C
D
E
0
one A foo 1.418757 ‑0.179666
1
one B foo ‑1.879024 1.291836
2
two C foo 0.536826 ‑0.009614
3 three A bar 1.006160 0.392149
4
one B bar ‑0.029716 0.264599
5
one C bar ‑1.146178 ‑0.057409
6
two A foo 0.100900 ‑1.425638
7 three B foo ‑1.035018 1.024098
8
one C foo 0.314665 ‑0.106062
9
one A bar ‑0.773723 1.824375
10
two B bar ‑1.170653 0.595974
11 three C bar 0.648740 1.167115
我们可以很方便的得到透视表:
In [107]: pd.pivot_table(df, values='D', index=['A', 'B'], columns=['C'])
Out[107]:
C
bar foo
A
B
one
A ‑0.773723 1.418757
B ‑0.029716 ‑1.879024
C ‑1.146178 0.314665
three
A 1.006160
NaN
B
NaN ‑1.035018
C 0.648740
NaN
two
A
NaN 0.100900
B ‑1.170653
NaN
C
NaN 0.536826
时间序列
pandas拥有在频率转换时进行重采样的简单、强大、高效的函数(比如,将1秒频率采样的数据转换为5分钟频率采样的数据)。这种操作在
金融数据分析中非常常见,但不局限于此。详细介绍,查阅:时间序列。
5.9
In [108]: rng = pd.date_range('1/1/2012', periods=100, freq='S')
In [109]: ts = pd.Series(np.random.randint(0, 500, len(rng)), index=rng)
In [110]: ts.resample('5Min').sum()
Out[110]:
2012‑01‑01 25083
Freq: 5T, dtype: int64
时区表示方法:
In [111]: rng = pd.date_range('3/6/2012 00:00', periods=5, freq='D')
In [112]: ts = pd.Series(np.random.randn(len(rng)), rng)
In [113]: ts
Out[113]:
2012‑03‑06 0.464000
2012‑03‑07 0.227371
2012‑03‑08 ‑0.496922
2012‑03‑09 0.306389
2012‑03‑10 ‑2.290613
Freq: D, dtype: float64
In [114]: ts_utc = ts.tz_localize('UTC')
In [115]: ts_utc
Out[115]:
2012‑03‑06 00:00:00+00:00 0.464000
2012‑03‑07 00:00:00+00:00 0.227371
2012‑03‑08 00:00:00+00:00 ‑0.496922
2012‑03‑09 00:00:00+00:00 0.306389
2012‑03‑10 00:00:00+00:00 ‑2.290613
Freq: D, dtype: float64
转换为另一个时区:
In [116]: ts_utc.tz_convert('US/Eastern')
Out[116]:
2012‑03‑05 19:00:00‑05:00 0.464000
2012‑03‑06 19:00:00‑05:00 0.227371
2012‑03‑07 19:00:00‑05:00 ‑0.496922
2012‑03‑08 19:00:00‑05:00 0.306389
2012‑03‑09 19:00:00‑05:00 ‑2.290613
Freq: D, dtype: float64
在时间跨度间转换:
In [117]: rng = pd.date_range('1/1/2012', periods=5, freq='M')
In [118]: ts = pd.Series(np.random.randn(len(rng)), index=rng)
In [119]: ts
Out[119]:
2012‑01‑31 ‑1.134623
2012‑02‑29 ‑1.561819
2012‑03‑31 ‑0.260838
2012‑04‑30 0.281957
2012‑05‑31 1.523962
Freq: M, dtype: float64
In [120]: ps = ts.to_period()
In [121]: ps
Out[121]:
2012‑01 ‑1.134623
2012‑02 ‑1.561819
2012‑03 ‑0.260838
2012‑04 0.281957
2012‑05 1.523962
Freq: M, dtype: float64
In [122]: ps.to_timestamp()
Out[122]
2012‑01‑01 ‑1.134623
2012‑02‑01 ‑1.561819
2012‑03‑01 ‑0.260838
2012‑04‑01 0.281957
2012‑05‑01 1.523962
Freq: MS, dtype: float64
在时间周期和时间戳之间转换,使得一些十分方便的算术函数变得可用。下面的例子中,我们将一个结束于11月的季度频率转换为季度结束后
的一个月月末的上午9点。
In [123]: prng = pd.period_range('1990Q1', '2000Q4', freq='Q‑NOV')
In [124]: ts = pd.Series(np.random.randn(len(prng)), prng)
In [125]: ts.index = (prng.asfreq('M', 'e') + 1).asfreq('H', 's') + 9
In [126]: ts.head()
Out[126]:
1990‑03‑01 09:00 ‑0.902937
1990‑06‑01 09:00 0.068159
1990‑09‑01 09:00 ‑0.057873
1990‑12‑01 09:00 ‑0.368204
1991‑03‑01 09:00 ‑1.144073
Freq: H, dtype: float64
(分类)
pandas的DataFrame支持categorical类型的数据。完整文档,请查阅:Categorical介绍和API文档。
5.10 Categoricals
In [127]: df = pd.DataFrame({"id":[1,2,3,4,5,6], "raw_grade":['a', 'b', 'b', 'a', 'a', 'e']})
将原生的grade转换为categorical数据类型:
In [128]: df["grade"] = df["raw_grade"].astype("category")
In [129]: df["grade"]
Out[129]:
0 a
1 b
2 b
3 a
4 a
5 e
Name: grade, dtype: category
Categories (3, object): [a, b, e]
重命名为更有意义的名称(赋值给 Series.cat.categories 即可):
In [130]: df["grade"].cat.categories = ["very good", "good", "very bad"]
重新排序类别,同时添加缺失类别( Series.cat 下面的方法默认返回新的 Series ):
In [131]: df["grade"] = df["grade"].cat.set_categories(["very bad", "bad", "medium", "good", "ver
y good"])
In [132]: df["grade"]
Out[132]:
0 very good
1
good
2
good
3 very good
4 very good
5 very bad
Name: grade, dtype: category
Categories (5, object): [very bad, bad, medium, good, very good]
排序是按类别顺序排序而不是语法顺序:
In [133]: df.sort_values(by="grade")
Out[133]:
id raw_grade
grade
5 6
e very bad
1 2
b
good
2 3
b
good
0 1
a very good
3 4
a very good
4 5
a very good
按类别列分组也显示空类别:
In [134]: df.groupby("grade").size()
Out[134]:
grade
very bad 1
bad
0
medium
0
good
2
very good 3
dtype: int64
绘图
详细介绍,请查阅:绘图文档。
5.11
译者注:该部分使用原文档的图形,链接可能失效。
In [135]: ts = pd.Series(np.random.randn(1000), index=pd.date_range('1/1/2000', periods=1000))
In [136]: ts = ts.cumsum()
In [137]: ts.plot()
Out[137]: <matplotlib.axes._subplots.AxesSubplot at 0x1122395c0>
在DataFrame中,plot()可以非常方便的按标签绘制每列数据:
In [138]: df = pd.DataFrame(np.random.randn(1000, 4), index=ts.index,
.....:
columns=['A', 'B', 'C', 'D'])
.....:
In [139]: df = df.cumsum()
In [140]: plt.figure(); df.plot(); plt.legend(loc='best')
Out[140]: <matplotlib.legend.Legend at 0x112541f60>
image
5.12
数据IO
5.12.1 CSV
写入csv文件
In [141]: df.to_csv('foo.csv')
从csv文件读取数据
In [142]: pd.read_csv('foo.csv')
Out[142]:
Unnamed: 0
A
B
C
0
2000‑01‑01 0.266457 ‑0.399641 ‑0.219582
1
2000‑01‑02 ‑1.170732 ‑0.345873
1.653061
2
2000‑01‑03 ‑1.734933
0.530468
2.060811
3
2000‑01‑04 ‑1.555121
1.452620
0.239859
4
2000‑01‑05 0.578117
0.511371
0.103552
5
2000‑01‑06 0.478344
0.449933 ‑0.741620
6
2000‑01‑07 1.235339 ‑0.091757 ‑1.543861
.. ... ... ... ... ...
993 2002‑09‑20 ‑10.628548 ‑9.153563 ‑7.883146
994 2002‑09‑21 ‑10.390377 ‑8.727491 ‑6.399645
995 2002‑09‑22 ‑8.985362 ‑8.485624 ‑4.669462
996 2002‑09‑23 ‑9.558560 ‑8.781216 ‑4.499815
997 2002‑09‑24 ‑9.902058 ‑9.340490 ‑4.386639
998 2002‑09‑25 ‑10.216020 ‑9.480682 ‑3.933802
999 2002‑09‑26 ‑11.856774 ‑10.671012 ‑3.216025
[1000 rows x 5 columns]
D
1.186860
‑0.282953
‑0.515536
‑1.156896
‑2.428202
‑1.962409
‑1.084753
28.313940
30.914107
31.367740
30.518439
30.105593
29.758560
29.369368
5.12.2 HDF5
读写HDFStores 写入HDF5存储:
In [143]: df.to_hdf('foo.h5','df')
从HDF5存储读取数据:
In [144]: pd.read_hdf('foo.h5','df')
Out[144]:
A
B
C
D
2000‑01‑01 0.266457 ‑0.399641 ‑0.219582 1.186860
2000‑01‑02 ‑1.170732 ‑0.345873
1.653061 ‑0.282953
2000‑01‑03 ‑1.734933
0.530468
2.060811 ‑0.515536
2000‑01‑04 ‑1.555121
1.452620
0.239859 ‑1.156896
2000‑01‑05 0.578117
0.511371
0.103552 ‑2.428202
2000‑01‑06 0.478344
0.449933 ‑0.741620 ‑1.962409
2000‑01‑07 1.235339 ‑0.091757 ‑1.543861 ‑1.084753
... ... ... ... ...
2002‑09‑20 ‑10.628548 ‑9.153563 ‑7.883146 28.313940
2002‑09‑21 ‑10.390377 ‑8.727491 ‑6.399645 30.914107
2002‑09‑22 ‑8.985362 ‑8.485624 ‑4.669462 31.367740
2002‑09‑23 ‑9.558560 ‑8.781216 ‑4.499815 30.518439
2002‑09‑24 ‑9.902058 ‑9.340490 ‑4.386639 30.105593
2002‑09‑25 ‑10.216020 ‑9.480682 ‑3.933802 29.758560
2002‑09‑26 ‑11.856774 ‑10.671012 ‑3.216025 29.369368
[1000 rows x 4 columns]
5.12.3 Excel
读写MS Excel
写入excel文件:
In [145]: df.to_excel('foo.xlsx', sheet_name='Sheet1')
从excel文件读取数据:
In [146]: pd.read_excel('foo.xlsx', 'Sheet1', index_col=None, na_values=['NA'])
Out[146]:
A
B
C
D
2000‑01‑01 0.266457 ‑0.399641 ‑0.219582 1.186860
2000‑01‑02 ‑1.170732 ‑0.345873
1.653061 ‑0.282953
2000‑01‑03 ‑1.734933
0.530468
2.060811 ‑0.515536
2000‑01‑04 ‑1.555121
1.452620
0.239859 ‑1.156896
2000‑01‑05 0.578117
0.511371
0.103552 ‑2.428202
2000‑01‑06 0.478344
0.449933 ‑0.741620 ‑1.962409
2000‑01‑07 1.235339 ‑0.091757 ‑1.543861 ‑1.084753
... ... ... ... ...
2002‑09‑20 ‑10.628548 ‑9.153563 ‑7.883146 28.313940
2002‑09‑21 ‑10.390377 ‑8.727491 ‑6.399645 30.914107
2002‑09‑22 ‑8.985362 ‑8.485624 ‑4.669462 31.367740
2002‑09‑23 ‑9.558560 ‑8.781216 ‑4.499815 30.518439
2002‑09‑24 ‑9.902058 ‑9.340490 ‑4.386639 30.105593
2002‑09‑25 ‑10.216020 ‑9.480682 ‑3.933802 29.758560
2002‑09‑26 ‑11.856774 ‑10.671012 ‑3.216025 29.369368
[1000 rows x 4 columns]
(陷阱)
如果尝试某种操作时遇到类似下面的异常:
5.13 Gotchas
>>> if pd.Series([False, True, False]):
print("I was true")
Traceback
...
ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().
查看 Comparisons ,获得关于该异常的解释和处理方法。
查看 Gotchas 也可以。
教程
本章是学习pandas的教程,主要面向新手。
内部指南
pandas有一个简版教程:10分钟了解pandas。 更多详细教程,可以查阅:cookbook。
6.1
6.2 pandas Cookbook
(作者是Julia Evans)的目标是提供一些具体的例子来学习pandas。这些例子包含真实的数据、bug和异常点。
这里是v0.2版本的链接。到pandas-cookbook GitHub repository)查阅最新的内容列表。运行本教程的例子,需要克隆一份repo,并且运行
IPython Notebook。查看如何使用本教程。
快速了解IPython Notebook:展示了IPython非常棒的tab自动补全功能和魔术方法。
第一章:读取数据非常简单,即使编码是错的。
第二章:如何从pandas数据框中选择数据不是很明显,这一章解释了基本的方法(如何做切片,如何获得列数据)。
第三章:这一章正式介绍数据切片/切割,按照复杂条件过滤数据,非常快。
第四章:分组聚合是我最喜欢的pandas功能,我一直在用。必读。
第五章:这一章你会发现蒙特利尔的冬天是不是寒冷的(是)。使用pandas爬取网页数据非常有趣。这一章我们组合使用DataFrame。
第六章:pandas的字符串操作非常厉害。它包含所有的向量化的操作,这是最好的。我们会在转眼之间把一组包含“Snow”的字符串转换
为向量。
第七章:数据清理非常乏味,但使用pandas来做很简单。
第八章:第一次解析Unix时间戳是令人困惑的,但使用pandas来做也非常简单。
cookbook
新手教程
更多的学习资源,访问这里。
第一课:导入库 - 创建数据集 - 创建数据框 - 从csv文件读取数据 - 导出数据到csv文件 - 找到最大值 - 绘图。
第二课:从txt文件读取数据 - 导出数据到txt文件 - 选取头部或尾部数据 - 统计描述 - 分组/排序数据。
第三课:创建函数 - 从Excel文件读取数据 - 导出数据到Excel文件 - 离群值 - Lambda函数 - 数据切片/切割。
第四课:添加/删除列 - 索引操作。
第五课:堆叠/拆分/转置函数。
第六课:groupby函数。
第七课:离群值的计算方法。
第八课:从Microsoft SQL数据库读取数据。
第九课:导出数据到csv/Excel/txt文件。
第十课:不同格式之间的转换。
第十一课:不同数据源之间的融合。
6.3
数据分析实践
这篇指南是使用Python生态系统和一组有趣的开放数据进行数据分析的综合介绍。教程分为四个主题:
数据规整
数据聚合
数据可视化
实践序列
6.4 Python
新手练习
使用真实的数据集和练习题实践技能。更多的资源,请访问这里。
01 - 获取数据,了解数据
02 - 过滤、排序
03 - 分组
04 - 调用函数
05 - 合并
06 - 统计
07 - 可视化
08 - 创建Series和DataFrame
09 - 时间序列
10 - 删除
6.5
6.6 Modern Pandas
Modern Pandas
方法链
索引
性能
整理数据
可视化
6.7
6.8
使用pandas、Vincent和xlsxwrite在Excel中作图
使用pandas和xlsxwriter在Excel中作图
各种教程
Pandas中文社区公众号
使用Scipy和pandas数据框做统计分析,Randal Olson
统计数据分析教程视频,Christopher Fonnesbeck, Scipy 2013
金融数据分析,Thomas Wiecki
pandas数据结构介绍,Greg Reda
pandas和Python的10个知识点,Manish Amde
pandas教程,Mikhail Smeniuk
pandas DataFrame教程,Karlijn Willems
真实生活示例简明教程
第七章 Pandas Cookbook
本章是一些简洁明了的例子的仓库和有用的pandas方法的链接。我们鼓励用户为本章添加文档。
为本部分内容添加有意思的链接或在线示例,会是非常棒的首次pull request。
简单、扼要、对新手友好的在线示例已经添加进有可能扩大Stack Overflow和GitHub链接的地方。许多链接在提供在线示例之外也提供扩展信
息。
只有pandas(pd)和Numpy(np)是默认引入的模块。其余的模块对新手都是显式引入的。 这些示例是使用Python3.4写的,使用更早的
Python版本的用户可能需要一些小的调整。
译者注:Stack Overflow上的一些示例可能有些过时,例如,最新版本的pandas不建议使用 .ix ,因为后面的版本会删除该用法。
惯例
以下这些是pandas的一些惯用操作
在一列上使用if-then或if-then-else条件语句,赋值给另一(多)列
7.1
In [1]: df = pd.DataFrame(
...:
{'AAA' : [4,5,6,7],
'BBB' : [10,20,30,40],
'CCC' : [100,50,‑30,‑50]}
);
df
...:
Out[1]:
AAA BBB CCC
0
4 10 100
1
5 20 50
2
6 30 ‑30
3
7 40 ‑50
条件语句
在一列上使用if-then条件语句:
7.1.1 if-then
In [2]: df.loc[df.AAA >= 5,'BBB'] = ‑1; df
Out[2]:
AAA BBB CCC
0
4 10 100
1
5 ‑1 50
2
6 ‑1 ‑30
3
7 ‑1 ‑50
条件语句作用于两列:
if-then
In [3]: df.loc[df.AAA >= 5,['BBB','CCC']] = 555; df
Out[3]:
AAA BBB CCC
0
4
10 100
1
5 555 555
2
6 555 555
3
7 555 555
不同逻辑作用于另外的行(else的情况):
In [4]: df.loc[df.AAA < 5,['BBB','CCC']] = 2000; df
Out[4]:
AAA
BBB
CCC
0
4 2000 2000
1
5
555
555
2
6
555
555
3
7
555
555
使用 mask :
In [5]: df_mask = pd.DataFrame({'AAA' : [True] * 4, 'BBB' : [False] * 4,'CCC' : [True,False] * 2
})
In [6]: df.where(df_mask,‑1000)
Out[6]:
AAA
BBB
CCC
0
4 ‑1000 2000
1
5 ‑1000 ‑1000
2
6 ‑1000
555
3
7 ‑1000 ‑1000
使用 Numpy 的 where() 方法:
In [7]: df = pd.DataFrame(
...:
{'AAA' : [4,5,6,7], 'BBB' : [10,20,30,40],'CCC' : [100,50,‑30,‑50]}); df
...:
Out[7]:
AAA BBB CCC
0
4
10 100
1
5
20
50
2
6
30 ‑30
3
7
40 ‑50
In [8]: df['logic'] = np.where(df['AAA'] > 5,'high','low'); df
Out[8]:
AAA BBB CCC logic
0
4
10 100
low
1
5
20
50
low
2
6
30 ‑30 high
3
7
40 ‑50 high
分解
使用布尔条件分解数据
7.1.2
In [9]: df = pd.DataFrame(
...:
{'AAA' : [4,5,6,7], 'BBB' : [10,20,30,40],'CCC' : [100,50,‑30,‑50]}); df
...:
Out[9]:
AAA BBB CCC
0
4
10 100
1
5
20
50
2
6
30 ‑30
3
7
40 ‑50
In [10]: dflow = df[df.AAA <= 5]; dflow
Out[10]:
AAA BBB CCC
0
4
10 100
1
5
20
50
In [11]: dfhigh = df[df.AAA > 5]; dfhigh
Out[11]:
AAA BBB CCC
2
6
30 ‑30
3
7
40 ‑50
构造条件
基于多列条件的筛选
创建DataFrame
7.1.3
In [12]: df = pd.DataFrame(
....:
{'AAA' : [4,5,6,7], 'BBB' : [10,20,30,40],'CCC' : [100,50,‑30,‑50]}); df
....:
Out[12]:
AAA BBB CCC
0
4
10 100
1
5
20
50
2
6
30 ‑30
3
7
40 ‑50
与 (不赋值给同一个DataFrame,返回一个Series)
In [13]: newseries = df.loc[(df['BBB'] < 25) & (df['CCC'] >= ‑40), 'AAA']; newseries
Out[13]:
0
4
1
5
Name: AAA, dtype: int64
或 (不赋值给同一个DataFrame,返回一个Series)
In [14]: newseries = df.loc[(df['BBB'] > 25) | (df['CCC'] >= ‑40), 'AAA']; newseries;
或 (赋值给同一个DataFrame,修改对应位置的值)
In [15]: df.loc[(df['BBB'] > 25) | (df['CCC'] >= 75), 'AAA'] = 0.1; df
Out[15]:
AAA BBB CCC
0 0.1
10 100
1 5.0
20
50
2 0.1
30 ‑30
3 0.1
40 ‑50
使用 argsort 选择离给定值最近的值
In [16]: df = pd.DataFrame(
....:
{'AAA' : [4,5,6,7], 'BBB' : [10,20,30,40],'CCC' : [100,50,‑30,‑50]}); df
....:
Out[16]:
AAA BBB CCC
0
4
10 100
1
5
20
50
2
6
30 ‑30
3
7
40 ‑50
In [17]: aValue = 43.0
In [18]: df.loc[(df.CCC‑aValue).abs().argsort()]
Out[18]:
AAA BBB CCC
1
5
20
50
0
4
10 100
2
6
30 ‑30
3
7
40 ‑50
动态地归纳二元操作符条件列表
In [19]: df = pd.DataFrame(
....:
{'AAA' : [4,5,6,7], 'BBB' : [10,20,30,40],'CCC' : [100,50,‑30,‑50]}); df
....:
Out[19]:
AAA BBB CCC
0
4
10 100
1
5
20
50
2
6
30 ‑30
3
7
40 ‑50
In [20]: Crit1 = df.AAA <= 5.5
In [21]: Crit2 = df.BBB == 10.0
In [22]: Crit3 = df.CCC > ‑40.0
硬编码 :
In [23]: AllCrit = Crit1 & Crit2 & Crit3
或者 动态创建条件列表 :
In [24]: CritList = [Crit1,Crit2,Crit3]
In [25]: AllCrit = functools.reduce(lambda x,y: x & y, CritList)
In [26]: df[AllCrit]
Out[26]:
AAA BBB CCC
0
4
10 100
7.2
筛选
7.2.1 DataFrames
索引文档
既使用行标签也使用值条件
In [27]: df = pd.DataFrame(
....:
{'AAA' : [4,5,6,7], 'BBB' : [10,20,30,40],'CCC' : [100,50,‑30,‑50]}); df
....:
Out[27]:
AAA BBB CCC
0
4
10 100
1
5
20
50
2
6
30 ‑30
3
7
40 ‑50
In [28]: df[(df.AAA <= 6) & (df.index.isin([0,2,4]))]
Out[28]:
AAA BBB CCC
0
4
10 100
2
6
30 ‑30
使用 loc 进行标签方向的索引,使用iloc进行位置索引
In [29]: data = {'AAA' : [4,5,6,7], 'BBB' : [10,20,30,40],'CCC' : [100,50,‑30,‑50]}
In [30]: df = pd.DataFrame(data=data,index=['foo','bar','boo','kar']); df
Out[30]:
AAA BBB CCC
foo
4
10 100
bar
5
20
50
boo
6
30 ‑30
kar
7
40 ‑50
这两个显式的索引方法,有三种一般情况:
位置方向的索引:Python索引风格 : 不包含结尾
标签方向的索引:非Python索引风格 : 包含结尾
一般情况, : 是不是包含结尾,需要根据索引是否包含标签或位置来判断
In [31]: df.loc['bar':'kar'] #Label
Out[31]:
AAA BBB CCC
bar
5
20
50
boo
6
30 ‑30
kar
7
40 ‑50
# Generic
In [32]: df.iloc[0:3]
Out[32]:
AAA BBB CCC
foo
4
10 100
bar
5
20
50
boo
6
30 ‑30
In [33]: df.loc['bar':'kar']
Out[33]:
AAA BBB CCC
bar
5
20
50
boo
6
30 ‑30
kar
7
40 ‑50
当索引由非零整数开始或不是按单位增加时,会产生歧义
In [34]: df2 = pd.DataFrame(data=data,index=[1,2,3,4]); #Note index starts at 1.
In [35]: df2.iloc[1:3] #Position‑oriented
Out[35]:
AAA BBB CCC
2
5
20
50
3
6
30 ‑30
In [36]: df2.loc[1:3] #Label‑oriented
Out[36]:
AAA BBB CCC
1
4
10 100
2
5
20
50
3
6
30 ‑30
使用否运算( ~ )取一组条件的补集
In [37]: df = pd.DataFrame(
....:
{'AAA' : [4,5,6,7], 'BBB' : [10,20,30,40], 'CCC' : [100,50,‑30,‑50]}); df
....:
Out[37]:
AAA BBB CCC
0
4
10 100
1
5
20
50
2
6
30 ‑30
3
7
40 ‑50
In [38]: df[~((df.AAA <= 6) & (df.index.isin([0,2,4])))]
Out[38]:
AAA BBB CCC
1
5
20
50
3
7
40 ‑50
7.2.2 Panels
通过转置扩展panel,增加一个新的维度,然后转置回原来的维度
In [39]: rng = pd.date_range('1/1/2013',periods=100,freq='D')
In [40]: data = np.random.randn(100, 4)
In [41]: cols = ['A','B','C','D']
In [42]: df1, df2, df3 = pd.DataFrame(data, rng, cols), pd.DataFrame(data, rng, cols), pd.DataFra
me(data, rng, cols)
In [43]: pf = pd.Panel({'df1':df1,'df2':df2,'df3':df3});pf
Out[43]:
<class 'pandas.core.panel.Panel'>
Dimensions: 3 (items) x 100 (major_axis) x 4 (minor_axis)
Items axis: df1 to df3
Major_axis axis: 2013‑01‑01 00:00:00 to 2013‑04‑10 00:00:00
Minor_axis axis: A to D
In [44]: pf.loc[:,:,'F'] = pd.DataFrame(data, rng, cols);pf
Out[44]:
<class 'pandas.core.panel.Panel'>
Dimensions: 3 (items) x 100 (major_axis) x 5 (minor_axis)
Items axis: df1 to df3
Major_axis axis: 2013‑01‑01 00:00:00 to 2013‑04‑10 00:00:00
Minor_axis axis: A to F
使用np.where创建一个新的panel,然后使用新的条件数值重塑panel
7.2.3
新的列
使用applymap来高效地、动态地创建新的列
In [45]: df = pd.DataFrame(
....:
{'AAA' : [1,2,1,3], 'BBB' : [1,1,2,2], 'CCC' : [2,1,3,1]}); df
....:
Out[45]:
AAA BBB CCC
0
1
1
2
1
2
1
1
2
1
2
3
3
3
2
1
In [46]: source_cols = df.columns # or some subset would work too.
In [47]: new_cols = [str(x) + "_cat" for x in source_cols]
In [48]: categories = {1 : 'Alpha', 2 : 'Beta', 3 : 'Charlie' }
In [49]: df[new_cols] = df[source_cols].applymap(categories.get);df
Out[49]:
AAA BBB CCC AAA_cat BBB_cat CCC_cat
0
1
1
2
Alpha
Alpha
Beta
1
2
1
1
Beta
Alpha
Alpha
2
1
2
3
Alpha
Beta Charlie
3
3
2
1 Charlie
Beta
Alpha
后使用min()时来保持其他列
groupby
In [50]: df = pd.DataFrame(
....:
{'AAA' : [1,1,1,2,2,2,3,3], 'BBB' : [2,1,3,4,5,1,2,3]}); df
....:
Out[50]:
AAA BBB
0
1
2
1
1
1
2
1
3
3
2
4
4
2
5
5
2
1
6
3
2
7
3
3
方法1:使用 idxmin() 获得最小值的索引
In [51]: df.loc[df.groupby("AAA")["BBB"].idxmin()]
Out[51]:
AAA BBB
1
1
1
5
2
1
6
3
2
方法2:排序,然后每组取第一个
In [52]: df.sort_values(by="BBB").groupby("AAA", as_index=False).first()
Out[52]:
AAA BBB
0
1
1
1
2
1
2
3
2
注意:除了索引之外,这两个方法的结果是一样的。
7.3
多重索引
多重索引文档
用标签化的数据框创建多级索引
In [53]: df = pd.DataFrame({'row' : [0,1,2],
....:
'One_X' : [1.1,1.1,1.1],
....:
'One_Y' : [1.2,1.2,1.2],
....:
'Two_X' : [1.11,1.11,1.11],
....:
'Two_Y' : [1.22,1.22,1.22]}); df
....:
Out[53]:
row One_X One_Y Two_X Two_Y
0
0
1.1
1.2
1.11
1.22
1
1
1.1
1.2
1.11
1.22
2
2
1.1
1.2
1.11
1.22
# As Labelled Index
In [54]: df = df.set_index('row');df
Out[54]:
One_X One_Y Two_X Two_Y
row
0
1.1
1.2
1.11
1.22
1
1.1
1.2
1.11
1.22
2
1.1
1.2
1.11
1.22
# With Hierarchical Columns
In [55]: df.columns = pd.MultiIndex.from_tuples([tuple(c.split('_')) for c in df.columns]);df
Out[55]:
One
Two
X
Y
X
Y
row
0
1.1 1.2 1.11 1.22
1
1.1 1.2 1.11 1.22
2
1.1 1.2 1.11 1.22
# Now stack & Reset
In [56]: df = df.stack(0).reset_index(1);df
Out[56]:
level_1
X
Y
row
0
One 1.10 1.20
0
Two 1.11 1.22
1
One 1.10 1.20
1
Two 1.11 1.22
2
One 1.10 1.20
2
Two 1.11 1.22
# And fix the labels ﴾Notice the label 'level_1' got added automatically﴿
In [57]: df.columns = ['Sample','All_X','All_Y'];df
Out[57]:
Sample All_X All_Y
row
0
One
1.10
1.20
0
Two
1.11
1.22
1
One
1.10
1.20
1
Two
1.11
1.22
2
One
1.10
1.20
2
Two
1.11
1.22
算术
需要广播的多级索引的算术运算
7.3.1
In [58]: cols = pd.MultiIndex.from_tuples([ (x,y) for x in ['A','B','C'] for y in ['O','I']])
In [59]: df = pd.DataFrame(np.random.randn(2,6),index=['n','m'],columns=cols); df
Out[59]:
A
B
C
O
I
O
I
O
I
n 1.920906 ‑0.388231 ‑2.314394 0.665508 0.402562 0.399555
m ‑1.765956 0.850423 0.388054 0.992312 0.744086 ‑0.739776
In [60]: df = df.div(df['C'],level=1); df
Out[60]:
A
B
O
I
O
I
n 4.771702 ‑0.971660 ‑5.749162 1.665625
m ‑2.373321 ‑1.149568 0.521518 ‑1.341367
C
O
1.0
1.0
I
1.0
1.0
索引
使用 xs 进行多重索引
7.3.2
In [61]: coords = [('AA','one'),('AA','six'),('BB','one'),('BB','two'),('BB','six')]
In [62]: index = pd.MultiIndex.from_tuples(coords)
In [63]: df = pd.DataFrame([11,22,33,44,55],index,['MyData']); df
Out[63]:
MyData
AA one
11
six
22
BB one
33
two
44
six
55
得到第一级索引和第一个轴的横截面
In [64]: df.xs('BB',level=0,axis=0)
Out[64]:
MyData
one
33
two
44
six
55
#Note : level and axis are optional, and default to zero
第二季索引和第一个轴
In [65]: df.xs('six',level=1,axis=0)
Out[65]:
MyData
AA
22
BB
55
使用 xs 进行多重索引,方法2
```python In [66]: index = list(itertools.product(['Ada','Quinn','Violet'],['Comp','Math','Sci']))
In [67]: headr = list(itertools.product(['Exams','Labs'],['I','II']))
In [68]: indx = pd.MultiIndex.from_tuples(index,names=['Student','Course'])
In [69]: cols = pd.MultiIndex.from_tuples(headr) #Notice these are un-named
In [70]: data = [[70+x+y+(x*y)%3 for x in range(4)] for y in range(9)]
In [71]: df = pd.DataFrame(data,indx,cols); df Out[71]: Exams Labs
I II I II Student Course
Ada Comp 70 71 72 73 Math 71 73 75 74 Sci 72 75 75 75 Quinn Comp 73 74 75 76 Math 74 76 78 77 Sci 75 78 78 78 Violet Comp 76 77
78 79 Math 77 79 81 80 Sci 78 81 81 81
In [72]: All = slice(None)
In [73]: df.loc['Violet'] Out[73]: Exams Labs
I II I II Course
Comp 76 77 78 79 Math 77 79 81 80 Sci 78 81 81 81
In [74]: df.loc[(All,'Math'),All] Out[74]: Exams Labs
I II I II Student Course
Ada Math 71 73 75 74 Quinn Math 74 76 78 77 Violet Math 77 79 81 80
In [75]: df.loc[(slice('Ada','Quinn'),'Math'),All] Out[75]: Exams Labs
I II I II Student Course
Ada Math 71 73 75 74 Quinn Math 74 76 78 77
In [76]: df.loc[(All,'Math'),('Exams')] Out[76]: I II Student Course
Ada Math 71 73 Quinn Math 74 76 Violet Math 77 79
In [77]: df.loc[(All,'Math'),(All,'II')] Out[77]: Exams Labs II II Student Course
Ada Math 73 74 Quinn Math 76 77 Violet Math 79 80 ```
使用 xs 设置多重索引的一部分
排序
对多重索引按照指定的列或排好序的列列表排序
7.3.3
In [78]: df.sort_values(by=('Labs', 'II'), ascending=False)
Out[78]:
Exams
Labs
I II
I II
Student Course
Violet Sci
78 81
81 81
Math
77 79
81 80
Comp
76 77
78 79
Quinn
Sci
75 78
78 78
Math
74 76
78 77
Comp
73 74
75 76
Ada
Sci
72 75
75 75
Math
71 73
75 74
Comp
70 71
72 73
部分选择,排序的需要
等级
在多重索引上增加索引等级
多层索引平整
7.3.4
缺失数据
缺失数据文档
填充一个颠倒的时间序列
7.4
In [79]: df = pd.DataFrame(np.random.randn(6,1), index=pd.date_range('2013‑08‑01', periods=6, fre
q='B'), columns=list('A'))
In [80]: df.loc[df.index[3], 'A'] = np.nan
In [81]: df
Out[81]:
A
2013‑08‑01 ‑1.054874
2013‑08‑02 ‑0.179642
2013‑08‑05 0.639589
2013‑08‑06
NaN
2013‑08‑07 1.906684
2013‑08‑08 0.104050
In [82]: df.reindex(df.index[::‑1]).ffill()
Out[82]:
A
2013‑08‑08 0.104050
2013‑08‑07 1.906684
2013‑08‑06 1.906684
2013‑08‑05 0.639589
2013‑08‑02 ‑0.179642
2013‑08‑01 ‑1.054874
累加重置NaN值
替换
使用反向引用进行替换
7.4.1
分组
分组文档
分组之后使用apply的基本使用
不用于 agg , apply 的调用对象是传入的一个能够让你访问所有列的子DataFrame
7.5
In [83]: df = pd.DataFrame({'animal': 'cat dog cat fish dog cat cat'.split(),
....:
'size': list('SSMMMLL'),
....:
'weight': [8, 10, 11, 1, 20, 12, 12],
....:
'adult' : [False] * 5 + [True] * 2}); df
....:
Out[83]:
animal size weight adult
0
cat
S
8 False
1
dog
S
10 False
2
cat
M
11 False
3
fish
M
1 False
4
dog
M
20 False
5
cat
L
12
True
6
cat
L
12
True
#List the size of the animals with the highest weight.
In [84]: df.groupby('animal').apply(lambda subf: subf['size'][subf['weight'].idxmax()])
Out[84]:
animal
cat
L
dog
M
fish
M
dtype: object
使用 get_group
In [85]: gb = df.groupby(['animal'])
In [86]: gb.get_group('cat')
Out[86]:
animal size weight adult
0
cat
S
8 False
2
cat
M
11 False
5
cat
L
12
True
6
cat
L
12
True
应用到一组的不同元素
In [87]: def GrowUp(x):
....:
avg_weight = sum(x[x['size'] == 'S'].weight * 1.5)
....:
avg_weight += sum(x[x['size'] == 'M'].weight * 1.25)
....:
avg_weight += sum(x[x['size'] == 'L'].weight)
....:
avg_weight /= len(x)
....:
return pd.Series(['L',avg_weight,True], index=['size', 'weight', 'adult'])
....:
In [88]: expected_df = gb.apply(GrowUp)
In [89]: expected_df
Out[89]:
size
weight
animal
cat
L 12.4375
dog
L 20.0000
fish
L
1.2500
adult
True
True
True
扩展 apply
In [90]: S = pd.Series([i / 100.0 for i in range(1,11)])
In [91]: def CumRet(x,y):
....:
return x * (1 + y)
....:
In [92]: def Red(x):
....:
return functools.reduce(CumRet,x,1.0)
....:
In [93]: S.expanding().apply(Red, raw=True)
Out[93]:
0
1.010000
1
1.030200
2
1.061106
3
1.103550
4
1.158728
5
1.228251
6
1.314229
7
1.419367
8
1.547110
9
1.701821
dtype: float64
用一组内剩余数据的均值填充缺失值
In [94]: df = pd.DataFrame({'A' : [1, 1, 2, 2], 'B' : [1, ‑1, 1, 2]})
In [95]: gb = df.groupby('A')
In [96]: def replace(g):
....:
mask = g < 0
....:
g.loc[mask] = g[~mask].mean()
....:
return g
....:
In [97]: gb.transform(replace)
Out[97]:
B
0 1.0
1 1.0
2 1.0
3 2.0
用聚合数据排序组
In [98]: df = pd.DataFrame({'code': ['foo', 'bar', 'baz'] * 2,
....:
'data': [0.16, ‑0.21, 0.33, 0.45, ‑0.59, 0.62],
....:
'flag': [False, True] * 3})
....:
In [99]: code_groups = df.groupby('code')
In [100]: agg_n_sort_order = code_groups[['data']].transform(sum).sort_values(by='data')
In [101]: sorted_df = df.loc[agg_n_sort_order.index]
In [102]: sorted_df
Out[102]:
code data
flag
1 bar ‑0.21
True
4 bar ‑0.59 False
0 foo 0.16 False
3 foo 0.45
True
2 baz 0.33 False
5 baz 0.62
True
创建多重聚合列
In [103]: rng = pd.date_range(start="2014‑10‑07",periods=10,freq='2min')
In [104]: ts = pd.Series(data = list(range(10)), index = rng)
In [105]: def MyCust(x):
.....:
if len(x) > 2:
.....:
return x[1] * 1.234
.....:
return pd.NaT
.....:
In [106]: mhc = {'Mean' : np.mean, 'Max' : np.max, 'Custom' : MyCust}
In [107]: ts.resample("5min").apply(mhc)
Out[107]:
Custom 2014‑10‑07 00:00:00
1.234
2014‑10‑07 00:05:00
NaT
2014‑10‑07 00:10:00
7.404
2014‑10‑07 00:15:00
NaT
Max
2014‑10‑07 00:00:00
2
2014‑10‑07 00:05:00
4
2014‑10‑07 00:10:00
7
2014‑10‑07 00:15:00
9
Mean
2014‑10‑07 00:00:00
1
2014‑10‑07 00:05:00
3.5
2014‑10‑07 00:10:00
6
2014‑10‑07 00:15:00
8.5
dtype: object
In [108]: ts
Out[108]:
2014‑10‑07 00:00:00
0
2014‑10‑07 00:02:00
1
2014‑10‑07 00:04:00
2
2014‑10‑07 00:06:00
3
2014‑10‑07 00:08:00
4
2014‑10‑07 00:10:00
5
2014‑10‑07 00:12:00
6
2014‑10‑07 00:14:00
7
2014‑10‑07 00:16:00
8
2014‑10‑07 00:18:00
9
Freq: 2T, dtype: int64
创建数值计数列然后赋值回DataFrame
In [109]: df = pd.DataFrame({'Color': 'Red Red Red Blue'.split(),
.....:
'Value': [100, 150, 50, 50]}); df
.....:
Out[109]:
Color Value
0
Red
100
1
Red
150
2
Red
50
3 Blue
50
In [110]: df['Counts'] = df.groupby(['Color']).transform(len)
In [111]: df
Out[111]:
Color Value
0
Red
100
1
Red
150
2
Red
50
3 Blue
50
Counts
3
3
3
1
基于索引的一列中的数值的组移位
In [112]: df = pd.DataFrame(
.....:
{u'line_race': [10, 10, 8, 10, 10, 8],
.....:
u'beyer': [99, 102, 103, 103, 88, 100]},
.....:
index=[u'Last Gunfighter', u'Last Gunfighter', u'Last Gunfighter',
.....:
u'Paynter', u'Paynter', u'Paynter']); df
.....:
Out[112]:
line_race beyer
Last Gunfighter
10
99
Last Gunfighter
10
102
Last Gunfighter
8
103
Paynter
10
103
Paynter
10
88
Paynter
8
100
In [113]: df['beyer_shifted'] = df.groupby(level=0)['beyer'].shift(1)
In [114]: df
Out[114]:
Last Gunfighter
Last Gunfighter
Last Gunfighter
Paynter
Paynter
Paynter
line_race
10
10
8
10
10
8
beyer
99
102
103
103
88
100
beyer_shifted
NaN
99.0
102.0
NaN
103.0
88.0
选择每组数据中的最大值
In [115]: df = pd.DataFrame({'host':['other','other','that','this','this'],
.....:
'service':['mail','web','mail','mail','web'],
.....:
'no':[1, 2, 1, 2, 1]}).set_index(['host', 'service'])
.....:
In [116]: mask = df.groupby(level=0).agg('idxmax')
In [117]: df_count = df.loc[mask['no']].reset_index()
In [118]: df_count
Out[118]:
host service no
0 other
web
2
1
that
mail
1
2
this
mail
2
像Python的 itertools.groupby 那样分组
In [119]: df = pd.DataFrame([0, 1, 0, 1, 1, 1, 0, 1, 1], columns=['A'])
In [120]: df.A.groupby((df.A != df.A.shift()).cumsum()).groups
Out[120]:
{1: Int64Index([0], dtype='int64'),
2: Int64Index([1], dtype='int64'),
3: Int64Index([2], dtype='int64'),
4: Int64Index([3, 4, 5], dtype='int64'),
5: Int64Index([6], dtype='int64'),
6: Int64Index([7, 8], dtype='int64')}
In [121]: df.A.groupby((df.A != df.A.shift()).cumsum()).cumsum()
Out[121]:
0
0
1
1
2
0
3
1
4
2
5
3
6
0
7
1
8
2
Name: A, dtype: int64
扩展数据
数据规整和日期转换
在数据(而不是计数)上执行窗口移动计算
根据时间间隔滚动求平均值
7.5.1
分解
分解一个结构
7.5.2
创建一组DataFrame,使用行之间包含的逻辑描述分解数据。
In [122]: df = pd.DataFrame(data={'Case' : ['A','A','A','B','A','A','B','A','A'],
.....:
'Data' : np.random.randn(9)})
.....:
In [123]: dfs = list(zip(*df.groupby((1*(df['Case']=='B')).cumsum().rolling(window=3,min_periods=
1).median())))[‑1]
In [124]: dfs[0]
Out[124]:
Case
Data
0
A 0.174068
1
A ‑0.439461
2
A ‑0.741343
3
B ‑0.079673
In [125]: dfs[1]
Out[125]:
Case
Data
4
A ‑0.922875
5
A 0.303638
6
B ‑0.917368
In [126]: dfs[2]
Out[126]:
Case
Data
7
A ‑1.624062
8
A ‑0.758514
透视表
透视表文档
局部求和与小计
7.5.3
In [127]: df = pd.DataFrame(data={'Province' : ['ON','QC','BC','AL','AL','MN','ON'],
.....:
'City' : ['Toronto','Montreal','Vancouver','Calgary','Edmonto
n','Winnipeg','Windsor'],
.....:
'Sales' : [13,6,16,8,4,3,1]})
.....:
In [128]: table = pd.pivot_table(df,values=['Sales'],index=['Province'],columns=['City'],aggfunc=
np.sum,margins=True)
In [129]: table.stack('City')
Out[129]:
Sales
Province City
AL
All
12.0
Calgary
8.0
Edmonton
4.0
BC
All
16.0
Vancouver
16.0
MN
All
3.0
Winnipeg
3.0
...
...
All
Calgary
8.0
Edmonton
4.0
Montreal
6.0
Toronto
13.0
Vancouver
16.0
Windsor
1.0
Winnipeg
3.0
[20 rows x 1 columns]
像R中的 pylr 那种的频率表
In [130]: grades = [48,99,75,80,42,80,72,68,36,78]
In [131]: df = pd.DataFrame( {'ID': ["x%d" % r for r in range(10)],
.....:
'Gender' : ['F', 'M', 'F', 'M', 'F', 'M', 'F', 'M', 'M', 'M'],
.....:
'ExamYear': ['2007','2007','2007','2008','2008','2008','2008','200
9','2009','2009'],
.....:
'Class': ['algebra', 'stats', 'bio', 'algebra', 'algebra', 'stats',
'stats', 'algebra', 'bio', 'bio'],
.....:
'Participated': ['yes','yes','yes','yes','no','yes','yes','yes','ye
s','yes'],
.....:
'Passed': ['yes' if x > 50 else 'no' for x in grades],
.....:
'Employed': [True,True,True,False,False,False,False,True,True,False
],
.....:
'Grade': grades})
.....:
In [132]: df.groupby('ExamYear').agg({'Participated': lambda x: x.value_counts()['yes'],
.....:
'Passed': lambda x: sum(x == 'yes'),
.....:
'Employed' : lambda x : sum(x),
.....:
'Grade' : lambda x : sum(x) / len(x)})
.....:
Out[132]:
Participated Passed Employed
Grade
ExamYear
2007
3
2
3 74.000000
2008
3
3
0 68.500000
2009
3
2
2 60.666667
使用逐年的数据绘图
创建年与月交叉列表
In [133]: df = pd.DataFrame({'value': np.random.randn(36)},
.....:
index=pd.date_range('2011‑01‑01', freq='M', periods=36))
.....:
In [134]: pd.pivot_table(df, index=df.index.month, columns=df.index.year,
.....:
values='value', aggfunc='sum')
.....:
Out[134]:
2011
2012
2013
1 ‑0.560859 0.120930 0.516870
2 ‑0.589005 ‑0.210518 0.343125
3 ‑1.070678 ‑0.931184 2.137827
4 ‑1.681101 0.240647 0.452429
5
0.403776 ‑0.027462 0.483103
6
0.609862 0.033113 0.061495
7
0.387936 ‑0.658418 0.240767
8
1.815066 0.324102 0.782413
9
0.705200 ‑1.403048 0.628462
10 ‑0.668049 ‑0.581967 ‑0.880627
11 0.242501 ‑1.233862 0.777575
12 0.313421 ‑3.520876 ‑0.779367
调用
滚动调用来组织数据 - 将嵌入式列表转换成多重索引数据框。
7.5.4
In [135]: df = pd.DataFrame(data={'A' : [[2,4,8,16],[100,200],[10,20,30]], 'B' : [['a','b','c'],[
'jj','kk'],['ccc']]},index=['I','II','III'])
In [136]: def SeriesFromSubList(aList):
.....:
return pd.Series(aList)
.....:
In [137]: df_orgz = pd.concat(dict([ (ind,row.apply(SeriesFromSubList)) for ind,row in df.iterrow
s() ]))
对一个DataFrame滚动调用,返回Series
对多个列滚动调用函数,这个函数在一个Series的一个标量返回之前计算这个Series。
In [138]: df = pd.DataFrame(data=np.random.randn(2000,2)/10000,
.....:
index=pd.date_range('2001‑01‑01',periods=2000),
.....:
columns=['A','B']); df
.....:
Out[138]:
A
B
2001‑01‑01 0.000032 ‑0.000004
2001‑01‑02 ‑0.000001 0.000207
2001‑01‑03 0.000120 ‑0.000220
2001‑01‑04 ‑0.000083 ‑0.000165
2001‑01‑05 ‑0.000047 0.000156
2001‑01‑06 0.000027 0.000104
2001‑01‑07 0.000041 ‑0.000101
...
...
...
2006‑06‑17 ‑0.000034 0.000034
2006‑06‑18 0.000002 0.000166
2006‑06‑19 0.000023 ‑0.000081
2006‑06‑20 ‑0.000061 0.000012
2006‑06‑21 ‑0.000111 0.000027
2006‑06‑22 ‑0.000061 ‑0.000009
2006‑06‑23 0.000074 ‑0.000138
[2000 rows x 2 columns]
In [139]: def gm(aDF,Const):
.....:
v = ((((aDF.A+aDF.B)+1).cumprod())‑1)*Const
.....:
return (aDF.index[0],v.iloc[‑1])
.....:
In [140]: S = pd.Series(dict([ gm(df.iloc[i:min(i+51,len(df)‑1)],5) for i in range(len(df)‑50)
])); S
Out[140]:
2001‑01‑01
‑0.001373
2001‑01‑02
‑0.001705
2001‑01‑03
‑0.002885
2001‑01‑04
‑0.002987
2001‑01‑05
‑0.002384
2001‑01‑06
‑0.004700
2001‑01‑07
‑0.005500
...
2006‑04‑28
‑0.002682
2006‑04‑29
‑0.002436
2006‑04‑30
‑0.002602
2006‑05‑01
‑0.001785
2006‑05‑02
‑0.001799
2006‑05‑03
‑0.000605
2006‑05‑04
‑0.000541
Length: 1950, dtype: float64
对一个DataFrame滚动调用,返回一个标量
对多个列滚动调用函数,这个函数返回一个标量( Volume 加权平均 price )。
In [141]: rng = pd.date_range(start = '2014‑01‑01',periods = 100)
In [142]: df = pd.DataFrame({'Open' : np.random.randn(len(rng)),
.....:
'Close' : np.random.randn(len(rng)),
.....:
'Volume' : np.random.randint(100,2000,len(rng))}, index=rng); df
.....:
Out[142]:
Open
Close Volume
2014‑01‑01 0.011174 ‑0.653039
1581
2014‑01‑02 0.214258 1.314205
1707
2014‑01‑03 ‑1.046922 ‑0.341915
1768
2014‑01‑04 ‑0.752902 ‑1.303586
836
2014‑01‑05 ‑0.410793 0.396288
694
2014‑01‑06 0.648401 ‑0.548006
796
2014‑01‑07 0.737320 0.481380
265
...
...
...
...
2014‑04‑04 0.120378 ‑2.548128
564
2014‑04‑05 0.231661 0.223346
1908
2014‑04‑06 0.952664 1.228841
1090
2014‑04‑07 ‑0.176090 0.552784
1813
2014‑04‑08 1.781318 ‑0.795389
1103
2014‑04‑09 ‑0.753493 ‑0.018815
1456
2014‑04‑10 ‑1.047997 1.138197
1193
[100 rows x 3 columns]
In [143]: def vwap(bars): return ((bars.Close*bars.Volume).sum()/bars.Volume.sum())
In [144]: window = 5
In [145]: s = pd.concat([ (pd.Series(vwap(df.iloc[i:i+window]), index=[df.index[i+window]])) for
i in range(len(df)‑window) ]);
In [146]: s.round(2)
Out[146]:
2014‑01‑06
‑0.03
2014‑01‑07
0.07
2014‑01‑08
‑0.40
2014‑01‑09
‑0.81
2014‑01‑10
‑0.63
2014‑01‑11
‑0.86
2014‑01‑12
‑0.36
...
2014‑04‑04
‑1.27
2014‑04‑05
‑1.36
2014‑04‑06
‑0.73
2014‑04‑07
0.04
2014‑04‑08
0.21
2014‑04‑09
0.07
2014‑04‑10
0.25
Length: 95, dtype: float64
7.6
时间序列
时间之间
在时间之间使用索引器
构造一个不包含周末包含特定时间的时间序列
矢量化查找
聚合、绘制时间序列
将列是小时数据、行是天数据的矩阵转换成连续的时间序列形式的行序列。如何重塑Python pandas DataFrame?
在包含重复值的时间序列上按特定的频率重建索引
计算一个DatatimeIndex中每个月的第一天
7```python In [147]: dates = pd.date_range('2000-01-01', periods=5)
In [148]: dates.to_period(freq='M').to_timestamp() Out[148]: DatetimeIndex(['2000-01-01', '2000-01-01', '2000-01-01', '2000-01-01', '200001-01'], dtype='datetime64[ns]', freq=None) ```
重采样
重采样文档
使用Grouper而不是TimeGrouper来分组数据
包含缺失值的时间分组
合法的Grouper频率参数
使用多重索引分组
使用TimeGrouper和另一个分组创建子分组,然后调用自定义函数
使用自定义周期重采样
不添加新的天重采样当天的数据
重采样分钟数据
使用groupby重采样
7.6.1
合并
Concat文档。Join文档
追加两个有重复索引的DataFrame(模仿R的rbind)
7.7
In [149]: rng = pd.date_range('2000‑01‑01', periods=6)
In [150]: df1 = pd.DataFrame(np.random.randn(6, 3), index=rng, columns=['A', 'B', 'C'])
In [151]: df2 = df1.copy()
视DataFrame的结构,可能需要指定 ignore_index
In [152]: df = df1.append(df2,ignore_index=True); df
Out[152]:
A
B
C
0 ‑0.480676 ‑1.305282 ‑0.212846
1
1.979901 0.363112 ‑0.275732
2 ‑1.433852 0.580237 ‑0.013672
3
1.776623 ‑0.803467 0.521517
4 ‑0.302508 ‑0.442948 ‑0.395768
5 ‑0.249024 ‑0.031510 2.413751
6 ‑0.480676 ‑1.305282 ‑0.212846
7
1.979901 0.363112 ‑0.275732
8 ‑1.433852 0.580237 ‑0.013672
9
1.776623 ‑0.803467 0.521517
10 ‑0.302508 ‑0.442948 ‑0.395768
11 ‑0.249024 ‑0.031510 2.413751
的左连接
DataFrame
In [153]: df = pd.DataFrame(data={'Area' : ['A'] * 5 + ['C'] * 2,
.....:
'Bins' : [110] * 2 + [160] * 3 + [40] * 2,
.....:
'Test_0' : [0, 1, 0, 1, 2, 0, 1],
.....:
'Data' : np.random.randn(7)});df
.....:
Out[153]:
Area Bins Test_0
Data
0
A
110
0 ‑0.378914
1
A
110
1 ‑1.032527
2
A
160
0 ‑1.402816
3
A
160
1 0.715333
4
A
160
2 ‑0.091438
5
C
40
0 1.608418
6
C
40
1 0.753207
In [154]: df['Test_1'] = df['Test_0'] ‑ 1
In [155]: pd.merge(df, df, left_on=['Bins', 'Area','Test_0'], right_on=['Bins', 'Area','Test_1'],
suffixes=('_L','_R'))
Out[155]:
Area Bins Test_0_L
Data_L Test_1_L Test_0_R
Data_R Test_1_R
0
A
110
0 ‑0.378914
‑1
1 ‑1.032527
0
1
A
160
0 ‑1.402816
‑1
1 0.715333
0
2
A
160
1 0.715333
0
2 ‑0.091438
1
3
C
40
0 1.608418
‑1
1 0.753207
0
如何设置索引并连接
类似KDB的asof连接
使用基于数值的条件进行连接
基于一个范围内的数值使用 searchsorted 合并
7.8
绘图
绘图文档
让 matplotlib 看起来像R
设置x轴的最大最小标签
在iPython notebook中绘制多个图表
创建多线图
绘制热力图
为时间序列图添加注释
为时间序列图添加注释 #2
使用 Pandas 、 Vincent 和 xlsxwrite 在Excel文件中生成嵌套图表
为分层变量的每个四分位点绘制盒须图
In [156]: df = pd.DataFrame(
.....:
{u'stratifying_var': np.random.uniform(0, 100, 20),
.....:
u'price': np.random.normal(100, 5, 20)})
.....:
In [157]: df[u'quartiles'] = pd.qcut(
.....:
df[u'stratifying_var'],
.....:
4,
.....:
labels=[u'0‑25%', u'25‑50%', u'50‑75%', u'75‑100%'])
.....:
In [158]: df.boxplot(column=u'price', by=u'quartiles')
Out[158]: <matplotlib.axes._subplots.AxesSubplot at 0x1198cd940>
7.9
数据IO
和
的性能比较
SQL HDF5
7.9.1 CSV
文档
CSV
实战
追加到一个csv文件
按块读取csv数据
读取数据结构开始的一些行
读取不是用gzip或bz2(这两种压缩方法是 read_csv 能理解的原生压缩格式)压缩的压缩文件。后面的这个示例展示了(如何处理)一个用
winzip压缩的文件,但是是一个使用上下文管理器打开并且使用句柄读取的一般应用程序。查看这里。
(译者注:这里原文有语病,大概的意思是,这个链接介绍了处理压缩文件的一般方法。不过,如果使用gzip或bz2压缩的话,read_csv自己
可以处理)
read_csv
从文件推断dtype
处理包含脏数据的行
处理包含脏数据的行 #2
读取包含Unix时间戳的csv文件,转换成本地时区时间格式
不写重复值的情况下,写一个多行索引csv文件
读取多个文件,创建一个DataFrame
合并多个文件到一个DataFrame的最好方式是,挨个读取数据框,将它们放入一个列表,然后使用 pd.concat 合并它们。
7.9.2
In [159]: for i in range(3):
.....:
data = pd.DataFrame(np.random.randn(10, 4))
.....:
data.to_csv('file_{}.csv'.format(i))
.....:
In [160]: files = ['file_0.csv', 'file_1.csv', 'file_2.csv']
In [161]: result = pd.concat([pd.read_csv(f) for f in files], ignore_index=True)
可以使用同样的方法读取符合某种模式的所有文件。下面是使用glob的一个示例:
In [162]: import glob
In [163]: files = glob.glob('file_*.csv')
In [164]: result = pd.concat([pd.read_csv(f) for f in files], ignore_index=True)
最后,这个方法也可以和io文档中其他的 pd.read_*(...) 函数一起工作。
解析多个列中的日期元素
使用一个格式解析多个列中的日期元素更快
7.9.3
In [30]: i = pd.date_range('20000101',periods=10000)
In [31]: df = pd.DataFrame(dict(year = i.year, month = i.month, day = i.day))
In [32]: df.head()
Out[32]:
day month year
0
1
1 2000
1
2
1 2000
2
3
1 2000
3
4
1 2000
4
5
1 2000
In [33]: %timeit pd.to_datetime(df.year*10000+df.month*100+df.day,format='%Y%m%d')
100 loops, best of 3: 7.08 ms per loop
# simulate combinging into a string, then parsing
In [34]: ds = df.apply(lambda x: "%04d%02d%02d" % (x['year'],x['month'],x['day']),axis=1)
In [35]: ds.head()
Out[35]:
0
20000101
1
20000102
2
20000103
3
20000104
4
20000105
dtype: object
In [36]: %timeit pd.to_datetime(ds)
1 loops, best of 3: 488 ms per loop
7.9.4
跳过头部和数据之间的行
In [165]: data = """;;;;
.....: ;;;;
.....: ;;;;
.....: ;;;;
.....: ;;;;
.....: ;;;;
.....: ;;;;
.....: ;;;;
.....: ;;;;
.....: ;;;;
.....: date;Param1;Param2;Param4;Param5
.....:
;m²;°C;m²;m
.....: ;;;;
.....: 01.01.1990 00:00;1;1;2;3
.....: 01.01.1990 01:00;5;3;4;5
.....: 01.01.1990 02:00;9;5;6;7
.....: 01.01.1990 03:00;13;7;8;9
.....: 01.01.1990 04:00;17;9;10;11
.....: 01.01.1990 05:00;21;11;12;13
.....: """
.....:
方法1:明确地指出要跳过的行,传递给 skiprows 参数
In [166]: pd.read_csv(StringIO(data), sep=';', skiprows=[11,12],
.....:
index_col=0, parse_dates=True, header=10)
.....:
Out[166]:
Param1 Param2 Param4 Param5
date
1990‑01‑01 00:00:00
1
1
2
3
1990‑01‑01 01:00:00
5
3
4
5
1990‑01‑01 02:00:00
9
5
6
7
1990‑01‑01 03:00:00
13
7
8
9
1990‑01‑01 04:00:00
17
9
10
11
1990‑01‑01 05:00:00
21
11
12
13
方法2:读取列名,然后读取数据
In [167]: pd.read_csv(StringIO(data), sep=';', header=10, nrows=10).columns
Out[167]: Index(['date', 'Param1', 'Param2', 'Param4', 'Param5'], dtype='object')
In [168]: columns = pd.read_csv(StringIO(data), sep=';', header=10, nrows=10).columns
In [169]: pd.read_csv(StringIO(data), sep=';', index_col=0,
.....:
header=12, parse_dates=True, names=columns)
.....:
Out[169]:
Param1 Param2 Param4 Param5
date
1990‑01‑01 00:00:00
1
1
2
3
1990‑01‑01 01:00:00
5
3
4
5
1990‑01‑01 02:00:00
9
5
6
7
1990‑01‑01 03:00:00
13
7
8
9
1990‑01‑01 04:00:00
17
9
10
11
1990‑01‑01 05:00:00
21
11
12
13
7.10 SQL
文档
使用SQL从数据库读取数据
SQL
7.11 Excel
文档
从一个类file的句柄中读取数据
在 XlsxWriter 输出中修改格式
Excel
7.12 HTML
从一个不能处理缺省请求头的服务器上读取HTML表格
7.13 HDFStore
文档
时间戳索引的简单查询
使用相互关联的多表层级管理异构数据
合并包含百万行量级的本地表格
从多个进程/线程写入仓库时避免不一致发生
使用块减少大型仓库的重复,本质上是一个递归减少操作。后面的这个示例展示了一个函数,这个函数用来从csv文件中获得数据,按块创建
仓库,同时进行日期解析。查看这里。
按块读取csv文件,创建一个仓库
追加到一个仓库,同时创建唯一索引
海量数据工作流
HDFStores
从一个文件序列中读取数据,追加到一个仓库的同时提供全局唯一索引
组别密度低的情况下,在一个HDFStore上执行分组
组别密度高的前提下,在一个HDFStore上执行分组
在HDFStore上执行分成查询
在HDFStore上执行计数
HDFStore异常排查
设置字符串的 min_itemsize
使用 ptrepack 在仓库中创建一个完全排序的索引
将属性存储到组节点
In [170]: df = pd.DataFrame(np.random.randn(8,3))
In [171]: store = pd.HDFStore('test.h5')
In [172]: store.put('df',df)
# you can store an arbitrary Python object via pickle
In [173]: store.get_storer('df').attrs.my_attribute = dict(A = 10)
In [174]: store.get_storer('df').attrs.my_attribute
Out[174]: {'A': 10}
7.14
二进制文件
如果你需要从一个有C结构的数组组成的二进制文件中读取数据,Pandas可以很容易地接受Numpy数组。例如,给一个64位机器上名为
main.c 的文件中的由 gcc main.c ‑std=gnu99 编译的C程序,
#include <stdio.h>
#include <stdint.h>
typedef struct _Data
{
int32_t count;
double avg;
float scale;
} Data;
int main(int argc, const char *argv[])
{
size_t n = 10;
Data d[n];
for (int i = 0; i < n; ++i)
{
d[i].count = i;
d[i].avg = i + 1.0;
d[i].scale = (float) i + 2.0f;
}
FILE *file = fopen("binary.dat", "wb");
fwrite(&d, sizeof(Data), n, file);
fclose(file);
return 0;
}
下面的Python代码将读取二进制文件 binary.dat 到Pandas DataFrame,结构体中每一个元素对应到数据结构中的一列:
names = 'count', 'avg', 'scale'
# note that the offsets are larger than the size of the type because of
# struct padding
offsets = 0, 8, 16
formats = 'i4', 'f8', 'f4'
dt = np.dtype({'names': names, 'offsets': offsets, 'formats': formats},
align=True)
df = pd.DataFrame(np.fromfile('binary.dat', dt))
注意:结构体元素的偏移可能会不同,这取决于创建文件的机器的架构。不建议使用这样的原生二进制文件作为一般的数据存
储,因为不能跨平台。我们建议使用HDF5或者msgpack,Pandas的IO工具两种都支持。
计算
(基于抽样的)时间序列的数值积分
7.15
(时间差)
7.16 Timedeltas
文档
Timedeltas
使用Timedeltas
In [175]: s
= pd.Series(pd.date_range('2012‑1‑1', periods=3, freq='D'))
In [176]: s ‑ s.max()
Out[176]:
0
‑2 days
1
‑1 days
2
0 days
dtype: timedelta64[ns]
In [177]: s.max() ‑ s
Out[177]:
0
2 days
1
1 days
2
0 days
dtype: timedelta64[ns]
In [178]: s ‑ datetime.datetime(2011,1,1,3,5)
Out[178]:
0
364 days 20:55:00
1
365 days 20:55:00
2
366 days 20:55:00
dtype: timedelta64[ns]
In [179]: s + datetime.timedelta(minutes=5)
Out[179]:
0
2012‑01‑01 00:05:00
1
2012‑01‑02 00:05:00
2
2012‑01‑03 00:05:00
dtype: datetime64[ns]
In [180]: datetime.datetime(2011,1,1,3,5) ‑ s
Out[180]:
0
‑365 days +03:05:00
1
‑366 days +03:05:00
2
‑367 days +03:05:00
dtype: timedelta64[ns]
In [181]: datetime.timedelta(minutes=5) + s
Out[181]:
0
2012‑01‑01 00:05:00
1
2012‑01‑02 00:05:00
2
2012‑01‑03 00:05:00
dtype: datetime64[ns]
日期和时间差的加、减
In [182]: deltas = pd.Series([ datetime.timedelta(days=i) for i in range(3) ])
In [183]: df = pd.DataFrame(dict(A = s, B = deltas)); df
Out[183]:
A
B
0 2012‑01‑01 0 days
1 2012‑01‑02 1 days
2 2012‑01‑03 2 days
In [184]: df['New Dates'] = df['A'] + df['B'];
In [185]: df['Delta'] = df['A'] ‑ df['New Dates']; df
Out[185]:
A
B New Dates
Delta
0 2012‑01‑01 0 days 2012‑01‑01 0 days
1 2012‑01‑02 1 days 2012‑01‑03 ‑1 days
2 2012‑01‑03 2 days 2012‑01‑05 ‑2 days
In [186]: df.dtypes
Out[186]:
A
datetime64[ns]
B
timedelta64[ns]
New Dates
datetime64[ns]
Delta
timedelta64[ns]
dtype: object
另一示例
使用np.nan,数值会被设置成NAT,就像datetime
In [187]: y = s ‑ s.shift(); y
Out[187]:
0
NaT
1
1 days
2
1 days
dtype: timedelta64[ns]
In [188]: y[1] = np.nan; y
Out[188]:
0
NaT
1
NaT
2
1 days
dtype: timedelta64[ns]
重命名轴名称
使用者可以用两个函数全局重命名轴名称
7.17
In [189]: def set_axis_alias(cls, axis, alias):
.....:
if axis not in cls._AXIS_NUMBERS:
.....:
raise Exception("invalid axis [%s] for alias [%s]" % (axis, alias))
.....:
cls._AXIS_ALIASES[alias] = axis
.....:
In [190]: def clear_axis_alias(cls, axis, alias):
.....:
if axis not in cls._AXIS_NUMBERS:
.....:
raise Exception("invalid axis [%s] for alias [%s]" % (axis, alias))
.....:
cls._AXIS_ALIASES.pop(alias,None)
.....:
In [191]: set_axis_alias(pd.DataFrame,'columns', 'myaxis2')
In [192]: df2 = pd.DataFrame(np.random.randn(3,2),columns=['c1','c2'],index=['i1','i2','i3'])
In [193]: df2.sum(axis='myaxis2')
Out[193]:
i1
0.745167
i2
‑0.176251
i3
0.014354
dtype: float64
In [194]: clear_axis_alias(pd.DataFrame,'columns', 'myaxis2')
创建示例数据
像R的 expand.grid() 函数那样,从给定值的每一个组合中创建DataFrame,我们可以创建一个字典,字典中的键是列名,值是数据值的列
表
7.18
In [195]: def expand_grid(data_dict):
.....:
rows = itertools.product(*data_dict.values())
.....:
return pd.DataFrame.from_records(rows, columns=data_dict.keys())
.....:
In [196]: df = expand_grid(
.....:
{'height': [60, 70],
.....:
'weight': [100, 140, 180],
.....:
'sex': ['Male', 'Female']})
.....:
In [197]: df
Out[197]:
height weight
0
60
100
1
60
100
2
60
140
3
60
140
4
60
180
5
60
180
6
70
100
7
70
100
8
70
140
9
70
140
10
70
180
11
70
180
sex
Male
Female
Male
Female
Male
Female
Male
Female
Male
Female
Male
Female
第八章 数据结构介绍
我们将开始一个快速的,非综合性的对Pandas的基础数据结构的概览。数据类型、索引、轴标签/对齐的基本行为适用于所有的对象。一开
始,我们在命名空间中引入NumPy和Pandas
In [1]: import NumPy as np
In [2]: import pandas as pd
记住一个原则:数据对齐是内在的。除非你明确指定,否则标签和数据之间的联系不会被打破。
我们给出了数据结构的一个简介,然后在单独的小节考虑函数和方法的大的类别。
8.1 Series
是一个一维的标签化数组,能够保存任何数据类型(整型、字符串、浮点型、Python对象等等)的数据。轴标签统称为索引。创建
Series的基本方法:
Series
>>> s = pd.Series(data, index=index)
这里, data 可以是许多不同的事物:
一个Python字典
一个ndarray(NumPy数组)
一个标量(比如5)
传入的 index 是轴标签的列表。所以,按data的不同,这将分为几种情况:
从ndarray
如果 data 是ndarray,index的长度必须和data的长度相同。如果没有传入index,会创建一个默认的值为[0, ..., len(data)-1]的index。
In [3]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
In [4]: s
Out[4]:
a
0.4691
b
‑0.2829
c
‑1.5091
d
‑1.1356
e
1.2121
dtype: float64
In [5]: s.index
Out[5]: Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
In [6]: pd.Series(np.random.randn(5))
Out[6]:
0
‑0.1732
1
0.1192
2
‑1.0442
3
‑0.8618
4
‑2.1046
dtype: float64
注意:Pandas支持不唯一的索引值。如果尝试执行不支持重复索引的操作,会引发异常。懒处理的原因几乎都是基于性能考
虑。许多参与计算的实例都不会使用索引,例如GroupBy的一部分。
从字典
Series可以从字典实例化
In [7]: d = {'b' : 1, 'a' : 0, 'c' : 2}
In [8]: pd.Series(d)
Out[8]:
b
1
a
0
c
2
dtype: int64
注意:当data是一个字典,并且没有传入index时,如果使用的Python版本高于3.6(含)或Pandas版本高于0.23(含),
Series的索引会按照字典的插入顺序排序。
如果使用的Python版本低于3.6或Pandas的版本低于0.23,并且没有传入index,Series的索引会按照字典键的词法顺序排序。
在上面的示例中,如果你使用的Python版本低于3.6或Pandas版本低于0.23,Series会按照字典键的词法顺序排序(["a", "b", "c"]而不是["b",
"a", "c"])。
如果传入了index参数,与索引中的标签对应的数据中的值将被提取出来。
In [9]: d = {'a' : 0., 'b' : 1., 'c' : 2.}
In [10]: pd.Series(d)
Out[10]:
a
0.0
b
1.0
c
2.0
dtype: float64
In [11]: pd.Series(d, index=['b', 'c', 'd', 'a'])
Out[11]:
b
1.0
c
2.0
d
NaN
a
0.0
dtype: float64
注意:NaN(not a number)是Pandas默认表示缺失数据的标记。
从标量
如果data是标量,必须提供index。数据会被复制,以匹配index的长度。
In [12]: pd.Series(5., index=['a', 'b', 'c', 'd', 'e'])
Out[12]:
a
5.0
b
5.0
c
5.0
d
5.0
e
5.0
dtype: float64
类似于ndarray
Series按照类似于ndarray的方式工作,并且可以作为大多数NumPy函数的合法参数。不过,一些类似切片的操作也同时对索引切片。
8.1.1 Series
In [13]: s[0]
Out[13]: 0.46911229990718628
In [14]: s[:3]
Out[14]:
a
0.4691
b
‑0.2829
c
‑1.5091
dtype: float64
In [15]: s[s > s.median()]
Out[15]:
a
0.4691
e
1.2121
dtype: float64
In [16]: s[[4, 3, 1]]
Out[16]:
e
1.2121
d
‑1.1356
b
‑0.2829
dtype: float64
In [17]: np.exp(s)
Out[17]:
a
1.5986
b
0.7536
c
0.2211
d
0.3212
e
3.3606
dtype: float64
我们将在一个单独的小节介绍基于数组的索引。
类似于字典
Series类似于一个固定大小的字典,你可以通过索引标签获取和设置数据:
8.1.2 Series
In [18]: s['a']
Out[18]: 0.46911229990718628
In [19]: s['e'] = 12.
In [20]: s
Out[20]:
a
0.4691
b
‑0.2829
c
‑1.5091
d
‑1.1356
e
12.0000
dtype: float64
In [21]: 'e' in s
Out[21]: True
In [22]: 'f' in s
Out[22]: False
如果一个标签没有包含在Series之内,将会引发异常
>>> s['f']
KeyError: 'f'
使用 get 方法,确实的标签将会返回 None 或指定的默认值
In [23]: s.get('f')
In [24]: s.get('f', np.nan)
Out[24]: nan
查看属性访问小节,获得更多信息。
矢量化操作,标签对齐
操作原生NumPy数组时,循环操作数值通常是不必要的。操作Pandas的Series也是一样。Series也可以作为参数传入那些参数预期类型是
ndarray的方法。
8.1.3
In [25]: s + s
Out[25]:
a
0.9382
b
‑0.5657
c
‑3.0181
d
‑2.2713
e
24.0000
dtype: float64
In [26]: s * 2
Out[26]:
a
0.9382
b
‑0.5657
c
‑3.0181
d
‑2.2713
e
24.0000
dtype: float64
In [27]: np.exp(s)
Out[27]:
a
1.5986
b
0.7536
c
0.2211
d
0.3212
e
162754.7914
dtype: float64
和ndarray的主要区别是,Series之间的操作会自动按照标签对齐数据。所以,你在编写计算指令时,不需要考虑待处理的Series是不是
有相同的标签。
Series
In [28]: s[1:] + s[:‑1]
Out[28]:
a
NaN
b
‑0.5657
c
‑3.0181
d
‑2.2713
e
NaN
dtype: float64
没有对齐的Series的操作的结果会合并涉及的索引。如果一个标签没有在所有Series中发现,结果会被标记成确实值NaN。在不做显式数据对
齐的情况下编写代码,在交互式数据分析和研究中能够提高自由度和灵活性。Pandas数据结构的综合数据对齐功能,将Pandas与大部分相关
的处理标签数据的相关工具区别开来。
注意:一般而言,我们在操作不同索引的对象时,将默认结果设为涉及到的索引的并集,以避免丢失数据。有标签,但是数据
缺失,作为计算的一部分,也是一种典型的重要信息。你当然可以选择使用drop函数丢弃缺失数据的标签。
8.1.4 name
属性
还有一个 name 属性
Series
In [29]: s = pd.Series(np.random.randn(5), name='something')
In [30]: s
Out[30]:
0
‑0.4949
1
1.0718
2
0.7216
3
‑0.7068
4
‑1.0396
Name: something, dtype: float64
In [31]: s.name
Out[31]: 'something'
多数情况下,Series的 name 会被自动赋值,特别是在对DataFrame执行一维切片时,就像下面你将看到的一样。
这是0.18.0版本的新特性。
你可以使用 pandas.Series.rename() 重命名一个Series。
In [32]: s2 = s.rename("different")
In [33]: s2.name
Out[33]: 'different'
注意, s 和 s2 代表不同的对象。
8.2 DataFrame
是一个二维标签化的数据结构,包含不同类型的列。你可以将其想象成一个电子表格或SQL表格,或一个有Series对象组成的字
典。它是
最常使用的对象。像Series一样,DataFrame接受很多不同种类的输入:
一维ndarray组成的字典、列表、字典或Series
二维NumPy.ndarray
结构化或记录ndarray
一个Series
另一个DataFrame
DataFrame
Pandas
传入data的同时,你也可以(可选的)传入index(行标签)和columns(列标签)参数。如果你传入一个index和(或)columns,你正在为
生成的DataFrame指定index和(或)columns。所以,Series字典加上一个特定的index将丢弃所有不匹配传入的index的数据。
如果没有传入轴标签,他们将会根据常识规则从输入的数据中构建。
注意:当data是字典,并且没有指定columns时,如果使用的Python版本高于3.6(含)或Pandas版本高于0.23(含),
DataFrame的列将会按照字典的插入顺序排序。
如果使用的Python版本低于3.6或Pandas版本低于0.23,并且没有指定columns,DataFrame的列将会按照字典的键的词法顺
序排序。
从Series的字典或字典的字典
结果的索引将会是各个Series的索引的并集。如果有嵌套字典,将首先转化为Series。如果没有传入columns,列将会是字典键的排序后的列
表。
In [34]: d = {'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']),
....:
'two' : pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])}
....:
In [35]: df = pd.DataFrame(d)
In [36]: df
Out[36]:
one two
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
In [37]: pd.DataFrame(d, index=['d', 'b', 'a'])
Out[37]:
one two
d NaN 4.0
b 2.0 2.0
a 1.0 1.0
In [38]: pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three'])
Out[38]:
two three
d 4.0
NaN
b 2.0
NaN
a 1.0
NaN
行和列可以分别通过index和columns属性访问:
注意:如果传入data的同时,传入特定的columns集合,传入的columns将会覆盖字典的键。
In [39]: df.index
Out[39]: Index(['a', 'b', 'c', 'd'], dtype='object')
In [40]: df.columns
Out[40]: Index(['one', 'two'], dtype='object')
从ndarray或lists的字典
所有的ndarray必须有相同的长度。如果传入了index,也必须和ndarray有相同的长度。如果没有传入index,结果中index将会是range(n),其
中n是ndarray的长度。
In [41]: d = {'one' : [1., 2., 3., 4.],
....:
'two' : [4., 3., 2., 1.]}
....:
In [42]: pd.DataFrame(d)
Out[42]:
one two
0 1.0 4.0
1 2.0 3.0
2 3.0 2.0
3 4.0 1.0
In [43]: pd.DataFrame(d, index=['a', 'b', 'c', 'd'])
Out[43]:
one two
a 1.0 4.0
b 2.0 3.0
c 3.0 2.0
d 4.0 1.0
从结构化或记录数组
这种情况的处理方式和数组字典的方式是一样的
In [44]: data = np.zeros((2,), dtype=[('A', 'i4'),('B', 'f4'),('C', 'a10')])
In [45]: data[:] = [(1,2.,'Hello'), (2,3.,"World")]
In [46]: pd.DataFrame(data)
Out[46]:
A
B
C
0 1 2.0 b'Hello'
1 2 3.0 b'World'
In [47]: pd.DataFrame(data, index=['first', 'second'])
Out[47]:
A
B
C
first
1 2.0 b'Hello'
second 2 3.0 b'World'
In [48]: pd.DataFrame(data, columns=['C', 'A', 'B'])
Out[48]:
C A
B
0 b'Hello' 1 2.0
1 b'World' 2 3.0
注意:DataFrame并不是像二维NumPy数组那样工作。
从字典组成的列表
In [49]: data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}]
In [50]: pd.DataFrame(data2)
Out[50]:
a
b
c
0 1
2
NaN
1 5 10 20.0
In [51]: pd.DataFrame(data2, index=['first', 'second'])
Out[51]:
a
b
c
first
1
2
NaN
second 5 10 20.0
In [52]: pd.DataFrame(data2, columns=['a', 'b'])
Out[52]:
a
b
0 1
2
1 5 10
从元组组成的字典
你可以通过传入元组组成的字典自动创建多级索引的数据框。
In [53]: pd.DataFrame({('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2},
....:
('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4},
....:
('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6},
....:
('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8},
....:
('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}})
....:
Out[53]:
a
b
b
a
c
a
b
A B 1.0 4.0 5.0 8.0 10.0
C 2.0 3.0 6.0 7.0
NaN
D NaN NaN NaN NaN
9.0
从Series
结果将会是一个和输入的Series拥有同样的索引的DataFrame,只有一列,列名是原始的Series的名称(当没有提供其他的名字时)。
缺失数据
在缺失数据章节将会更多的讨论这个主题。使用包含缺失值的数据构造DataFrame,我们使用 np.nan 来代表缺失值。另一个选择是,你可以
为DataFrame构造函数传入一个 NumPy.MaskedArray 作为 data 参数,它的掩蔽条目将会被认为是缺失的。
8.3
其他构造函数
DataFrame.from_dict
读取字典组成的字典或类似列表的对象组成的字典,返回一个DataFrame。除了将 orient 参数默认置为
columns之外 ,它像DataFrame构造函数那样操作,不过也可以将 orient 置为 index ,以便将字典的键作为行标签。
DataFrame.from_dict
In [54]: pd.DataFrame.from_dict(dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]))
Out[54]:
A B
0 1 4
1 2 5
2 3 6
如果传入 orient='index' ,字典的键将会是行标签。这种情况下,你也可以传入期望的列名:
In [55]: pd.DataFrame.from_dict(dict([('A', [1, 2, 3]), ('B', [4, 5, 6])]),
....:
orient='index', columns=['one', 'two', 'three'])
....:
Out[55]:
one two three
A
1
2
3
B
4
5
6
DataFrame.from_records
读取由元组组成的列表或有结构化类型的ndarray。除了结果DataFrame的索引可能是结构化类型的一个特定字
段之外,它的工作方式和普通的DataFrame构造函数类似。示例:
DataFrame.from_records
In [56]: data
Out[56]:
array([(1, 2., b'Hello'), (2, 3., b'World')],
dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')])
In [57]: pd.DataFrame.from_records(data, index='C')
Out[57]:
A
B
C
b'Hello' 1 2.0
b'World' 2 3.0
选择,增加,删除列
语义上,你可以把DataFrame看做是一个由类索引的Series对象组成的字典。获取、设置、删除列的语法和类似的字典操作是一样的。
8.4
In [58]: df['one']
Out[58]:
a
1.0
b
2.0
c
3.0
d
NaN
Name: one, dtype: float64
In [59]: df['three'] = df['one'] * df['two']
In [60]: df['flag'] = df['one'] > 2
In [61]: df
Out[61]:
one two
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN 4.0
three
1.0
4.0
9.0
NaN
flag
False
False
True
False
可以像操作字典那样删除或取出列
In [62]: del df['two']
In [63]: three = df.pop('three')
In [64]: df
Out[64]:
one
flag
a 1.0 False
b 2.0 False
c 3.0
True
d NaN False
插入一个标量时,将会自然地填充满整列
In [65]: df['foo'] = 'bar'
In [66]: df
Out[66]:
one
flag
a 1.0 False
b 2.0 False
c 3.0
True
d NaN False
foo
bar
bar
bar
bar
插入一个和DataFrame有不同的索引的Series时,将会被统一到DataFrame的索引:
In [67]: df['one_trunc'] = df['one'][:2]
In [68]: df
Out[68]:
one
flag
a 1.0 False
b 2.0 False
c 3.0
True
d NaN False
foo
bar
bar
bar
bar
one_trunc
1.0
2.0
NaN
NaN
你可以插入原生ndarray,但是他们的长度必须匹配DataFrame的索引长度。
默认地,列被插入到尾部。 insert 函数可以指定在特定的位置插入列:
In [69]: df.insert(1, 'bar', df['one'])
In [70]: df
Out[70]:
one bar
a 1.0 1.0
b 2.0 2.0
c 3.0 3.0
d NaN NaN
flag
False
False
True
False
foo
bar
bar
bar
bar
one_trunc
1.0
2.0
NaN
NaN
在方法链中赋值新的列
收dplyr的 mutate 动词的启发, DataFrame 有一个assign()方法,它允许你很容易地从现有的列中派生出新的列。
8.5
In [71]: iris = pd.read_csv('data/iris.data')
In [72]: iris.head()
Out[72]:
SepalLength SepalWidth
0
5.1
3.5
1
4.9
3.0
2
4.7
3.2
3
4.6
3.1
4
5.0
3.6
PetalLength
1.4
1.4
1.3
1.5
1.4
PetalWidth
0.2
0.2
0.2
0.2
0.2
Name
Iris‑setosa
Iris‑setosa
Iris‑setosa
Iris‑setosa
Iris‑setosa
In [73]: (iris.assign(sepal_ratio = iris['SepalWidth'] / iris['SepalLength'])
....:
.head())
....:
Out[73]:
SepalLength SepalWidth PetalLength PetalWidth
Name sepal_ratio
0
5.1
3.5
1.4
0.2 Iris‑setosa
0.6863
1
4.9
3.0
1.4
0.2 Iris‑setosa
0.6122
2
4.7
3.2
1.3
0.2 Iris‑setosa
0.6809
3
4.6
3.1
1.5
0.2 Iris‑setosa
0.6739
4
5.0
3.6
1.4
0.2 Iris‑setosa
0.7200
在上面的例子中,我们插入了一个预先计算的数值。我们也可以传入一个单参数的函数,这个函数会在正在赋值的DataFrame上进行计算。
In [74]: iris.assign(sepal_ratio = lambda x: (x['SepalWidth'] /
....:
x['SepalLength'])).head()
....:
Out[74]:
SepalLength SepalWidth PetalLength PetalWidth
Name sepal_ratio
0
5.1
3.5
1.4
0.2 Iris‑setosa
0.6863
1
4.9
3.0
1.4
0.2 Iris‑setosa
0.6122
2
4.7
3.2
1.3
0.2 Iris‑setosa
0.6809
3
4.6
3.1
1.5
0.2 Iris‑setosa
0.6739
4
5.0
3.6
1.4
0.2 Iris‑setosa
0.7200
一直返回数据的拷贝,源数据会保留。
传入一个可调用对象,而不是实际的数值,在你没有DataFrame的引用时非常实用。这在链式操作中很常见。举例而言,我们可以先限制
Sepal Length大于5的观察值,计算比率,然后绘图:
assign
In [75]: (iris.query('SepalLength > 5')
....:
.assign(SepalRatio = lambda x: x.SepalWidth / x.SepalLength,
....:
PetalRatio = lambda x: x.PetalWidth / x.PetalLength)
....:
.plot(kind='scatter', x='SepalRatio', y='PetalRatio'))
....:
Out[75]: <matplotlib.axes._subplots.AxesSubplot at 0x1c29ff5b70>
函数一旦作为参数传入,会在要赋值的DataFrame上计算。重要的是,这是那个被按Sepal Length大于5的行过滤的DataFrame。首先是过滤,
然后计算比率。这里,我们没有过滤后的DataFrame变量的引用。
assign 的函数签名是简单的 **kwargs 。键,是新的字段的列名;值,要么是一个要插入的值(比如一个Series或NumPy数组),要么是
一个DataFrame调用的单参数函数。插入数据之后,返回源数据的一个拷贝。
以下是0.23.0版本之后的一个修改。
Python3.6版本开始, **kwargs 的顺序被保留。这样可以实现依赖赋值,靠后的 **kwargs 中的表达式可以引用同一个assign()在之前创建
的列。
In [76]: dfa = pd.DataFrame({"A": [1, 2, 3],
....:
"B": [4, 5, 6]})
....:
In [77]: dfa.assign(C=lambda x: x['A'] + x['B'],
....:
D=lambda x: x['A'] + x['C'])
....:
Out[77]:
A B C
D
0 1 4 5
6
1 2 5 7
9
2 3 6 9 12
在第二个表达式中, x['C'] 将引用新创建的列,等于 dfa['A'] + dfa['B'] 。
想要兼容所有版本的Python的话,将赋值操作一分为二:
In [78]: dependent = pd.DataFrame({"A": [1, 1, 1]})
In [79]: (dependent.assign(A=lambda x: x['A'] + 1)
....:
.assign(B=lambda x: x['A'] + 2))
....:
Out[79]:
A B
0 2 4
1 2 4
2 2 4
注意:依赖赋值在Python3.6版本前后之间会有细微的差异。
如果想要同时支持Python3.6前后的版本,执行赋值操作时需要特别注意:
更新现有的列
在同一个assign操作中引用更新后的列
例如,我们将更新列A,然后在创建列B时引用它:
>>> dependent = pd.DataFrame({"A": [1, 1, 1]})
>>> dependent.assign(A=lambda x: x["A"] + 1,
B=lambda x: x["A"] + 2)
对于Python3.5和之前的版本,创建列B的表达式将引用A列的旧值,[1, 1, 1],输出将会是:
A
0
1
2
B
2
2
2
3
3
3
对于Python3.6和之后的版本,创建列B(译者注:此处原文有误)的表达式将引用A列的新值,[2, 2, 2],输出将会是:
A
0
1
2
8.6
B
2
2
2
4
4
4
索引/选择
索引的基本语法如下表:
操作
语法
结果
df[col]
Series
选择一列
Series
按标签选择一行 df.loc[label]
Series
按整数位置选择一行 df.iloc[loc]
df[5:10] DataFrame
行切片
按布尔向量选择行 df[bool_vec] DataFrame
选取行,将会返回一个Series,这个Series的索引是DataFrame的列。
In [80]: df.loc['b']
Out[80]:
one
2
bar
2
flag
False
foo
bar
one_trunc
2
Name: b, dtype: object
In [81]: df.iloc[2]
Out[81]:
one
3
bar
3
flag
True
foo
bar
one_trunc
NaN
Name: c, dtype: object
获得更详尽的处理基于复杂标签的索引和切片的信息,请查看索引章节,我们将会在该章节介绍在新的标签集上重建索引或确保一致的基本原
理。
数据对齐和算术运算
DataFrame对象之间的数据对齐自动对齐在列和索引(行标签)上。再一次,结果对象将会包含列和行标签的并集。
8.6.1
In [82]: df = pd.DataFrame(np.random.randn(10, 4), columns=['A', 'B', 'C', 'D'])
In [83]: df2 = pd.DataFrame(np.random.randn(7, 3), columns=['A', 'B', 'C'])
In [84]: df + df2
Out[84]:
A
B
C
D
0 0.0457 ‑0.0141 1.3809 NaN
1 ‑0.9554 ‑1.5010 0.0372 NaN
2 ‑0.6627 1.5348 ‑0.8597 NaN
3 ‑2.4529 1.2373 ‑0.1337 NaN
4 1.4145 1.9517 ‑2.3204 NaN
5 ‑0.4949 ‑1.6497 ‑1.0846 NaN
6 ‑1.0476 ‑0.7486 ‑0.8055 NaN
7
NaN
NaN
NaN NaN
8
NaN
NaN
NaN NaN
9
NaN
NaN
NaN NaN
当在DataFrame和Series之间操作时,默认的处理方式是,将Series的索引对齐到DataFrame的列上,因此将会进行行式广播。比如:
In [85]: df ‑ df.iloc[0]
Out[85]:
A
B
C
D
0 0.0000 0.0000 0.0000 0.0000
1 ‑1.3593 ‑0.2487 ‑0.4534 ‑1.7547
2 0.2531 0.8297 0.0100 ‑1.9912
3 ‑1.3111 0.0543 ‑1.7249 ‑1.6205
4 0.5730 1.5007 ‑0.6761 1.3673
5 ‑1.7412 0.7820 ‑1.2416 ‑2.0531
6 ‑1.2408 ‑0.8696 ‑0.1533 0.0004
7 ‑0.7439 0.4110 ‑0.9296 ‑0.2824
8 ‑1.1949 1.3207 0.2382 ‑1.4826
9 2.2938 1.8562 0.7733 ‑1.4465
特别地,当处理时间序列数据,并且DataFrame的索引也包含日期时,广播方式将会是列式的:
In [86]: index = pd.date_range('1/1/2000', periods=8)
In [87]: df = pd.DataFrame(np.random.randn(8, 3), index=index, columns=list('ABC'))
In [88]: df
Out[88]:
A
B
C
2000‑01‑01 ‑1.2268 0.7698 ‑1.2812
2000‑01‑02 ‑0.7277 ‑0.1213 ‑0.0979
2000‑01‑03 0.6958 0.3417 0.9597
2000‑01‑04 ‑1.1103 ‑0.6200 0.1497
2000‑01‑05 ‑0.7323 0.6877 0.1764
2000‑01‑06 0.4033 ‑0.1550 0.3016
2000‑01‑07 ‑2.1799 ‑1.3698 ‑0.9542
2000‑01‑08 1.4627 ‑1.7432 ‑0.8266
In [89]: type(df['A'])
Out[89]: pandas.core.series.Series
In [90]: df ‑ df['A']
Out[90]:
2000‑01‑01 00:00:00
2000‑01‑01
NaN
2000‑01‑02
NaN
2000‑01‑03
NaN
2000‑01‑04
NaN
2000‑01‑05
NaN
2000‑01‑06
NaN
2000‑01‑07
NaN
2000‑01‑08
NaN
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‑02 00:00:00
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
2000‑01‑04 00:00:00 ...
NaN ...
NaN ...
NaN ...
NaN ...
NaN ...
NaN ...
NaN ...
NaN ...
2000‑01‑03 00:00:00
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
2000‑01‑08 00:00:00
A
B
C
NaN NaN NaN NaN
NaN NaN NaN NaN
NaN NaN NaN NaN
NaN NaN NaN NaN
NaN NaN NaN NaN
NaN NaN NaN NaN
NaN NaN NaN NaN
NaN NaN NaN NaN
[8 rows x 11 columns]
注意:
df ‑ df['A']
现在被弃用了,未来也将会移除。这种操作的首选方法是:
df.sub(df['A'], axis=0)
获得显示地控制匹配和广播行为的知识,请查看灵活的二元操作章节。
常量操作会和你预期的一样:
\
In [91]: df * 5 + 2
Out[91]:
A
B
C
2000‑01‑01 ‑4.1341 5.8490 ‑4.4062
2000‑01‑02 ‑1.6385 1.3935 1.5106
2000‑01‑03 5.4789 3.7087 6.7986
2000‑01‑04 ‑3.5517 ‑1.0999 2.7487
2000‑01‑05 ‑1.6617 5.4387 2.8822
2000‑01‑06 4.0165 1.2252 3.5081
2000‑01‑07 ‑8.8993 ‑4.8492 ‑2.7710
2000‑01‑08 9.3135 ‑6.7158 ‑2.1330
In [92]: 1 / df
Out[92]:
A
B
C
2000‑01‑01 ‑0.8151 1.2990 ‑0.7805
2000‑01‑02 ‑1.3742 ‑8.2436 ‑10.2163
2000‑01‑03 1.4372 2.9262
1.0420
2000‑01‑04 ‑0.9006 ‑1.6130
6.6779
2000‑01‑05 ‑1.3655 1.4540
5.6675
2000‑01‑06 2.4795 ‑6.4537
3.3154
2000‑01‑07 ‑0.4587 ‑0.7300 ‑1.0480
2000‑01‑08 0.6837 ‑0.5737 ‑1.2098
In [93]: df ** 4
Out[93]:
A
2.2653
0.2804
0.2344
1.5199
0.2876
0.0265
22.5795
4.5774
2000‑01‑01
2000‑01‑02
2000‑01‑03
2000‑01‑04
2000‑01‑05
2000‑01‑06
2000‑01‑07
2000‑01‑08
B
0.3512
0.0002
0.0136
0.1477
0.2237
0.0006
3.5212
9.2332
C
2.6948e+00
9.1796e‑05
8.4838e‑01
5.0286e‑04
9.6924e‑04
8.2769e‑03
8.2903e‑01
4.6683e‑01
布尔操作也是一样:
In [94]: df1 = pd.DataFrame({'a' : [1, 0, 1], 'b' : [0, 1, 1] }, dtype=bool)
In [95]: df2 = pd.DataFrame({'a' : [0, 1, 1], 'b' : [1, 1, 0] }, dtype=bool)
In [96]: df1 & df2
Out[96]:
a
b
0 False False
1 False
True
2
True False
In [97]: df1 | df2
Out[97]:
a
b
0 True True
1 True True
2 True True
In [98]: df1 ^ df2
Out[98]:
a
b
0
True
True
1
True False
2 False
True
In [99]: ‑df1
Out[99]:
a
b
0 False
True
1
True False
2 False False
转置
使用T属性转置(同时也是 tranpose 函数),类似于 ndarray :
8.6.2
# only show the first 5 rows
In [100]: df[:5].T
Out[100]:
2000‑01‑01 2000‑01‑02 2000‑01‑03
A
‑1.2268
‑0.7277
0.6958
B
0.7698
‑0.1213
0.3417
C
‑1.2812
‑0.0979
0.9597
2000‑01‑04
‑1.1103
‑0.6200
0.1497
2000‑01‑05
‑0.7323
0.6877
0.1764
和
函数的互操作性
当操作的数据是数值型时,NumPy的全局的元素操作函数(log,exp,sqrt等)和许多其他的NumPy函数都可以在DataFrame中使用。
8.6.3 DataFrame NumPy
In [101]: np.exp(df)
Out[101]:
A
B
2000‑01‑01 0.2932 2.1593
2000‑01‑02 0.4830 0.8858
2000‑01‑03 2.0053 1.4074
2000‑01‑04 0.3294 0.5380
2000‑01‑05 0.4808 1.9892
2000‑01‑06 1.4968 0.8565
2000‑01‑07 0.1131 0.2541
2000‑01‑08 4.3176 0.1750
C
0.2777
0.9068
2.6110
1.1615
1.1930
1.3521
0.3851
0.4375
In [102]: np.asarray(df)
Out[102]:
array([[‑1.2268, 0.7698, ‑1.2812],
[‑0.7277, ‑0.1213, ‑0.0979],
[ 0.6958, 0.3417, 0.9597],
[‑1.1103, ‑0.62 , 0.1497],
[‑0.7323, 0.6877, 0.1764],
[ 0.4033, ‑0.155 , 0.3016],
[‑2.1799, ‑1.3698, ‑0.9542],
[ 1.4627, ‑1.7432, ‑0.8266]])
的 dot 方法实现了矩阵乘法:
DataFrame
In [103]: df.T.dot(df)
Out[103]:
A
B
C
A 11.3419 ‑0.0598 3.0080
B ‑0.0598 6.5206 2.0833
C
3.0080 2.0833 4.3105
类似地,Series的 dot 方法实现了点积:
In [104]: s1 = pd.Series(np.arange(5,10))
In [105]: s1.dot(s1)
Out[105]: 255
并不打算取代ndarray,因为它的索引方法从语义上来说与矩阵是完全不同的。
DataFrame
控制台显示
太大的DataFrame将会被截断以便在控制台显示。你也可以通过info()得到数据概要。(这里,我从plyr R包中读取了一个CSV版本的棒球数
据)
8.6.4
In [106]: baseball = pd.read_csv('data/baseball.csv')
In [107]: print(baseball)
id
player year
0
88641 womacto01 2006
1
88643 schilcu01 2006
..
...
...
...
98 89533
aloumo01 2007
99 89534 alomasa02 2007
stint
2
1
...
1
1
...
...
...
...
...
...
hbp
0.0
0.0
...
2.0
0.0
sh
3.0
0.0
...
0.0
0.0
sf
0.0
0.0
...
3.0
0.0
gidp
0.0
0.0
...
13.0
0.0
[100 rows x 23 columns]
In [108]: baseball.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 100 entries, 0 to 99
Data columns (total 23 columns):
id
100 non‑null int64
player
100 non‑null object
year
100 non‑null int64
stint
100 non‑null int64
team
100 non‑null object
lg
100 non‑null object
g
100 non‑null int64
ab
100 non‑null int64
r
100 non‑null int64
h
100 non‑null int64
X2b
100 non‑null int64
X3b
100 non‑null int64
hr
100 non‑null int64
rbi
100 non‑null float64
sb
100 non‑null float64
cs
100 non‑null float64
bb
100 non‑null int64
so
100 non‑null float64
ibb
100 non‑null float64
hbp
100 non‑null float64
sh
100 non‑null float64
sf
100 non‑null float64
gidp
100 non‑null float64
dtypes: float64(9), int64(11), object(3)
memory usage: 18.0+ KB
不过,使用 to_string 将会以表格形式返回DataFrame的字符串表示,但不是总能匹配控制台宽度。
In [109]: print(baseball.iloc[‑20:, :12].to_string())
id
player year stint team lg
g
ab
r
80 89474 finlest01 2007
1 COL NL
43
94
9
81 89480 embreal01 2007
1 OAK AL
4
0
0
82 89481 edmonji01 2007
1 SLN NL 117 365 39
83 89482 easleda01 2007
1 NYN NL
76 193 24
84 89489 delgaca01 2007
1 NYN NL 139 538 71
85 89493 cormirh01 2007
1 CIN NL
6
0
0
86 89494 coninje01 2007
2 NYN NL
21
41
2
87 89495 coninje01 2007
1 CIN NL
80 215 23
88 89497 clemero02 2007
1 NYA AL
2
2
0
89 89498 claytro01 2007
2 BOS AL
8
6
1
90 89499 claytro01 2007
1 TOR AL
69 189 23
91 89501 cirilje01 2007
2 ARI NL
28
40
6
92 89502 cirilje01 2007
1 MIN AL
50 153 18
93 89521 bondsba01 2007
1 SFN NL 126 340 75
94 89523 biggicr01 2007
1 HOU NL 141 517 68
95 89525 benitar01 2007
2 FLO NL
34
0
0
96 89526 benitar01 2007
1 SFN NL
19
0
0
97 89530 ausmubr01 2007
1 HOU NL 117 349 38
98 89533
aloumo01 2007
1 NYN NL
87 328 51
99 89534 alomasa02 2007
1 NYN NL
8
22
1
h
17
0
92
54
139
0
8
57
1
0
48
8
40
94
130
0
0
82
112
3
X2b
3
0
15
6
30
0
2
11
0
0
14
4
9
14
31
0
0
16
19
1
X3b
0
0
2
0
0
0
0
1
0
0
0
0
2
0
3
0
0
3
1
0
默认地,宽的DataFrame将会跨多行显示:
In [110]: pd.DataFrame(np.random.randn(3, 12))
Out[110]:
0
1
2
3
4
5
6
7
8
9
10
11
0 ‑0.345352 1.314232 0.690579 0.995761 2.396780 0.014871 3.357427 ‑0.317441 ‑1.236269 0.89
6171 ‑0.487602 ‑0.082240
1 ‑2.182937 0.380396 0.084844 0.432390 1.519970 ‑0.493662 0.600178 0.274230 0.132885 ‑0.02
3688 2.410179 1.450520
2 0.206053 ‑0.251905 ‑2.213588 1.063327 1.266143 0.299368 ‑0.863838 0.408204 ‑1.048089 ‑0.02
5747 ‑0.988387 0.094055
你可以通过设置 display.width 来改变一行显示多少:
In [111]: pd.set_option('display.width', 40) # default is 80
In [112]: pd.DataFrame(np.random.randn(3, 12))
Out[112]:
0
1
2
3
4
5
6
7
8
9
10
11
0 1.262731 1.289997 0.082423 ‑0.055758 0.536580 ‑0.489682 0.369374 ‑0.034571 ‑2.484478 ‑0.28
1461 0.030711 0.109121
1 1.126203 ‑0.977349 1.474071 ‑0.064034 ‑1.282782 0.781836 ‑1.071357 0.441153 2.353925 0.58
3787 0.221471 ‑0.744471
2 0.758527 1.729689 ‑0.964980 ‑0.845696 ‑1.340896 1.846883 ‑1.328865 1.682706 ‑1.717693 0.88
8782 0.228440 0.901805
可以通过设置 display.max_colwidth 来调整一列的最大宽度:
In [113]: datafile={'filename': ['filename_01','filename_02'],
.....:
'path': ["media/user_name/storage/folder_01/filename_01",
.....:
"media/user_name/storage/folder_02/filename_02"]}
.....:
In [114]: pd.set_option('display.max_colwidth',30)
In [115]: pd.DataFrame(datafile)
Out[115]:
filename
path
0 filename_01 media/user_name/storage/fo...
1 filename_02 media/user_name/storage/fo...
In [116]: pd.set_option('display.max_colwidth',100)
In [117]: pd.DataFrame(datafile)
Out[117]:
filename
path
0 filename_01 media/user_name/storage/folder_01/filename_01
1 filename_02 media/user_name/storage/folder_02/filename_02
也可以通过设置 expand_frame_repr 来禁用这个特性,这将把表格打印在一个块中。
列属性访问和IPython自动完成
如果DataFrame的列标签是合法的Python变量名,列可以通过类似于属性的方式访问:
译者注:这里所说的 合法 主要是指不要使用Python关键字(比如 sum 等)作为列名,否则无法使用 . 操作。
8.6.5 DataFrame
In [118]: df = pd.DataFrame({'foo1' : np.random.randn(5),
.....:
'foo2' : np.random.randn(5)})
.....:
In [119]: df
Out[119]:
foo1
foo2
0 1.171216 ‑0.858447
1 0.520260 0.306996
2 ‑1.197071 ‑0.028665
3 ‑1.066969 0.384316
4 ‑0.303421 1.574159
In [120]: df.foo1
Out[120]:
0
1.171216
1
0.520260
2
‑1.197071
3
‑1.066969
4
‑0.303421
Name: foo1, dtype: float64
列与IPython的完成机制相连接,所以可以通过 tab 自动完成:
In [5]: df.fo<TAB>
df.foo1 df.foo2
8.7 Panel
注意:在0.20版本中,Panel被弃用了,将来也会被移除。查看弃用Panel部分。
从略。
第九章 基本功能
本章我们将讨论很多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'])
...:
In [4]: wp = pd.Panel(np.random.randn(2, 5, 4), items=['Item1', 'Item2'],
...:
major_axis=pd.date_range('1/1/2000', periods=5),
...:
minor_axis=['A', 'B', 'C', 'D'])
...:
和Tail
可以通过head()和tail()方法查看Series或DataFrame的一个小样本。默认显示的元素个数是5,可以传入自定义的数值:
9.1 Head
In [5]: long_series = pd.Series(np.random.randn(1000))
In [6]: long_series.head()
Out[6]:
0
0.229453
1
0.304418
2
0.736135
3
‑0.859631
4
‑0.424100
dtype: float64
In [7]: long_series.tail(3)
Out[7]:
997
‑0.351587
998
1.136249
999
‑0.448789
dtype: float64
属性和原生ndarray
Pandas对象拥有许多属性,你可以通过这些属性访问元数据。
shape:给出对象的维度,和ndarray是一致的。
轴标签
Series:索引(只有轴)
DataFrame:索引(行)和列
Panel:项目,最大轴和最小轴
注意,这些属性可以安全的赋值。
9.2
In [8]: df[:2]
Out[8]:
A
B
C
2000‑01‑01 0.048869 ‑1.360687 ‑0.47901
2000‑01‑02 ‑0.859661 ‑0.231595 ‑0.52775
In [9]: df.columns = [x.lower() for x in df.columns]
In [10]: df
Out[10]:
a
b
c
2000‑01‑01 0.048869 ‑1.360687 ‑0.479010
2000‑01‑02 ‑0.859661 ‑0.231595 ‑0.527750
2000‑01‑03 ‑1.296337 0.150680 0.123836
2000‑01‑04 0.571764 1.555563 ‑0.823761
2000‑01‑05 0.535420 ‑1.032853 1.469725
2000‑01‑06 1.304124 1.449735 0.203109
2000‑01‑07 ‑1.032011 0.969818 ‑0.962723
2000‑01‑08 1.382083 ‑0.938794 0.669142
获得数据结构中的数,只需要通过values属性:
In [11]: s.values
Out[11]: array([‑1.9339,
0.3773,
0.7341,
2.1416, ‑0.0112])
In [12]: df.values
Out[12]:
array([[ 0.0489, ‑1.3607, ‑0.479 ],
[‑0.8597, ‑0.2316, ‑0.5278],
[‑1.2963, 0.1507, 0.1238],
[ 0.5718, 1.5556, ‑0.8238],
[ 0.5354, ‑1.0329, 1.4697],
[ 1.3041, 1.4497, 0.2031],
[‑1.032 , 0.9698, ‑0.9627],
[ 1.3821, ‑0.9388, 0.6691]])
In [13]: wp.values
Out[13]:
array([[[‑0.4336, ‑0.2736, 0.6804, ‑0.3084],
[‑0.2761, ‑1.8212, ‑1.9936, ‑1.9274],
[‑2.0279, 1.625 , 0.5511, 3.0593],
[ 0.4553, ‑0.0307, 0.9357, 1.0612],
[‑2.1079, 0.1999, 0.3236, ‑0.6416]],
[[‑0.5875, 0.0539, 0.1949, ‑0.382 ],
[ 0.3186, 2.0891, ‑0.7283, ‑0.0903],
[‑0.7482, 1.3189, ‑2.0298, 0.7927],
[ 0.461 , ‑0.5427, ‑0.3054, ‑0.4792],
[ 0.095 , ‑0.2701, ‑0.7071, ‑0.7739]]])
如果一个DataFrame或Panel包含相同类型的数据,ndarray实际上可以就地修改,并且这些修改将会反映到数据结构中。对于不同类型的数
据,例如DataFrame的一些列的数据类型不同,将不是这样。values属性本身不能被赋值,这与轴标签不同。
注意:当面对不同类型的数据时,结果的类型将会选择最大限度容纳所有数据的那个。例如,如果涉及到字符串,结果将会是
object类型。如果只有浮点型数据和整型数据,结果将会是浮点型。
加速操作
Pandas支持使用 numexpr 库和 bottleneck 库来加速某些类型的二进制数字和布尔操作。
这两个库在处理大型数据是格外有用,能够大幅提升速度。 numexpr 使用智能分块、缓存和多核技术。 bottleneck 是一系列专门的
cython例程,这些例程在处理包含 nan 的数组时特别快。
这里有一个示例(使用100行100列的DataFrame):
9.3
操作 0.11.0 版(ms) 之前的版本 (ms) 0.11版占之前版本的比例
df1 > df2
13.32
125.35
0.1063
df1 * df2
21.71
36.63
0.5928
df1 + df2
22.04
36.50
0.6039
我们特别鼓励安装这两个库。查看推荐依赖,获得更多的安装信息。
这两个库默认使用,你可以通过设置选项控制是否使用:
pd.set_option('compute.use_bottleneck', False)
pd.set_option('compute.use_numexpr', False)
灵活的二进制操作
在Pandas数据结构中使用二进制操作时,有两点我们比较关心:
高维数据结构(DataFrame)和低维数据结构(Series)之间的广播方式。
缺失数据如何计算。
我们将展示如何独立地处理这两个问题,不过它们可以同时处理。
9.4
匹配/广播行为
DataFrame有一些执行二进制运算的方法:add()、sub()、mul()、div()和相关的radd()、rsub()等。广播行为对Series的输入更感兴趣。使用这些
函数,你可以通过axis关键字在索引和列上做匹配。
9.4.1
In [14]: 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 [15]: df
Out[15]:
one
two
three
a ‑1.101558 1.124472
NaN
b ‑0.177289 2.487104 ‑0.634293
c 0.462215 ‑0.486066 1.931194
d
NaN ‑0.456288 ‑1.222918
In [16]: row = df.iloc[1]
In [17]: column = df['two']
In [18]: df.sub(row, axis='columns')
Out[18]:
one
two
three
a ‑0.924269 ‑1.362632
NaN
b 0.000000 0.000000 0.000000
c 0.639504 ‑2.973170 2.565487
d
NaN ‑2.943392 ‑0.588625
In [19]: df.sub(row, axis=1)
Out[19]:
one
two
three
a ‑0.924269 ‑1.362632
NaN
b 0.000000 0.000000 0.000000
c 0.639504 ‑2.973170 2.565487
d
NaN ‑2.943392 ‑0.588625
In [20]: df.sub(column, axis='index')
Out[20]:
one two
three
a ‑2.226031 0.0
NaN
b ‑2.664393 0.0 ‑3.121397
c 0.948280 0.0 2.417260
d
NaN 0.0 ‑0.766631
In [21]: df.sub(column, axis=0)
Out[21]:
one two
three
a ‑2.226031 0.0
NaN
b ‑2.664393 0.0 ‑3.121397
c 0.948280 0.0 2.417260
d
NaN 0.0 ‑0.766631
更进一步,你也可以将一个多级索引的DataFrame的一个等级与一个Series对齐
In [22]: dfmi = df.copy()
In [23]: dfmi.index = pd.MultiIndex.from_tuples([(1,'a'),(1,'b'),(1,'c'),(2,'a')],
....:
names=['first','second'])
....:
In [24]: dfmi.sub(column, axis=0, level='second')
Out[24]:
one
two
three
first second
1
a
‑2.226031 0.00000
NaN
b
‑2.664393 0.00000 ‑3.121397
c
0.948280 0.00000 2.417260
2
a
NaN ‑1.58076 ‑2.347391
对于Panel来说,描述广播行为有一点困难,所以算术方法让你来指定广播的轴(可能更加令人难以理解?)。例如,假设我们希望将一个特
定的轴上的数据去均值化。这可以通过将这个轴上的数值的平均值在该轴上广播来实现:
In [25]: major_mean = wp.mean(axis='major')
In [26]: major_mean
Out[26]:
Item1
Item2
A ‑0.878036 ‑0.092218
B ‑0.060128 0.529811
C 0.099453 ‑0.715139
D 0.248599 ‑0.186535
In [27]: wp.sub(major_mean, axis='major')
Out[27]:
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 5 (major_axis) x 4 (minor_axis)
Items axis: Item1 to Item2
Major_axis axis: 2000‑01‑01 00:00:00 to 2000‑01‑05 00:00:00
Minor_axis axis: A to D
axis="items"
和 axis="minor" 类似。
注意:我可以确定,DataFrame中的axis参数能够匹配Panel中的广播行为。尽管这需要一个过渡期,以便用户可以修改他们的
代码...
和Index也支持divmod()内置函数。这个函数同时接受向下取整除法和取模操作,返回一个与左边相同类型的双元组。例如:
Series
In [28]: s = pd.Series(np.arange(10))
In [29]: s
Out[29]:
0
0
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
8
9
9
dtype: int64
In [30]: div, rem = divmod(s, 3)
In [31]: div
Out[31]:
0
0
1
0
2
0
3
1
4
1
5
1
6
2
7
2
8
2
9
3
dtype: int64
In [32]: rem
Out[32]:
0
0
1
1
2
2
3
0
4
1
5
2
6
0
7
1
8
2
9
0
dtype: int64
In [33]: idx = pd.Index(np.arange(10))
In [34]: idx
Out[34]: Int64Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64')
In [35]: div, rem = divmod(idx, 3)
In [36]: div
Out[36]: Int64Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64')
In [37]: rem
Out[37]: Int64Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64')
我们也可以按元素执行divmod():
In [38]: div, rem = divmod(s, [2, 2, 3, 3, 4, 4, 5, 5, 6, 6])
In [39]: div
Out[39]:
0
0
1
0
2
0
3
1
4
1
5
1
6
1
7
1
8
1
9
1
dtype: int64
In [40]: rem
Out[40]:
0
0
1
1
2
2
3
0
4
0
5
1
6
1
7
2
8
2
9
3
dtype: int64
缺失数据/操作填充值
在Series和DataFrame中,算术函数有一个输入 fill_value 的选项,也就是说,一个位置上只要有数据缺失就会被替换。例如,当把两个
DataFrame相加时,你可能希望把NaN处理成0,当两个DataFrame都缺失了那个数据时才处理成NaN(如果你愿意,你可以在后面使用
fillna 把NaN替换为其他的数值)
9.4.2
In [41]: df
Out[41]:
one
two
three
a ‑1.101558 1.124472
NaN
b ‑0.177289 2.487104 ‑0.634293
c 0.462215 ‑0.486066 1.931194
d
NaN ‑0.456288 ‑1.222918
In [42]: df2
Out[42]:
one
two
three
a ‑1.101558 1.124472 1.000000
b ‑0.177289 2.487104 ‑0.634293
c 0.462215 ‑0.486066 1.931194
d
NaN ‑0.456288 ‑1.222918
In [43]: df + df2
Out[43]:
one
two
three
a ‑2.203116 2.248945
NaN
b ‑0.354579 4.974208 ‑1.268586
c 0.924429 ‑0.972131 3.862388
d
NaN ‑0.912575 ‑2.445837
In [44]: df.add(df2, fill_value=0)
Out[44]:
one
two
three
a ‑2.203116 2.248945 1.000000
b ‑0.354579 4.974208 ‑1.268586
c 0.924429 ‑0.972131 3.862388
d
NaN ‑0.912575 ‑2.445837
灵活的对比
Series和DataFrame有一些二进制对比方法 eq , ne , lt , gt , le 和 ge ,这些方法和上面二进制算术操作类似:
9.4.3
In [45]: df.gt(df2)
Out[45]:
one
two three
a False False False
b False False False
c False False False
d False False False
In [46]: df2.ne(df)
Out[46]:
one
two three
a False False
True
b False False False
c False False False
d
True False False
这些操作产生一个和bool类型左手边的输入有相同数据类型的Pandas对象,这些布尔对象可以在索引操作中使用,查看布尔索引章节,获得
更多信息。
布尔规约操作
你可以调用这些规约操作:empty,any(), all()和bool来为总结一个布尔结果提供一种方式。
9.4.4
In [47]: (df > 0).all()
Out[47]:
one
False
two
False
three
False
dtype: bool
In [48]: (df > 0).any()
Out[48]:
one
True
two
True
three
True
dtype: bool
你可以通过规约得到最终的布尔值
In [49]: (df > 0).any().any()
Out[49]: True
通过empty属性测试一个Pandas对象是不是空的:
In [50]: df.empty
Out[50]: False
In [51]: pd.DataFrame(columns=list('ABC')).empty
Out[51]: True
通过bool()方法在一个布尔环境中评估一个单元素的Pandas对象:
In [52]: pd.Series([True]).bool()
Out[52]: True
In [53]: pd.Series([False]).bool()
Out[53]: False
In [54]: pd.DataFrame([[True]]).bool()
Out[54]: True
In [55]: pd.DataFrame([[False]]).bool()
Out[55]: False
注意:你可以想要尝试下面的操作:
>>> if df:
...
或
>>> df and df2
这都会触发错误,因为你在尝试比较多个值。
ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all()
.
查看陷阱,获得更多的讨论信息。
比较对象是否相等
你经常可以发现,计算同一个结果可能有不同的方法。一个简单的例子,考虑 df + df 和 df * 2 。为了测试这两种计算是否会得到相同的
结果,参考上面给出的工具,你可能想要使用 (df + df == df * 2).all() 。但是,这个表达式实际上是有欺骗性的。
9.4.5
In [56]: df+df == df*2
Out[56]:
one
two three
a
True True False
b
True True
True
c
True True
True
d False True
True
In [57]: (df+df == df*2).all()
Out[57]:
one
False
two
True
three
False
dtype: bool
注意, df + df == df * 2 得到的布尔DataFrame包含了一些False值。这是因为NaN值是不相等的。
In [58]: np.nan == np.nan
Out[58]: False
所以,NDFrames (比如Series,DataFrame和Panel)有一个equals()方法来比较是否相等。equals()方法会将相关位置上的NaN值按相等处
理。
In [59]: (df+df).equals(df*2)
Out[59]: True
注意,Series和DataFrame的索引必须是相同的顺序,才会得到True。
In [60]: df1 = pd.DataFrame({'col':['foo', 0, np.nan]})
In [61]: df2 = pd.DataFrame({'col':[np.nan, 0, 'foo']}, index=[2,1,0])
In [62]: df1.equals(df2)
Out[62]: False
In [63]: df1.equals(df2.sort_index())
Out[63]: True
比较类数组对象
你可以很方便的按元素比较一个Pandas数据结构和一个标量
9.4.6
In [64]: pd.Series(['foo', 'bar', 'baz']) == 'foo'
Out[64]:
0
True
1
False
2
False
dtype: bool
In [65]: pd.Index(['foo', 'bar', 'baz']) == 'foo'
Out[65]: array([ True, False, False], dtype=bool)
也可以在长度相同的类数组对象之间按元素比较:
Pandas
In [66]: pd.Series(['foo', 'bar', 'baz']) == pd.Index(['foo', 'bar', 'qux'])
Out[66]:
0
True
1
True
2
False
dtype: bool
In [67]: pd.Series(['foo', 'bar', 'baz']) == np.array(['foo', 'bar', 'qux'])
Out[67]:
0
True
1
True
2
False
dtype: bool
比较不同长度的Index或Series对象将会触发ValueError:
In [55]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar'])
ValueError: Series lengths must match to compare
In [56]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo'])
ValueError: Series lengths must match to compare
注意,这与Numpy的处理方式不同,在Numpy中,一个表达式可以广播:
In [68]: np.array([1, 2, 3]) == np.array([2])
Out[68]: array([False, True, False], dtype=bool)
或者,在不能广播的情况下,返回False
In [69]: np.array([1, 2, 3]) == np.array([1, 2])
Out[69]: False
合并重叠的数据集
偶尔出现的一个问题是,合并两个相似的数据集时,其中一个数据集的数比另一个优先度高。比如,两个表现特定的经济指标的数据序列,其
中一个被认为"质量更高"。但是,低质量的序列可能在历史上可以回溯的更远或覆盖范围更全。同样的,我们希望合并两个DataFrame,其中
一个DataFrame的缺失值有条件地以另一个DataFrame的同标签值替换。实现这种操作的函数是combine_first()。举例来说:
9.4.7
In [70]: df1 = pd.DataFrame({'A' : [1., np.nan, 3., 5., np.nan],
....:
'B' : [np.nan, 2., 3., np.nan, 6.]})
....:
In [71]: df2 = pd.DataFrame({'A' : [5., 2., 4., np.nan, 3., 7.],
....:
'B' : [np.nan, np.nan, 3., 4., 6., 8.]})
....:
In [72]: df1
Out[72]:
A
B
0 1.0 NaN
1 NaN 2.0
2 3.0 3.0
3 5.0 NaN
4 NaN 6.0
In [73]: df2
Out[73]:
A
B
0 5.0 NaN
1 2.0 NaN
2 4.0 3.0
3 NaN 4.0
4 3.0 6.0
5 7.0 8.0
In [74]: df1.combine_first(df2)
Out[74]:
A
B
0 1.0 NaN
1 2.0 2.0
2 3.0 3.0
3 5.0 4.0
4 3.0 6.0
5 7.0 8.0
一般的DataFrame合并
上面的combine_first()函数调用了更一般的DataFrame.combine() 。这个方法接受另一个DataFrame和合并函数,对齐输入的DataFrame,然后
传入合并函数一对Series(即:这些列的名称是相同的)。
所以,例如,重新生成上面的combine_first():
9.4.8
In [75]: combiner = lambda x, y: np.where(pd.isna(x), y, x)
In [76]: df1.combine(df2, combiner)
Out[76]:
A
B
0 1.0 NaN
1 2.0 2.0
2 3.0 3.0
3 5.0 4.0
4 3.0 6.0
5 7.0 8.0
描述统计
Series,DataFrame和Panel有大量计算描述统计值的方法和相关操作。这些操作中大部分都是聚合操作(进而产生一个低纬度的结果),比如
sum(),mean()和quantile(),但是其中的一部分操作,比如cumsum()和cumprod(),会产生一个同样大小的对象。一般而言,这些操作会有一
个轴参数,比如ndarray,{sum,std,……},但是这个轴可以用名称或整数来指定:
Series:不需要轴参数。
DataFrame:"index"(axis=0)或"columns"(axis=1)。
Panel:"items"(axis=1),"major"(axis=1,默认),"minor"(axis=2)。
举例:
9.5
In [77]: df
Out[77]:
one
two
three
a ‑1.101558 1.124472
NaN
b ‑0.177289 2.487104 ‑0.634293
c 0.462215 ‑0.486066 1.931194
d
NaN ‑0.456288 ‑1.222918
In [78]: df.mean(0)
Out[78]:
one
‑0.272211
two
0.667306
three
0.024661
dtype: float64
In [79]: df.mean(1)
Out[79]:
a
0.011457
b
0.558507
c
0.635781
d
‑0.839603
dtype: float64
所有这些操作都有一个skipna选项来标记是否排除缺失值。默认为True(排除)。
In [80]: df.sum(0, skipna=False)
Out[80]:
one
NaN
two
2.669223
three
NaN
dtype: float64
In [81]: df.sum(axis=1, skipna=True)
Out[81]:
a
0.022914
b
1.675522
c
1.907343
d
‑1.679206
dtype: float64
合并广播和算术操作,可以描述各种统计程序,比如标准化(将数据转化为均值为0,标准差为1的数据),非常简洁:
In [82]: ts_stand = (df ‑ df.mean()) / df.std()
In [83]: ts_stand.std()
Out[83]:
one
1.0
two
1.0
three
1.0
dtype: float64
In [84]: xs_stand = df.sub(df.mean(1), axis=0).div(df.std(1), axis=0)
In [85]: xs_stand.std(1)
Out[85]:
a
1.0
b
1.0
c
1.0
d
1.0
dtype: float64
注意,类似cumsum()和cumprod()的方法保留了缺失值的位置。这与expanding()和rolling()略有不同。更多的细节,请查阅这里。
In [86]: df.cumsum()
Out[86]:
one
two
three
a ‑1.101558 1.124472
NaN
b ‑1.278848 3.611576 ‑0.634293
c ‑0.816633 3.125511 1.296901
d
NaN 2.669223 0.073983
这里有一个常见函数的速查表。如果被操作的对象含有多级索引,每个函数还有一个level参数。
函数
描述
count 非空观察值的个数
sum
数值的和
mean
数值的平均值
mad
平均绝对偏差
median 数值的算术平均值
min
最小值
max
最大值
mode
众数
abs
绝对值
prod
数值的乘积
std
标准差
var
方差
sem
平均值的标准误
skew
样本偏度
kurt
样本峰度
quantile
分位数
cumsum
累加值
cumprod
累乘值
cummax
累计最大值
cummin
累计最小值
注意,意外地,一些Numpy方法,比如mean,std和sum,会默认排除输入的Series中的NA值:
In [87]: np.mean(df['one'])
Out[87]: ‑0.27221094480450114
In [88]: np.mean(df['one'].values)
Out[88]: nan
Series.nunique()
将返回Series中唯一的非空数据的个数:
In [89]: series = pd.Series(np.random.randn(500))
In [90]: series[20:500] = np.nan
In [91]: series[10:20]
= 5
In [92]: series.nunique()
Out[92]: 11
数据概览:描述
有一个非常方便的函数 describe() 来计算一个Series或DataFrame的若干列的摘要统计(默认排除空值):
9.5.1
In [93]: series = pd.Series(np.random.randn(1000))
In [94]: series[::2] = np.nan
In [95]: series.describe()
Out[95]:
count
500.000000
mean
‑0.032127
std
1.067484
min
‑3.463789
25%
‑0.725523
50%
‑0.053230
75%
0.679790
max
3.120271
dtype: float64
In [96]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=['a', 'b', 'c', 'd', 'e'])
In [97]: frame.iloc[::2] = np.nan
In [98]: frame.describe()
Out[98]:
a
b
count 500.000000 500.000000
mean
‑0.045109
‑0.052045
std
1.029268
1.002320
min
‑2.915767
‑3.294023
25%
‑0.763783
‑0.720389
50%
‑0.086033
‑0.048843
75%
0.663399
0.620980
max
3.400646
2.925597
c
500.000000
0.024520
1.042793
‑3.610499
‑0.609600
0.006093
0.728382
3.416896
d
500.000000
0.006117
1.040134
‑2.907036
‑0.665896
0.043191
0.735973
3.331522
e
500.000000
0.001141
1.005207
‑3.010899
‑0.682900
‑0.001651
0.656439
3.007143
你可以选择特定的分位数来输出:
In [99]: series.describe(percentiles=[.05, .25, .75, .95])
Out[99]:
count
500.000000
mean
‑0.032127
std
1.067484
min
‑3.463789
5%
‑1.733545
25%
‑0.725523
50%
‑0.053230
75%
0.679790
95%
1.854383
max
3.120271
dtype: float64
默认地,结果中一直包含平均值。
对于非数值Series对象, describe() 将给出唯一数据个数和最常出现的数值的简单摘要:
In [100]: s = pd.Series(['a', 'a', 'b', 'b', 'a', 'a', np.nan, 'c', 'd', 'a'])
In [101]: s.describe()
Out[101]:
count
9
unique
4
top
a
freq
5
dtype: object
注意,在混合类型的DataFrame对象上, describe() 将限制摘要只包含数值列,或(如果没有数值列),包含类别列:
In [102]: frame = pd.DataFrame({'a': ['Yes', 'Yes', 'No', 'No'], 'b': range(4)})
In [103]: frame.describe()
Out[103]:
b
count 4.000000
mean
1.500000
std
1.290994
min
0.000000
25%
0.750000
50%
1.500000
75%
2.250000
max
3.000000
这个操作行为可以通过给定一个类型列表作为 include 或 exclude 参数来控制。特别的值 all 也可以使用:
In [104]: frame.describe(include=['object'])
Out[104]:
a
count
4
unique
2
top
Yes
freq
2
In [105]: frame.describe(include=['number'])
Out[105]:
b
count 4.000000
mean
1.500000
std
1.290994
min
0.000000
25%
0.750000
50%
1.500000
75%
2.250000
max
3.000000
In [106]: frame.describe(include='all')
Out[106]:
a
b
count
4 4.000000
unique
2
NaN
top
Yes
NaN
freq
2
NaN
mean
NaN 1.500000
std
NaN 1.290994
min
NaN 0.000000
25%
NaN 0.750000
50%
NaN 1.500000
75%
NaN 2.250000
max
NaN 3.000000
这个特性依赖于 select_dtypes 。可以到那里查看更多被接受的输入。
最大值/最小值索引
idxmin() 和 idxmax() 函数计算一个Series和DataFrame的最小值和最大值的索引标签:
9.5.2
In [107]: s1 = pd.Series(np.random.randn(5))
In [108]: s1
Out[108]:
0
‑1.649461
1
0.169660
2
1.246181
3
0.131682
4
‑2.001988
dtype: float64
In [109]: s1.idxmin(), s1.idxmax()
Out[109]: (4, 2)
In [110]: df1 = pd.DataFrame(np.random.randn(5,3), columns=['A','B','C'])
In [111]: df1
Out[111]:
A
B
C
0 ‑1.273023 0.870502 0.214583
1 0.088452 ‑0.173364 1.207466
2 0.546121 0.409515 ‑0.310515
3 0.585014 ‑0.490528 ‑0.054639
4 ‑0.239226 0.701089 0.228656
In [112]: df1.idxmin(axis=0)
Out[112]:
A
0
B
3
C
2
dtype: int64
In [113]: df1.idxmax(axis=1)
Out[113]:
0
B
1
C
2
A
3
A
4
B
dtype: object
如果有多个行(或列)满足最大值和最小值, idxmin() 和 idxmax() 返回第一个符合的索引:
In [114]: df3 = pd.DataFrame([2, 1, 1, 3, np.nan], columns=['A'], index=list('edcba'))
In [115]: df3
Out[115]:
A
e 2.0
d 1.0
c 1.0
b 3.0
a NaN
In [116]: df3['A'].idxmin()
Out[116]: 'd'
注意:在Numpy中, idxmin 和 idxmax 称为 argmin 和 argmax 。
计数(直方图)/ 众数
Series的 value_counts() 方法和顶级函数计算一个一维数组的每个值的个数。它也可以作为一个函数用在一个常规数组上:
9.5.3
In [117]: data = np.random.randint(0, 7, size=50)
In [118]: data
Out[118]:
array([3, 3, 0, 2, 1, 0, 5, 5, 3, 6, 1, 5, 6, 2, 0, 0, 6, 3, 3, 5, 0, 4, 3,
3, 3, 0, 6, 1, 3, 5, 5, 0, 4, 0, 6, 3, 6, 5, 4, 3, 2, 1, 5, 0, 1, 1,
6, 4, 1, 4])
In [119]: s = pd.Series(data)
In [120]: s.value_counts()
Out[120]:
3
11
0
9
5
8
6
7
1
7
4
5
2
3
dtype: int64
In [121]: pd.value_counts(data)
Out[121]:
3
11
0
9
5
8
6
7
1
7
4
5
2
3
dtype: int64
类似地,你可以得到一个Series或DataFrame的最常出现的数值(众数):
In [122]: s5 = pd.Series([1, 1, 3, 3, 3, 5, 5, 7, 7, 7])
In [123]: s5.mode()
Out[123]:
0
3
1
7
dtype: int64
In [124]: df5 = pd.DataFrame({"A": np.random.randint(0, 7, size=50),
.....:
"B": np.random.randint(‑10, 15, size=50)})
.....:
In [125]: df5.mode()
Out[125]:
A B
0 2 ‑5
离散化和求分位数
可以使用 cut() (基于数值分段)和 qcut() (基于样本分位点分段)来离散化连续数值:
9.5.4
In [126]: arr = np.random.randn(20)
In [127]: factor = pd.cut(arr, 4)
In [128]: factor
Out[128]:
[(‑2.611, ‑1.58], (0.473, 1.499], (‑2.611, ‑1.58], (‑1.58, ‑0.554], (‑0.554, 0.473], ..., (0.473,
1.499], (0.473, 1.499], (‑0.554, 0.473], (‑0.554, 0.473], (‑0.554, 0.473]]
Length: 20
Categories (4, interval[float64]): [(‑2.611, ‑1.58] < (‑1.58, ‑0.554] < (‑0.554, 0.473] <
(0.473, 1.499]]
In [129]: factor = pd.cut(arr, [‑5, ‑1, 0, 1, 5])
In [130]: factor
Out[130]:
[(‑5, ‑1], (0, 1], (‑5, ‑1], (‑1, 0], (‑1, 0], ..., (1, 5], (1, 5], (‑1, 0], (‑1, 0], (‑1, 0]]
Length: 20
Categories (4, interval[int64]): [(‑5, ‑1] < (‑1, 0] < (0, 1] < (1, 5]]
qcut()
计算样本分位点。例如,我们可以把一些普通分布数据切成相等大小的四份:
In [131]: arr = np.random.randn(30)
In [132]: factor = pd.qcut(arr, [0, .25, .5, .75, 1])
In [133]: factor
Out[133]:
[(0.544, 1.976], (0.544, 1.976], (‑1.255, ‑0.375], (0.544, 1.976], (‑0.103, 0.544], ..., (‑0.103,
0.544], (0.544, 1.976], (‑0.103, 0.544], (‑1.255, ‑0.375], (‑0.375, ‑0.103]]
Length: 30
Categories (4, interval[float64]): [(‑1.255, ‑0.375] < (‑0.375, ‑0.103] < (‑0.103, 0.544] <
(0.544, 1.976]]
In [134]: pd.value_counts(factor)
Out[134]:
(0.544, 1.976]
8
(‑1.255, ‑0.375]
8
(‑0.103, 0.544]
7
(‑0.375, ‑0.103]
7
dtype: int64
我们也可以传入无穷值来定义分组:
In [135]: arr = np.random.randn(20)
In [136]: factor = pd.cut(arr, [‑np.inf, 0, np.inf])
In [137]: factor
Out[137]:
[(0.0, inf], (0.0, inf], (0.0, inf], (0.0, inf], (‑inf, 0.0], ..., (‑inf, 0.0], (‑inf, 0.0], (0.0
, inf], (‑inf, 0.0], (0.0, inf]]
Length: 20
Categories (2, interval[float64]): [(‑inf, 0.0] < (0.0, inf]]
调用函数
在Pandas对象上调用你自定义的或其他库中的函数,你应该了解下面的三个方法。至于使用哪个方法,取决于你的函数期望在整个
DataFrame、Series、按行、按列还是按元素操作数据。
1. 按表调用函数: pipe()
2. 按行或列调用函数: apply()
3. 聚合API: agg() 和 transform()
4. 按元素调用函数: applymap()
9.6
按表调用函数
DataFrames 和 Series 当然可以作为参数传入一个函数。不过,如果一个函数需要在一个方法链中调用,考虑使用 pipe() 方法。对
比以下两个表达式:
9.6.1
# f, g, and h are functions taking and returning ``DataFrames``
>>> f(g(h(df), arg1=1), arg2=2, arg3=3)
等价的:
>>> (df.pipe(h)
.pipe(g, arg1=1)
.pipe(f, arg2=2, arg3=3)
)
推荐使用第二种方式(方法链)。 pipe 使得在方法链中与Pandas方法一起使用自定义函数或其他库中的函数变得很容易。
在上面的例子中,函数f,g和h,每个都接受DataFrame作为第一位置参数。如果你要调用的函数将它的数据作为第二个参数怎么办?在这种
情况下,Pandas提供了pipe方法,包含一个(callable, data_keyword)元组。pipe将DataFrame指向该元组的指定参数。
例如,我们可以使用statsmodels做回归拟合。他们的API接受一个公式(作为第一个参数),一个DataFrame作为第二个参数 data 。我们为
pipe向这个函数传入关键词对(sm.ols,'data'):
Pandas
In [138]: import statsmodels.formula.api as sm
In [139]: bb = pd.read_csv('data/baseball.csv', index_col='id')
In [140]: (bb.query('h > 0')
.....:
.assign(ln_h = lambda df: np.log(df.h))
.....:
.pipe((sm.ols, 'data'), 'hr ~ ln_h + year + g + C(lg)')
.....:
.fit()
.....:
.summary()
.....: )
.....:
Out[140]:
<class 'statsmodels.iolib.summary.Summary'>
"""
OLS Regression Results
==============================================================================
Dep. Variable:
hr
R‑squared:
0.685
Model:
OLS
Adj. R‑squared:
0.665
Method:
Least Squares
F‑statistic:
34.28
Date:
Tue, 12 Jun 2018
Prob ﴾F‑statistic﴿:
3.48e‑15
Time:
12:53:14
Log‑Likelihood:
‑205.92
No. Observations:
68
AIC:
421.8
Df Residuals:
63
BIC:
432.9
Df Model:
4
Covariance Type:
nonrobust
===============================================================================
coef
std err
t
P>|t|
[0.025
0.975]
‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑
Intercept
‑8484.7720
4664.146
‑1.819
0.074
‑1.78e+04
835.780
C﴾lg﴿[T.NL]
‑2.2736
1.325
‑1.716
0.091
‑4.922
0.375
ln_h
‑1.3542
0.875
‑1.547
0.127
‑3.103
0.395
year
4.2277
2.324
1.819
0.074
‑0.417
8.872
g
0.1841
0.029
6.258
0.000
0.125
0.243
==============================================================================
Omnibus:
10.875
Durbin‑Watson:
1.999
Prob﴾Omnibus﴿:
0.004
Jarque‑Bera ﴾JB﴿:
17.298
Skew:
0.537
Prob﴾JB﴿:
0.000175
Kurtosis:
5.225
Cond. No.
1.49e+07
==============================================================================
Warnings:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[2] The condition number is large, 1.49e+07. This might indicate that there are
strong multicollinearity or other numerical problems.
"""
方法受到Unix管道和更近的为R引入流行的(%>%)(读管道)的dplyr 和magrittr 的启发。这里,pipe的实现非常干净,在Python中感
觉很好。我们鼓励你去读一下源码。
pipe
按行、按列调用函数
可以使用 apply() 方法在DataFrame的一个轴上调用任意函数,比如,描述统计方法,就有一个axis参数:
9.6.2
In [141]: df.apply(np.mean)
Out[141]:
one
‑0.272211
two
0.667306
three
0.024661
dtype: float64
In [142]: df.apply(np.mean, axis=1)
Out[142]:
a
0.011457
b
0.558507
c
0.635781
d
‑0.839603
dtype: float64
In [143]: df.apply(lambda x: x.max() ‑ x.min())
Out[143]:
one
1.563773
two
2.973170
three
3.154112
dtype: float64
In [144]: df.apply(np.cumsum)
Out[144]:
one
two
three
a ‑1.101558 1.124472
NaN
b ‑1.278848 3.611576 ‑0.634293
c ‑0.816633 3.125511 1.296901
d
NaN 2.669223 0.073983
In [145]: df.apply(np.exp)
Out[145]:
one
two
three
a 0.332353
3.078592
NaN
b 0.837537 12.026397 0.53031
c 1.587586
0.615041 6.89774
d
NaN
0.633631 0.29437
apply()
方法也能够调用字符串方法名:
In [146]: df.apply('mean')
Out[146]:
one
‑0.272211
two
0.667306
three
0.024661
dtype: float64
In [147]: df.apply('mean', axis=1)
Out[147]:
a
0.011457
b
0.558507
c
0.635781
d
‑0.839603
dtype: float64
传入 apply() 的函数返回值的类型会影响 DataFrame.apply 默认的最终的输出。
如果传入的函数返回Series,最终输出DataFrame。调用的函数返回匹配Series的索引的列。
如果传入的函数返回其他的类型,最终输出Series。
这种默认处理方式可以通过 result_type 来覆写, result_type 接受三种选项: reduce , broadcast 和 expand 。这将决定类列
表返回值如何扩展(或不扩展)成DataFrame。
apply() 合结合一些聪明的处理方式可以解决很多数据集的问题。例如,假设我们想要提取每列最大值对应的日期:
In [148]: tsdf = pd.DataFrame(np.random.randn(1000, 3), columns=['A', 'B', 'C'],
.....:
index=pd.date_range('1/1/2000', periods=1000))
.....:
In [149]: tsdf.apply(lambda x: x.idxmax())
Out[149]:
A
2001‑04‑25
B
2002‑05‑31
C
2002‑09‑25
dtype: datetime64[ns]
你也可以为 apply() 方法传入额外的参数和关键字参数。比如,考虑接下来这个你想调用的函数:
def subtract_and_divide(x, sub, divide=1):
return (x ‑ sub) / divide
你可以这么调用:
df.apply(subtract_and_divide, args=(5,), divide=3)
另一个有用的特性是能够传入Series方法在每一列或行上执行Series操作:
In [150]: tsdf
Out[150]:
A
B
C
2000‑01‑01 ‑0.720299 0.546303 ‑0.082042
2000‑01‑02 0.200295 ‑0.577554 ‑0.908402
2000‑01‑03 0.102533 1.653614 0.303319
2000‑01‑04
NaN
NaN
NaN
2000‑01‑05
NaN
NaN
NaN
2000‑01‑06
NaN
NaN
NaN
2000‑01‑07
NaN
NaN
NaN
2000‑01‑08 0.532566 0.341548 0.150493
2000‑01‑09 0.330418 1.761200 0.567133
2000‑01‑10 ‑0.251020 1.020099 1.893177
In [151]: tsdf.apply(pd.Series.interpolate)
Out[151]:
A
B
C
2000‑01‑01 ‑0.720299 0.546303 ‑0.082042
2000‑01‑02 0.200295 ‑0.577554 ‑0.908402
2000‑01‑03 0.102533 1.653614 0.303319
2000‑01‑04 0.188539 1.391201 0.272754
2000‑01‑05 0.274546 1.128788 0.242189
2000‑01‑06 0.360553 0.866374 0.211624
2000‑01‑07 0.446559 0.603961 0.181059
2000‑01‑08 0.532566 0.341548 0.150493
2000‑01‑09 0.330418 1.761200 0.567133
2000‑01‑10 ‑0.251020 1.020099 1.893177
最后, apply() 有一个 raw 参数,默认值为False,在调用函数之前将每一行或列转变为Series。当设为True时,传入的函数变为接收一个
ndarray对象,在不需要操作索引时,能够提升性能。
聚合API
0.20.0版本新特性。
聚合API允许使用者以一个简洁的方式表达可能的多种聚合操作。这个API类似于Pandas对象,查看分组 API,窗口函数API和重采样API。聚
合操作的入口是 DataFrame.aggregate() 或其别名 DataFrame.agg() 。
我们将用一个类似上面的起始数据框(开始):
9.6.3
In [152]: tsdf = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
.....:
index=pd.date_range('1/1/2000', periods=10))
.....:
In [153]: tsdf.iloc[3:7] = np.nan
In [154]: tsdf
Out[154]:
A
B
C
2000‑01‑01 0.170247 ‑0.916844 0.835024
2000‑01‑02 1.259919 0.801111 0.445614
2000‑01‑03 1.453046 2.430373 0.653093
2000‑01‑04
NaN
NaN
NaN
2000‑01‑05
NaN
NaN
NaN
2000‑01‑06
NaN
NaN
NaN
2000‑01‑07
NaN
NaN
NaN
2000‑01‑08 ‑1.874526 0.569822 ‑0.609644
2000‑01‑09 0.812462 0.565894 ‑1.461363
2000‑01‑10 ‑0.985475 1.388154 ‑0.078747
使用一个单独的函数等价于调用 apply() 。你也可以传入以字符串命名的方法。作为聚合操作的输出,这些将会返回一个Series:
In [155]: tsdf.agg(np.sum)
Out[155]:
A
0.835673
B
4.838510
C
‑0.216025
dtype: float64
In [156]: tsdf.agg('sum')
Out[156]:
A
0.835673
B
4.838510
C
‑0.216025
dtype: float64
# these are equivalent to a ``.sum﴾﴿`` because we are aggregating on a single function
In [157]: tsdf.sum()
Out[157]:
A
0.835673
B
4.838510
C
‑0.216025
dtype: float64
在一个Series上执行单独的聚合操作将返回一个标量:
pyhton
In [158]: tsdf.A.agg('sum')
Out[158]: 0.83567297915820504
调用多个函数执行聚合操作
你可以传入多个聚合参数组成的列表。每一个传入的函数的返回结果将会是结果DataFrame中的一行。这些都是自然地有聚合函数命名的:
9.6.3.1
In [159]: tsdf.agg(['sum'])
Out[159]:
A
B
C
sum 0.835673 4.83851 ‑0.216025
多个函数生成多个行:
In [160]: tsdf.agg(['sum', 'mean'])
Out[160]:
A
B
C
sum
0.835673 4.838510 ‑0.216025
mean 0.139279 0.806418 ‑0.036004
在一个Series上执行多个聚合操作,返回一个Series,这个Series的索引由聚合函数命名:
In [161]: tsdf.A.agg(['sum', 'mean'])
Out[161]:
sum
0.835673
mean
0.139279
Name: A, dtype: float64
传入lambda匿名函数,将生成一个由 <lambda> 命名的行:
In [162]: tsdf.A.agg(['sum', lambda x: x.mean()])
Out[162]:
sum
0.835673
<lambda>
0.139279
Name: A, dtype: float64
传入一个命名函数,将生成一个命名为该函数名的行:
In [163]: def mymean(x):
.....:
return x.mean()
.....:
In [164]: tsdf.A.agg(['sum', mymean])
Out[164]:
sum
0.835673
mymean
0.139279
Name: A, dtype: float64
使用字典执行聚合操作
为 DataFrame.agg 传入一个列名作为键,一个标量或标量列表作为值的字典,允许你自定义哪个列调用哪个函数。注意,结果没有排序,
你可以使用一个 OrderedDict 替代保证排序。
9.6.3.2
In [165]: tsdf.agg({'A': 'mean', 'B': 'sum'})
Out[165]:
A
0.139279
B
4.838510
dtype: float64
传入列表将生产一个DataFrame输出。你将得到一个含有所有聚合操作的类矩阵输出。这个输出由所有的独立函数组成。那些没有执行聚合操
作的列会返回NaN:
In [166]: tsdf.agg({'A': ['mean', 'min'], 'B': 'sum'})
Out[166]:
A
B
mean 0.139279
NaN
min ‑1.874526
NaN
sum
NaN 4.83851
混合数据类型
当提供的数据为不能执行聚合操作的混合数据类型时, .agg 将只执行合法的聚合操作。这个分组中的 .agg 工作模式相似:
9.6.3.3
In [167]: mdf = pd.DataFrame({'A': [1, 2, 3],
.....:
'B': [1., 2., 3.],
.....:
'C': ['foo', 'bar', 'baz'],
.....:
'D': pd.date_range('20130101', periods=3)})
.....:
In [168]: mdf.dtypes
Out[168]:
A
int64
B
float64
C
object
D
datetime64[ns]
dtype: object
In [169]: mdf.agg(['min', 'sum'])
Out[169]:
A
B
C
D
min 1 1.0
bar 2013‑01‑01
sum 6 6.0 foobarbaz
NaT
自定义描述
使用 .agg 可以很方便地创建自定义描述函数,类似于内置的描述函数:
9.6.3.4
In [170]: from functools import partial
In [171]: q_25 = partial(pd.Series.quantile, q=0.25)
In [172]: q_25.__name__ = '25%'
In [173]: q_75 = partial(pd.Series.quantile, q=0.75)
In [174]: q_75.__name__ = '75%'
In [175]: tsdf.agg(['count', 'mean', 'std', 'min', q_25, 'median', q_75, 'max'])
Out[175]:
A
B
C
count
6.000000 6.000000 6.000000
mean
0.139279 0.806418 ‑0.036004
std
1.323362 1.100830 0.874990
min
‑1.874526 ‑0.916844 ‑1.461363
25%
‑0.696544 0.566876 ‑0.476920
median 0.491354 0.685467 0.183433
75%
1.148055 1.241393 0.601223
max
1.453046 2.430373 0.835024
转换API
0.20.0版本新特性
9.6.4
方法返回一个和原始索引相同(相同大小)的对象。这个API允许你同时提供多个操作,而不是一个接着一个。它的API
和 .agg 的API很相似。
我们创建一个数据框,和上一节用到的类似。
transform()
In [176]: tsdf = pd.DataFrame(np.random.randn(10, 3), columns=['A', 'B', 'C'],
.....:
index=pd.date_range('1/1/2000', periods=10))
.....:
In [177]: tsdf.iloc[3:7] = np.nan
In [178]: tsdf
Out[178]:
A
B
C
2000‑01‑01 ‑0.578465 ‑0.503335 ‑0.987140
2000‑01‑02 ‑0.767147 ‑0.266046 1.083797
2000‑01‑03 0.195348 0.722247 ‑0.894537
2000‑01‑04
NaN
NaN
NaN
2000‑01‑05
NaN
NaN
NaN
2000‑01‑06
NaN
NaN
NaN
2000‑01‑07
NaN
NaN
NaN
2000‑01‑08 ‑0.556397 0.542165 ‑0.308675
2000‑01‑09 ‑1.010924 ‑0.672504 ‑1.139222
2000‑01‑10 0.354653 0.563622 ‑0.365106
对整个数据框执行转换操作, .tranform() 允许输入函数是:一个Numpy函数,一个字符串函数名或用户自定义函数。
In [179]: tsdf.transform(np.abs)
Out[179]:
A
B
C
2000‑01‑01 0.578465 0.503335 0.987140
2000‑01‑02 0.767147 0.266046 1.083797
2000‑01‑03 0.195348 0.722247 0.894537
2000‑01‑04
NaN
NaN
NaN
2000‑01‑05
NaN
NaN
NaN
2000‑01‑06
NaN
NaN
NaN
2000‑01‑07
NaN
NaN
NaN
2000‑01‑08 0.556397 0.542165 0.308675
2000‑01‑09 1.010924 0.672504 1.139222
2000‑01‑10 0.354653 0.563622 0.365106
In [180]: tsdf.transform('abs')
Out[180]:
A
B
C
2000‑01‑01 0.578465 0.503335 0.987140
2000‑01‑02 0.767147 0.266046 1.083797
2000‑01‑03 0.195348 0.722247 0.894537
2000‑01‑04
NaN
NaN
NaN
2000‑01‑05
NaN
NaN
NaN
2000‑01‑06
NaN
NaN
NaN
2000‑01‑07
NaN
NaN
NaN
2000‑01‑08 0.556397 0.542165 0.308675
2000‑01‑09 1.010924 0.672504 1.139222
2000‑01‑10 0.354653 0.563622 0.365106
In [181]: tsdf.transform(lambda x: x.abs())
Out[181]:
A
B
C
2000‑01‑01 0.578465 0.503335 0.987140
2000‑01‑02 0.767147 0.266046 1.083797
2000‑01‑03 0.195348 0.722247 0.894537
2000‑01‑04
NaN
NaN
NaN
2000‑01‑05
NaN
NaN
NaN
2000‑01‑06
NaN
NaN
NaN
2000‑01‑07
NaN
NaN
NaN
2000‑01‑08 0.556397 0.542165 0.308675
2000‑01‑09 1.010924 0.672504 1.139222
2000‑01‑10 0.354653 0.563622 0.365106
这里 transform() 接受了一个单独的函数,等价于调用Numpy的全局函数:
In [182]: np.abs(tsdf)
Out[182]:
A
B
2000‑01‑01 0.578465 0.503335
2000‑01‑02 0.767147 0.266046
2000‑01‑03 0.195348 0.722247
2000‑01‑04
NaN
NaN
2000‑01‑05
NaN
NaN
2000‑01‑06
NaN
NaN
2000‑01‑07
NaN
NaN
2000‑01‑08 0.556397 0.542165
2000‑01‑09 1.010924 0.672504
2000‑01‑10 0.354653 0.563622
C
0.987140
1.083797
0.894537
NaN
NaN
NaN
NaN
0.308675
1.139222
0.365106
在Series上为 .transform() 传入一个单独的函数,将返回一个Series:
In [183]: tsdf.A.transform(np.abs)
Out[183]:
2000‑01‑01
0.578465
2000‑01‑02
0.767147
2000‑01‑03
0.195348
2000‑01‑04
NaN
2000‑01‑05
NaN
2000‑01‑06
NaN
2000‑01‑07
NaN
2000‑01‑08
0.556397
2000‑01‑09
1.010924
2000‑01‑10
0.354653
Freq: D, Name: A, dtype: float64
使用多个函数进行转换
传入多个函数,将会生成一个列多级索引的DataFrame。第一级将会是原始数据框的列名,第二级将会是变换函数的名称:
9.6.4.1
In [184]: tsdf.transform([np.abs, lambda x: x+1])
Out[184]:
A
B
absolute <lambda> absolute <lambda>
2000‑01‑01 0.578465 0.421535 0.503335 0.496665
2000‑01‑02 0.767147 0.232853 0.266046 0.733954
2000‑01‑03 0.195348 1.195348 0.722247 1.722247
2000‑01‑04
NaN
NaN
NaN
NaN
2000‑01‑05
NaN
NaN
NaN
NaN
2000‑01‑06
NaN
NaN
NaN
NaN
2000‑01‑07
NaN
NaN
NaN
NaN
2000‑01‑08 0.556397 0.443603 0.542165 1.542165
2000‑01‑09 1.010924 ‑0.010924 0.672504 0.327496
2000‑01‑10 0.354653 1.354653 0.563622 1.563622
C
absolute <lambda>
0.987140 0.012860
1.083797 2.083797
0.894537 0.105463
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
0.308675 0.691325
1.139222 ‑0.139222
0.365106 0.634894
为Series传入多个函数,将生成一个DataFrame,结果的列名将会是转换函数名:
In [185]: tsdf.A.transform([np.abs, lambda x: x+1])
Out[185]:
absolute <lambda>
2000‑01‑01 0.578465 0.421535
2000‑01‑02 0.767147 0.232853
2000‑01‑03 0.195348 1.195348
2000‑01‑04
NaN
NaN
2000‑01‑05
NaN
NaN
2000‑01‑06
NaN
NaN
2000‑01‑07
NaN
NaN
2000‑01‑08 0.556397 0.443603
2000‑01‑09 1.010924 ‑0.010924
2000‑01‑10 0.354653 1.354653
使用字典进行转换
传入一个函数字典,将允许每列有选择的转换。
9.6.4.2
In [186]: tsdf.transform({'A': np.abs, 'B': lambda x: x+1})
Out[186]:
A
B
2000‑01‑01 0.578465 0.496665
2000‑01‑02 0.767147 0.733954
2000‑01‑03 0.195348 1.722247
2000‑01‑04
NaN
NaN
2000‑01‑05
NaN
NaN
2000‑01‑06
NaN
NaN
2000‑01‑07
NaN
NaN
2000‑01‑08 0.556397 1.542165
2000‑01‑09 1.010924 0.327496
2000‑01‑10 0.354653 1.563622
转入一个含有列表的字典,将返回一个多级索引的DataFrame:
In [187]: tsdf.transform({'A': np.abs, 'B': [lambda x: x+1, 'sqrt']})
Out[187]:
A
B
absolute <lambda>
sqrt
2000‑01‑01 0.578465 0.496665
NaN
2000‑01‑02 0.767147 0.733954
NaN
2000‑01‑03 0.195348 1.722247 0.849851
2000‑01‑04
NaN
NaN
NaN
2000‑01‑05
NaN
NaN
NaN
2000‑01‑06
NaN
NaN
NaN
2000‑01‑07
NaN
NaN
NaN
2000‑01‑08 0.556397 1.542165 0.736318
2000‑01‑09 1.010924 0.327496
NaN
2000‑01‑10 0.354653 1.563622 0.750748
调用按元素执行的函数
因为并不是所有的函数都能向量化(接受Numpy数组,返回另一个数组或值),Pandas提供了一个在applymap()方法,允许在DataFrame上
执行,接受任意的单参数单返回值的Python函数。类似地,在Series上执行map()方法,也是一样。例如:
9.6.5
In [188]: df4
Out[188]:
one
two
three
a ‑1.101558 1.124472
NaN
b ‑0.177289 2.487104 ‑0.634293
c 0.462215 ‑0.486066 1.931194
d
NaN ‑0.456288 ‑1.222918
In [189]: f = lambda x: len(str(x))
In [190]: df4['one'].map(f)
Out[190]:
a
19
b
20
c
18
d
3
Name: one, dtype: int64
In [191]: df4.applymap(f)
Out[191]:
one two three
a
19
18
3
b
20
18
19
c
18
20
18
d
3
19
19
还有附加属性:它可以用来方便地"链接"或"映射"被第二个Series定义的值。这与合并/连接功能密切相关:
Series.map()
In [192]: s = pd.Series(['six', 'seven', 'six', 'seven', 'six'],
.....:
index=['a', 'b', 'c', 'd', 'e'])
.....:
In [193]: t = pd.Series({'six' : 6., 'seven' : 7.})
In [194]: s
Out[194]:
a
six
b
seven
c
six
d
seven
e
six
dtype: object
In [195]: s.map(t)
Out[195]:
a
6.0
b
7.0
c
6.0
d
7.0
e
6.0
dtype: float64
9.6.6
从略
在Panel上调用函数
重建索引、改变标签
reindex() 是Pandas数据规整的基本方法。它用来执行几乎所有的依赖标签对齐函数的其他特性。重建索引,意味着把数据与特定轴上的
一组标签集匹配。这实现了几件事:
重新排序数据来匹配新的标签集。
在没有值匹配给定标签的位置插入缺失值标记(NA)。
如果指定,对缺失标签使用逻辑填充数据(与时间序列数据高度相关)。
下面是一个示例:
9.7
In [216]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
In [217]: s
Out[217]:
a
‑0.454087
b
‑0.360309
c
‑0.951631
d
‑0.535459
e
0.835231
dtype: float64
In [218]: s.reindex(['e', 'b', 'f', 'd'])
Out[218]:
e
0.835231
b
‑0.360309
f
NaN
d
‑0.535459
dtype: float64
这里, f 标签没有包含在Series中,因此在最终的结果中以NaN出现。
对于DataFrame,可以同时对行和列重建索引:
In [219]: df
Out[219]:
one
two
three
a ‑1.101558 1.124472
NaN
b ‑0.177289 2.487104 ‑0.634293
c 0.462215 ‑0.486066 1.931194
d
NaN ‑0.456288 ‑1.222918
In [220]: df.reindex(index=['c', 'f', 'b'], columns=['three', 'two', 'one'])
Out[220]:
three
two
one
c 1.931194 ‑0.486066 0.462215
f
NaN
NaN
NaN
b ‑0.634293 2.487104 ‑0.177289
也可以在使用reindex的时候传入axis关键字:
In [221]: df.reindex(['c', 'f', 'b'], axis='index')
Out[221]:
one
two
three
c 0.462215 ‑0.486066 1.931194
f
NaN
NaN
NaN
b ‑0.177289 2.487104 ‑0.634293
注意,包含实际axis标签的Index对象可以在对象之间共享。因此,如果我们有一个Series和DataFrame,可以执行下面的操作:
In [222]: rs = s.reindex(df.index)
In [223]: rs
Out[223]:
a
‑0.454087
b
‑0.360309
c
‑0.951631
d
‑0.535459
dtype: float64
In [224]: rs.index is df.index
Out[224]: True
这意味着,重建索引之后的Series索引和DataFrame索引是同样的Python对象。
0.21.0新特性。
DataFrame.reindex() 还支持一个"axis_style"调用惯例:指定一个单独的lables参数和它所调用的axis:
In [225]: df.reindex(['c', 'f', 'b'], axis='index')
Out[225]:
one
two
three
c 0.462215 ‑0.486066 1.931194
f
NaN
NaN
NaN
b ‑0.177289 2.487104 ‑0.634293
In [226]: df.reindex(['three', 'two', 'one'], axis='columns')
Out[226]:
three
two
one
a
NaN 1.124472 ‑1.101558
b ‑0.634293 2.487104 ‑0.177289
c 1.931194 ‑0.486066 0.462215
d ‑1.222918 ‑0.456288
NaN
查看:
MultiIndex / Advanced Indexing
是一种更简洁的重建索引方法。
当写性能敏感的代码时,有一个很好的理由来花些时间成为重建索引的忍者:许多操作在预先规整之后的数据上执行的更快。
将两个DataFrame相加,在内部会默认触发重建索引的步骤。对探索性分析来说,你将很难注意到不同(因为reindex被很大程
度的优化过),但是,当CPU周期比较重要时,一些小的显示的重建索引会有影响。
重建索引,和另一个对象对齐
你可能想要对一个对象重建索引,使他的轴标签和另一个对象相同。这一语法很简单,但冗长,它是一个通用的操作,可以用
reindex_like() 来简化:
9.7.1
In [227]: df2
Out[227]:
one
two
a ‑1.101558 1.124472
b ‑0.177289 2.487104
c 0.462215 ‑0.486066
In [228]: df3
Out[228]:
one
two
a ‑0.829347 0.082635
b 0.094922 1.445267
c 0.734426 ‑1.527903
In [229]: df.reindex_like(df2)
Out[229]:
one
two
a ‑1.101558 1.124472
b ‑0.177289 2.487104
c 0.462215 ‑0.486066
使用align在对象之间互相对齐
align() 方法是同时对齐两个对象的最快方法。它支持一个 join 参数(和 joining and merging 有关):
join='outer' :使用两个对象的索引的并集(默认)
join='left' :使用调用该方法的对象的索引
join='right' :使用作为参数传入该方法的对象的索引
join='inner' :使用两个对象的索引的交集
它将返回一个包含两个重建之后的索引的元组:
9.7.2
In [230]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
In [231]: s1 = s[:4]
In [232]: s2 = s[1:]
In [233]: s1.align(s2)
Out[233]:
(a
0.505453
b
1.788110
c
‑0.405908
d
‑0.801912
e
NaN
dtype: float64, a
b
1.788110
c
‑0.405908
d
‑0.801912
e
0.768460
dtype: float64)
NaN
In [234]: s1.align(s2, join='inner')
Out[234]:
(b
1.788110
c
‑0.405908
d
‑0.801912
dtype: float64, b
1.788110
c
‑0.405908
d
‑0.801912
dtype: float64)
In [235]: s1.align(s2, join='left')
Out[235]:
(a
0.505453
b
1.788110
c
‑0.405908
d
‑0.801912
dtype: float64, a
NaN
b
1.788110
c
‑0.405908
d
‑0.801912
dtype: float64)
对DataFrame,join方法会默认在行和列上都执行。
In [236]: df.align(df2, join='inner')
Out[236]:
(
one
two
a ‑1.101558 1.124472
b ‑0.177289 2.487104
c 0.462215 ‑0.486066,
one
a ‑1.101558 1.124472
b ‑0.177289 2.487104
c 0.462215 ‑0.486066)
two
也可以传入axis参数,来指定在特定的列上执行:
In [237]: df.align(df2, join='inner', axis=0)
Out[237]:
(
one
two
three
a ‑1.101558 1.124472
NaN
b ‑0.177289 2.487104 ‑0.634293
c 0.462215 ‑0.486066 1.931194,
one
a ‑1.101558 1.124472
b ‑0.177289 2.487104
c 0.462215 ‑0.486066)
two
如果为 DataFrame.align() 传入以Series,可以通过axis参数指定在DataFrame的索引或列上执行:
In [238]: df.align(df2.iloc[0], axis=1)
Out[238]:
(
one
three
two
a ‑1.101558
NaN 1.124472
b ‑0.177289 ‑0.634293 2.487104
c 0.462215 1.931194 ‑0.486066
d
NaN ‑1.222918 ‑0.456288, one
three
NaN
two
1.124472
Name: a, dtype: float64)
‑1.101558
重建索引时填充数据
reindex() 有一个参数选项method,它按照下表的方式来填充数据:
9.7.3
Method
Action
使用缺失值前面的值填充
bfill / backfill 使用缺失值后面的值填充
nearest 使用最近的索引的值填充
pad / ffill
我们在一个简单的Series演示一下:
In [239]: rng = pd.date_range('1/3/2000', periods=8)
In [240]: ts = pd.Series(np.random.randn(8), index=rng)
In [241]: ts2 = ts[[0, 3, 6]]
In [242]: ts
Out[242]:
2000‑01‑03
0.466284
2000‑01‑04
‑0.457411
2000‑01‑05
‑0.364060
2000‑01‑06
0.785367
2000‑01‑07
‑1.463093
2000‑01‑08
1.187315
2000‑01‑09
‑0.493153
2000‑01‑10
‑1.323445
Freq: D, dtype: float64
In [243]: ts2
Out[243]:
2000‑01‑03
0.466284
2000‑01‑06
0.785367
2000‑01‑09
‑0.493153
dtype: float64
In [244]: ts2.reindex(ts.index)
Out[244]:
2000‑01‑03
0.466284
2000‑01‑04
NaN
2000‑01‑05
NaN
2000‑01‑06
0.785367
2000‑01‑07
NaN
2000‑01‑08
NaN
2000‑01‑09
‑0.493153
2000‑01‑10
NaN
Freq: D, dtype: float64
In [245]: ts2.reindex(ts.index, method='ffill')
Out[245]:
2000‑01‑03
0.466284
2000‑01‑04
0.466284
2000‑01‑05
0.466284
2000‑01‑06
0.785367
2000‑01‑07
0.785367
2000‑01‑08
0.785367
2000‑01‑09
‑0.493153
2000‑01‑10
‑0.493153
Freq: D, dtype: float64
In [246]: ts2.reindex(ts.index, method='bfill')
Out[246]:
2000‑01‑03
0.466284
2000‑01‑04
0.785367
2000‑01‑05
0.785367
2000‑01‑06
0.785367
2000‑01‑07
‑0.493153
2000‑01‑08
‑0.493153
2000‑01‑09
‑0.493153
2000‑01‑10
NaN
Freq: D, dtype: float64
In [247]: ts2.reindex(ts.index, method='nearest')
Out[247]:
2000‑01‑03
0.466284
2000‑01‑04
0.466284
2000‑01‑05
0.785367
2000‑01‑06
0.785367
2000‑01‑07
0.785367
2000‑01‑08
‑0.493153
2000‑01‑09
‑0.493153
2000‑01‑10
‑0.493153
Freq: D, dtype: float64
这些方法要求索引是排好序的。
注意,使用fillna 或interpolate 也可以得到同样的结果(除了 method='nearest' ):
In [248]: ts2.reindex(ts.index).fillna(method='ffill')
Out[248]:
2000‑01‑03
0.466284
2000‑01‑04
0.466284
2000‑01‑05
0.466284
2000‑01‑06
0.785367
2000‑01‑07
0.785367
2000‑01‑08
0.785367
2000‑01‑09
‑0.493153
2000‑01‑10
‑0.493153
Freq: D, dtype: float64
如果索引不是单调递增或递减, reindex() 将会引发错误。 fillna() 和 interpolate() 不会对索引顺序做任何检查。
创建索引时填充数据的限制操作
limit 和 tolerance 参数为重建索引时填充数据提供了额外的控制选项。 limit 指定连续匹配的最大次数:
9.7.4
In [249]: ts2.reindex(ts.index, method='ffill', limit=1)
Out[249]:
2000‑01‑03
0.466284
2000‑01‑04
0.466284
2000‑01‑05
NaN
2000‑01‑06
0.785367
2000‑01‑07
0.785367
2000‑01‑08
NaN
2000‑01‑09
‑0.493153
2000‑01‑10
‑0.493153
Freq: D, dtype: float64
与之对比, tolerance 指定索引和索引器的值之间的最大距离:
pyhton
In [250]: ts2.reindex(ts.index, method='ffill', tolerance='1 day')
Out[250]:
2000‑01‑03
0.466284
2000‑01‑04
0.466284
2000‑01‑05
NaN
2000‑01‑06
0.785367
2000‑01‑07
0.785367
2000‑01‑08
NaN
2000‑01‑09
‑0.493153
2000‑01‑10
‑0.493153
Freq: D, dtype: float64
注意:在DatatimeIndex、TimedeltaIndex或PeriodIndex上使用时,如果可能, tolerance 将强制转换为Timedelta。这允许你用合适的字符
串来制定tolerance。
从一个轴丢弃数据
与reindex()关系密切的一个函数是 drop() 。它从一个轴移除一些标签集:
9.7.5
In [251]: df
Out[251]:
one
two
three
a ‑1.101558 1.124472
NaN
b ‑0.177289 2.487104 ‑0.634293
c 0.462215 ‑0.486066 1.931194
d
NaN ‑0.456288 ‑1.222918
In [252]: df.drop(['a', 'd'], axis=0)
Out[252]:
one
two
three
b ‑0.177289 2.487104 ‑0.634293
c 0.462215 ‑0.486066 1.931194
In [253]: df.drop(['one'], axis=1)
Out[253]:
two
three
a 1.124472
NaN
b 2.487104 ‑0.634293
c ‑0.486066 1.931194
d ‑0.456288 ‑1.222918
注意,下面的方法也可行,但显而易见会稍微慢一些:
In [254]: df.reindex(df.index.difference(['a', 'd']))
Out[254]:
one
two
three
b ‑0.177289 2.487104 ‑0.634293
c 0.462215 ‑0.486066 1.931194
重命名/映射标签
rename() 方法允许你使用映射(一个字典或Series)或任意函数来重命名标签。
9.7.6
In [255]: s
Out[255]:
a
0.505453
b
1.788110
c
‑0.405908
d
‑0.801912
e
0.768460
dtype: float64
In [256]: s.rename(str.upper)
Out[256]:
A
0.505453
B
1.788110
C
‑0.405908
D
‑0.801912
E
0.768460
dtype: float64
如果传入一个函数,当在任意标签上调用这个函数时,它必须返回一个值(而且必须生成一组唯一值)。字典和Series也可以使用。
In [257]: df.rename(columns={'one': 'foo', 'two': 'bar'},
.....:
index={'a': 'apple', 'b': 'banana', 'd': 'durian'})
.....:
Out[257]:
foo
bar
three
apple ‑1.101558 1.124472
NaN
banana ‑0.177289 2.487104 ‑0.634293
c
0.462215 ‑0.486066 1.931194
durian
NaN ‑0.456288 ‑1.222918
如果映射不包含行/列标签,将不会重命名。注意,映射中额外的标签名不会触发错误。 0.21.0版本的新特定。 DataFrame.rename() 也支
持一种"axis-style"调用惯例,可以提供一个单独的映射并且指定在哪个抽上执行:
In [258]: df.rename({'one': 'foo', 'two': 'bar'}, axis='columns')
Out[258]:
foo
bar
three
a ‑1.101558 1.124472
NaN
b ‑0.177289 2.487104 ‑0.634293
c 0.462215 ‑0.486066 1.931194
d
NaN ‑0.456288 ‑1.222918
In [259]: df.rename({'a': 'apple', 'b': 'banana', 'd': 'durian'}, axis='index')
Out[259]:
one
two
three
apple ‑1.101558 1.124472
NaN
banana ‑0.177289 2.487104 ‑0.634293
c
0.462215 ‑0.486066 1.931194
durian
NaN ‑0.456288 ‑1.222918
方法提供了一个 inplace 命名参数(默认是False,代表复制一份数据),来控制是否在内部复制数据。可以将其置为True,替
换现有的数据。
0.18.0新特性。
最后, rename() 也接受一个标量或类列表的值来更改 Series.name 的属性。
rename()
In [260]: s.rename("scalar‑name")
Out[260]:
a
0.505453
b
1.788110
c
‑0.405908
d
‑0.801912
e
0.768460
Name: scalar‑name, dtype: float64
类有一个相关的 rename_axis() 类来修改它的三个轴的名称。
Panel
9.8
迭代
在Pandas对象上执行基本迭代操作的处理方式取决于数据类型。迭代Series时,将会把它当做数组来处理,生成一组数据。其他数据结构
(DataFrame和Panel)将会像迭代字典一样按照对象的 key 进行迭代。 简单来说,基本迭代操作(for i in object)将会生成:
Series:生成数值
DataFrame:生成列标签
Panel:生成元素标签
所以,举例来说,在DataFrame上执行迭代,将会得到列名:
In [261]: df = pd.DataFrame({'col1' : np.random.randn(3), 'col2' : np.random.randn(3)},
.....:
index=['a', 'b', 'c'])
.....:
In [262]: for col in df:
.....:
print(col)
.....:
col1
col2
对象也有一个类似字典的iteritems()方法来迭代(key, value)对。
可以使用下面的方法来迭代DataFrame的行:
iterrows() :以(index, Series)对的形式迭代DataFrame的行,将行转换成可以更改数据类型并且能够获得一些性能提升的Series。
itertuples() : 以命名元组的形式迭代DataFrame的行。这个方法比 iterrows() 快一些,也是多数情况下经常用到的。
Pandas
警告:
迭代Pandas对象通常比较慢,很多情况下,不需要手动迭代,而且可以通过下面的方式代替迭代:
使用向量化方案:许多操作可以通过使用内置方法、Numpy函数或布尔索引等来获得性能提升。
如果你的函数不能作用在整个DataFrame或Series上,使用 apply() 比使用迭代强。查看函数调用获得更多信息。
如果你非要迭代数据而且性能也重要,考虑用cython或numba来写内部的循环。查看增强性能的部分来获得示例。
警告:
不要修改迭代对象。不一定在所有情况下都能正常工作。取决于数据类型,迭代器(有可能)返回一份拷贝,而不是视图。修
改拷贝不会起作用。
举例来说,下面的示例中设置数值没有效果:
In [263]: df = pd.DataFrame({'a': [1, 2, 3], 'b': ['a', 'b', 'c']})
In [264]: for index, row in df.iterrows():
.....:
row['a'] = 10
.....:
In [265]: df
Out[265]:
a b
0 1 a
1 2 b
2 3 c
9.8.1 iteritems
与字典的接口相似, iteritems() 以key-value对的形式迭代数据:
Series:(index, scale value)对
DataFrame:(column, Series)对
Panel:(item, DataFrame)对
例如:
In [266]: for item, frame in wp.iteritems():
.....:
print(item)
.....:
print(frame)
.....:
Item1
A
B
C
D
2000‑01‑01 ‑0.433567 ‑0.273610 0.680433 ‑0.308450
2000‑01‑02 ‑0.276099 ‑1.821168 ‑1.993606 ‑1.927385
2000‑01‑03 ‑2.027924 1.624972 0.551135 3.059267
2000‑01‑04 0.455264 ‑0.030740 0.935716 1.061192
2000‑01‑05 ‑2.107852 0.199905 0.323586 ‑0.641630
Item2
A
B
C
D
2000‑01‑01 ‑0.587514 0.053897 0.194889 ‑0.381994
2000‑01‑02 0.318587 2.089075 ‑0.728293 ‑0.090255
2000‑01‑03 ‑0.748199 1.318931 ‑2.029766 0.792652
2000‑01‑04 0.461007 ‑0.542749 ‑0.305384 ‑0.479195
2000‑01‑05 0.095031 ‑0.270099 ‑0.707140 ‑0.773882
9.8.2 iterrows
iterrows()
允许把DataFrame的每一行当做Series对象来迭代,返回一个迭代器,每一个索引值对应DataFrame的一行。
In [267]: for row_index, row in df.iterrows():
.....:
print('%s\n%s' % (row_index, row))
.....:
0
a
1
b
a
Name: 0, dtype: object
1
a
2
b
b
Name: 1, dtype: object
2
a
3
b
c
Name: 2, dtype: object
注意:
因为 iterrows() 为每一行返回一个Series,它不会跨行保留数据类型(DataFrame跨列会保留数据类型)。例如:
In [268]: df_orig = pd.DataFrame([[1, 1.5]], columns=['int', 'float'])
In [269]: df_orig.dtypes
Out[269]:
int
int64
float
float64
dtype: object
In [270]: row = next(df_orig.iterrows())[1]
In [271]: row
Out[271]:
int
1.0
float
1.5
Name: 0, dtype: float64
以Series返回的 row 中的所有数值,现在向上转换为float类型,这也是X列中的原始整型数值。
In [272]: row['int'].dtype
Out[272]: dtype('float64')
In [273]: df_orig['int'].dtype
Out[273]: dtype('int64')
为了在迭代行时保留数据类型,最好使用 itertuples() , itertuples() 返回数值组成的命名元组,而且
比 iterrows() 快。
作为示例,一种人为的转置DataFrame的方式可能是:
In [274]: df2 = pd.DataFrame({'x': [1, 2, 3], 'y': [4, 5, 6]})
In [275]: print(df2)
x y
0 1 4
1 2 5
2 3 6
In [276]: print(df2.T)
0 1 2
x 1 2 3
y 4 5 6
In [277]: df2_t = pd.DataFrame(dict((idx,values) for idx, values in df2.iterrows()))
In [278]: print(df2_t)
0 1 2
x 1 2 3
y 4 5 6
itertuples
itertuples()
素是该行的值。
例如:
方法会返回一个迭代器,DataFrame的每一行被当做一个命名元组,命名元组的第一个元素是该行对应的索引值,剩下的元
In [279]: for row in df.itertuples():
.....:
print(row)
.....:
Pandas(Index=0, a=1, b='a')
Pandas(Index=1, a=2, b='b')
Pandas(Index=2, a=3, b='c')
这个方法没有将行转换成Series对象,它只返回命名元组中的值。因此, itertuples() 保留了数值的数据类型,而且通常比
iterrows() 快。
注意:
如果列名是无效的Python标识符、重复的或以下划线开头,列名将会被重命名为位置名称。对于大量的列(大于255),将返
回普通元组。
访问器
如果一个Series是类型datetime或类period的,它会有一个可以方便地返回datetime的访问器,就像是Series的属性一样。这将会返回一个
Series,像原来的Series一样索引。
9.9 .dt
# datetime
In [280]: s = pd.Series(pd.date_range('20130101 09:10:12', periods=4))
In [281]: s
Out[281]:
0
2013‑01‑01 09:10:12
1
2013‑01‑02 09:10:12
2
2013‑01‑03 09:10:12
3
2013‑01‑04 09:10:12
dtype: datetime64[ns]
In [282]: s.dt.hour
Out[282]:
0
9
1
9
2
9
3
9
dtype: int64
In [283]: s.dt.second
Out[283]:
0
12
1
12
2
12
3
12
dtype: int64
In [284]: s.dt.day
Out[284]:
0
1
1
2
2
3
3
4
dtype: int64
这会得到下面这样的漂亮的表达式:
In [285]: s[s.dt.day==2]
Out[285]:
1
2013‑01‑02 09:10:12
dtype: datetime64[ns]
可以很容易生成时区转换
In [286]: stz = s.dt.tz_localize('US/Eastern')
In [287]: stz
Out[287]:
0
2013‑01‑01 09:10:12‑05:00
1
2013‑01‑02 09:10:12‑05:00
2
2013‑01‑03 09:10:12‑05:00
3
2013‑01‑04 09:10:12‑05:00
dtype: datetime64[ns, US/Eastern]
In [288]: stz.dt.tz
Out[288]: <DstTzInfo 'US/Eastern' LMT‑1 day, 19:04:00 STD>
也可以把这些操作连起来
In [289]: s.dt.tz_localize('UTC').dt.tz_convert('US/Eastern')
Out[289]:
0
2013‑01‑01 04:10:12‑05:00
1
2013‑01‑02 04:10:12‑05:00
2
2013‑01‑03 04:10:12‑05:00
3
2013‑01‑04 04:10:12‑05:00
dtype: datetime64[ns, US/Eastern]
也可以通过 Series.dt.strftime() 来把datetime转换成字符串。 Series.dt.strftime() 支持标准 strftime() 的同样的格式。
# DatetimeIndex
In [290]: s = pd.Series(pd.date_range('20130101', periods=4))
In [291]: s
Out[291]:
0
2013‑01‑01
1
2013‑01‑02
2
2013‑01‑03
3
2013‑01‑04
dtype: datetime64[ns]
In [292]: s.dt.strftime('%Y/%m/%d')
Out[292]:
0
2013/01/01
1
2013/01/02
2
2013/01/03
3
2013/01/04
dtype: object
# PeriodIndex
In [293]: s = pd.Series(pd.period_range('20130101', periods=4))
In [294]: s
Out[294]:
0
2013‑01‑01
1
2013‑01‑02
2
2013‑01‑03
3
2013‑01‑04
dtype: object
In [295]: s.dt.strftime('%Y/%m/%d')
Out[295]:
0
2013/01/01
1
2013/01/02
2
2013/01/03
3
2013/01/04
dtype: object
.dt
访问器也可以作用在period和timedelta数据类型上
# period
In [296]: s = pd.Series(pd.period_range('20130101', periods=4, freq='D'))
In [297]: s
Out[297]:
0
2013‑01‑01
1
2013‑01‑02
2
2013‑01‑03
3
2013‑01‑04
dtype: object
In [298]: s.dt.year
Out[298]:
0
2013
1
2013
2
2013
3
2013
dtype: int64
In [299]: s.dt.day
Out[299]:
0
1
1
2
2
3
3
4
dtype: int64
# timedelta
In [300]: s = pd.Series(pd.timedelta_range('1 day 00:00:05', periods=4, freq='s'))
In [301]: s
Out[301]:
0
1 days 00:00:05
1
1 days 00:00:06
2
1 days 00:00:07
3
1 days 00:00:08
dtype: timedelta64[ns]
In [302]: s.dt.days
Out[302]:
0
1
1
1
2
1
3
1
dtype: int64
In [303]: s.dt.seconds
Out[303]:
0
5
1
6
2
7
3
8
dtype: int64
In [304]: s.dt.components
Out[304]:
days hours minutes seconds
0
1
0
0
5
1
1
0
0
6
2
1
0
0
7
3
1
0
0
8
milliseconds
0
0
0
0
microseconds
0
0
0
0
nanoseconds
0
0
0
0
注意:
如果你访问了一个非类datetime的Series, Series.dt 将会触发TypeError异常。
向量化字符串方法
Series配备了一系列字符串处理方法,操作数组中的每个元素非常容易。也许是最重要的,这些方法自动排除缺失数据。这些方法是通过
Series的 .str 属性来实现,一般来说,都有一个内置的等同的字符串方法与其同名。例如:
9.10
In [305]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
In [306]: s.str.lower()
Out[306]:
0
a
1
b
2
c
3
aaba
4
baca
5
NaN
6
caba
7
dog
8
cat
dtype: object
也有强大的模式匹配方法,但是,请注意,模式匹配一般默认使用正则表达式(在某些情况下一直使用)。
查看向量化字符串方法获得更多信息。
排序
支持三种排序:按索引标签、按列名、按索引标签和列名。
Pandas
按索引
Series.sort_index()
和 DataFrame.sort_index() 是按照索引标签索引Pandas对象的方法。
In [307]: 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 [308]: unsorted_df = df.reindex(index=['a', 'd', 'c', 'b'],
.....:
columns=['three', 'two', 'one'])
.....:
In [309]: unsorted_df
Out[309]:
three
two
one
a
NaN 0.708543 0.036274
d ‑0.540166 0.586626
NaN
c 0.410238 1.121731 1.044630
b ‑0.282532 ‑2.038777 ‑0.490032
# DataFrame
In [310]: unsorted_df.sort_index()
Out[310]:
three
two
one
a
NaN 0.708543 0.036274
b ‑0.282532 ‑2.038777 ‑0.490032
c 0.410238 1.121731 1.044630
d ‑0.540166 0.586626
NaN
In [311]: unsorted_df.sort_index(ascending=False)
Out[311]:
three
two
one
d ‑0.540166 0.586626
NaN
c 0.410238 1.121731 1.044630
b ‑0.282532 ‑2.038777 ‑0.490032
a
NaN 0.708543 0.036274
In [312]: unsorted_df.sort_index(axis=1)
Out[312]:
one
three
two
a 0.036274
NaN 0.708543
d
NaN ‑0.540166 0.586626
c 1.044630 0.410238 1.121731
b ‑0.490032 ‑0.282532 ‑2.038777
# Series
In [313]: unsorted_df['three'].sort_index()
Out[313]:
a
NaN
b
‑0.282532
c
0.410238
d
‑0.540166
Name: three, dtype: float64
按数值
方法是用来按数值排序Series的方法。 DataFrame.sort_values() 是按照列数值或行数值排序DataFrame的
方法。 DataFrame.sort_values() 的可选参数 by 用来指定使用一个或多个列来确定排序的顺序。
Series.sort_values()
In [314]: df1 = pd.DataFrame({'one':[2,1,1,1],'two':[1,3,2,4],'three':[5,4,3,2]})
In [315]: df1.sort_values(by='two')
Out[315]:
one two three
0
2
1
5
2
1
2
3
1
1
3
4
3
1
4
2
by
参数可以输入一个列名列表,例如:
In [316]: df1[['one', 'two', 'three']].sort_values(by=['one','two'])
Out[316]:
one two three
2
1
2
3
1
1
3
4
3
1
4
2
0
2
1
5
na_position
参数可以为这个方法指定如何处理NA值:
In [317]: s[2] = np.nan
In [318]: s.sort_values()
Out[318]:
0
A
3
Aaba
1
B
4
Baca
6
CABA
8
cat
7
dog
2
NaN
5
NaN
dtype: object
In [319]: s.sort_values(na_position='first')
Out[319]:
2
NaN
5
NaN
0
A
3
Aaba
1
B
4
Baca
6
CABA
8
cat
7
dog
dtype: object
按索引标签和值
0.23.0新增特性
传入 DataFrame.sort_values() 的 by 参数的字符串可能指向列或索引级别名:
# Build MultiIndex
In [320]: idx = pd.MultiIndex.from_tuples([('a', 1), ('a', 2), ('a', 2),
.....:
('b', 2), ('b', 1), ('b', 1)])
.....:
In [321]: idx.names = ['first', 'second']
# Build DataFrame
In [322]: df_multi = pd.DataFrame({'A': np.arange(6, 0, ‑1)},
.....:
index=idx)
.....:
In [323]: df_multi
Out[323]:
A
first second
a
1
6
2
5
2
4
b
2
3
1
2
1
1
以 second (索引)和 A (列)排序:
In [324]: df_multi.sort_values(by=['second', 'A'])
Out[324]:
A
first second
b
1
1
1
2
a
1
6
b
2
3
a
2
4
2
5
注意:
如果一个字符换既能匹配列名也能匹配索引级别名,将会引起一个警告,并且列名优先。将来的版本将会触发 ambiguity 错
误。
searchsorted
有一个 searchsorted() 方法,和 numpy.ndarray.searchsorted() 的工作方式类似。
Series
In [325]: ser = pd.Series([1, 2, 3])
In [326]: ser.searchsorted([0, 3])
Out[326]: array([0, 2])
In [327]: ser.searchsorted([0, 4])
Out[327]: array([0, 3])
In [328]: ser.searchsorted([1, 3], side='right')
Out[328]: array([1, 3])
In [329]: ser.searchsorted([1, 3], side='left')
Out[329]: array([0, 2])
In [330]: ser = pd.Series([3, 1, 2])
In [331]: ser.searchsorted([0, 3], sorter=np.argsort(ser))
Out[331]: array([0, 2])
最小值、最大值
Series的 nsmallest() 和 nlargest() 方法将会返回第n小或第n大的值。对于大数据量的Series,这两个方法将会比排序整个Series再执
行head(n)快。
In [332]: s = pd.Series(np.random.permutation(10))
In [333]: s
Out[333]:
0
8
1
2
2
9
3
5
4
6
5
0
6
1
7
7
8
4
9
3
dtype: int64
In [334]: s.sort_values()
Out[334]:
5
0
6
1
1
2
9
3
8
4
3
5
4
6
7
7
0
8
2
9
dtype: int64
In [335]: s.nsmallest(3)
Out[335]:
5
0
6
1
1
2
dtype: int64
In [336]: s.nlargest(3)
Out[336]:
2
9
0
8
7
7
dtype: int64
也有这两个方法:
DataFrame
In [337]: df = pd.DataFrame({'a': [‑2, ‑1, 1, 10, 8, 11, ‑1],
.....:
'b': list('abdceff'),
.....:
'c': [1.0, 2.0, 4.0, 3.2, np.nan, 3.0, 4.0]})
.....:
In [338]: df.nlargest(3, 'a')
Out[338]:
a b
c
5 11 f 3.0
3 10 c 3.2
4
8 e NaN
In [339]: df.nlargest(5, ['a', 'c'])
Out[339]:
a b
c
6 ‑1 f 4.0
5 11 f 3.0
3 10 c 3.2
4
8 e NaN
2
1 d 4.0
In [340]: df.nsmallest(3, 'a')
Out[340]:
a b
c
0 ‑2 a 1.0
1 ‑1 b 2.0
6 ‑1 f 4.0
In [341]: df.nsmallest(5, ['a', 'c'])
Out[341]:
a b
c
0 ‑2 a 1.0
2 1 d 4.0
4 8 e NaN
1 ‑1 b 2.0
6 ‑1 f 4.0
按多重索引排序
当按多重索引排序时,必须明确指定所有的索引等级:
In [342]: df1.columns = pd.MultiIndex.from_tuples([('a','one'),('a','two'),('b','three')])
In [343]: df1.sort_values(by=('a','two'))
Out[343]:
a
b
one two three
0
2
1
5
2
1
2
3
1
1
3
4
3
1
4
2
复制
在Pandas对象上执行 copy() 方法,将会复制内部的数据(虽然不是轴索引,因为它们是不可变的)并且返回一个新的对象。注意,很小需
要复制对象。比如:只有少数几种方法可以就地更改一个DataFrame:
插入、删除、修改列
为 index 和 columns 属性赋值
对同类型的数据,直接通过 values 属性修改数据或高级索引
更清楚地说,没有Pandas方法修改数据时有副作用;几乎所有的方法返回一个新的对象,源数据不会动。如果数据被修改,肯定是因为你明
确指定了。
dtypes
对象的主要数据类型时float、int、bool、datetime64[ns]、datetime64[ns, tz]、timedelta[ns]、category和object。另外,这些数据类型
都有项的大小,比如int64和int32。查看Series with TZ获取更多关于datetime64[ns, tz]数据类型的细节。
DataFrame的一个方便的 dtypes 属性返回一个包含每一列的数据类型的Series:
Pandas
In [344]: dft = pd.DataFrame(dict(A = np.random.rand(3),
.....:
B = 1,
.....:
C = 'foo',
.....:
D = pd.Timestamp('20010102'),
.....:
E = pd.Series([1.0]*3).astype('float32'),
.....:
F = False,
.....:
G = pd.Series([1]*3,dtype='int8')))
.....:
In [345]: dft
Out[345]:
A B
0 0.809585 1
1 0.128238 1
2 0.775752 1
C
D
foo 2001‑01‑02
foo 2001‑01‑02
foo 2001‑01‑02
E
1.0
1.0
1.0
F
False
False
False
G
1
1
1
In [346]: dft.dtypes
Out[346]:
A
float64
B
int64
C
object
D
datetime64[ns]
E
float32
F
bool
G
int8
dtype: object
在Series对象上使用 dtype 属性
In [347]: dft['A'].dtype
Out[347]: dtype('float64')
如果Pandas对象的一列上包含多种数据类型,列的数据类型将会选择能够包含所有数据的类型(一般是object)
# these ints are coerced to floats
In [348]: pd.Series([1, 2, 3, 4, 5, 6.])
Out[348]:
0
1.0
1
2.0
2
3.0
3
4.0
4
5.0
5
6.0
dtype: float64
# string data forces an ``object`` dtype
In [349]: pd.Series([1, 2, 3, 6., 'foo'])
Out[349]:
0
1
1
2
2
3
3
6
4
foo
dtype: object
一个DataFrame的各列的数据类型数量可以通过调用 get_dtype_counts() 来获得
In [350]: dft.get_dtype_counts()
Out[350]:
float64
1
float32
1
int64
1
int8
1
datetime64[ns]
1
bool
1
object
1
dtype: int64
数值类型将传播,并且可以在DataFrame中共存。如果传入了一个数据类型(直接通过dtype关键字、传入的ndarray或传入的Series),那么
它将在DataFrame操作中被保存。另外,不同的数值类型不会被合并。下面的示例将是一个尝试:
In [351]: df1 = pd.DataFrame(np.random.randn(8, 1), columns=['A'], dtype='float32')
In [352]: df1
Out[352]:
A
0 0.890400
1 0.283331
2 ‑0.303613
3 ‑1.192210
4 0.065420
5 0.455918
6 2.008328
7 0.188942
In [353]: df1.dtypes
Out[353]:
A
float32
dtype: object
In [354]: df2 = pd.DataFrame(dict( A = pd.Series(np.random.randn(8), dtype='float16'),
.....:
B = pd.Series(np.random.randn(8)),
.....:
C = pd.Series(np.array(np.random.randn(8), dtype='uint8')) ))
.....:
In [355]: df2
Out[355]:
A
B
0 ‑0.454346 0.200071
1 ‑0.916504 ‑0.557756
2 0.640625 ‑0.141988
3 2.675781 ‑0.174060
4 ‑0.007866 0.258626
5 ‑0.204224 0.941688
6 ‑0.100098 ‑1.849045
7 ‑0.402100 ‑0.949458
C
255
255
0
0
0
0
0
0
In [356]: df2.dtypes
Out[356]:
A
float16
B
float64
C
uint8
dtype: object
默认情况
默认地,不管平台是64位还是32位,整型数据类型是int64,浮点型是float64。下面的示例将都会返回int64数据类型:
In [357]: pd.DataFrame([1, 2], columns=['a']).dtypes
Out[357]:
a
int64
dtype: object
In [358]: pd.DataFrame({'a': [1, 2]}).dtypes
Out[358]:
a
int64
dtype: object
In [359]: pd.DataFrame({'a': 1 }, index=list(range(2))).dtypes
Out[359]:
a
int64
dtype: object
注意,Numpy创建数组时会根据平台类型选择数据类型。下面的示例在32位平台上将会返回int32类型。
In [360]: frame = pd.DataFrame(np.array([1, 2]))
向上转型
合并不同的数据类型时,有可能会向上转型,这表示,结果将会从现有的数据类型提升获得:
In [361]: df3 = df1.reindex_like(df2).fillna(value=0.0) + df2
In [362]: df3
Out[362]:
A
B
0 0.436054 0.200071
1 ‑0.633173 ‑0.557756
2 0.337012 ‑0.141988
3 1.483571 ‑0.174060
4 0.057555 0.258626
5 0.251695 0.941688
6 1.908231 ‑1.849045
7 ‑0.213158 ‑0.949458
C
255.0
255.0
0.0
0.0
0.0
0.0
0.0
0.0
In [363]: df3.dtypes
Out[363]:
A
float32
B
float64
C
float64
dtype: object
的 values 属性会返回数据类型的最大公约数,就是说,在返回的Numpy同类型数组中的数据类型将能够容纳所有的数据类型。
这会强制执行一些向上转型:
DataFrame
In [364]: df3.values.dtype
Out[364]: dtype('float64')
astype
可以通过 astype() 方法来明确地将一个数据类型转为另一个。默认地,这将返回一个拷贝,即使数据类型没有改变(传入 copy=False 可
以不执行拷贝)。另外,如果astype操作是非法的将会触发异常。
向上取整一直按照Numpy规则来执行。如果一个操作中涉及两个不同的数据类型,包含范围更大的那个将会在结果中使用。
In [365]: df3
Out[365]:
A
B
0 0.436054 0.200071
1 ‑0.633173 ‑0.557756
2 0.337012 ‑0.141988
3 1.483571 ‑0.174060
4 0.057555 0.258626
5 0.251695 0.941688
6 1.908231 ‑1.849045
7 ‑0.213158 ‑0.949458
C
255.0
255.0
0.0
0.0
0.0
0.0
0.0
0.0
In [366]: df3.dtypes
Out[366]:
A
float32
B
float64
C
float64
dtype: object
# conversion of dtypes
In [367]: df3.astype('float32').dtypes
Out[367]:
A
float32
B
float32
C
float32
dtype: object
使用 astype() 将列的子集转换为特定的数据类型:
In [368]: dft = pd.DataFrame({'a': [1,2,3], 'b': [4,5,6], 'c': [7, 8, 9]})
In [369]: dft[['a','b']] = dft[['a','b']].astype(np.uint8)
In [370]: dft
Out[370]:
a b c
0 1 4 7
1 2 5 8
2 3 6 9
In [371]: dft.dtypes
Out[371]:
a
uint8
b
uint8
c
int64
dtype: object
版新增特性
为 astype() 传入一个字典来把特定的列转成指定的数据类型
0.19.0
In [372]: dft1 = pd.DataFrame({'a': [1,0,1], 'b': [4,5,6], 'c': [7, 8, 9]})
In [373]: dft1 = dft1.astype({'a': np.bool, 'c': np.float64})
In [374]: dft1
Out[374]:
a b
c
0
True 4 7.0
1 False 5 8.0
2
True 6 9.0
In [375]: dft1.dtypes
Out[375]:
a
bool
b
int64
c
float64
dtype: object
注意:
当使用 astype() 和 loc() 尝试将列的子集转换为特定数据类型时,将会发生向上取整。
loc() 试着匹配我们指定给当前类型的内容,同时 [] 将使用右边的数据类型覆写它们。所以,下面的代码片段产生了意想
不到的结果:
In [376]: dft = pd.DataFrame({'a': [1,2,3], 'b': [4,5,6], 'c': [7, 8, 9]})
In [377]: dft.loc[:, ['a', 'b']].astype(np.uint8).dtypes
Out[377]:
a
uint8
b
uint8
dtype: object
In [378]: dft.loc[:, ['a', 'b']] = dft.loc[:, ['a', 'b']].astype(np.uint8)
In [379]: dft.dtypes
Out[379]:
a
int64
b
int64
c
int64
dtype: object
数据类型转换
Pandas提供了许多函数来强制把object数据类型转换成其他数据类型。为了处理数据使用了正确的数据类型,但被存为object类型的情况,
DataFrame.infer_objects() 和 Series.infer_objects() 方法可以用来将数据软转换为正确的类型:
object
In [380]: import datetime
In [381]: df = pd.DataFrame([[1, 2],
.....:
['a', 'b'],
.....:
[datetime.datetime(2016, 3, 2), datetime.datetime(2016, 3, 2)]])
.....:
In [382]: df = df.T
In [383]: df
Out[383]:
0 1
2
0 1 a 2016‑03‑02 00:00:00
1 2 b 2016‑03‑02 00:00:00
In [384]: df.dtypes
Out[384]:
0
object
1
object
2
object
dtype: object
因为数据被转置了,原型推断将所有的列存为了object类型, infer_objects 将会展示实际的类型:
In [385]: df.infer_objects().dtypes
Out[385]:
0
int64
1
object
2
datetime64[ns]
dtype: object
下面的函数可以将一维object数组或标量硬转换为指定的类型:
to_numeric() (转换为数值类型)
In [386]: m = ['1.1', 2, 3]
In [387]: pd.to_numeric(m)
Out[387]: array([ 1.1, 2. ,
to_datetime()
3. ])
(转换为datetime类型)
In [388]: import datetime
In [389]: m = ['2016‑07‑09', datetime.datetime(2016, 3, 2)]
In [390]: pd.to_datetime(m)
Out[390]: DatetimeIndex(['2016‑07‑09', '2016‑03‑02'], dtype='datetime64[ns]', freq=None)
to_timedelta()
(转换为timedelta类型)
In [391]: m = ['5us', pd.Timedelta('1day')]
In [392]: pd.to_timedelta(m)
Out[392]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[n
s]', freq=None
强制转换时,我们可以转入一个 errors 参数,来指定那些不能正确转换为指定的数据类型或对象的数据应该如何处理。默认地,
erros='raise' ,意味着任何错误在转换时都会抛出。不过,如果 erros='coerce' ,这些错误将会被忽略并且Pandas将把有问题的元
素转换为 pd.NaT (datetime和timedelta)或 np.na (数值型)。在读取大部分是期待的数据类型(比如数值型,时间型)但偶尔有一些不
一致的混在一起的你想用缺失代替的数据时,这种处理方式也许有用。
In [393]: import datetime
In [394]: m = ['apple', datetime.datetime(2016, 3, 2)]
In [395]: pd.to_datetime(m, errors='coerce')
Out[395]: DatetimeIndex(['NaT', '2016‑03‑02'], dtype='datetime64[ns]', freq=None)
In [396]: m = ['apple', 2, 3]
In [397]: pd.to_numeric(m, errors='coerce')
Out[397]: array([ nan,
2.,
3.])
In [398]: m = ['apple', pd.Timedelta('1day')]
In [399]: pd.to_timedelta(m, errors='coerce')
Out[399]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None)
errors
参数还有第三个选项 erros='ignore' ,这种情况下,在遇到不能正确转换为期待的数据类型时,将简单地返回传入的数据。
In [400]: import datetime
In [401]: m = ['apple', datetime.datetime(2016, 3, 2)]
In [402]: pd.to_datetime(m, errors='ignore')
Out[402]: array(['apple', datetime.datetime(2016, 3, 2, 0, 0)], dtype=object)
In [403]: m = ['apple', 2, 3]
In [404]: pd.to_numeric(m, errors='ignore')
Out[404]: array(['apple', 2, 3], dtype=object)
In [405]: m = ['apple', pd.Timedelta('1day')]
In [406]: pd.to_timedelta(m, errors='ignore')
Out[406]: array(['apple', Timedelta('1 days 00:00:00')], dtype=object)
转换之外, to_numeric() 提供了另一参数 downcast ,给出了一个将新的(或已经存在的)数据转换为较小的数据类型的节省内存
的选项。
object
In [407]: m = ['1', 2, 3]
In [408]: pd.to_numeric(m, downcast='integer')
Out[408]: array([1, 2, 3], dtype=int8)
# smallest signed int dtype
In [409]: pd.to_numeric(m, downcast='signed')
Out[409]: array([1, 2, 3], dtype=int8)
# same as 'integer'
In [410]: pd.to_numeric(m, downcast='unsigned')
Out[410]: array([1, 2, 3], dtype=uint8)
# smallest unsigned int dtype
In [411]: pd.to_numeric(m, downcast='float')
Out[411]: array([ 1., 2., 3.], dtype=float32)
# smallest float dtype
由于这些方法都是只能作用在一维数组、列表或标量上,所以不能直接在多维对象(例如DataFrame)上使用。不过,使用 apply() ,们可
以在每一列上高效地调用这些函数。
In [412]: import datetime
In [413]: df = pd.DataFrame([['2016‑07‑09', datetime.datetime(2016, 3, 2)]] * 2, dtype='O')
In [414]: df
Out[414]:
0
2016‑07‑09
2016‑07‑09
0
1
1
2016‑03‑02 00:00:00
2016‑03‑02 00:00:00
In [415]: df.apply(pd.to_datetime)
Out[415]:
0
1
0 2016‑07‑09 2016‑03‑02
1 2016‑07‑09 2016‑03‑02
In [416]: df = pd.DataFrame([['1.1', 2, 3]] * 2, dtype='O')
In [417]: df
Out[417]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [418]: df.apply(pd.to_numeric)
Out[418]:
0 1 2
0 1.1 2 3
1 1.1 2 3
In [419]: df = pd.DataFrame([['5us', pd.Timedelta('1day')]] * 2, dtype='O')
In [420]: df
Out[420]:
0
1
0 5us 1 days 00:00:00
1 5us 1 days 00:00:00
In [421]: df.apply(pd.to_timedelta)
Out[421]:
0
1
0 00:00:00.000005 1 days
1 00:00:00.000005 1 days
陷阱
在整型数据类型数据上执行选择操作会很容易将数据向上转型为浮点型。在不引入nan类型的情况下,输入数据的类型将会被保留。查看支持
整型NA,获得更多介绍。
In [422]: dfi = df3.astype('int32')
In [423]: dfi['E'] = 1
In [424]: dfi
Out[424]:
A B
C E
0 0 0 255 1
1 0 0 255 1
2 0 0
0 1
3 1 0
0 1
4 0 0
0 1
5 0 0
0 1
6 1 ‑1
0 1
7 0 0
0 1
In [425]: dfi.dtypes
Out[425]:
A
int32
B
int32
C
int32
E
int64
dtype: object
In [426]: casted = dfi[dfi>0]
In [427]: casted
Out[427]:
A
B
C
0 NaN NaN 255.0
1 NaN NaN 255.0
2 NaN NaN
NaN
3 1.0 NaN
NaN
4 NaN NaN
NaN
5 NaN NaN
NaN
6 1.0 NaN
NaN
7 NaN NaN
NaN
E
1
1
1
1
1
1
1
1
In [428]: casted.dtypes
Out[428]:
A
float64
B
float64
C
float64
E
int64
dtype: object
同时,浮点型没有改变
In [429]: dfa = df3.copy()
In [430]: dfa['A'] = dfa['A'].astype('float32')
In [431]: dfa.dtypes
Out[431]:
A
float32
B
float64
C
float64
dtype: object
In [432]: casted = dfa[df2>0]
In [433]: casted
Out[433]:
A
B
0
NaN 0.200071
1
NaN
NaN
2 0.337012
NaN
3 1.483571
NaN
4
NaN 0.258626
5
NaN 0.941688
6
NaN
NaN
7
NaN
NaN
C
255.0
255.0
NaN
NaN
NaN
NaN
NaN
NaN
In [434]: casted.dtypes
Out[434]:
A
float32
B
float64
C
float64
dtype: object
基于dtype选择列
方法实现了基于列的类型得到列的子集。
首先,我们创建一个包含一些类不同数据类型的列的 DataFrame 。
select_dtypes()
In [435]: df = pd.DataFrame({'string': list('abc'),
.....:
'int64': list(range(1, 4)),
.....:
'uint8': np.arange(3, 6).astype('u1'),
.....:
'float64': np.arange(4.0, 7.0),
.....:
'bool1': [True, False, True],
.....:
'bool2': [False, True, False],
.....:
'dates': pd.date_range('now', periods=3).values,
.....:
'category': pd.Series(list("ABC")).astype('category')})
.....:
In [436]: df['tdeltas'] = df.dates.diff()
In [437]: df['uint64'] = np.arange(3, 6).astype('u8')
In [438]: df['other_dates'] = pd.date_range('20130101', periods=3).values
In [439]: df['tz_aware_dates'] = pd.date_range('20130101', periods=3, tz='US/Eastern')
In [440]: df
Out[440]:
string int64 uint8 float64
ates
tz_aware_dates
0
a
1
3
4.0
1‑01 2013‑01‑01 00:00:00‑05:00
1
b
2
4
5.0
1‑02 2013‑01‑02 00:00:00‑05:00
2
c
3
5
6.0
1‑03 2013‑01‑03 00:00:00‑05:00
bool1
...
category tdeltas uint64 other_d
True
...
A
NaT
3
2013‑0
False
...
B
1 days
4
2013‑0
True
...
C
1 days
5
2013‑0
[3 rows x 12 columns]
数据类型
In [441]: df.dtypes
Out[441]:
string
object
int64
int64
uint8
uint8
float64
float64
bool1
bool
bool2
bool
dates
datetime64[ns]
category
category
tdeltas
timedelta64[ns]
uint64
uint64
other_dates
datetime64[ns]
tz_aware_dates
datetime64[ns, US/Eastern]
dtype: object
select_dtypes()
有两个参数 include 和 exclude ,允许你明示"包含这些数据"(include)或"排除这些数据"(exclude)。
例如,选择布尔数据类型的列:
In [442]: df.select_dtypes(include=[bool])
Out[442]:
bool1 bool2
0
True False
1 False
True
2
True False
也可以为Numpy类型层级传入数据类型名
In [443]: df.select_dtypes(include=['bool'])
Out[443]:
bool1 bool2
0
True False
1 False
True
2
True False
配合一般的数据类型也能很好的工作
例如,选择所有的数值型和布尔型列,并且排除无符号整型
select_dtypes()
In [444]: df.select_dtypes(include=['number', 'bool'], exclude=['unsignedinteger'])
Out[444]:
int64 float64 bool1 bool2 tdeltas
0
1
4.0
True False
NaT
1
2
5.0 False
True 1 days
2
3
6.0
True False 1 days
选择字符串列,必须使用object数类型
In [445]: df.select_dtypes(include=['object'])
Out[445]:
string
0
a
1
b
2
c
查看一般的数据类型(比如numpy.number)的子类型,可以定义一个返回子类型树的函数
In [446]: def subdtypes(dtype):
.....:
subs = dtype.__subclasses__()
.....:
if not subs:
.....:
return dtype
.....:
return [dtype, [subdtypes(dt) for dt in subs]]
.....:
所有的Numpy数据类型都是numpy.generic的子类:
In [447]: subdtypes(np.generic)
Out[447]:
[numpy.generic,
[[numpy.number,
[[numpy.integer,
[[numpy.signedinteger,
[numpy.int8,
numpy.int16,
numpy.int32,
numpy.int64,
numpy.int64,
numpy.timedelta64]],
[numpy.unsignedinteger,
[numpy.uint8,
numpy.uint16,
numpy.uint32,
numpy.uint64,
numpy.uint64]]]],
[numpy.inexact,
[[numpy.floating,
[numpy.float16, numpy.float32, numpy.float64, numpy.float128]],
[numpy.complexfloating,
[numpy.complex64, numpy.complex128, numpy.complex256]]]]]],
[numpy.flexible,
[[numpy.character, [numpy.bytes_, numpy.str_]],
[numpy.void, [numpy.record]]]],
numpy.bool_,
numpy.datetime64,
numpy.object_]]
注意:
也定义 category 和 datetime[ns, tz] 数据类型,这两个数据类型没有集成到普通Numpy类型层级中,在上面
的函数中不会展示。
Pandas
第10章 文本数据处理
和
配备了一系列字符串处理方法,操作数组中的每个元素非常容易。也许是最重要的,这些方法自动排除缺失数据。这些方法是通
的
属性来实现,一般来说,都有一个内置的等同的字符串方法与其同名:
Series Index
Series .str
过
In [1]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
In [2]: s.str.lower()
Out[2]:
0
a
1
b
2
c
3
aaba
4
baca
5
NaN
6
caba
7
dog
8
cat
dtype: object
In [3]: s.str.upper()
Out[3]:
0
A
1
B
2
C
3
AABA
4
BACA
5
NaN
6
CABA
7
DOG
8
CAT
dtype: object
In [4]: s.str.len()
Out[4]:
0
1.0
1
1.0
2
1.0
3
4.0
4
4.0
5
NaN
6
4.0
7
3.0
8
3.0
dtype: float64
In [5]: idx = pd.Index([' jack', 'jill ', ' jesse ', 'frank'])
In [6]: idx.str.strip()
Out[6]: Index(['jack', 'jill', 'jesse', 'frank'], dtype='object')
In [7]: idx.str.lstrip()
Out[7]: Index(['jack', 'jill ', 'jesse ', 'frank'], dtype='object')
In [8]: idx.str.rstrip()
Out[8]: Index([' jack', 'jill', ' jesse', 'frank'], dtype='object')
作用在索引上的字符串方法对清洗或转换DataFrame特别有用。例如,你的列可能开头或末尾有空格:
In [9]: df = pd.DataFrame(randn(3, 2), columns=[' Column A ', ' Column B '],
...:
index=range(3))
...:
In [10]: df
Out[10]:
Column A
0
‑1.425575
1
0.740933
2
‑1.585660
Column B
‑1.336299
1.032121
0.913812
由于 df.columns 是一个Index对象,我们可以使用 .str 访问器:
In [11]: df.columns.str.strip()
Out[11]: Index(['Column A', 'Column B'], dtype='object')
In [12]: df.columns.str.lower()
Out[12]: Index([' column a ', ' column b '], dtype='object')
需要时,这些字符串方法可以用来清洗列名。这里,我们去除了开头和结尾的空格,将所有名称小写,并且将剩余的所有空格以下划线替换:
In [13]: df.columns = df.columns.str.strip().str.lower().str.replace(' ', '_')
In [14]: df
Out[14]:
column_a column_b
0 ‑1.425575 ‑1.336299
1 0.740933 1.032121
2 ‑1.585660 0.913812
注意:
如果你的Series中有很多重复的元素(唯一元素数量远小于Series的长度),将原始Series转换成一种 category 类型,然后
使用 .str.<method> 或 .dt.<property> 将会比较快。中间的不同点在于,对于 category 类型的Series,字符串方法
在 category 上操作,而不是Series的每个元素。
请注意,使用 .categories 操作 category 类型的Series和字符串类型的Series相比有些限制(例如:不能进行字符串加
法:如果Series是 category 类型, s + "" + s 将不会工作)。另外,作用在 list 类型的元素上的 .str 方法在这样的
Series中是不可用的。
分裂和替换
类似 split 的方法将返回一个Series列表:
10. 1
In [15]: s2 = pd.Series(['a_b_c', 'c_d_e', np.nan, 'f_g_h'])
In [16]: s2.str.split('_')
Out[16]:
0
[a, b, c]
1
[c, d, e]
2
NaN
3
[f, g, h]
dtype: object
分裂后得到的列表中的元素可以通过 get 或 [] 符号访问:
In [17]: s2.str.split('_').str.get(1)
Out[17]:
0
b
1
d
2
NaN
3
g
dtype: object
In [18]: s2.str.split('_').str[1]
Out[18]:
0
b
1
d
2
NaN
3
g
dtype: object
可以很方便的使用 expand 参数将其扩展为返回DataFrame:
In [19]: s2.str.split('_', expand=True)
Out[19]:
0
1
2
0
a
b
c
1
c
d
e
2 NaN NaN NaN
3
f
g
h
也可以限制分裂的次数:
In [20]: s2.str.split('_', expand=True, n=1)
Out[20]:
0
1
0
a b_c
1
c d_e
2 NaN NaN
3
f g_h
rsplit
与 split 工作方式类似,不过它在相反的方向上操作,比如,从字符串尾部向头部操作:
In [21]: s2.str.rsplit('_', expand=True, n=1)
Out[21]:
0
1
0 a_b
c
1 c_d
e
2 NaN NaN
3 f_g
h
replace
默认替换正则表达式:
In [22]: s3 = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca',
....:
'', np.nan, 'CABA', 'dog', 'cat'])
....:
In [23]: s3
Out[23]:
0
A
1
B
2
C
3
Aaba
4
Baca
5
6
NaN
7
CABA
8
dog
9
cat
dtype: object
In [24]: s3.str.replace('^.a|dog', 'XX‑XX ', case=False)
Out[24]:
0
A
1
B
2
C
3
XX‑XX ba
4
XX‑XX ca
5
6
NaN
7
XX‑XX BA
8
XX‑XX
9
XX‑XX t
dtype: object
使用正则表达式需要注意一些点。比如,因为 $ 在正则表达式中的特殊含义(匹配字符串的结尾),下面的代码会引起麻烦:
```python
Consider the following badly formatted financial data
In [25]: dollars = pd.Series(['12', '-10′ ,′ 10,000'])
This does what you'd naively expect:
In [26]: dollars.str.replace('$', '') Out[26]: 0 12 1 -10 2 10,000 dtype: object
But this doesn't:
In [27]: dollars.str.replace('-′ ,′ −′ )
[27] : 0121−
10 2 $10,000 dtype: object
We need to escape the special character (for >1 len patterns)
In [28]: dollars.str.replace(r'-$', '-') Out[28]: 0 12 1 -10 2 $10,000 dtype: object
版本新特性:
如果你确实需要执行普通字符串替换(与 [`str.replace()`](https://docs.python.org/3/library/stdtypes.html#
str.replace)等价),你可以将可选项`regex`参数设置为`False`,而不是转义字符。在这种情况下,`pat`和`repl`必须是
字符串:
0.23.0
```python
# These lines are equivalent
In [29]: dollars.str.replace(r'‑\$', '‑')
Out[29]:
0
12
1
‑10
2
$10,000
dtype: object
In [30]: dollars.str.replace('‑$', '‑', regex=False)
Out[30]:
0
12
1
‑10
2
$10,000
dtype: object
版本新特性:
replace 方法可以接受一个可调用对象作为替换值。它会用 re.sub() 在每个 pat 上调用。这个可调用对象应该有一个位置参数(一个正
则表达式对象)并且返回一个字符串:
0.22.0
# Reverse every lowercase alphabetic word
In [31]: pat = r'[a‑z]+'
In [32]: repl = lambda m: m.group(0)[::‑1]
In [33]: pd.Series(['foo 123', 'bar baz', np.nan]).str.replace(pat, repl)
Out[33]:
0
oof 123
1
rab zab
2
NaN
dtype: object
# Using regex groups
In [34]: pat = r"(?P<one>\w+) (?P<two>\w+) (?P<three>\w+)"
In [35]: repl = lambda m: m.group('two').swapcase()
In [36]: pd.Series(['Foo Bar Baz', np.nan]).str.replace(pat, repl)
Out[36]:
0
bAR
1
NaN
dtype: object
版本新特性:
replace 方法也可以接受一个通过 re.compile() 编译后的正则表达式对象作为模式。所有的标志都应该包含在这个编译后的正则表达式
对象内。
0.20.0
In [37]: import re
In [38]: regex_pat = re.compile(r'^.a|dog', flags=re.IGNORECASE)
In [39]: s3.str.replace(regex_pat, 'XX‑XX ')
Out[39]:
0
A
1
B
2
C
3
XX‑XX ba
4
XX‑XX ca
5
6
NaN
7
XX‑XX BA
8
XX‑XX
9
XX‑XX t
dtype: object
使用编译后的正则表达式调用 replace 时,如果传入 flags 参数将会抛出 ValueError 异常:
pyhton
In [40]: s3.str.replace(regex_pat, 'XX‑XX ', flags=re.IGNORECASE)
‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑‑
ValueError: case and flags cannot be set when pat is a compiled regex
连接
有几种方法连接Series或Index,与其自身或其他对象,所有操作都基于 cat() ,分别地, Index.str.cat 。
10.2
将一个字符串Series连接成字符串
Series(或Index)的内容可以连接起来:
10.2.1
In [41]: s = pd.Series(['a', 'b', 'c', 'd'])
In [42]: s.str.cat(sep=',')
Out[42]: 'a,b,c,d'
如果不指定 sep 关键字,默认是"":
In [43]: s.str.cat()
Out[43]: 'abcd'
默认地,忽略缺失值。使用 na_rep 可以将缺失值展示出来:
In [44]: t = pd.Series(['a', 'b', np.nan, 'd'])
In [45]: t.str.cat(sep=',')
Out[45]: 'a,b,d'
In [46]: t.str.cat(sep=',', na_rep='‑')
Out[46]: 'a,b,‑,d'
把Series和类list对象连接成Series
cat() 的第一个参数可以是类list的对象,它的长度需要与调用的Series(或Index)的长度相匹配:
10.2.2
In [47]: s.str.cat(['A', 'B', 'C', 'D'])
Out[47]:
0
aA
1
bB
2
cC
3
dD
dtype: object
除非指定 na_rep ,否则任何一边有缺失值结果中相应位置就会返回缺失值:
In [48]: s.str.cat(t)
Out[48]:
0
aa
1
bb
2
NaN
3
dd
dtype: object
In [49]: s.str.cat(t, na_rep='‑')
Out[49]:
0
aa
1
bb
2
c‑
3
dd
dtype: object
把Series和类数组对象连接成Series
0.23.0版本新特性
others 参数也可以是二维的。这种情况下,(数组)行数需要与调用的Series(或Index)的长度相匹配:
10.2.3
In [50]: d = pd.concat([t, s], axis=1)
In [51]: s
Out[51]:
0
a
1
b
2
c
3
d
dtype: object
In [52]: d
Out[52]:
0 1
0
a a
1
b b
2 NaN c
3
d d
In [53]: s.str.cat(d, na_rep='‑')
Out[53]:
0
aaa
1
bbb
2
c‑c
3
ddd
dtype: object
把Series和索引对象连接成Series,并且对齐
0.23.0版本新特性。
Series或DataFrame的连接,可以通过设置 join 关键字在连接之前先对齐索引:
10.2.4
In [54]: u = pd.Series(['b', 'd', 'a', 'c'], index=[1, 3, 0, 2])
In [55]: s
Out[55]:
0
a
1
b
2
c
3
d
dtype: object
In [56]: u
Out[56]:
1
b
3
d
0
a
2
c
dtype: object
In [57]: s.str.cat(u)
Out[57]:
0
ab
1
bd
2
ca
3
dc
dtype: object
In [58]: s.str.cat(u, join='left')
Out[58]:
0
aa
1
bb
2
cc
3
dd
dtype: object
注意:
如果不指定 join 关键字, cat() 方法的工作方式和0.23.0版本之前一样(不对齐),但是如果涉及到的索引有不同的,则
会抛出 FutureWarining ,因为未来的版本会默认将 join 关键字设为 left 。
在 join 中,也有类似的选项( left 、 outer 、 inner 和 right 中的一个)。特别地,对齐也意味着操作的对象的长度不必一致。
In [59]: v = pd.Series(['z', 'a', 'b', 'd', 'e'], index=[‑1, 0, 1, 3, 4])
In [60]: s
Out[60]:
0
a
1
b
2
c
3
d
dtype: object
In [61]: v
Out[61]:
‑1
z
0
a
1
b
3
d
4
e
dtype: object
In [62]: s.str.cat(v, join='left', na_rep='‑')
Out[62]:
0
aa
1
bb
2
c‑
3
dd
dtype: object
In [63]: s.str.cat(v, join='outer', na_rep='‑')
Out[63]:
‑1
‑z
0
aa
1
bb
2
c‑
3
dd
4
‑e
dtype: object
当 others 是DataFrame时,也可以用同样的对齐:
In [64]: f = d.loc[[3, 2, 1, 0], :]
In [65]: s
Out[65]:
0
a
1
b
2
c
3
d
dtype: object
In [66]: f
Out[66]:
0 1
3
d d
2 NaN c
1
b b
0
a a
In [67]: s.str.cat(f, join='left', na_rep='‑')
Out[67]:
0
aaa
1
bbb
2
c‑c
3
ddd
dtype: object
把Series和许多对象连接成Series
所有的一维类数组都可以随意的连接成类数组容器(包括迭代器,字典视图等等):
10.2.5
In [68]: s
Out[68]:
0
a
1
b
2
c
3
d
dtype: object
In [69]: u
Out[69]:
1
b
3
d
0
a
2
c
dtype: object
In [70]: s.str.cat([u, pd.Index(u.values), ['A', 'B', 'C', 'D'], map(str, u.index)], na_rep='‑')
Out[70]:
0
abbA1
1
bddB3
2
caaC0
3
dccD2
dtype: object
所有的元素的长度都要和调用的Series(或Index)一致,除非它们有索引而且指定了 join :
In [71]: v
Out[71]:
‑1
z
0
a
1
b
3
d
4
e
dtype: object
In [72]: s.str.cat([u, v, ['A', 'B', 'C', 'D']], join='outer', na_rep='‑')
Out[72]:
‑1
‑‑z‑
0
aaaA
1
bbbB
2
cc‑C
3
dddD
4
‑‑e‑
dtype: object
如果在有不同的索引的 others 列表中使用 join='right' ,那么最后得到的连接结果将使用这些索引的并集:
In [73]: u.loc[[3]]
Out[73]:
3
d
dtype: object
In [74]: v.loc[[‑1, 0]]
Out[74]:
‑1
z
0
a
dtype: object
In [75]: s.str.cat([u.loc[[3]], v.loc[[‑1, 0]]], join='right', na_rep='‑')
Out[75]:
‑1
‑‑z
0
a‑a
3
dd‑
dtype: object
使用 .str 进行索引操作
你可以使用 [] 符号直接对位置进行索引。如果索引超过了字符串的边界,将会返回NaN。
10.3
In [76]: s = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan,
....:
'CABA', 'dog', 'cat'])
....:
In [77]: s.str[0]
Out[77]:
0
A
1
B
2
C
3
A
4
B
5
NaN
6
C
7
d
8
c
dtype: object
In [78]: s.str[1]
Out[78]:
0
NaN
1
NaN
2
NaN
3
a
4
a
5
NaN
6
A
7
o
8
a
dtype: object
10.4
提取子字符串
10.4.1
在每一个主题中提取第一个匹配项(提取)
注意:
在0.18.0版本中, extract 有了 expand 参数。当 expand=False 时,返回Series,Index或DataFrame,取决于主题和正
则表达式模式(和0.18.0版本之前一样)。当 expand=True 时,一直返回DataFrame,为使用者减少了困惑。0.23.0版本之
后,默认值为 expand=True 。
方法接受一个至少有一个捕获组的正则表达式。
提取有多个捕获组的正则表达式将会返回每个捕获组作为一列的DataFrame。
extract
In [79]: pd.Series(['a1', 'b2', 'c3']).str.extract('([ab])(\d)', expand=False)
Out[79]:
0
1
0
a
1
1
b
2
2 NaN NaN
没有匹配到的元素会用NaN代替。因此,由凌乱的字符串组成的Series可以转换成由规整后的或更有用的字符串组成的类似索引的Series或
DataFrame,不需要必须使用 get() 访问元组或 re.match 对象。结果的类型始终是 object ,即使没有发现匹配项结果全是NaN。
类似下面的命名的组:
In [80]: pd.Series(['a1', 'b2', 'c3']).str.extract('(?P<letter>[ab])(?P<digit>\d)', expand=False)
Out[80]:
letter digit
0
a
1
1
b
2
2
NaN
NaN
和类似下面的随意的组:
In [81]: pd.Series(['a1', 'b2', '3']).str.extract('([ab])?(\d)', expand=False)
Out[81]:
0 1
0
a 1
1
b 2
2 NaN 3
也可以使用。注意,任何捕获的在正则表达式中的组名都将被用在列名中,另外,组号也会使用。
如果 expand=True ,提取有一个组的正则表达式将会返回有一列的DataFrame。
In [82]: pd.Series(['a1', 'b2', 'c3']).str.extract('[ab](\d)', expand=True)
Out[82]:
0
0
1
1
2
2 NaN
如果 expand=False ,将会返回一个Series:
In [83]: pd.Series(['a1', 'b2', 'c3']).str.extract('[ab](\d)', expand=False)
Out[83]:
0
1
1
2
2
NaN
dtype: object
如果 expand=True ,在Index上调用只有一个捕获组的正则表达式,将会返回有一列的DataFrame:
In [84]: s = pd.Series(["a1", "b2", "c3"], ["A11", "B22", "C33"])
In [85]: s
Out[85]:
A11
a1
B22
b2
C33
c3
dtype: object
In [86]: s.index.str.extract("(?P<letter>[a‑zA‑Z])", expand=True)
Out[86]:
letter
0
A
1
B
2
C
如果 expand=False ,将会返回一个Series:
In [87]: s.index.str.extract("(?P<letter>[a‑zA‑Z])", expand=False)
Out[87]: Index(['A', 'B', 'C'], dtype='object', name='letter')
如果 expand=True ,在Index上调用多于一个捕获组的正则表达式,将会返回DataFrame:
In [88]: s.index.str.extract("(?P<letter>[a‑zA‑Z])([0‑9]+)", expand=True)
Out[88]:
letter
1
0
A 11
1
B 22
2
C 33
如果 expand=False ,将会抛出 ValueError :
>>> s.index.str.extract("(?P<letter>[a‑zA‑Z])([0‑9]+)", expand=False)
ValueError: only one regex group is supported with Index
下表展示了 extract(expand=False) 的工作方式(第一列是输入主题,第一行是正则表达式的捕获组的数量)
1 group
>1 group
Index
Index
ValueError
Series
Series
DataFrame
在每个主题上提取所有匹配项(提取所有)
0.18.0版新特性:
与只返回第一个匹配项的 extract 不同
10.4.2
In [89]: s = pd.Series(["a1a2", "b1", "c1"], index=["A", "B", "C"])
In [90]: s
Out[90]:
A
a1a2
B
b1
C
c1
dtype: object
In [91]: two_groups = '(?P<letter>[a‑z])(?P<digit>[0‑9])'
In [92]: s.str.extract(two_groups, expand=True)
Out[92]:
letter digit
A
a
1
B
b
1
C
c
1
方法会返回所有的匹配项。 extractall 的返回值一直是在行上有多重索引的DataFrame。多重索引的最后一级被命名为
,并且使用主题中的顺序。
extractall
match
In [93]: s.str.extractall(two_groups)
Out[93]:
letter digit
match
A 0
a
1
1
a
2
B 0
b
1
C 0
c
1
如果Series中每个主题都只有一个匹配项:
In [94]: s = pd.Series(['a3', 'b3', 'c2'])
In [95]: s
Out[95]:
0
a3
1
b3
2
c2
dtype: object
extractall(pat).xs(0, level='match')
和 extract(pat) 的结果相同:
In [96]: extract_result = s.str.extract(two_groups, expand=True)
In [97]: extract_result
Out[97]:
letter digit
0
a
3
1
b
3
2
c
2
In [98]: extractall_result = s.str.extractall(two_groups)
In [99]: extractall_result
Out[99]:
letter digit
match
0 0
a
3
1 0
b
3
2 0
c
2
In [100]: extractall_result.xs(0, level="match")
Out[100]:
letter digit
0
a
3
1
b
3
2
c
2
也支持 .str.extractall 。和 Series.str.extractall 一样,它会返回一个有默认索引(从0开始)的DataFrame。
0.19.0版本新特性。
Index
In [101]: pd.Index(["a1a2", "b1", "c1"]).str.extractall(two_groups)
Out[101]:
letter digit
match
0 0
a
1
1
a
2
1 0
b
1
2 0
c
1
In [102]: pd.Series(["a1a2", "b1", "c1"]).str.extractall(two_groups)
Out[102]:
letter digit
match
0 0
a
1
1
a
2
1 0
b
1
2 0
c
1
测试字符串是否满足或包含一个模式
你可以检查一个元素是否包含一个模式:
10.5
In [103]: pattern = r'[0‑9][a‑z]'
In [104]: pd.Series(['1', '2', '3a', '3b', '03c']).str.contains(pattern)
Out[104]:
0
False
1
False
2
True
3
True
4
True
dtype: bool
或者,元素是否匹配一个模式:
In [105]: pd.Series(['1', '2', '3a', '3b', '03c']).str.match(pattern)
Out[105]:
0
False
1
False
2
True
3
True
4
False
dtype: bool
和 contains 之间的不同点是其严格程度: match 严格依赖于 re.match , contains 依赖于 re.search 。
类似于 match 、 contains 、 startswith 和 endswith 的方法都有一个额外的 na 关键字参数,用来指定缺失数据应该被处理成True还
是False:
match
In [106]: s4 = pd.Series(['A', 'B', 'C', 'Aaba', 'Baca', np.nan, 'CABA', 'dog', 'cat'])
In [107]: s4.str.contains('A', na=False)
Out[107]:
0
True
1
False
2
False
3
True
4
False
5
False
6
True
7
False
8
False
dtype: bool
10.6
创建指标变量
你可以从字符串列中提取虚拟变量。例如如果用 '|' 分割它们:
In [108]: s = pd.Series(['a', 'a|b', np.nan, 'a|c'])
In [109]: s.str.get_dummies(sep='|')
Out[109]:
a b c
0 1 0 0
1 1 1 0
2 0 0 0
3 1 0 1
字符串 Index 也支持 get_dummies 函数,返回以 MultiIndex :
0.18.0版本新特性
In [110]: idx = pd.Index(['a', 'a|b', np.nan, 'a|c'])
In [111]: idx.str.get_dummies(sep='|')
Out[111]:
MultiIndex(levels=[[0, 1], [0, 1], [0, 1]],
labels=[[1, 1, 0, 1], [0, 1, 0, 0], [0, 0, 0, 1]],
names=['a', 'b', 'c'])
查看 get_dummies() 获得更多信息。
10.7
方法概览
Method
cat()
split()
rsplit()
get()
join()
get_dummies()
contains()
replace()
repeat()
pad()
center()
ljust()
rjust()
zfill()
wrap()
slice()
slice_replace()
count()
startswith()
endswith()
findall()
match()
extract()
extractall()
len()
strip()
rstrip()
lstrip()
partition()
rpartition()
lower()
upper()
find()
rfind()
index()
rindex()
capitalize()
swapcase()
normalize()
translate()
isalnum()
isalpha()
isdigit()
isspace()
islower()
isupper()
istitle()
isnumeric()
isdecimal()
Description
连接字符串
以某个分隔符分割字符串
从字符串的末尾以某个分隔符分割字符串
索引每个元素(检索第i个元素)
使用传入的分隔符连接Series的每个字符串元素
按分隔符分割字符串,返回由虚拟变量组成的DataFrame
返回每个字符串是否包含一个模式或正则表达式的布尔数组
使用另外的字符串或给定事件的可调用对象的返回值替换模式、正则表达式或字符串
重复数据 ( s.str.repeat(3) , 等价于 x * 3
在左边、右边或两边用空格填充字符串
等价于 str.center
等价于 str.ljust
等价于 str.rjust
等价于 str.zfill
用小于给定宽度的长度将字符串分裂成行
切片Series中每个字符串
用输入的值替换每个字符串的切片
计算某个模式出现的次数
等价于对每个元素执行 str.startswith(pat)
等价于对每个元素执行 str.endswith(pat)
计算每个字符串中某个模式或正则表达式的出现组成的list
在每个元素上执行 re.match ,返回由匹配的组组成的list
在每个元素上执行 re.search , 返回一个DataFrame,每个元素一行,每个正则表达式捕获组一列。
在每个元素上执行 re.findall ,返回一个DataFrame,每个匹配项一行 ,每个正则表达式捕获组一列。
计算字符串长度
等价于 str.strip
等价于 str.rstrip
等价于 str.lstrip
等价于 str.partition
等价于 str.rpartition
等价于 str.lower
等价于 str.upper
等价于 str.find
等价于 str.rfind
等价于 str.index
等价于 str.rindex
等价于 str.capitalize
等价于 str.swapcase
返回Unicode标准格式。等价于 unicodedata.normalize
等价于 str.translate
等价于 str.isalnum
等价于 str.isalpha
等价于 str.isdigit
等价于 str.isspace
等价于 str.islower
等价于 str.isupper
等价于 str.istitle
等价于 str.isnumeric
等价于 str.isdecimal
第十一章 配置选项
概述
Pandas有一套选项系统来让用户定制它的工作方式的某些方面,使用者最可能调整的是设置显示相关的一些选项。
这些选项有完整的“点式操作”,不区分大小写的名字(例如:display.max_rows)。你可以直接用顶级的 options 属性来获取或设置选项。
11.1
In [1]: import pandas as pd
In [2]: pd.options.display.max_rows
Out[2]: 15
In [3]: pd.options.display.max_rows = 999
In [4]: pd.options.display.max_rows
Out[4]: 999
由 个相关的函数组成,可以通过pandas命名空间获得。
get_option() / set_option() :获取或设置一个单独的选项。
reset_option() :充值一个或多个选项的默认值。
describe_option() :打印一个或多个选项的描述文字。
option_context() :执行一个由一组执行后将恢复到上一次设置的设置集合组成的代码块。
注意:开发者可以查看pandas/core/config.py获取更多的信息。
上面的所有函数都接受一个正则表达式( re.search 风格)参数,因此,传入一个子字符串也可以工作,只要这个子字符串是明确的。
API 5
In [5]: pd.get_option("display.max_rows")
Out[5]: 999
In [6]: pd.set_option("display.max_rows",101)
In [7]: pd.get_option("display.max_rows")
Out[7]: 101
In [8]: pd.set_option("max_r",102)
In [9]: pd.get_option("display.max_rows")
Out[9]: 102
下面的代码将不能工作,因为它匹配到了多个设置项名字。比如, display.max_colwidth , display.max_rows ,
display.max_columns :
In [10]: try:
....:
pd.get_option("column")
....: except KeyError as e:
....:
print(e)
....:
'Pattern matched multiple keys'
注意:如果未来的版本有新的相似的名字添加进来,使用这种简便的处理方式可能会中断。
你可以使用 describe_option 来获得一个可用的选项的描述列表。不带参数调用 describe_option 会打印所有的可用设置项的描述。
获取和设置选项
像上面描述的一样, get_option() 和 set_option() 可以通过Pandas命名空间获取。调用 set_option('option regex',
new_value) 可以修改选项。
11.2
In [11]: pd.get_option('mode.sim_interactive')
Out[11]: False
In [12]: pd.set_option('mode.sim_interactive', True)
In [13]: pd.get_option('mode.sim_interactive')
Out[13]: True
注意: mode.sim_interactive 通常用来达到debug的目的。
所有上面的选项都有默认值,可以使用 reset_option 来修改:
In [14]: pd.get_option("display.max_rows")
Out[14]: 60
In [15]: pd.set_option("display.max_rows",999)
In [16]: pd.get_option("display.max_rows")
Out[16]: 999
In [17]: pd.reset_option("display.max_rows")
In [18]: pd.get_option("display.max_rows")
Out[18]: 60
也可以使用正则表达式一次修改多个选项:
In [19]: pd.reset_option("^display")
option_context
的值)。
环境管理器已经通过顶级API公开,它允许执行给定选项值的代码。退出 with 代码块时,选项值会自动恢复(到执行前
In [20]: with pd.option_context("display.max_rows",10,"display.max_columns", 5):
....:
print(pd.get_option("display.max_rows"))
....:
print(pd.get_option("display.max_columns"))
....:
10
5
In [21]: print(pd.get_option("display.max_rows"))
60
In [22]: print(pd.get_option("display.max_columns"))
0
设置Python或IPython环境的的启动选项
使用Python或IPython环境的启动脚本来导入Pandas并且设置选项可以更高效的使用Pandas。为了达到这个目的,先在需要的配置文件所在的
启动目录创建一个.py或.ipy脚本。可以在下面的位置找到启动文件夹位于默认ipython配置文件中的示例:
11.3
$IPYTHONDIR/profile_default/startup
更多的信息可以在ipython文档查看。下面展示了Pandas的一个启动脚本示例:
import pandas as pd
pd.set_option('display.max_rows', 999)
pd.set_option('precision', 5)
11.4
常用的选项
下面是最常用的显示选项的演练。
display.max_rows 和 display.max_columns 用来设置打印一个数据框时最多要显示的行数和列数。截断的行会被省略号替代。
In [23]: df = pd.DataFrame(np.random.randn(7,2))
In [24]: pd.set_option('max_rows', 7)
In [25]: df
Out[25]:
0
1
0 0.469112 ‑0.282863
1 ‑1.509059 ‑1.135632
2 1.212112 ‑0.173215
3 0.119209 ‑1.044236
4 ‑0.861849 ‑2.104569
5 ‑0.494929 1.071804
6 0.721555 ‑0.706771
In [26]: pd.set_option('max_rows', 5)
In [27]: df
Out[27]:
0
1
0
0.469112 ‑0.282863
1 ‑1.509059 ‑1.135632
..
...
...
5 ‑0.494929 1.071804
6
0.721555 ‑0.706771
[7 rows x 2 columns]
In [28]: pd.reset_option('max_rows')
display.expand_frame_repr
允许数据框的表示跨页,在整个列和行之间叠起。
In [29]: df = pd.DataFrame(np.random.randn(5,10))
In [30]: pd.set_option('expand_frame_repr', True)
In [31]: df
Out[31]:
0
1
2
3
4
5
6
7
8
9
0 ‑1.039575 0.271860 ‑0.424972 0.567020 0.276232 ‑1.087401 ‑0.673690 0.113648 ‑1.478427 0.52
4988
1 0.404705 0.577046 ‑1.715002 ‑1.039268 ‑0.370647 ‑1.157892 ‑1.344312 0.844885 1.075770 ‑0.10
9050
2 1.643563 ‑1.469388 0.357021 ‑0.674600 ‑1.776904 ‑0.968914 ‑1.294524 0.413738 0.276662 ‑0.47
2035
3 ‑0.013960 ‑0.362543 ‑0.006154 ‑0.923061 0.895717 0.805244 ‑1.206412 2.565646 1.431256 1.34
0309
4 ‑1.170299 ‑0.226169 0.410835 0.813850 0.132003 ‑0.827317 ‑0.076467 ‑1.187678 1.130127 ‑1.43
6737
In [32]: pd.set_option('expand_frame_repr', False)
In [33]: df
Out[33]:
0
1
2
3
4
5
6
7
8
9
0 ‑1.039575 0.271860 ‑0.424972 0.567020 0.276232 ‑1.087401 ‑0.673690 0.113648 ‑1.478427 0.52
4988
1 0.404705 0.577046 ‑1.715002 ‑1.039268 ‑0.370647 ‑1.157892 ‑1.344312 0.844885 1.075770 ‑0.10
9050
2 1.643563 ‑1.469388 0.357021 ‑0.674600 ‑1.776904 ‑0.968914 ‑1.294524 0.413738 0.276662 ‑0.47
2035
3 ‑0.013960 ‑0.362543 ‑0.006154 ‑0.923061 0.895717 0.805244 ‑1.206412 2.565646 1.431256 1.34
0309
4 ‑1.170299 ‑0.226169 0.410835 0.813850 0.132003 ‑0.827317 ‑0.076467 ‑1.187678 1.130127 ‑1.43
6737
In [34]: pd.reset_option('expand_frame_repr')
display.large_repr
让你选择是否将超过 max_columns 或 max_rows 的数据框显示为截断或摘要:
In [35]: df = pd.DataFrame(np.random.randn(10,10))
In [36]: pd.set_option('max_rows', 5)
In [37]: pd.set_option('large_repr', 'truncate')
In [38]: df
Out[38]:
0
1
2
9
0 ‑1.413681 1.607920 1.024180
78638
1
0.545952 ‑1.219217 ‑1.226825
41734
..
...
...
...
...
8 ‑2.484478 ‑0.281461 0.030711
81836
9 ‑1.071357 0.441153 2.353925
45696
3
4
0.569605
5
6
0.875906 ‑2.211372
7
0.974466 ‑2.006747 ‑0.410001 ‑0.0
0.769804 ‑1.281247 ‑0.727707 ‑0.121306 ‑0.097883
...
...
8
...
...
0.695775
...
0.3
...
0.109121
1.126203 ‑0.977349
1.474071 ‑0.064034 ‑1.282782
0.583787
0.221471 ‑0.744471
0.758527
0.7
1.729689 ‑0.964980 ‑0.8
[10 rows x 10 columns]
In [39]: pd.set_option('large_repr', 'info')
In [40]: df
Out[40]:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 10 columns):
0
10 non‑null float64
1
10 non‑null float64
2
10 non‑null float64
3
10 non‑null float64
4
10 non‑null float64
5
10 non‑null float64
6
10 non‑null float64
7
10 non‑null float64
8
10 non‑null float64
9
10 non‑null float64
dtypes: float64(10)
memory usage: 880.0 bytes
In [41]: pd.reset_option('large_repr')
In [42]: pd.reset_option('max_rows')
display.max_colwidth
设置列的最大宽度。大于等于这个宽度的单元格将会用省略号替代。
In [43]: df = pd.DataFrame(np.array([['foo', 'bar', 'bim', 'uncomfortably long string'],
....:
['horse', 'cow', 'banana', 'apple']]))
....:
In [44]: pd.set_option('max_colwidth',40)
In [45]: df
Out[45]:
0
1
0
foo bar
1 horse cow
2
bim
banana
3
uncomfortably long string
apple
In [46]: pd.set_option('max_colwidth', 6)
In [47]: df
Out[47]:
0
1
0
foo bar
1 horse cow
2
bim
ba...
3
un...
apple
In [48]: pd.reset_option('max_colwidth')
display.max_info_columns
设置何时给出逐列信息的阈值:
In [49]: df = pd.DataFrame(np.random.randn(10,10))
In [50]: pd.set_option('max_info_columns', 11)
In [51]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 10 columns):
0
10 non‑null float64
1
10 non‑null float64
2
10 non‑null float64
3
10 non‑null float64
4
10 non‑null float64
5
10 non‑null float64
6
10 non‑null float64
7
10 non‑null float64
8
10 non‑null float64
9
10 non‑null float64
dtypes: float64(10)
memory usage: 880.0 bytes
In [52]: pd.set_option('max_info_columns', 5)
In [53]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Columns: 10 entries, 0 to 9
dtypes: float64(10)
memory usage: 880.0 bytes
In [54]: pd.reset_option('max_info_columns')
: df.info() 通常会展示每列的空值数量。对于大数据框来说,这会很慢。 max_info_rows 和
max_info_cols 限制了这种空值检验只在指定的低维度数据框进行。注意,你可以指定 df.info(null_counts=True) 来覆盖显示特定
的数据框。
display.max_info_rows
In [55]: df = pd.DataFrame(np.random.choice([0,1,np.nan], size=(10,10)))
In [56]: df
Out[56]:
0
1
0 0.0 1.0
1 1.0 NaN
2 NaN NaN
3 0.0 1.0
4 0.0 1.0
5 0.0 NaN
6 0.0 1.0
7 0.0 NaN
8 0.0 0.0
9 NaN NaN
2
1.0
0.0
NaN
1.0
0.0
1.0
0.0
1.0
NaN
0.0
3
0.0
0.0
1.0
NaN
0.0
NaN
0.0
1.0
0.0
NaN
4
1.0
1.0
1.0
0.0
1.0
NaN
NaN
NaN
NaN
NaN
5
1.0
1.0
0.0
NaN
0.0
NaN
1.0
1.0
1.0
NaN
6
0.0
NaN
NaN
1.0
0.0
NaN
NaN
1.0
0.0
0.0
7
NaN
1.0
0.0
NaN
NaN
0.0
NaN
1.0
0.0
1.0
8
1.0
0.0
1.0
NaN
0.0
1.0
0.0
1.0
NaN
1.0
9
NaN
1.0
NaN
0.0
0.0
NaN
NaN
NaN
NaN
NaN
In [57]: pd.set_option('max_info_rows', 11)
In [58]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 10 columns):
0
8 non‑null float64
1
5 non‑null float64
2
8 non‑null float64
3
7 non‑null float64
4
5 non‑null float64
5
7 non‑null float64
6
6 non‑null float64
7
6 non‑null float64
8
8 non‑null float64
9
3 non‑null float64
dtypes: float64(10)
memory usage: 880.0 bytes
In [59]: pd.set_option('max_info_rows', 5)
In [60]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 10 columns):
0
float64
1
float64
2
float64
3
float64
4
float64
5
float64
6
float64
7
float64
8
float64
9
float64
dtypes: float64(10)
memory usage: 880.0 bytes
In [61]: pd.reset_option('max_info_rows')
display.precision
设置输入显示的精度(小数位数),只作为一个建议:
In [62]: df = pd.DataFrame(np.random.randn(5,5))
In [63]: pd.set_option('precision',7)
In [64]: df
Out[64]:
0
1
2
3
0 ‑2.0490276 2.8466122 ‑1.2080493 ‑0.4503923
1 0.1211080 0.2669165 0.8438259 ‑0.2225400
2 ‑0.7167894 ‑2.2244851 ‑1.0611370 ‑0.2328247
3 ‑0.6654779 1.8298075 ‑1.4065093 1.0782481
4 0.2003243 0.8900241 0.1948132 0.3516326
4
2.4239054
2.0219807
0.4307933
0.3227741
0.4488815
In [65]: pd.set_option('precision',4)
In [66]: df
Out[66]:
0
1
2
3
0 ‑2.0490 2.8466 ‑1.2080 ‑0.4504
1 0.1211 0.2669 0.8438 ‑0.2225
2 ‑0.7168 ‑2.2245 ‑1.0611 ‑0.2328
3 ‑0.6655 1.8298 ‑1.4065 1.0782
4 0.2003 0.8900 0.1948 0.3516
display.chop_threshold
4
2.4239
2.0220
0.4308
0.3228
0.4489
设置当Pandas一个DataFrame的Series时在什么级别舍入到0。这个设置不改变数字存储的精度。
In [67]: df = pd.DataFrame(np.random.randn(6,6))
In [68]: pd.set_option('chop_threshold', 0)
In [69]: df
Out[69]:
0
1
2
3
4
5
0 ‑0.1979 0.9657 ‑1.5229 ‑0.1166 0.2956 ‑1.0477
1 1.6406 1.9058 2.7721 0.0888 ‑1.1442 ‑0.6334
2 0.9254 ‑0.0064 ‑0.8204 ‑0.6009 ‑1.0393 0.8248
3 ‑0.8241 ‑0.3377 ‑0.9278 ‑0.8401 0.2485 ‑0.1093
4 0.4320 ‑0.4607 0.3365 ‑3.2076 ‑1.5359 0.4098
5 ‑0.6731 ‑0.7411 ‑0.1109 ‑2.6729 0.8645 0.0609
In [70]: pd.set_option('chop_threshold', .5)
In [71]: df
Out[71]:
0
1
2
3
4
5
0 0.0000 0.9657 ‑1.5229 0.0000 0.0000 ‑1.0477
1 1.6406 1.9058 2.7721 0.0000 ‑1.1442 ‑0.6334
2 0.9254 0.0000 ‑0.8204 ‑0.6009 ‑1.0393 0.8248
3 ‑0.8241 0.0000 ‑0.9278 ‑0.8401 0.0000 0.0000
4 0.0000 0.0000 0.0000 ‑3.2076 ‑1.5359 0.0000
5 ‑0.6731 ‑0.7411 0.0000 ‑2.6729 0.8645 0.0000
In [72]: pd.reset_option('chop_threshold')
display.colheader_justify
控制数据头的对齐方式,“右”或“左”。
In [73]: df = pd.DataFrame(np.array([np.random.randn(6), np.random.randint(1,9,6)*.1, np.zeros(6
)]).T,
....:
columns=['A', 'B', 'C'], dtype='float')
....:
In [74]: pd.set_option('colheader_justify', 'right')
In [75]: df
Out[75]:
A
B
0 0.9331 0.3
1 0.2888 0.2
2 1.3250 0.2
3 0.5892 0.7
4 0.5314 0.1
5 ‑1.1987 0.7
C
0.0
0.0
0.0
0.0
0.0
0.0
In [76]: pd.set_option('colheader_justify', 'left')
In [77]: df
Out[77]:
A
B
0 0.9331 0.3
1 0.2888 0.2
2 1.3250 0.2
3 0.5892 0.7
4 0.5314 0.1
5 ‑1.1987 0.7
C
0.0
0.0
0.0
0.0
0.0
0.0
In [78]: pd.reset_option('colheader_justify')
11.5
可用的选项(略)
11.6
数字格式化
也允许设置如何在控制打印数字。这个选项不通过 set_options API设置。
Pandas
使用 set_eng_float_format 函数改变Pandas对象的浮点带引格式,创造一个特别的格式:
In [79]: import numpy as np
In [80]: pd.set_eng_float_format(accuracy=3, use_eng_prefix=True)
In [81]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e'])
In [82]: s/1.e3
Out[82]:
a
‑236.866u
b
846.974u
c
‑685.597u
d
609.099u
e
‑303.961u
dtype: float64
In [83]: s/1.e6
Out[83]:
a
‑236.866n
b
846.974n
c
‑685.597n
d
609.099n
e
‑303.961n
dtype: float64
根据具体情况舍入浮点数据,还可以使用 round() 。
格式
11.7 Unicode
注意:开启这个选项将会影响打印DataFrame和Series的性能(大约慢2倍)。所以,只有确实需要时再开启。
一些东亚国家使用宽度是2个Latin字符的Unicode字符。如果DataFrame或Series中包含这些字符,默认输出格式不会将它们对齐。
注意:每个输出都有截屏来展示实际结果。
国籍': ['UK', u'日本'], u'名前': ['Alice', u'しのぶ']})
In [84]: df = pd.DataFrame({u'
In [85]: df;
开启 display.unicode.east_asian_width 允许Pandas检查每个字符是不是 东亚宽度 。把这个选项设为True,这些字符将会在打印时
对齐。不过,这将会比标准的 len 函数花更多的渲染时间。
In [86]: pd.set_option('display.unicode.east_asian_width', True)
In [87]: df;
另外,Unicode字符究竟占1位字符宽度还是2位字符宽度取决于终端设置和编码方式。 display.unicode.ambiguous_as_wide 选项可
以用来处理这种模糊性。
默认地,模糊的字符宽度,比如下面示例中的“¡“(倒置感叹号),被认为是1。
In [88]: df = pd.DataFrame({'a': ['xxx', u'¡¡'], 'b': ['yyy', u'¡¡']})
In [89]: df;
开启 display.unicode.ambiguous_as_wide 使得Pandas将这些字符的宽度解释为2。(注:这个选项只有当开启
display.unicode.east_asian_width 时才生效)。
但是,如果没有针对你的终端设置正确这个选项,这些字符将不会正确的对齐。
In [90]: pd.set_option('display.unicode.ambiguous_as_wide', True)
In [91]: df;
表格概要显示
0.20.0版本新特性
DataFrame和Series将默认公布一个表格概要表示法。默认为False,可以通过全局设置 display.html.table_schema 来开启。
11.8
In [92]: pd.set_option('display.html.table_schema', True)
只有 display.max_rows 被序列化和公布。
第十二章索引和选择数据
对象的轴标签信息能够提供很多功能:
使用已知的索引器标识数据(例如:提供元数据),这在数据分析,可视化和交互式终端显示中很重要。
支持自动地、明确地数据对齐。
允许直观地获取、设置数据子集。
本章我们将聚焦在最后一点,即如何切片、切块、一般的获取和设置Pandas对象的子集,主要关注的是Series和DataFrame,因为它们在这个
领域受到更多的开发关注。
Pandas
注意:Python和Numpy的索引操作符 [] 提供了快速、简单的广泛使用的访问Pandas数据结构的方法。这使得交互式工作变
得直观,因为如果已经知道如何使用Python字典和Numpy数组,几乎不需要学习什么新的东西。不过,因为待访问的数据的类
型不会提前知道,直接使用标准的操作符有一些优化限制。在生产代码中,我们建议你优先使用本章中介绍的优化的Pandas数
据访问方法。
警告:设置操作返回一个副本还是引用,可能取决于上下文。这有时称为 chained assignment ,并且应该避免。查看
Returning a View versus Copy获得更多信息。
警告:使用浮点数对基于整数的索引进行索引的操作已在0.18.0版本中进行了说明,有关修改的摘要,请查看这里。
查看MultiIndex / Advanced Indexing获得关于MultiIndex和更多的高级索引的文档。
查看 cookbook获得一些高级使用方法。
12.1
不同的索引选择
对象选择操作已经有一些用户请求的添加,以支持更明确的基于位置的索引。Pandas现在支持三种多轴索引:
.loc 是主要的基于标签的索引方式。但是,也可以和布尔数组一起使用。当没有找到元素时, .loc 将抛出 KeyError 。允许的输入
有:
单独的标签,比如 5 或 a (注意: 5 会被解释为index标签,这种用法 不是 index 的整数位置)。
标签列表或数组 ['a', 'b', 'c', 'd'] 。
标签切片对象 'a':'f' (注意:与通常的Python切片相反,在索引中使用时,包含开始和结束,查看按标签索引)。
布尔数组。
单一参数(可调用的Series、DataFrame或Panel)、返回合法的支持索引的输出(上述几项中的一项)的可调用函数。
查看按标签选择获得更多信息。
.iloc 是主要的基于位置(0到轴的长度-1)的索引方式。但是,也可以和布尔数组一起使用。如果请求的索引器超出了范围, iloc
将会抛出 IndexError ,除非切片索引器允许越界索引。这符合Python或Numpy的切片语义。允许的输入有:
整数,例如 5 。
整数列表或数组: [4, 3, 0] 。
带有整数的切片对象: 1:7 。
布尔数组。
单一参数(可调用的Series、DataFrame或Panel)、返回合法的支持索引的输出(上述几项中的一项)的可调用函数。
查看按位置选择,高级索引和高级分层获得更多信息。
.loc , .iloc 和 [] 索引方式都接受一个可调用对象作为索引器。查看按可调用对象选择获得更多信息。
从具有多轴选择的对象获取值使用以下符号(以使用 .loc 为例,使用 .iloc 也同样适用)。每个轴的访问器都有可能是空的切片 : ,未指
定的轴为被假定为 : ,例如, p.loc['a'] 等价于 p.loc['a',:,:] 。
12.2
对象类型
索引器
Series
s.loc[indexer]
DataFrame
df.loc[row_indexer,column_indexer]
Panel
p.loc[item_indexer,major_indexer,minor_indexer]
基本操作
就像在介绍数据结构时的最后一节中提到的,使用 [] (对于熟悉在Python中实现类行为的人来说又称作 __getitem__ )索引的主要函数
是选出低维切片。下面的表格展示了使用 [] 索引Pandas对象时的返回类型:
对象类型
Series
DataFrame
Panel
选择
返回值类型
series[label]
标量值
frame[colname]
对应列名的Series
panel[itemname] 对应 itemname的DataFrame
我们构造了一个简单的时间序列数据集,来说明索引功能:
In [1]: dates = pd.date_range('1/1/2000', periods=8)
In [2]: df = pd.DataFrame(np.random.randn(8, 4), index=dates, columns=['A', 'B', 'C', 'D'])
In [3]: df
Out[3]:
A
B
C
D
2000‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
2000‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2000‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
2000‑01‑04 0.721555 ‑0.706771 ‑1.039575 0.271860
2000‑01‑05 ‑0.424972 0.567020 0.276232 ‑1.087401
2000‑01‑06 ‑0.673690 0.113648 ‑1.478427 0.524988
2000‑01‑07 0.404705 0.577046 ‑1.715002 ‑1.039268
2000‑01‑08 ‑0.370647 ‑1.157892 ‑1.344312 0.844885
In [4]: panel = pd.Panel({'one' : df, 'two' : df ‑ df.mean()})
In [5]: panel
Out[5]:
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 8 (major_axis) x 4 (minor_axis)
Items axis: one to two
Major_axis axis: 2000‑01‑01 00:00:00 to 2000‑01‑08 00:00:00
Minor_axis axis: A to D
注意:除非特别说明,否则索引功能都不是时间序列特有的。
因此,如上所述,我们使用 [] 进行大部分基本索引:
In [6]: s = df['A']
In [7]: s[dates[5]]
Out[7]: ‑0.67368970808837059
In [8]: panel['two']
Out[8]:
A
B
C
D
2000‑01‑01 0.409571 0.113086 ‑0.610826 ‑0.936507
2000‑01‑02 1.152571 0.222735 1.017442 ‑0.845111
2000‑01‑03 ‑0.921390 ‑1.708620 0.403304 1.270929
2000‑01‑04 0.662014 ‑0.310822 ‑0.141342 0.470985
2000‑01‑05 ‑0.484513 0.962970 1.174465 ‑0.888276
2000‑01‑06 ‑0.733231 0.509598 ‑0.580194 0.724113
2000‑01‑07 0.345164 0.972995 ‑0.816769 ‑0.840143
2000‑01‑08 ‑0.430188 ‑0.761943 ‑0.446079 1.044010
你可以传入列名组成的列表来按照顺序选择那些列。如果某个类没有包含的DataFrame中,会触发异常。也可以用这种方式设置多列。
In [9]: df
Out[9]:
A
B
C
D
2000‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
2000‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2000‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
2000‑01‑04 0.721555 ‑0.706771 ‑1.039575 0.271860
2000‑01‑05 ‑0.424972 0.567020 0.276232 ‑1.087401
2000‑01‑06 ‑0.673690 0.113648 ‑1.478427 0.524988
2000‑01‑07 0.404705 0.577046 ‑1.715002 ‑1.039268
2000‑01‑08 ‑0.370647 ‑1.157892 ‑1.344312 0.844885
In [10]: df[['B', 'A']] = df[['A', 'B']]
In [11]: df
Out[11]:
A
B
C
D
2000‑01‑01 ‑0.282863 0.469112 ‑1.509059 ‑1.135632
2000‑01‑02 ‑0.173215 1.212112 0.119209 ‑1.044236
2000‑01‑03 ‑2.104569 ‑0.861849 ‑0.494929 1.071804
2000‑01‑04 ‑0.706771 0.721555 ‑1.039575 0.271860
2000‑01‑05 0.567020 ‑0.424972 0.276232 ‑1.087401
2000‑01‑06 0.113648 ‑0.673690 ‑1.478427 0.524988
2000‑01‑07 0.577046 0.404705 ‑1.715002 ‑1.039268
2000‑01‑08 ‑1.157892 ‑0.370647 ‑1.344312 0.844885
你可能发现,这在对列的子集进行就地变换时很有用。
警告:当使用 .loc 或 .iloc 设置Series或DataFrame时,Pandas会对齐所有轴。这不会修改 df ,因为对齐在赋值之前。
pyhton
In [12]: df[['A', 'B']]
Out[12]:
A
B
2000‑01‑01 ‑0.282863 0.469112
2000‑01‑02 ‑0.173215 1.212112
2000‑01‑03 ‑2.104569 ‑0.861849
2000‑01‑04 ‑0.706771 0.721555
2000‑01‑05 0.567020 ‑0.424972
2000‑01‑06 0.113648 ‑0.673690
2000‑01‑07 0.577046 0.404705
2000‑01‑08 ‑1.157892 ‑0.370647
In [13]: df.loc[:,['B', 'A']] = df[['A', 'B']]
In [14]: df[['A', 'B']]
Out[14]:
A
B
2000‑01‑01 ‑0.282863 0.469112
2000‑01‑02 ‑0.173215 1.212112
2000‑01‑03 ‑2.104569 ‑0.861849
2000‑01‑04 ‑0.706771 0.721555
2000‑01‑05 0.567020 ‑0.424972
2000‑01‑06 0.113648 ‑0.673690
2000‑01‑07 0.577046 0.404705
2000‑01‑08 ‑1.157892 ‑0.370647
正确的交换列值的方式是使用原始值:
In [15]: df.loc[:,['B', 'A']] = df[['A', 'B']].values
In [16]: df[['A', 'B']]
Out[16]:
A
B
2000‑01‑01 0.469112 ‑0.282863
2000‑01‑02 1.212112 ‑0.173215
2000‑01‑03 ‑0.861849 ‑2.104569
2000‑01‑04 0.721555 ‑0.706771
2000‑01‑05 ‑0.424972 0.567020
2000‑01‑06 ‑0.673690 0.113648
2000‑01‑07 0.404705 0.577046
2000‑01‑08 ‑0.370647 ‑1.157892
12.3
访问属性
你可能以访问属性的方式访问Series的索引,DataFrame的列和Panel的Item。
In [17]: sa = pd.Series([1,2,3],index=list('abc'))
In [18]: dfa = df.copy()
In [19]: sa.b
Out[19]: 2
In [20]: dfa.A
Out[20]:
2000‑01‑01
0.469112
2000‑01‑02
1.212112
2000‑01‑03
‑0.861849
2000‑01‑04
0.721555
2000‑01‑05
‑0.424972
2000‑01‑06
‑0.673690
2000‑01‑07
0.404705
2000‑01‑08
‑0.370647
Freq: D, Name: A, dtype: float64
In [21]: panel.one
Out[21]:
A
B
C
D
2000‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
2000‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2000‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
2000‑01‑04 0.721555 ‑0.706771 ‑1.039575 0.271860
2000‑01‑05 ‑0.424972 0.567020 0.276232 ‑1.087401
2000‑01‑06 ‑0.673690 0.113648 ‑1.478427 0.524988
2000‑01‑07 0.404705 0.577046 ‑1.715002 ‑1.039268
2000‑01‑08 ‑0.370647 ‑1.157892 ‑1.344312 0.844885
In [22]: sa.a = 5
In [23]: sa
Out[23]:
a
5
b
2
c
3
dtype: int64
In [24]: dfa.A = list(range(len(dfa.index)))
# ok if A already exists
In [25]: dfa
Out[25]:
A
B
C
D
0 ‑0.282863 ‑1.509059 ‑1.135632
1 ‑0.173215 0.119209 ‑1.044236
2 ‑2.104569 ‑0.494929 1.071804
3 ‑0.706771 ‑1.039575 0.271860
4 0.567020 0.276232 ‑1.087401
5 0.113648 ‑1.478427 0.524988
6 0.577046 ‑1.715002 ‑1.039268
7 ‑1.157892 ‑1.344312 0.844885
2000‑01‑01
2000‑01‑02
2000‑01‑03
2000‑01‑04
2000‑01‑05
2000‑01‑06
2000‑01‑07
2000‑01‑08
In [26]: dfa['A'] = list(range(len(dfa.index)))
# use this form to create a new column
In [27]: dfa
Out[27]:
A
B
C
D
0 ‑0.282863 ‑1.509059 ‑1.135632
1 ‑0.173215 0.119209 ‑1.044236
2 ‑2.104569 ‑0.494929 1.071804
3 ‑0.706771 ‑1.039575 0.271860
4 0.567020 0.276232 ‑1.087401
5 0.113648 ‑1.478427 0.524988
6 0.577046 ‑1.715002 ‑1.039268
7 ‑1.157892 ‑1.344312 0.844885
2000‑01‑01
2000‑01‑02
2000‑01‑03
2000‑01‑04
2000‑01‑05
2000‑01‑06
2000‑01‑07
2000‑01‑08
警告:
只有索引元素是合法的Python识别码时才可以用这种方式访问。例如不允许使用 s.1 ,查看这里了解合法的识别码的解
释。
当属性和已经存在方法名冲突时,不能访问。例如不允许使用 s.min 。
当属性和 index, major_axis, minor_axis, items 冲突时,也不能访问。
上述几种情况,使用保准的索引方式都是可行的。例如 s['a'] , s['min'] , s['index'] 会访问对应的元素或列。
如果你正在使用IPython环境,你可以使用 tab 自动补全功能来查看所有看访问的属性。
你也可以用一个字典为DataFrame的一行赋值。
In [28]: x = pd.DataFrame({'x': [1, 2, 3], 'y': [3, 4, 5]})
In [29]: x.iloc[1] = dict(x=9, y=99)
In [30]: x
Out[30]:
x
y
0 1
3
1 9 99
2 3
5
你可以使用属性访问来修改一个已经存在的Series的元素或DataFrame的列,但是要注意,如果你尝试使用属性访问来创建一个新的列,它会
创建一个新的属性,而不是一个新的列。在0.21.0及以后的版本,这将会抛出一个 UserWarning 。
In[1]: df = pd.DataFrame({'one': [1., 2., 3.]})
In[2]: df.two = [4, 5, 6]
UserWarning: Pandas doesn't allow Series to be assigned into nonexistent columns ‑ see https://pa
ndas.pydata.org/pandas‑docs/stable/indexing.html#attribute_access
In[3]: df
Out[3]:
one
0 1.0
1 2.0
2 3.0
切割范围
沿着任意轴切割范围的最稳健和一致的方法在按位置选择的部分会介绍详细介绍 .iloc 方法。现在,我们解释一下使用 [] 操作符切片的语
义。
对于Series,这个语法和Numpy数组的工作方式一样,返回Series的值的一部分和对应的标签。
12.4
pyhton
In [31]: s[:5]
Out[31]:
2000‑01‑01
0.469112
2000‑01‑02
1.212112
2000‑01‑03
‑0.861849
2000‑01‑04
0.721555
2000‑01‑05
‑0.424972
Freq: D, Name: A, dtype: float64
In [32]: s[::2]
Out[32]:
2000‑01‑01
0.469112
2000‑01‑03
‑0.861849
2000‑01‑05
‑0.424972
2000‑01‑07
0.404705
Freq: 2D, Name: A, dtype: float64
In [33]: s[::‑1]
Out[33]:
2000‑01‑08
‑0.370647
2000‑01‑07
0.404705
2000‑01‑06
‑0.673690
2000‑01‑05
‑0.424972
2000‑01‑04
0.721555
2000‑01‑03
‑0.861849
2000‑01‑02
1.212112
2000‑01‑01
0.469112
Freq: ‑1D, Name: A, dtype: float64
注意,设置操作同样也可以工作
In [34]: s2 = s.copy()
In [35]: s2[:5] = 0
In [36]: s2
Out[36]:
2000‑01‑01
0.000000
2000‑01‑02
0.000000
2000‑01‑03
0.000000
2000‑01‑04
0.000000
2000‑01‑05
0.000000
2000‑01‑06
‑0.673690
2000‑01‑07
0.404705
2000‑01‑08
‑0.370647
Freq: D, Name: A, dtype: float64
对应DataFrame, [] 的内部切片是在切割行。这主要是为了方便,因为这是非常常见的操作。
In [37]: df[:3]
Out[37]:
A
B
C
D
2000‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
2000‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2000‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
In [38]: df[::‑1]
Out[38]:
A
B
C
D
2000‑01‑08 ‑0.370647 ‑1.157892 ‑1.344312 0.844885
2000‑01‑07 0.404705 0.577046 ‑1.715002 ‑1.039268
2000‑01‑06 ‑0.673690 0.113648 ‑1.478427 0.524988
2000‑01‑05 ‑0.424972 0.567020 0.276232 ‑1.087401
2000‑01‑04 0.721555 ‑0.706771 ‑1.039575 0.271860
2000‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
2000‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2000‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
使用标签选择
警告:设置操作返回一个副本还是引用,可能取决于上下文。这有时称为 chained assignment ,并且应该避免。查看
Returning a View versus Copy获得更多信息。
警告:当使用的的切片器和索引类型不兼容(或可转换)时, .loc 是严格的。例如,在DatatimeIndex中使用整数,这将抛
出 TypeError 。
12.5
In [39]: dfl = pd.DataFrame(np.random.randn(5,4), columns=list('ABCD'), index=pd.date
_range('20130101',periods=5))
In [40]: dfl
Out[40]:
A
B
C
D
2013‑01‑01 1.075770 ‑0.109050 1.643563 ‑1.469388
2013‑01‑02 0.357021 ‑0.674600 ‑1.776904 ‑0.968914
2013‑01‑03 ‑1.294524 0.413738 0.276662 ‑0.472035
2013‑01‑04 ‑0.013960 ‑0.362543 ‑0.006154 ‑0.923061
2013‑01‑05 0.895717 0.805244 ‑1.206412 2.565646
In [4]: dfl.loc[2:3]
TypeError: cannot do slice indexing on <class 'pandas.tseries.index.DatetimeIndex'> w
ith these indexers [2] of <type 'int'>
索引中的类字符串可以转换成index的类型,并且得到自然切片
In [41]: dfl.loc['20130102':'20130104']
Out[41]:
A
B
C
D
2013‑01‑02 0.357021 ‑0.674600 ‑1.776904 ‑0.968914
2013‑01‑03 ‑1.294524 0.413738 0.276662 ‑0.472035
2013‑01‑04 ‑0.013960 ‑0.362543 ‑0.006154 ‑0.923061
警告:从0.21.0版开始,如果用包含缺失标签的列表进行索引时,Pandas将显示 FutureWarning 。将来,
这将会抛出 KeyError 。查看 list-like Using loc with missing keys in a list is Deprecated获得更多信息。
为了支持纯粹的基于标签的索引,Pandas提供了一组方法。这是一个严格的基于包含的协议。每个被使用的标签必须是在索引中,否则会抛
出一个 KerError 错误。执行切片时,如果首尾边界在索引中,它们都包含在内。整型是合法的标签,但它们指的是标签而不是位置。
.loc 属性是主要的访问方法。下面列出了合法的输入:
单独的标签,比如 5 或 a (注意: 5 会被解释为index的标签,这种用法 不是 index 的整数位置)。
标签列表或数组 ['a', 'b', 'c', 'd'] 。
标签切片对象 'a':'f' (注意:与通常的Python切片相反,在索引中使用时,包含开始和结束,查看按标签索引)。
布尔数组。
可调用对象,查看使用可调用对象进行选择。
In [42]: s1 = pd.Series(np.random.randn(6),index=list('abcdef'))
In [43]: s1
Out[43]:
a
1.431256
b
1.340309
c
‑1.170299
d
‑0.226169
e
0.410835
f
0.813850
dtype: float64
In [44]: s1.loc['c':]
Out[44]:
c
‑1.170299
d
‑0.226169
e
0.410835
f
0.813850
dtype: float64
In [45]: s1.loc['b']
Out[45]: 1.3403088497993827
注意,也可以用于赋值操作。
In [46]: s1.loc['c':] = 0
In [47]: s1
Out[47]:
a
1.431256
b
1.340309
c
0.000000
d
0.000000
e
0.000000
f
0.000000
dtype: float64
操作DataFrame:
In [48]: df1 = pd.DataFrame(np.random.randn(6,4),
....:
index=list('abcdef'),
....:
columns=list('ABCD'))
....:
In [49]: df1
Out[49]:
A
B
C
D
a 0.132003 ‑0.827317 ‑0.076467 ‑1.187678
b 1.130127 ‑1.436737 ‑1.413681 1.607920
c 1.024180 0.569605 0.875906 ‑2.211372
d 0.974466 ‑2.006747 ‑0.410001 ‑0.078638
e 0.545952 ‑1.219217 ‑1.226825 0.769804
f ‑1.281247 ‑0.727707 ‑0.121306 ‑0.097883
In [50]: df1.loc[['a', 'b', 'd'], :]
Out[50]:
A
B
C
D
a 0.132003 ‑0.827317 ‑0.076467 ‑1.187678
b 1.130127 ‑1.436737 ‑1.413681 1.607920
d 0.974466 ‑2.006747 ‑0.410001 ‑0.078638
使用标签切片访问:
In [51]: df1.loc['d':, 'A':'C']
Out[51]:
A
B
C
d 0.974466 ‑2.006747 ‑0.410001
e 0.545952 ‑1.219217 ‑1.226825
f ‑1.281247 ‑0.727707 ‑0.121306
使用标签获得一个横截面(等价于 df.xs('a') ):
In [53]: df1.loc['a'] > 0
Out[53]:
A
True
B
False
C
False
D
False
Name: a, dtype: bool
In [54]: df1.loc[:, df1.loc['a'] > 0]
Out[54]:
A
a 0.132003
b 1.130127
c 1.024180
d 0.974466
e 0.545952
f ‑1.281247
显示地获取一个值(等价于已经弃用的df.get_value('a', 'A')):
# this is also equivalent to ``df1.at['a','A']``
In [55]: df1.loc['a', 'A']
Out[55]: 0.13200317033032932
使用标签进行切片
使用 .loc 进行切片时,如果起始和结束标签都在索引中,将会返回这两个标签中间的元素(包含开始和结束):
12.5.1
In [56]: s = pd.Series(list('abcde'), index=[0,3,2,5,4])
In [57]: s.loc[3:5]
Out[57]:
3
b
2
c
5
d
dtype: object
如果开始和结束标签中至少有一个缺失,但是索引是排序后的,并且可以和起始标签和结束标签进行比较,那么,切片操作还是会按照预想的
进行,选择序号在开始和结束标签中间的标签:
In [58]: s.sort_index()
Out[58]:
0
a
2
c
3
b
4
e
5
d
dtype: object
In [59]: s.sort_index().loc[1:6]
Out[59]:
2
c
3
b
4
e
5
d
dtype: object
不过,如果开始和结束标签中至少有一个缺失,但是索引未被排序,将会抛出异常(因为不这样做计算成本很高,而且,对于混合类型的索引
会有歧义)。在上面的例子中, s.loc[1:6] 将会抛出 KerError 。
按位置选择
12.6
警告:设置操作返回一个副本还是引用,可能取决于上下文。这有时称为 chained assignment ,并且应该避免。查看
Returning a View versus Copy获得更多信息。
为了支持纯粹的基于整型的索引,Pandas提供了一组方法。语义基本遵循Python和Numpy的切片操作。这是从0开始的索引。切片时,包含开
始边界,排除上限。使用非整型,即使是合法的标签也会抛出 IndexError 。
属性是主要的访问方法。下面列出了合法的输入:
单独的标签,比如 5 。
整型列表或数组 [4,3,0] 。
整型切片对象 1:7 。
布尔数组。
可调用对象,查看使用可调用对象进行选择。
.iloc
In [60]: s1 = pd.Series(np.random.randn(5), index=list(range(0,10,2)))
In [61]: s1
Out[61]:
0
0.695775
2
0.341734
4
0.959726
6
‑1.110336
8
‑0.619976
dtype: float64
In [62]: s1.iloc[:3]
Out[62]:
0
0.695775
2
0.341734
4
0.959726
dtype: float64
In [63]: s1.iloc[3]
Out[63]: ‑1.1103361028911669
同样可以用于赋值:
In [64]: s1.iloc[:3] = 0
In [65]: s1
Out[65]:
0
0.000000
2
0.000000
4
0.000000
6
‑1.110336
8
‑0.619976
dtype: float64
操作DataFrame:
In [66]: df1 = pd.DataFrame(np.random.randn(6,4),
....:
index=list(range(0,12,2)),
....:
columns=list(range(0,8,2)))
....:
In [67]: df1
Out[67]:
0
2
0
0.149748 ‑0.732339
2
0.403310 ‑0.154951
4 ‑1.369849 ‑0.954208
6 ‑0.826591 ‑0.345352
8
0.995761 2.396780
10 ‑0.317441 ‑1.236269
4
6
0.687738 0.176444
0.301624 ‑2.179861
1.462696 ‑1.743161
1.314232 0.690579
0.014871 3.357427
0.896171 ‑0.487602
按整型切片:
In [68]: df1.iloc[:3]
Out[68]:
0
2
0 0.149748 ‑0.732339
2 0.403310 ‑0.154951
4 ‑1.369849 ‑0.954208
4
6
0.687738 0.176444
0.301624 ‑2.179861
1.462696 ‑1.743161
In [69]: df1.iloc[1:5, 2:4]
Out[69]:
4
6
2 0.301624 ‑2.179861
4 1.462696 ‑1.743161
6 1.314232 0.690579
8 0.014871 3.357427
通过整型列表:
In [70]: df1.iloc[[1, 3, 5], [1, 3]]
Out[70]:
2
6
2 ‑0.154951 ‑2.179861
6 ‑0.345352 0.690579
10 ‑1.236269 ‑0.487602
In [71]: df1.iloc[1:3, :]
Out[71]:
0
2
4
6
2 0.403310 ‑0.154951 0.301624 ‑2.179861
4 ‑1.369849 ‑0.954208 1.462696 ‑1.743161
In [72]: df1.iloc[:, 1:3]
Out[72]:
2
4
0 ‑0.732339 0.687738
2 ‑0.154951 0.301624
4 ‑0.954208 1.462696
6 ‑0.345352 1.314232
8
2.396780 0.014871
10 ‑1.236269 0.896171
# this is also equivalent to ``df1.iat[1,1]``
In [73]: df1.iloc[1, 1]
Out[73]: ‑0.15495077442490321
使用整型位置获取一个横截面(等价于`df.xs(1)):
In [74]: df1.iloc[1]
Out[74]:
0
0.403310
2
‑0.154951
4
0.301624
6
‑2.179861
Name: 2, dtype: float64
想在Python和Numpy那样优雅地处理超出范围的切片索引:
# these are allowed in python/numpy.
In [75]: x = list('abcdef')
In [76]: x
Out[76]: ['a', 'b', 'c', 'd', 'e', 'f']
In [77]: x[4:10]
Out[77]: ['e', 'f']
In [78]: x[8:10]
Out[78]: []
In [79]: s = pd.Series(x)
In [80]: s
Out[80]:
0
a
1
b
2
c
3
d
4
e
5
f
dtype: object
In [81]: s.iloc[4:10]
Out[81]:
4
e
5
f
dtype: object
In [82]: s.iloc[8:10]
Out[82]: Series([], dtype: object)
注意:使用超出范围的切片会返回空轴(例如,返回一个空的DataFrame):
In [83]: dfl = pd.DataFrame(np.random.randn(5,2), columns=list('AB'))
In [84]: dfl
Out[84]:
A
B
0 ‑0.082240 ‑2.182937
1 0.380396 0.084844
2 0.432390 1.519970
3 ‑0.493662 0.600178
4 0.274230 0.132885
In [85]: dfl.iloc[:, 2:3]
Out[85]:
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3, 4]
In [86]: dfl.iloc[:, 1:3]
Out[86]:
B
0 ‑2.182937
1 0.084844
2 1.519970
3 0.600178
4 0.132885
In [87]: dfl.iloc[4:6]
Out[87]:
A
B
4 0.27423 0.132885
超出范围的单个索引器将会抛出 IndexError 。所有元素都在范围外的索引器列表将会抛出 IndexError :
dfl.iloc[[4, 5, 6]]
IndexError: positional indexers are out‑of‑bounds
dfl.iloc[:, 4]
IndexError: single positional indexer is out‑of‑bounds
使用可调用对象选择
12.7
版本新特性。
.loc 、 .iloc 和 [] 索引都可以接受一个可调用对象作为索引器。这个可调用对象必须是一个单参数(被操作的Series,DataFrame或
Panel)函数并且返回一个可用于索引的合法输出:
0.18.1
In [88]: df1 = pd.DataFrame(np.random.randn(6, 4),
....:
index=list('abcdef'),
....:
columns=list('ABCD'))
....:
In [89]: df1
Out[89]:
A
B
C
D
a ‑0.023688 2.410179 1.450520 0.206053
b ‑0.251905 ‑2.213588 1.063327 1.266143
c 0.299368 ‑0.863838 0.408204 ‑1.048089
d ‑0.025747 ‑0.988387 0.094055 1.262731
e 1.289997 0.082423 ‑0.055758 0.536580
f ‑0.489682 0.369374 ‑0.034571 ‑2.484478
In [90]: df1.loc[lambda df: df.A > 0, :]
Out[90]:
A
B
C
D
c 0.299368 ‑0.863838 0.408204 ‑1.048089
e 1.289997 0.082423 ‑0.055758 0.536580
In [91]: df1.loc[:, lambda df: ['A', 'B']]
Out[91]:
A
B
a ‑0.023688 2.410179
b ‑0.251905 ‑2.213588
c 0.299368 ‑0.863838
d ‑0.025747 ‑0.988387
e 1.289997 0.082423
f ‑0.489682 0.369374
In [92]: df1.iloc[:, lambda df: [0, 1]]
Out[92]:
A
B
a ‑0.023688 2.410179
b ‑0.251905 ‑2.213588
c 0.299368 ‑0.863838
d ‑0.025747 ‑0.988387
e 1.289997 0.082423
f ‑0.489682 0.369374
In [93]: df1[lambda df: df.columns[0]]
Out[93]:
a
‑0.023688
b
‑0.251905
c
0.299368
d
‑0.025747
e
1.289997
f
‑0.489682
Name: A, dtype: float64
可以使用可调用对象对Series进行索引:
In [94]: df1.A.loc[lambda s: s > 0]
Out[94]:
c
0.299368
e
1.289997
Name: A, dtype: float64
使用这些方法或索引器,你可以不使用临时变量连接数据选择操作:
In [95]: bb = pd.read_csv('data/baseball.csv', index_col='id')
In [96]: (bb.groupby(['year', 'team']).sum()
....:
.loc[lambda df: df.r > 100])
....:
Out[96]:
stint
g
ab
r
h X2b X3b
sh
sf gidp
year team
2007 CIN
1.0 15.0
DET
4.0
8.0
HOU
6.0
6.0
LAN
3.0
8.0
NYN
8.0 15.0
SFN
6.0
6.0
TEX
2.0
8.0
TOR
4.0 16.0
6
18.0
5
28.0
4
17.0
11
29.0
13
48.0
5
41.0
2
16.0
4
38.0
hr
rbi
sb
cs
bb
so
ibb
hbp
379
745
101
203
35
2
36
125.0
10.0
1.0
105
127.0
14.0
1.0
301
1062
162
283
54
4
37
144.0
24.0
7.0
97
176.0
3.0
10.0
311
926
109
218
47
6
14
77.0
10.0
4.0
60
212.0
3.0
9.0
413
1021
153
293
61
3
36
154.0
7.0
5.0
114
141.0
8.0
9.0
622
1854
240
509
101
3
61
243.0
22.0
4.0
174
310.0
24.0
23.0
1
482
1305
198
337
67
6
40
171.0
26.0
7.0
235
188.0
51.0
8.0
1
198
729
115
200
40
4
28
115.0
21.0
4.0
73
140.0
4.0
5.0
459
1408
187
378
96
2
58
223.0
4.0
2.0
190
265.0
16.0
12.0
1
索引器被弃用
12.8 IX
注意:从0.20.0版开始,为了支持更严格的 .loc 和 .iloc 操作, .ix 索引器被弃用了。
在推断用户想做什么方面提供了许多奇妙功能。 .ix 可以按位置索引,也可以按照取决于索引类型的标签进行索引。多年来,这给用户
带来了很多困惑。
.ix
推荐的索引方法是:
.loc :按标签索引。
.iloc :按位置索引。
In [97]: dfd = pd.DataFrame({'A': [1, 2, 3],
....:
'B': [4, 5, 6]},
....:
index=list('abc'))
....:
In [98]: dfd
Out[98]:
A B
a 1 4
b 2 5
c 3 6
按照之前的做法,如果你想得到A列的第0和第2个元素:
In [3]: dfd.ix[[0, 2], 'A']
Out[3]:
a
1
c
3
Name: A, dtype: int64
使用 .loc 。这里,我们将从索引中选择合适的索引器,然后使用标签进行索引:
In [99]: dfd.loc[dfd.index[[0, 2]], 'A']
Out[99]:
a
1
c
3
Name: A, dtype: int64
这个问题也可以使用 .iloc ,先显式地获取索引器上的位置,然后再使用位置进行索引:
In [100]: dfd.iloc[[0, 2], dfd.columns.get_loc('A')]
Out[100]:
a
1
c
3
Name: A, dtype: int64
获取多个索引器,可以使用 .get_indexer :
In [101]: dfd.iloc[[0, 2], dfd.columns.get_indexer(['A', 'B'])]
Out[101]:
A B
a 1 4
c 3 6
12.9
使用包含缺失标签的列表进行索引被弃用
注意:从0.21.0版开始,为了支持 reindex 操作,使用包含缺失标签的列表进行 .loc 或 [] 操作被弃用了。
在之前的版本中,使用 .loc[list‑of‑labels] 时,只要列表中有一个标签能够查到数据就能正常执行(否则会抛出KeyError错误)。这
种使用方式被弃用了,推荐用法是使用 .reindex() 。
例如:
In [102]: s = pd.Series([1, 2, 3])
In [103]: s
Out[103]:
0
1
1
2
2
3
dtype: int64
所有的key都能查到数据的使用方式没有修改:
In [104]: s.loc[[1, 2]]
Out[104]:
1
2
2
3
dtype: int64
之前的处理方式:
In [4]: s.loc[[1, 2, 3]]
Out[4]:
1
2.0
2
3.0
3
NaN
dtype: float64
现在的处理方式:
In [4]: s.loc[[1, 2, 3]]
Passing list‑likes to .loc with any non‑matching elements will raise
KeyError in the future, you can use .reindex() as an alternative.
See the documentation here:
http://pandas.pydata.org/pandas‑docs/stable/indexing.html#deprecate‑loc‑reindex‑listlike
Out[4]:
1
2.0
2
3.0
3
NaN
dtype: float64
重建索引
实现选择可能找不到的元素的惯用方法是使用 .reindex() 。查看reindexing章节获取更多信息。
12.9.1
In [105]: s.reindex([1, 2, 3])
Out[105]:
1
2.0
2
3.0
3
NaN
dtype: float64
可选地,如果你只想选择合法的key,下面的方法是常用而有效的,它能保留选择项的数据类型。
In [106]: labels = [1, 2, 3]
In [107]: s.loc[s.index.intersection(labels)]
Out[107]:
1
2
2
3
dtype: int64
如果索引中有重复值,将会抛出异常。
In [108]: s = pd.Series(np.arange(4), index=['a', 'a', 'b', 'c'])
In [109]: labels = ['c', 'd']
In [17]: s.reindex(labels)
ValueError: cannot reindex from a duplicate axis
通常,你可以将所需的标签与当前轴交叉,然后重建索引。
In [110]: s.loc[s.index.intersection(labels)].reindex(labels)
Out[110]:
c
3.0
d
NaN
dtype: float64
不过 ,如果索引中有重复值,同样会抛出异常。
In [41]: labels = ['a', 'd']
In [42]: s.loc[s.index.intersection(labels)].reindex(labels)
ValueError: cannot reindex from a duplicate axis
选择随机样本
sample() 方法从Series、DataFrame或Panel中随机选择一定的行或列。默认地,这个方法随机选择行,同时接受一个数字来指定返回行还
是列,或行的一部分。
12.10
In [111]: s = pd.Series([0,1,2,3,4,5])
# When no arguments are passed, returns 1 row.
In [112]: s.sample()
Out[112]:
4
4
dtype: int64
# One may specify either a number of rows:
In [113]: s.sample(n=3)
Out[113]:
0
0
4
4
1
1
dtype: int64
# Or a fraction of the rows:
In [114]: s.sample(frac=0.5)
Out[114]:
5
5
3
3
1
1
dtype: int64
默认地,使用 sample 时,每一行最多返回一次,但是也可以通过 replace 选项来决定抽样时是否替换。
In [115]: s = pd.Series([0,1,2,3,4,5])
# Without replacement ﴾default﴿:
In [116]: s.sample(n=6, replace=False)
Out[116]:
0
0
1
1
5
5
3
3
2
2
4
4
dtype: int64
# With replacement:
In [117]: s.sample(n=6, replace=True)
Out[117]:
0
0
4
4
3
3
2
2
4
4
4
4
dtype: int64
默认地,每一行都有可能被选择。如果你想让每一行被选择的概率不同,可以为sample函数传入一个weights参数。这个weights参数可以是一
个列表,Numpy数组或一个Series,但它的长度必须和想要抽样的对象的长度一样。缺失值的权重会被处理成0,无穷大不允许使用。如果权
重的和不等于1,将会重新计算权重,计算方法是每一个的权重除以权重之和。例如:
In [118]: s = pd.Series([0,1,2,3,4,5])
In [119]: example_weights = [0, 0, 0.2, 0.2, 0.2, 0.4]
In [120]: s.sample(n=3, weights=example_weights)
Out[120]:
5
5
4
4
3
3
dtype: int64
# Weights will be re‑normalized automatically
In [121]: example_weights2 = [0.5, 0, 0, 0, 0, 0]
In [122]: s.sample(n=1, weights=example_weights2)
Out[122]:
0
0
dtype: int64
对DataFrame抽样时,可以指定某一列(只要指定该列的列名字符串)作为抽样权重。
In [123]: df2 = pd.DataFrame({'col1':[9,8,7,6], 'weight_column':[0.5, 0.4, 0.1, 0]})
In [124]: df2.sample(n = 3, weights = 'weight_column')
Out[124]:
col1 weight_column
1
8
0.4
0
9
0.5
2
7
0.1
传入 axis 参数可以对列进行抽样
In [125]: df3 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
In [126]: df3.sample(n=1, axis=1)
Out[126]:
col1
0
1
1
2
2
3
最后,可以传入 random_state 参数设置随机种子,该参数接受一个整数(作为种子)或一个Numpy RandomState对象。
In [127]: df4 = pd.DataFrame({'col1':[1,2,3], 'col2':[2,3,4]})
# With a given seed, the sample will always draw the same rows.
In [128]: df4.sample(n=2, random_state=2)
Out[128]:
col1 col2
2
3
4
1
2
3
In [129]: df4.sample(n=2, random_state=2)
Out[129]:
col1 col2
2
3
4
1
2
3
设置与扩大
.loc 或 [] 操作可以在设置某个轴上不存在的key时执行扩大操作。
对于Series,这是一种有效的追加操作。
12.11
In [130]: se = pd.Series([1,2,3])
In [131]: se
Out[131]:
0
1
1
2
2
3
dtype: int64
In [132]: se[5] = 5.
In [133]: se
Out[133]:
0
1.0
1
2.0
2
3.0
5
5.0
dtype: float64
可以通过 .loc 在任意轴上执行扩大操作:
DataFrame
In [134]: dfi = pd.DataFrame(np.arange(6).reshape(3,2),
.....:
columns=['A','B'])
.....:
In [135]: dfi
Out[135]:
A B
0 0 1
1 2 3
2 4 5
In [136]: dfi.loc[:,'C'] = dfi.loc[:,'A']
In [137]: dfi
Out[137]:
A B C
0 0 1 0
1 2 3 2
2 4 5 4
这和 append 操作类似:
In [138]: dfi.loc[3] = 5
In [139]: dfi
Out[139]:
A B C
0 0 1 0
1 2 3 2
2 4 5 4
3 5 5 5
快速获取标量数据
由于通过 [] 来索引必须处理大部分情况(单标签访问,切片,布尔索引等等),它会有一些开销来确定你想要的内容。如果你只想获取一个
标量数据,最快的方式是使用 .at 和 .iat 方法,这两个方法在所有的数据结构上都实现了。
与 .loc 类似, .at 提供了基于标签的标量查找, .iat 提供了基于整数的查找,类似于 .iloc :
12.12
In [140]: s.iat[5]
Out[140]: 5
In [141]: df.at[dates[5], 'A']
Out[141]: ‑0.67368970808837059
In [142]: df.iat[3, 0]
Out[142]: 0.72155516224436689
也可以使用同样的索引器设置数值
In [143]: df.at[dates[5], 'E'] = 7
In [144]: df.iat[3, 0] = 7
如果索引器缺失, .at 将会按照上面的方式放大对象
In [145]: df.at[dates[‑1]+1, 0] = 7
In [146]: df
Out[146]:
A
B
C
D
2000‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632
2000‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236
2000‑01‑03 ‑0.861849 ‑2.104569 ‑0.494929 1.071804
2000‑01‑04 7.000000 ‑0.706771 ‑1.039575 0.271860
2000‑01‑05 ‑0.424972 0.567020 0.276232 ‑1.087401
2000‑01‑06 ‑0.673690 0.113648 ‑1.478427 0.524988
2000‑01‑07 0.404705 0.577046 ‑1.715002 ‑1.039268
2000‑01‑08 ‑0.370647 ‑1.157892 ‑1.344312 0.844885
2000‑01‑09
NaN
NaN
NaN
NaN
E
NaN
NaN
NaN
NaN
NaN
7.0
NaN
NaN
NaN
0
NaN
NaN
NaN
NaN
NaN
NaN
NaN
NaN
7.0
布尔索引
另一个常用的操作是按照布尔向量选择数据。操作符是 | (代表or), & (代表and)和 ~ (代表not)。这些操作必须用括号分组,因为
Python会计算一个表达式,比如 df.A > 2 & df.B < 3 as df.A > (2 & df.B) < 3 的期望计算顺序是 (df.A > 2) & (df.B <
3) 。
使用布尔向量来索引一个Series时,和在Numpy数组中的工作方式一样:
12.13
In [147]: s = pd.Series(range(‑3, 4))
In [148]: s
Out[148]:
0
‑3
1
‑2
2
‑1
3
0
4
1
5
2
6
3
dtype: int64
In [149]: s[s > 0]
Out[149]:
4
1
5
2
6
3
dtype: int64
In [150]: s[(s < ‑1) | (s > 0.5)]
Out[150]:
0
‑3
1
‑2
4
1
5
2
6
3
dtype: int64
In [151]: s[~(s < 0)]
Out[151]:
3
0
4
1
5
2
6
3
dtype: int64
您可以使用与DataFrame索引长度相同的布尔向量从DataFrame中选择行(例如,从DataFrame的一列派生的内容):
In [152]: df[df['A'] > 0]
Out[152]:
A
B
C
D
E
0
2000‑01‑01 0.469112 ‑0.282863 ‑1.509059 ‑1.135632 NaN NaN
2000‑01‑02 1.212112 ‑0.173215 0.119209 ‑1.044236 NaN NaN
2000‑01‑04 7.000000 ‑0.706771 ‑1.039575 0.271860 NaN NaN
2000‑01‑07 0.404705 0.577046 ‑1.715002 ‑1.039268 NaN NaN
的列表推导和map方法也可以用来生成更复杂的条件:
Series
In [153]: df2 = pd.DataFrame({'a' : ['one', 'one', 'two', 'three', 'two', 'one', 'six'],
.....:
'b' : ['x', 'y', 'y', 'x', 'y', 'x', 'x'],
.....:
'c' : np.random.randn(7)})
.....:
# only want 'two' or 'three'
In [154]: criterion = df2['a'].map(lambda x: x.startswith('t'))
In [155]: df2[criterion]
Out[155]:
a b
c
2
two y 0.041290
3 three x 0.361719
4
two y ‑0.238075
# equivalent but slower
In [156]: df2[[x.startswith('t') for x in df2['a']]]
Out[156]:
a b
c
2
two y 0.041290
3 three x 0.361719
4
two y ‑0.238075
# Multiple criteria
In [157]: df2[criterion & (df2['b'] == 'x')]
Out[157]:
a b
c
3 three x 0.361719
使用选择方法Selection by Label, Selection by Position, and Advanced Indexing,你可以使用布尔向量和其他索引表达式结合对多个轴进行索
引。
In [158]: df2.loc[criterion & (df2['b'] == 'x'),'b':'c']
Out[158]:
b
c
3 x 0.361719
使用isin进行索引
考虑Series的 isin() 方法,该方法返回一个布尔向量,Series的元素存在于传入的列表中,则布尔向量的对应位置为True。这允许你选择一
列或多列具有你需要的值的行:
12.14
In [159]: s = pd.Series(np.arange(5), index=np.arange(5)[::‑1], dtype='int64')
In [160]: s
Out[160]:
4
0
3
1
2
2
1
3
0
4
dtype: int64
In [161]: s.isin([2, 4, 6])
Out[161]:
4
False
3
False
2
True
1
False
0
True
dtype: bool
In [162]: s[s.isin([2, 4, 6])]
Out[162]:
2
2
0
4
dtype: int64
对象可以使用相同的方法,当您不知道哪些搜索标签实际存在时,它们非常有用:
Index
In [163]: s[s.index.isin([2, 4, 6])]
Out[163]:
4
0
2
2
dtype: int64
# compare it to the following
In [164]: s.reindex([2, 4, 6])
Out[164]:
2
2.0
4
0.0
6
NaN
dtype: float64
除此之外,MultiIndex允许选择在成员资格检查中使用的单独等级:
In [165]: s_mi = pd.Series(np.arange(6),
.....:
index=pd.MultiIndex.from_product([[0, 1], ['a', 'b', 'c']]))
.....:
In [166]: s_mi
Out[166]:
0 a
0
b
1
c
2
1 a
3
b
4
c
5
dtype: int64
In [167]: s_mi.iloc[s_mi.index.isin([(1, 'a'), (2, 'b'), (0, 'c')])]
Out[167]:
0 c
2
1 a
3
dtype: int64
In [168]: s_mi.iloc[s_mi.index.isin(['a', 'c', 'e'], level=1)]
Out[168]:
0 a
0
c
2
1 a
3
c
5
dtype: int64
也有 isin() 方法。当调用 isin 时,传入一个数值集合,可以是数组也可以是字典。如果数据是一个数组, isin 放回一个值
为布尔值的和原始DataFrame形状一样的DataFrame,原始DataFrame某个位置的值存在于传入的数值序列中,返回的DataFrame中该位置的
值为True。
DataFrame
In [169]: df = pd.DataFrame({'vals': [1, 2, 3, 4], 'ids': ['a', 'b', 'f', 'n'],
.....:
'ids2': ['a', 'n', 'c', 'n']})
.....:
In [170]: values = ['a', 'b', 1, 3]
In [171]: df.isin(values)
Out[171]:
vals
ids
ids2
0
True
True
True
1 False
True False
2
True False False
3 False False False
许多时候,你想要将某些值与特定的列对应。只需要传入一个字典,字典的key是列名,值为你想要检查的元素列表。
In [172]: values = {'ids': ['a', 'b'], 'vals': [1, 3]}
In [173]: df.isin(values)
Out[173]:
vals
ids
ids2
0
True
True False
1 False
True False
2
True False False
3 False False False
将DataFrame的 isin 方法与 any() 和 all() 方法结合使用,可以快速选择满足给定条件的数据子集。比如,选择每列符合其自身条件的
行:
In [174]: values = {'ids': ['a', 'b'], 'ids2': ['a', 'c'], 'vals': [1, 3]}
In [175]: row_mask = df.isin(values).all(1)
In [176]: df[row_mask]
Out[176]:
vals ids ids2
0
1
a
a
12.15 where()
方法和 mask() 方法
使用布尔向量从Series中选择数据返回数据的一个子集。要确保输出与原始数据具有相同的形状,可以使用Series和DataFrame的 where 方
法。
只返回选择的行:
In [177]: s[s > 0]
Out[177]:
3
1
2
2
1
3
0
4
dtype: int64
返回与原始数据形状相同的Series:
In [178]: s.where(s > 0)
Out[178]:
4
NaN
3
1.0
2
2.0
1
3.0
0
4.0
dtype: float64
现在,使用布尔条件从DataFrame中选择数据也可以保留输入数据的形状。这种情况下,实现方式是在引擎下使用 where 。下面的代码等价
于 df.where(df<0) :
In [179]: df[df < 0]
Out[179]:
A
B
C
D
2000‑01‑01 ‑2.104139 ‑1.309525
NaN
NaN
2000‑01‑02 ‑0.352480
NaN ‑1.192319
NaN
2000‑01‑03 ‑0.864883
NaN ‑0.227870
NaN
2000‑01‑04
NaN ‑1.222082
NaN ‑1.233203
2000‑01‑05
NaN ‑0.605656 ‑1.169184
NaN
2000‑01‑06
NaN ‑0.948458
NaN ‑0.684718
2000‑01‑07 ‑2.670153 ‑0.114722
NaN ‑0.048048
2000‑01‑08
NaN
NaN ‑0.048788 ‑0.808838
另外, where 有一个可选的 other 参数,来设置返回的副本中值为False的位置的替换方法。
In [180]: df.where(df < 0, ‑df)
Out[180]:
A
B
C
D
2000‑01‑01 ‑2.104139 ‑1.309525 ‑0.485855 ‑0.245166
2000‑01‑02 ‑0.352480 ‑0.390389 ‑1.192319 ‑1.655824
2000‑01‑03 ‑0.864883 ‑0.299674 ‑0.227870 ‑0.281059
2000‑01‑04 ‑0.846958 ‑1.222082 ‑0.600705 ‑1.233203
2000‑01‑05 ‑0.669692 ‑0.605656 ‑1.169184 ‑0.342416
2000‑01‑06 ‑0.868584 ‑0.948458 ‑2.297780 ‑0.684718
2000‑01‑07 ‑2.670153 ‑0.114722 ‑0.168904 ‑0.048048
2000‑01‑08 ‑0.801196 ‑1.392071 ‑0.048788 ‑0.808838
您可能希望根据某些布尔条件设置值。 这可以直观地完成,如下所示:
In [181]: s2 = s.copy()
In [182]: s2[s2 < 0] = 0
In [183]: s2
Out[183]:
4
0
3
1
2
2
1
3
0
4
dtype: int64
In [184]: df2 = df.copy()
In [185]: df2[df2 < 0] = 0
In [186]: df2
Out[186]:
2000‑01‑01
2000‑01‑02
2000‑01‑03
2000‑01‑04
2000‑01‑05
2000‑01‑06
2000‑01‑07
2000‑01‑08
A
0.000000
0.000000
0.000000
0.846958
0.669692
0.868584
0.000000
0.801196
B
0.000000
0.390389
0.299674
0.000000
0.000000
0.000000
0.000000
1.392071
C
0.485855
0.000000
0.000000
0.600705
0.000000
2.297780
0.168904
0.000000
D
0.245166
1.655824
0.281059
0.000000
0.342416
0.000000
0.000000
0.000000
默认情况下, where 方法返回修改后的数据副本。有一个可选参数 inplace ,可以在不创建副本的情况下修改原始数据:
In [187]: df_orig = df.copy()
In [188]: df_orig.where(df > 0, ‑df, inplace=True);
In [189]: df_orig
Out[189]:
2000‑01‑01
2000‑01‑02
2000‑01‑03
2000‑01‑04
2000‑01‑05
2000‑01‑06
2000‑01‑07
2000‑01‑08
A
2.104139
0.352480
0.864883
0.846958
0.669692
0.868584
2.670153
0.801196
B
1.309525
0.390389
0.299674
1.222082
0.605656
0.948458
0.114722
1.392071
C
0.485855
1.192319
0.227870
0.600705
1.169184
2.297780
0.168904
0.048788
D
0.245166
1.655824
0.281059
1.233203
0.342416
0.684718
0.048048
0.808838
注意: DataFrame.where() 的签名与 numpy.where() 不同。 大概地, df1.where(m,df2) 等价于 np.where(m,
df1,df2) 。
In [190]: df.where(df < 0, ‑df) == np.where(df < 0, df, ‑df)
Out[190]:
A
B
C
D
2000‑01‑01 True True True True
2000‑01‑02 True True True True
2000‑01‑03 True True True True
2000‑01‑04 True True True True
2000‑01‑05 True True True True
2000‑01‑06 True True True True
2000‑01‑07 True True True True
2000‑01‑08 True True True True
12.15.1
对齐
另外, where 对齐了输入布尔条件(数组或DataFrame),因此,使用部分选择进行设置成为可能。这类似于使用 .loc 进行部分设置(但
是在内容上而不是在轴标签上)
In [191]: df2 = df.copy()
In [192]: df2[ df2[1:4] > 0] = 3
In [193]: df2
Out[193]:
A
B
C
D
2000‑01‑01 ‑2.104139 ‑1.309525 0.485855 0.245166
2000‑01‑02 ‑0.352480 3.000000 ‑1.192319 3.000000
2000‑01‑03 ‑0.864883 3.000000 ‑0.227870 3.000000
2000‑01‑04 3.000000 ‑1.222082 3.000000 ‑1.233203
2000‑01‑05 0.669692 ‑0.605656 ‑1.169184 0.342416
2000‑01‑06 0.868584 ‑0.948458 2.297780 ‑0.684718
2000‑01‑07 ‑2.670153 ‑0.114722 0.168904 ‑0.048048
2000‑01‑08 0.801196 1.392071 ‑0.048788 ‑0.808838
where
也接受 axis 和 level 参数来对齐输入:
In [194]: df2 = df.copy()
In [195]: df2.where(df2>0,df2['A'],axis='index')
Out[195]:
A
B
C
D
2000‑01‑01 ‑2.104139 ‑2.104139 0.485855 0.245166
2000‑01‑02 ‑0.352480 0.390389 ‑0.352480 1.655824
2000‑01‑03 ‑0.864883 0.299674 ‑0.864883 0.281059
2000‑01‑04 0.846958 0.846958 0.600705 0.846958
2000‑01‑05 0.669692 0.669692 0.669692 0.342416
2000‑01‑06 0.868584 0.868584 2.297780 0.868584
2000‑01‑07 ‑2.670153 ‑2.670153 0.168904 ‑2.670153
2000‑01‑08 0.801196 1.392071 0.801196 0.801196
这等价于(但更快)下面的代码
In [196]: df2 = df.copy()
In [197]: df.apply(lambda x, y: x.where(x>0,y), y=df['A'])
Out[197]:
A
B
C
D
2000‑01‑01 ‑2.104139 ‑2.104139 0.485855 0.245166
2000‑01‑02 ‑0.352480 0.390389 ‑0.352480 1.655824
2000‑01‑03 ‑0.864883 0.299674 ‑0.864883 0.281059
2000‑01‑04 0.846958 0.846958 0.600705 0.846958
2000‑01‑05 0.669692 0.669692 0.669692 0.342416
2000‑01‑06 0.868584 0.868584 2.297780 0.868584
2000‑01‑07 ‑2.670153 ‑2.670153 0.168904 ‑2.670153
2000‑01‑08 0.801196 1.392071 0.801196 0.801196
版本新特性
where 接受一个可调用对象作为条件和 other 参数。该函数必须只有一个参数(被调用的Series或DataFrame),必须返回合法的能够作为
条件和 other 参数的输出:
0.18.1
In [198]: df3 = pd.DataFrame({'A': [1, 2, 3],
.....:
'B': [4, 5, 6],
.....:
'C': [7, 8, 9]})
.....:
In [199]: df3.where(lambda x: x > 4, lambda x: x + 10)
Out[199]:
A
B C
0 11 14 7
1 12
5 8
2 13
6 9
12.15.2 Mask
mask()
是 where 方法的反布尔运算
In [200]: s.mask(s >= 0)
Out[200]:
4
NaN
3
NaN
2
NaN
1
NaN
0
NaN
dtype: float64
In [201]: df.mask(df >= 0)
Out[201]:
A
B
C
D
2000‑01‑01 ‑2.104139 ‑1.309525
NaN
NaN
2000‑01‑02 ‑0.352480
NaN ‑1.192319
NaN
2000‑01‑03 ‑0.864883
NaN ‑0.227870
NaN
2000‑01‑04
NaN ‑1.222082
NaN ‑1.233203
2000‑01‑05
NaN ‑0.605656 ‑1.169184
NaN
2000‑01‑06
NaN ‑0.948458
NaN ‑0.684718
2000‑01‑07 ‑2.670153 ‑0.114722
NaN ‑0.048048
2000‑01‑08
NaN
NaN ‑0.048788 ‑0.808838
方法
DataFrame 对象有一个 query() 方法,允许使用表达式选择数据。
你可以获取数据框中b列的值介于a列值和c列值之间的数据。例如:
12.16 query()
In [202]: n = 10
In [203]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [204]: df
Out[204]:
a
b
0 0.438921 0.118680
1 0.138138 0.577363
2 0.595307 0.564592
3 0.913052 0.926075
4 0.078718 0.854477
5 0.076404 0.523211
6 0.792342 0.216974
7 0.397890 0.454131
8 0.074315 0.437913
9 0.559209 0.502065
c
0.863670
0.686602
0.520630
0.616184
0.898725
0.591538
0.564056
0.915716
0.019794
0.026437
# pure python
In [205]: df[(df.a < df.b) & (df.b < df.c)]
Out[205]:
a
b
c
1 0.138138 0.577363 0.686602
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
7 0.397890 0.454131 0.915716
# query
In [206]: df.query('(a < b) & (b < c)')
Out[206]:
a
b
c
1 0.138138 0.577363 0.686602
4 0.078718 0.854477 0.898725
5 0.076404 0.523211 0.591538
7 0.397890 0.454131 0.915716
如果没有名称为a的列,则执行相同的操作但转而使用一个命名索引。
In [207]: df = pd.DataFrame(np.random.randint(n / 2, size=(n, 2)), columns=list('bc'))
In [208]: df.index.name = 'a'
In [209]: df
Out[209]:
b c
a
0 0 4
1 0 1
2 3 4
3 4 3
4 1 4
5 0 3
6 0 1
7 3 4
8 2 3
9 1 1
In [210]: df.query('a < b and b < c')
Out[210]:
b c
a
2 3 4
如果您不希望或不能命名索引,则可以在查询表达式中使用 index :
In [211]: df = pd.DataFrame(np.random.randint(n, size=(n, 2)), columns=list('bc'))
In [212]: df
Out[212]:
b c
0 3 1
1 3 0
2 5 6
3 5 2
4 7 4
5 0 1
6 2 5
7 0 1
8 6 0
9 7 9
In [213]: df.query('index < b < c')
Out[213]:
b c
2 5 6
注意:如果索引的名称与列名称重叠,则列名称优先。 例如:
In [214]: df = pd.DataFrame({'a': np.random.randint(5, size=5)})
In [215]: df.index.name = 'a'
In [216]: df.query('a > 2') # uses the column 'a', not the index
Out[216]:
a
a
1 3
3 3
您仍然可以使用特殊标识符 index 在查询表达式中使用索引:
In [217]: df.query('index > 2')
Out[217]:
a
a
3 3
4 2
如果由于某种原因你有一个名为index的列,那么你也可以将索引称为ilevel_0,但此时你应该考虑将列重命名为不那么模糊的
名称。
12.16.1 MultiIndex query﴾﴿
语法
您还可以将DataFrame的级别与MultiIndex一起使用,就像它们是数据框中的列一样:
In [218]: n = 10
In [219]: colors = np.random.choice(['red', 'green'], size=n)
In [220]: foods = np.random.choice(['eggs', 'ham'], size=n)
In [221]: colors
Out[221]:
array(['red', 'red', 'red', 'green', 'green', 'green', 'green', 'green',
'green', 'green'],
dtype='<U5')
In [222]: foods
Out[222]:
array(['ham', 'ham', 'eggs', 'eggs', 'eggs', 'ham', 'ham', 'eggs', 'eggs',
'eggs'],
dtype='<U4')
In [223]: index = pd.MultiIndex.from_arrays([colors, foods], names=['color', 'food'])
In [224]: df = pd.DataFrame(np.random.randn(n, 2), index=index)
In [225]: df
Out[225]:
0
1
color food
red
ham
0.194889 ‑0.381994
ham
0.318587 2.089075
eggs ‑0.728293 ‑0.090255
green eggs ‑0.748199 1.318931
eggs ‑2.029766 0.792652
ham
0.461007 ‑0.542749
ham ‑0.305384 ‑0.479195
eggs 0.095031 ‑0.270099
eggs ‑0.707140 ‑0.773882
eggs 0.229453 0.304418
In [226]: df.query('color == "red"')
Out[226]:
0
1
color food
red
ham
0.194889 ‑0.381994
ham
0.318587 2.089075
eggs ‑0.728293 ‑0.090255
如果MultiIndex没有命名,则可以使用特殊名称指代它们:
In [227]: df.index.names = [None, None]
In [228]: df
Out[228]:
0
1
red
ham
0.194889 ‑0.381994
ham
0.318587 2.089075
eggs ‑0.728293 ‑0.090255
green eggs ‑0.748199 1.318931
eggs ‑2.029766 0.792652
ham
0.461007 ‑0.542749
ham ‑0.305384 ‑0.479195
eggs 0.095031 ‑0.270099
eggs ‑0.707140 ‑0.773882
eggs 0.229453 0.304418
In [229]: df.query('ilevel_0 == "red"')
Out[229]:
0
1
red ham
0.194889 ‑0.381994
ham
0.318587 2.089075
eggs ‑0.728293 ‑0.090255
约定是,ilevel_0意思是 index level 0 ,指代index的第0级。
用例
query() 的一个用例是,当你拥有一组DataFrame对象时,这些对象具有共同的列名(或索引级别或名称)的子集。你可以将相同的查询传
递给两个数据框,无需指定你想查询的数据框。
12.16.2 query﴾﴿
In [230]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [231]: df
Out[231]:
a
b
0 0.224283 0.736107
1 0.302827 0.657803
2 0.611185 0.136624
3 0.195246 0.123436
4 0.618673 0.371660
5 0.480088 0.062993
6 0.568018 0.483467
7 0.309040 0.274580
8 0.258993 0.477769
9 0.550459 0.840870
c
0.139168
0.713897
0.984960
0.627712
0.047902
0.185760
0.445289
0.587101
0.370255
0.304611
In [232]: df2 = pd.DataFrame(np.random.rand(n + 2, 3), columns=df.columns)
In [233]: df2
Out[233]:
a
b
0
0.357579 0.229800
1
0.309059 0.957923
2
0.123102 0.336914
3
0.526506 0.323321
4
0.518736 0.486514
5
0.190804 0.505723
6
0.891939 0.623977
7
0.480559 0.378528
8
0.420223 0.136404
9
0.732206 0.419540
10 0.604466 0.848974
11 0.589168 0.920046
c
0.596001
0.965663
0.318616
0.860813
0.384724
0.614533
0.676639
0.460858
0.141295
0.604675
0.896165
0.732716
In [234]: expr = '0.0 <= a <= c <= 0.5'
In [235]: map(lambda frame: frame.query(expr), [df, df2])
Out[235]: <map at 0x1c2fbb2780>
与
语法比较
12.16.3 query﴾﴿ Python Pandas
完全类似Numpy语法
In [236]: df = pd.DataFrame(np.random.randint(n, size=(n, 3)), columns=list('abc'))
In [237]: df
Out[237]:
a b c
0 7 8 9
1 1 0 7
2 2 7 2
3 6 2 2
4 2 6 3
5 3 8 2
6 1 7 2
7 5 1 5
8 9 8 0
9 1 5 0
In [238]: df.query('(a < b) & (b < c)')
Out[238]:
a b c
0 7 8 9
In [239]: df[(df.a < df.b) & (df.b < df.c)]
Out[239]:
a b c
0 7 8 9
去掉括号更美观(通过绑定使比较运算符绑定比&和|更严格)
In [240]: df.query('a < b & b < c')
Out[240]:
a b c
0 7 8 9
使用英文替代符号
In [241]: df.query('a < b and b < c')
Out[241]:
a b c
0 7 8 9
非常接近你在纸上写的方式:
In [242]: df.query('a < b < c')
Out[242]:
a b c
0 7 8 9
和 not in 操作
query() 还支持Python的 in 和 not in 比较操作的特殊使用,为调用Series和DataFrame的 isin 方法提供了简洁的语法。
12.16.4 in
# get all rows where columns "a" and "b" have overlapping values
In [243]: df = pd.DataFrame({'a': list('aabbccddeeff'), 'b': list('aaaabbbbcccc'),
.....:
'c': np.random.randint(5, size=12),
.....:
'd': np.random.randint(9, size=12)})
.....:
In [244]: df
Out[244]:
a b c d
0
a a 2 6
1
a a 4 7
2
b a 1 6
3
b a 2 1
4
c b 3 6
5
c b 0 2
6
d b 3 3
7
d b 2 1
8
e c 4 3
9
e c 2 0
10 f c 0 6
11 f c 1 2
In [245]: df.query('a in b')
Out[245]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
# How you'd do it in pure Python
In [246]: df[df.a.isin(df.b)]
Out[246]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
3 b a 2 1
4 c b 3 6
5 c b 0 2
In [247]: df.query('a not in b')
Out[247]:
a b c d
6
d b 3 3
7
d b 2 1
8
e c 4 3
9
e c 2 0
10 f c 0 6
11 f c 1 2
# pure Python
In [248]: df[~df.a.isin(df.b)]
Out[248]:
a b c d
6
d b 3 3
7
d b 2 1
8
e c 4 3
9
e c 2 0
10 f c 0 6
11 f c 1 2
您可以把他与其他表达式结合使用,以获得非常简洁的查询:
# rows where cols a and b have overlapping values and col c's values are less than col d's
In [249]: df.query('a in b and c < d')
Out[249]:
a b c d
0 a a 2 6
1 a a 4 7
2 b a 1 6
4 c b 3 6
5 c b 0 2
# pure Python
In [250]: df[df.b.isin(df.a) & (df.c < df.d)]
Out[250]:
a b c d
0
a a 2 6
1
a a 4 7
2
b a 1 6
4
c b 3 6
5
c b 0 2
10 f c 0 6
11 f c 1 2
注意: in 和 not in 在Python中计算,因为 numexpr 没有对应的操作。不过,只有in/not in表达式自己会在普通Python中
计算。例如,在表达式
df.query('a in b + c + d')
中, (b + c + d) 会被 numexpr 计算,然后 in 操作在普通Python中计算。通常,任何可以用 numexpr 计算的操作都可
以。
列表对象的==操作的特殊使用
使用 == 或 != 将值列表与列进行比较的方式类似于 in 或 not in
12.16.5
In [251]: df.query('b == ["a", "b", "c"]')
Out[251]:
a b c d
0
a a 2 6
1
a a 4 7
2
b a 1 6
3
b a 2 1
4
c b 3 6
5
c b 0 2
6
d b 3 3
7
d b 2 1
8
e c 4 3
9
e c 2 0
10 f c 0 6
11 f c 1 2
# pure Python
In [252]: df[df.b.isin(["a", "b", "c"])]
Out[252]:
a b c d
0
a a 2 6
1
a a 4 7
2
b a 1 6
3
b a 2 1
4
c b 3 6
5
c b 0 2
6
d b 3 3
7
d b 2 1
8
e c 4 3
9
e c 2 0
10 f c 0 6
11 f c 1 2
In [253]: df.query('c == [1, 2]')
Out[253]:
a b c d
0
a a 2 6
2
b a 1 6
3
b a 2 1
7
d b 2 1
9
e c 2 0
11 f c 1 2
In [254]: df.query('c != [1, 2]')
Out[254]:
a b c d
1
a a 4 7
4
c b 3 6
5
c b 0 2
6
d b 3 3
8
e c 4 3
10 f c 0 6
# using in/not in
In [255]: df.query('[1, 2] in c')
Out[255]:
a b c d
0
a a 2 6
2
b a 1 6
3
b a 2 1
7
d b 2 1
9
e c 2 0
11 f c 1 2
In [256]: df.query('[1, 2] not in c')
Out[256]:
a b c d
1
a a 4 7
4
c b 3 6
5
c b 0 2
6
d b 3 3
8
e c 4 3
10 f c 0 6
# pure Python
In [257]: df[df.c.isin([1, 2])]
Out[257]:
a b c d
0
a a 2 6
2
b a 1 6
3
b a 2 1
7
d b 2 1
9
e c 2 0
11 f c 1 2
布尔操作
你可以使用 not 或 ~ 操作对布尔表达式取反:
12.16.6
In [258]: df = pd.DataFrame(np.random.rand(n, 3), columns=list('abc'))
In [259]: df['bools'] = np.random.rand(len(df)) > 0.5
In [260]: df.query('~bools')
Out[260]:
a
b
c
2 0.697753 0.212799 0.329209
7 0.275396 0.691034 0.826619
8 0.190649 0.558748 0.262467
bools
False
False
False
In [261]: df.query('not bools')
Out[261]:
a
b
c
2 0.697753 0.212799 0.329209
7 0.275396 0.691034 0.826619
8 0.190649 0.558748 0.262467
bools
False
False
False
In [262]: df.query('not bools') == df[~df.bools]
Out[262]:
a
b
c bools
2 True True True
True
7 True True True
True
8 True True True
True
当然,表达式也可以是任意复杂的:
# short query syntax
In [263]: shorter = df.query('a < b < c and (not bools) or bools > 2')
# equivalent in pure Python
In [264]: longer = df[(df.a < df.b) & (df.b < df.c) & (~df.bools) | (df.bools > 2)]
In [265]: shorter
Out[265]:
a
b
7 0.275396 0.691034
c
0.826619
bools
False
In [266]: longer
Out[266]:
a
b
7 0.275396 0.691034
c
0.826619
bools
False
In [267]: shorter == longer
Out[267]:
a
b
c bools
7 True True True
True
性能
对于大型数据框,使用 numexpr 的 DataFrame.query() 比Python略快
12.16.7 query﴾﴿
注意:如果您的框架超过大约200,000行,您将只看到使用带有 DataFrame.query() 的 numexpr 引擎的性能优势
这张图是使用一个包含3列的DataFrame创建的,每列包含使用 numpy.random.randn() 生成的浮点值。
重复数据
如果你想识别和删除DataFrame中的重复数据,有两个方法可以使用: duplicated 和 drop_duplicates 。每个方法都将用于标识重复
行的列作为参数。
duplicated :返回一个布尔向量,该向量的长度是行的数量,表示某一行是否重复。
drop_duplicates :删除重复的行。
默认地,重复数据中的第一个观察行将会被认为是唯一的,但每个方法都有一个 keep 参数来指定要保留的目标。
keep='first' :(默认),标记/删除重复值,除了第一个出现的行。
keep='last' :标记/删除重复值,除了最后一个出现的行。
keep=False :删除所有的重复行。
12.17
In [268]: df2 = pd.DataFrame({'a': ['one', 'one', 'two', 'two', 'two', 'three', 'four'],
.....:
'b': ['x', 'y', 'x', 'y', 'x', 'x', 'x'],
.....:
'c': np.random.randn(7)})
.....:
In [269]: df2
Out[269]:
a b
c
0
one x ‑1.067137
1
one y 0.309500
2
two x ‑0.211056
3
two y ‑1.842023
4
two x ‑0.390820
5 three x ‑1.964475
6
four x 1.298329
In [270]: df2.duplicated('a')
Out[270]:
0
False
1
True
2
False
3
True
4
True
5
False
6
False
dtype: bool
In [271]: df2.duplicated('a', keep='last')
Out[271]:
0
True
1
False
2
True
3
True
4
False
5
False
6
False
dtype: bool
In [272]: df2.duplicated('a', keep=False)
Out[272]:
0
True
1
True
2
True
3
True
4
True
5
False
6
False
dtype: bool
In [273]: df2.drop_duplicates('a')
Out[273]:
a b
c
0
one x ‑1.067137
2
two x ‑0.211056
5 three x ‑1.964475
6
four x 1.298329
In [274]: df2.drop_duplicates('a', keep='last')
Out[274]:
a b
c
1
one y 0.309500
4
two x ‑0.390820
5 three x ‑1.964475
6
four x 1.298329
In [275]: df2.drop_duplicates('a', keep=False)
Out[275]:
a b
c
5 three x ‑1.964475
6
four x 1.298329
另外,你可以传入一个列名的列表,来识别重复值。
In [276]: df2.duplicated(['a', 'b'])
Out[276]:
0
False
1
False
2
False
3
False
4
True
5
False
6
False
dtype: bool
In [277]: df2.drop_duplicates(['a', 'b'])
Out[277]:
a b
c
0
one x ‑1.067137
1
one y 0.309500
2
two x ‑0.211056
3
two y ‑1.842023
5 three x ‑1.964475
6
four x 1.298329
要按索引值删除重复项,请使用 Index.duplicated ,然后执行切片。 keep 参数可以使用相同的选项集。
In [278]: df3 = pd.DataFrame({'a': np.arange(6),
.....:
'b': np.random.randn(6)},
.....:
index=['a', 'a', 'b', 'c', 'b', 'a'])
.....:
In [279]: df3
Out[279]:
a
b
a 0 1.440455
a 1 2.456086
b 2 1.038402
c 3 ‑0.894409
b 4 0.683536
a 5 3.082764
In [280]: df3.index.duplicated()
Out[280]: array([False, True, False, False,
True,
True], dtype=bool)
In [281]: df3[~df3.index.duplicated()]
Out[281]:
a
b
a 0 1.440455
b 2 1.038402
c 3 ‑0.894409
In [282]: df3[~df3.index.duplicated(keep='last')]
Out[282]:
a
b
c 3 ‑0.894409
b 4 0.683536
a 5 3.082764
In [283]: df3[~df3.index.duplicated(keep=False)]
Out[283]:
a
b
c 3 ‑0.894409
类似字典的 get() 方法
12.18
、DataFrame和Panel都有一个 get 方法,该方法可以返回一个默认值。
Series
In [284]: s = pd.Series([1,2,3], index=['a','b','c'])
In [285]: s.get('a')
Out[285]: 1
# equivalent to s['a']
In [286]: s.get('x', default=‑1)
Out[286]: ‑1
12.19 lookup()
方法
有时,你希望在给定一系列行标签和列标签的情况下提取一组值,并且 lookup 方法允许此操作并返回NumPy数组。 例如:
In [287]: dflookup = pd.DataFrame(np.random.rand(20,4), columns = ['A','B','C','D'])
In [288]: dflookup.lookup(list(range(0,10,2)), ['B','C','A','B','D'])
Out[288]: array([ 0.3506, 0.4779, 0.4825, 0.9197, 0.5019])
对象
可以将Pandas的及其子类看成一个有序多集的实现。允许重复。不过,如果你想将有重复值的 Index 对象转换成一个 set ,将会触发异
常。
12.20 Index
Index
也提供查找、数据对齐和重建索引所需的基础结构。直接创建 Index 的最简单方法是将列表或其他序列传递给 Index :
In [289]: index = pd.Index(['e', 'd', 'a', 'b'])
In [290]: index
Out[290]: Index(['e', 'd', 'a', 'b'], dtype='object')
In [291]: 'd' in index
Out[291]: True
你也可以传入要存储在索引中的名称:
In [292]: index = pd.Index(['e', 'd', 'a', 'b'], name='something')
In [293]: index.name
Out[293]: 'something'
这个名称(如果设置了),将会在控制台中展示。
In [294]: index = pd.Index(list(range(5)), name='rows')
In [295]: columns = pd.Index(['A', 'B', 'C'], name='cols')
In [296]: df = pd.DataFrame(np.random.randn(5, 3), index=index, columns=columns)
In [297]: df
Out[297]:
cols
A
B
C
rows
0
1.295989 0.185778 0.436259
1
0.678101 0.311369 ‑0.528378
2
‑0.674808 ‑1.103529 ‑0.656157
3
1.889957 2.076651 ‑1.102192
4
‑1.211795 ‑0.791746 0.634724
In [298]: df['A']
Out[298]:
rows
0
1.295989
1
0.678101
2
‑0.674808
3
1.889957
4
‑1.211795
Name: A, dtype: float64
设置元数据
索引在大部分情况下是不可变的,但是,可以改变它们的元数据,例如: name ,或者多重索引的 levels 和 labels 。
你可以使用 rename , set_names , set_levels 和 set_labels 来直接修改这些属性。默认地,操作之后返回原数据的一个副本,不
过,你也可以使用 inplace=True 来修改原数据。
从Advanced Indexing查看多重索引的使用方式。
12.20.1
In [299]: ind = pd.Index([1, 2, 3])
In [300]: ind.rename("apple")
Out[300]: Int64Index([1, 2, 3], dtype='int64', name='apple')
In [301]: ind
Out[301]: Int64Index([1, 2, 3], dtype='int64')
In [302]: ind.set_names(["apple"], inplace=True)
In [303]: ind.name = "bob"
In [304]: ind
Out[304]: Int64Index([1, 2, 3], dtype='int64', name='bob')
set_names
, set_levels 和 set_labels 还有一个 level 参数:
In [305]: index = pd.MultiIndex.from_product([range(3), ['one', 'two']], names=['first', 'second'
])
In [306]: index
Out[306]:
MultiIndex(levels=[[0, 1, 2], ['one', 'two']],
labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
names=['first', 'second'])
In [307]: index.levels[1]
Out[307]: Index(['one', 'two'], dtype='object', name='second')
In [308]: index.set_levels(["a", "b"], level=1)
Out[308]:
MultiIndex(levels=[[0, 1, 2], ['a', 'b']],
labels=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
names=['first', 'second'])
对象间的集合操作
index对象间的集合操作主要有两个: union(|) 和 intersection(&) 。它们可以直接作为实例方法或通过重载操作符调用。差异判断是
通过 .difference() 操作来实现的。
12.20.2 index
In [309]: a = pd.Index(['c', 'b', 'a'])
In [310]: b = pd.Index(['c', 'e', 'd'])
In [311]: a | b
Out[311]: Index(['a', 'b', 'c', 'd', 'e'], dtype='object')
In [312]: a & b
Out[312]: Index(['c'], dtype='object')
In [313]: a.difference(b)
Out[313]: Index(['a', 'b'], dtype='object')
还有一种操作: symmetric_difference(^) ,返回在idx1或idx2中存在,但不同时在两个index中存在的元素,等价于:
idx1.difference(idx2).union(idx2.difference(idx1)) 操作之后再去重。
In [314]: idx1 = pd.Index([1, 2, 3, 4])
In [315]: idx2 = pd.Index([2, 3, 4, 5])
In [316]: idx1.symmetric_difference(idx2)
Out[316]: Int64Index([1, 5], dtype='int64')
In [317]: idx1 ^ idx2
Out[317]: Int64Index([1, 5], dtype='int64')
注意:集合操作的结果会按照升序排列。
缺失数据
重要:虽然Index可以操作缺失数据(NaN),但是如果不想遇到不可预期的结果,尽量避免这么操作。比如,有些操作会隐式地排除缺失数
据。
Index.fillna 将缺失数据填充为指定的标量值。
12.20.3
In [318]: idx1 = pd.Index([1, np.nan, 3, 4])
In [319]: idx1
Out[319]: Float64Index([1.0, nan, 3.0, 4.0], dtype='float64')
In [320]: idx1.fillna(2)
Out[320]: Float64Index([1.0, 2.0, 3.0, 4.0], dtype='float64')
In [321]: idx2 = pd.DatetimeIndex([pd.Timestamp('2011‑01‑01'), pd.NaT, pd.Timestamp('2011‑01‑03'
)])
In [322]: idx2
Out[322]: DatetimeIndex(['2011‑01‑01', 'NaT', '2011‑01‑03'], dtype='datetime64[ns]', freq=None)
In [323]: idx2.fillna(pd.Timestamp('2011‑01‑02'))
Out[323]: DatetimeIndex(['2011‑01‑01', '2011‑01‑02', '2011‑01‑03'], dtype='datetime64[ns]', freq=
None)
设置 / 重新设置Index
有时候,你创建好了一个DataFrame之后,还想添加一个索引。有许多方法可以达效果:
12.21
设置索引
Dataframe有一个 set_index() 方法,这个方法接受一个列名(用来设置Index)或一个列名列表(用来设置MultiIndex)。创建一个新的重
新设置索引的DataFrame:
12.21.1
In [324]: data
Out[324]:
a
b c
0 bar one z
1 bar two y
2 foo one x
3 foo two w
d
1.0
2.0
3.0
4.0
In [325]: indexed1 = data.set_index('c')
In [326]: indexed1
Out[326]:
a
b
d
c
z bar one 1.0
y bar two 2.0
x foo one 3.0
w foo two 4.0
In [327]: indexed2 = data.set_index(['a', 'b'])
In [328]: indexed2
Out[328]:
c
d
a
b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
append
关键字允许保持已经存在的Index,而把新加的列添加到MultiIndex。
In [329]: frame = data.set_index('c', drop=False)
In [330]: frame = frame.set_index(['a', 'b'], append=True)
In [331]: frame
Out[331]:
c
d
c a
b
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
set_index
的其他选项允许你不丢弃原来的索引列,或就地添加索引(不创建新的对象):
In [332]: data.set_index('c', drop=False)
Out[332]:
a
b c
d
c
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
In [333]: data.set_index(['a', 'b'], inplace=True)
In [334]: data
Out[334]:
c
d
a
b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
重新设置索引
DataFrame上有一个更方便的函数叫做 reset_index() ,它可以将索引数据转换为DataFrame的列,并且设置一个简单的整型索引。它是
set_index() 的反方法。
12.21.2
In [335]: data
Out[335]:
c
d
a
b
bar one z 1.0
two y 2.0
foo one x 3.0
two w 4.0
In [336]: data.reset_index()
Out[336]:
a
b c
d
0 bar one z 1.0
1 bar two y 2.0
2 foo one x 3.0
3 foo two w 4.0
输出更像是SQL表或记录数组。从索引中得到的列的名称保存在 names 属性中。
可以使用 level 关键字只移除一部分索引。
In [337]: frame
Out[337]:
c
d
c a
b
z bar one z 1.0
y bar two y 2.0
x foo one x 3.0
w foo two w 4.0
In [338]: frame.reset_index(level=1)
Out[338]:
a c
d
c b
z one bar z 1.0
y two bar y 2.0
x one foo x 3.0
w two foo w 4.0
reset_index
有一个 drop 参数,这个参数如果为True,将会简单地丢弃索引,而不是把索引的数据放到DataFrame的列里。
添加临时索引
如果你自己创建索引,可以将其赋值给index字段:
12.21.3
data.index = index
返回视图 VS 返回副本
当在一个Pandas对象上设置数据时,必须小心避免所谓的链式索引( chained indexing )。这里有个示例:
12.22
In [339]: dfmi = pd.DataFrame([list('abcd'),
.....:
list('efgh'),
.....:
list('ijkl'),
.....:
list('mnop')],
.....:
columns=pd.MultiIndex.from_product([['one','two'],
.....:
['first','second']]))
.....:
In [340]: dfmi
Out[340]:
one
two
first second first second
0
a
b
c
d
1
e
f
g
h
2
i
j
k
l
3
m
n
o
p
对比下面的两个方法:
In [341]: dfmi['one']['second']
Out[341]:
0
b
1
f
2
j
3
n
Name: second, dtype: object
In [342]: dfmi.loc[:,('one','second')]
Out[342]:
0
b
1
f
2
j
3
n
Name: (one, second), dtype: object
这两个方法产生同样的结果,那么应该使用哪种呢?理解这两种操作操作的执行顺序以及为什么第二种方法(.loc)比第一种方法([]链式操
作)更受欢迎,这具有指导意义。
dfmi['one'] 先选择列的第一层级,返回一个单索引的DataFrame,然后另一个Python操作 dfmi_with_on['second'] 以索引
second 选择了一个Series。以 dfmi_with_on 来表示是因为pandas将这些操作视为独立事件。比如,单独调用 __getitem__ ,所以必
须把它们当作线性操作,一个接一个的执行。
与此形成对比的是, df.loc[:,('one','second')] 将嵌套的元组 (slice(None),('one','second')) 传递给 __getitem__ 单
独调用。这允许pandas将它处理成一个单独的实体。这种操作会显著加快,并且如果需要的话,还可以索引两个轴。
为什么使用链式索引时赋值会失败
上一节提到的这个问题实际上是性能问题。 SettingWithCopy 是怎么回事?当您做一些可能需要额外几毫秒的事情时,我们通常不会抛出
警告。
12.23
但链式索引赋值会产生不可预测的结果。要了解这一点,请考虑Python解释器如何执行这段代码:
dfmi.loc[:,('one','second')] = value
# becomes
dfmi.loc.__setitem__((slice(None), ('one', 'second')), value)
但下面的代码的处理方式不同:
dfmi['one']['second'] = value
# becomes
dfmi.__getitem__('one').__setitem__('second', value)
看到这里的 __getitem 了吗?复杂情况下,很难预料某个操作是返回视图还是返回副本(取决于数组的内存布局,而Pandas对此没有保
证),因此, __setitem__ 会修改 dfmi 还是立即抛出一个临时对象也很难预料,这就是抛出警告的原因。
注意:你可能会关心我们是否应该关注第一个示例中的 loc 属性。但是, dfmi.loc 保证了 dfmi 的索引修改行为,因
此, dfmi.loc.__getitem__/dfmi.loc.__setitem__ 将直接操作 dfmi 。当然, dfmi.loc.__getitem__(idx)
有可能是 dfmi 的一个视图也有可能是一个副本。
有时,当没有明显的链式索引时,也可能抛出 SettingWithCopy 警告。这些是 SettingWithCopy 设计用来捕获的BUG。如果你这么操
作,Pandas将会警告你:
def do_something(df):
foo = df[['bar', 'baz']] # Is foo a view? A copy? Nobody knows!
# ... many lines here ...
foo['quux'] = value
# We don't know whether this will modify df or not!
return foo
计算顺序很重要
使用链式索引时,索引操作的顺序和类型会部分确定结果是原始对象的切片还是切片的副本。
Pandas有一个 SettingWithCopyWarning ,因为给一个切片的副本赋值经常不是故意的,但是链式索引引发的错误在本该返回切片的地方
返回了副本。
12.3
如果你希望Pandas或多或少信任链式索引的赋值,可以将 mode.chained_assignment 设置为如下值之一:
warn ,默认值,意味着将会打印 SettingWithCopyWarning 。
raise ,意味着Pandas将会引发一个必须处理的 SettingWithCopyException 。
None ,禁止警告。
In [343]: dfb = pd.DataFrame({'a' : ['one', 'one', 'two',
.....:
'three', 'two', 'one', 'six'],
.....:
'c' : np.arange(7)})
.....:
# This will show the SettingWithCopyWarning
# but the frame values will be set
In [344]: dfb['c'][dfb.a.str.startswith('o')] = 42
但是这是在副本上操作,并且不会见效
>>> pd.set_option('mode.chained_assignment','warn')
>>> dfb[dfb.a.str.startswith('o')]['c'] = 42
Traceback (most recent call last)
...
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
在混合dtype frame中也可能突然出现链式赋值。
注意:这些设置规则适用于 .loc/.iloc 。
这是正确的访问方法:
In [345]: dfc = pd.DataFrame({'A':['aaa','bbb','ccc'],'B':[1,2,3]})
In [346]: dfc.loc[0,'A'] = 11
In [347]: dfc
Out[347]:
A B
0
11 1
1 bbb 2
2 ccc 3
这有时会起作用,但不能保证,因此应当避免。
In [348]: dfc = dfc.copy()
In [349]: dfc['A'][0] = 111
In [350]: dfc
Out[350]:
A B
0 111 1
1 bbb 2
2 ccc 3
这根本不起作用,因此应当避免:
>>> pd.set_option('mode.chained_assignment','raise')
>>> dfc.loc[0]['A'] = 1111
Traceback (most recent call last)
...
SettingWithCopyException:
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_index,col_indexer] = value instead
警告:链式赋值警告或异常旨在提示用户可能无效的赋值。可能存在假阳性,意外报告链式赋值的情况。
第十三章 多重索引和高级索引
本章将介绍MultiIndex的索引方法以及一些高级的索引技巧。 你可以从索引和选择数据章节查看基本的索引方法。
警告:设置操作返回一个副本还是一个引用取决于不同的环境。有时这叫做链式赋值,应该尽量避免,查看返回视图或副本。
从cookbook查看高级策略。
分层索引(MultiIndex)
分层/分级索引令人兴奋,因为它为复杂的数据分析和操作,尤其是操作高维数据打开了一扇门。本质上,它令你可以在低纬数据结构(一维
的Series,二维的DataFrame)中存储和处理任意维度的数据。
13.1
本章,我们将展示什么是分层索引,以及如何将它与前面章节提到的索引方法相结合。稍后,当我们讨论分组、透视表和重塑数据时,我们将
展示重要的应用来说明它如何帮助结构化数据分析。
从cookbook查看高级策略。
创建MultiIndex(分层索引)对象
MultiIndex 对象是通常将轴标签存储在Pandas对象中的标准 Index 对象的分层模拟。你可以把 MultiIndex 想象成一个元组数组,其
中每个元组都是唯一的。 MultiIndex 可以用一组数组(使用 MultiIndex.from_arrays )、一组元组(使用
MultiIndex.from_tuples )或用一组相交的迭代器(使用 MultiIndex.from_product )来生成。当传入一个元组数组时, Index
构造器将尝试返回一个 MultiIndex 。下面的示例展示了初始化多重索引的的不同方法。
13.1.1
In [1]: arrays = [['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'],
...:
['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two']]
...:
In [2]: tuples = list(zip(*arrays))
In [3]: tuples
Out[3]:
[('bar', 'one'),
('bar', 'two'),
('baz', 'one'),
('baz', 'two'),
('foo', 'one'),
('foo', 'two'),
('qux', 'one'),
('qux', 'two')]
In [4]: index = pd.MultiIndex.from_tuples(tuples, names=['first', 'second'])
In [5]: index
Out[5]:
MultiIndex(levels=[['bar', 'baz', 'foo', 'qux'], ['one', 'two']],
labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
names=['first', 'second'])
In [6]: s = pd.Series(np.random.randn(8), index=index)
In [7]: s
Out[7]:
first second
bar
one
two
baz
one
two
foo
one
two
qux
one
two
dtype: float64
0.469112
‑0.282863
‑1.509059
‑1.135632
1.212112
‑0.173215
0.119209
‑1.044236
当你希望使用两个迭代器中的每一对元素时,使用MultiIndex.from product函数会更容易。
In [8]: iterables = [['bar', 'baz', 'foo', 'qux'], ['one', 'two']]
In [9]: pd.MultiIndex.from_product(iterables, names=['first', 'second'])
Out[9]:
MultiIndex(levels=[['bar', 'baz', 'foo', 'qux'], ['one', 'two']],
labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
names=['first', 'second'])
作为一种便利手段,你可以向一个Series或DataFrame中传入一个数组列表来自动构造多重索引。
In [10]: arrays = [np.array(['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux']),
....:
np.array(['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two'])]
....:
In [11]: s = pd.Series(np.random.randn(8), index=arrays)
In [12]: s
Out[12]:
bar one
‑0.861849
two
‑2.104569
baz one
‑0.494929
two
1.071804
foo one
0.721555
two
‑0.706771
qux one
‑1.039575
two
0.271860
dtype: float64
In [13]: df = pd.DataFrame(np.random.randn(8, 4), index=arrays)
In [14]: df
Out[14]:
0
1
2
3
bar one ‑0.424972 0.567020 0.276232 ‑1.087401
two ‑0.673690 0.113648 ‑1.478427 0.524988
baz one 0.404705 0.577046 ‑1.715002 ‑1.039268
two ‑0.370647 ‑1.157892 ‑1.344312 0.844885
foo one 1.075770 ‑0.109050 1.643563 ‑1.469388
two 0.357021 ‑0.674600 ‑1.776904 ‑0.968914
qux one ‑1.294524 0.413738 0.276662 ‑0.472035
two ‑0.013960 ‑0.362543 ‑0.006154 ‑0.923061
所有的 MultiIndex 构造器都接受一个 names 参数,用来保存索引中每一层的字符串名称。如果不传入 names ,将被赋值为None。
In [15]: df.index.names
Out[15]: FrozenList([None, None])
索引支持pandas对象的任何轴,索引的级别由你来定。
In [16]: df = pd.DataFrame(np.random.randn(3, 8), index=['A', 'B', 'C'], columns=index)
In [17]: df
Out[17]:
first
bar
second
one
A
0.895717
B
0.410835
C
‑1.413681
baz
foo
qux
two
one
two
one
two
one
two
0.805244 ‑1.206412 2.565646 1.431256 1.340309 ‑1.170299 ‑0.226169
0.813850 0.132003 ‑0.827317 ‑0.076467 ‑1.187678 1.130127 ‑1.436737
1.607920 1.024180 0.569605 0.875906 ‑2.211372 0.974466 ‑2.006747
In [18]: pd.DataFrame(np.random.randn(6, 6), index=index[:6], columns=index[:6])
Out[18]:
first
bar
baz
foo
second
one
two
one
two
one
two
first second
bar
one
‑0.410001 ‑0.078638 0.545952 ‑1.219217 ‑1.226825 0.769804
two
‑1.281247 ‑0.727707 ‑0.121306 ‑0.097883 0.695775 0.341734
baz
one
0.959726 ‑1.110336 ‑0.619976 0.149748 ‑0.732339 0.687738
two
0.176444 0.403310 ‑0.154951 0.301624 ‑2.179861 ‑1.369849
foo
one
‑0.954208 1.462696 ‑1.743161 ‑0.826591 ‑0.345352 1.314232
two
0.690579 0.995761 2.396780 0.014871 3.357427 ‑0.317441
为了看起来舒服,我们把高级的索引进行了稀疏化。索引如何显示可以通过 pandas.set_options() 设置 multi_sparse 来控制:
In [19]: with pd.option_context('display.multi_sparse', False):
....:
df
....:
值得记住的是,没有什么能阻止使用元组作为某个轴上的原子标签:
In [20]: pd.Series(np.random.randn(8), index=tuples)
Out[20]:
(bar, one)
‑1.236269
(bar, two)
0.896171
(baz, one)
‑0.487602
(baz, two)
‑0.082240
(foo, one)
‑2.182937
(foo, two)
0.380396
(qux, one)
0.084844
(qux, two)
0.432390
dtype: float64
之所以重要,是因为它允许我们进行分组、选择和重塑操作(在本章的下面以及文档的后续部分会介绍)。在后面的部分能够
看到,你可以使用多层索引而不必显式地创建它。不过,当从一个文件导入数据时,你可能希望生成自己的 MultiIndex 。
MultiIndex
13.1.2
重构标签级别
get_level_values
返回特定级别的每个位置上标签向量。
In [21]: index.get_level_values(0)
Out[21]: Index(['bar', 'bar', 'baz', 'baz', 'foo', 'foo', 'qux', 'qux'], dtype='object', name='fi
rst')
In [22]: index.get_level_values('second')
Out[22]: Index(['one', 'two', 'one', 'two', 'one', 'two', 'one', 'two'], dtype='object', name='se
cond')
基本索引方法
分层索引的一个重要特性是,你可以通过选择能够标识数据的子组的局部标签来选择数据。 局部选择 以完全类似于在常规DataFrame中选择
一列的方式降低结果中的分层索引的级别。
13.1.3 MultiIndex
In [23]: df['bar']
Out[23]:
second
one
two
A
0.895717 0.805244
B
0.410835 0.813850
C
‑1.413681 1.607920
In [24]: df['bar', 'one']
Out[24]:
A
0.895717
B
0.410835
C
‑1.413681
Name: (bar, one), dtype: float64
In [25]: df['bar']['one']
Out[25]:
A
0.895717
B
0.410835
C
‑1.413681
Name: one, dtype: float64
In [26]: s['qux']
Out[26]:
one
‑1.039575
two
0.271860
dtype: float64
通过分层索引界面来查看如何在深层级别选择数据。
定义(索引)级别
MultiIndex 的数据类型解释展示了一个索引上所有被定义了的级别,即使它们没有被使用。当对索引切片时,你会注意到这些。例如:
13.1.4
In [27]: df.columns # original MultiIndex
Out[27]:
MultiIndex(levels=[['bar', 'baz', 'foo', 'qux'], ['one', 'two']],
labels=[[0, 0, 1, 1, 2, 2, 3, 3], [0, 1, 0, 1, 0, 1, 0, 1]],
names=['first', 'second'])
In [28]: df[['foo','qux']].columns # sliced
Out[28]:
MultiIndex(levels=[['bar', 'baz', 'foo', 'qux'], ['one', 'two']],
labels=[[2, 2, 3, 3], [0, 1, 0, 1]],
names=['first', 'second'])
这么做避免了对索引级别的重新计算,从而提高了切片的性能。如果你只想看到被使用的(索引)级别,可以使用
MultiIndex.get_level_values() 方法查看。
In [29]: df[['foo','qux']].columns.values
Out[29]: array([('foo', 'one'), ('foo', 'two'), ('qux', 'one'), ('qux', 'two')], dtype=object)
# for a specific level
In [30]: df[['foo','qux']].columns.get_level_values(0)
Out[30]: Index(['foo', 'foo', 'qux', 'qux'], dtype='object', name='first')
如果想用被使用的索引级别重构 MultiIndex ,可以用 remove_unused_level 方法。
0.20.0版新增。
pyhton
In [31]: df[['foo','qux']].columns.remove_unused_levels()
Out[31]:
MultiIndex(levels=[['foo', 'qux'], ['one', 'two']],
labels=[[0, 0, 1, 1], [0, 1, 0, 1]],
names=['first', 'second'])
数据对齐,使用 reindex
轴上有多个索引的不同被索引对象之间的操作将像你期望的那样,数据对齐将像元组索引那样工作。
13.1.5
In [32]: s + s[:‑2]
Out[32]:
bar one
‑1.723698
two
‑4.209138
baz one
‑0.989859
two
2.143608
foo one
1.443110
two
‑1.413542
qux one
NaN
two
NaN
dtype: float64
In [33]: s + s[::2]
Out[33]:
bar one
‑1.723698
two
NaN
baz one
‑0.989859
two
NaN
foo one
1.443110
two
NaN
qux one
‑2.079150
two
NaN
dtype: float64
reindex
可以使用另一个 MultiIndex 来调用,或者甚至是使用一个列表、数组或元组:
In [34]: s.reindex(index[:3])
Out[34]:
first second
bar
one
‑0.861849
two
‑2.104569
baz
one
‑0.494929
dtype: float64
In [35]: s.reindex([('foo', 'two'), ('bar', 'one'), ('qux', 'one'), ('baz', 'one')])
Out[35]:
foo two
‑0.706771
bar one
‑0.861849
qux one
‑1.039575
baz one
‑0.494929
dtype: float64
分层索引的高级用法
在高级索引上对 MultiIndex 进行 .loc 语法整合是一个挑战。不过,我们做了所有的努力。通常,MultiIndex键采用元组的方式。例如,
下面的示例会像你所想象的那样工作:
13.2
In [36]: df = df.T
In [37]: df
Out[37]:
A
B
C
first second
bar
one
0.895717 0.410835 ‑1.413681
two
0.805244 0.813850 1.607920
baz
one
‑1.206412 0.132003 1.024180
two
2.565646 ‑0.827317 0.569605
foo
one
1.431256 ‑0.076467 0.875906
two
1.340309 ‑1.187678 ‑2.211372
qux
one
‑1.170299 1.130127 0.974466
two
‑0.226169 ‑1.436737 ‑2.006747
In [38]: df.loc[('bar', 'two'),]
Out[38]:
A
0.805244
B
0.813850
C
1.607920
Name: (bar, two), dtype: float64
注意,在上面的示例中, df.loc['bar', 'two'] 一样可以工作,但是这种简写容易引起歧义。
如果你想用 .loc 来索引一列,必须像下面这样使用:
In [39]: df.loc[('bar', 'two'), 'A']
Out[39]: 0.80524402538637851
你不用通过只想传递元组的第一个元素来指定 MultiIndex 的所有级别。例如,你可以用 df.loc['bar'] 来得到 bar 这一列第一个级别
的元素。
这是 df.loc[('bar',),] 的一种简写(在这个示例中等价于 df.loc['bar',] )
"局部"切片也可以优雅的工作:
In [40]: df.loc['baz':'foo']
Out[40]:
A
B
C
first second
baz
one
‑1.206412 0.132003 1.024180
two
2.565646 ‑0.827317 0.569605
foo
one
1.431256 ‑0.076467 0.875906
two
1.340309 ‑1.187678 ‑2.211372
你可以通过传入一个元组切片来对一个数据范围进行切片:
In [41]: df.loc[('baz', 'two'):('qux', 'one')]
Out[41]:
A
B
C
first second
baz
two
2.565646 ‑0.827317 0.569605
foo
one
1.431256 ‑0.076467 0.875906
two
1.340309 ‑1.187678 ‑2.211372
qux
one
‑1.170299 1.130127 0.974466
In [42]: df.loc[('baz', 'two'):'foo']
Out[42]:
A
B
C
first second
baz
two
2.565646 ‑0.827317 0.569605
foo
one
1.431256 ‑0.076467 0.875906
two
1.340309 ‑1.187678 ‑2.211372
传入标签列表或元组会像重建索引那样工作:
In [43]: df.loc[[('bar', 'two'), ('qux', 'one')]]
Out[43]:
A
B
C
first second
bar
two
0.805244 0.813850 1.607920
qux
one
‑1.170299 1.130127 0.974466
注意:在设计索引时,元组和列表在Pandas中没有被同等对待。元组被解释为一个多级别键,而列表用于指定多个键。或者
说,元组是水平的(遍历级别),列表是垂直的(扫描级别)。
重要的,元组的列表索引一些完整的 MultiIndex 键,因为元组的列表指代级别中的一些值:
In [44]: s = pd.Series([1, 2, 3, 4, 5, 6],
....:
index=pd.MultiIndex.from_product([["A", "B"], ["c", "d", "e"]]))
....:
In [45]: s.loc[[("A", "c"), ("B", "d")]]
Out[45]:
A c
1
B d
5
dtype: int64
# list of tuples
In [46]: s.loc[(["A", "B"], ["c", "d"])]
Out[46]:
A c
1
d
2
B c
4
d
5
dtype: int64
# tuple of lists
使用切片器
你可以通过提供多个索引器来对 MultiIndex 切片。
你可以提供任何的选择器,就像根据标签进行索引一样,查看根据标签选择数据,包括切片、标签列表、标签和布尔索引器。
你可以使用 slice(None) 来选择某个级别的所有内容,不必指定所有深层级别,它们将被暗示为 slice(None) 。
通常的,切片器的两边都包含在内,因为这是标签索引。
13.2.1
警告:你应该在 .loc 符号中指定所有轴,表示索引器的行和列。 在一些不明确的情况下,传递的索引器可能被错误地解释为
对两个轴都进行索引,而不是对行进行多索引 。
你应该这么用:
df.loc[(slice('A1','A3'),.....), :]
而不是这样:
df.loc[(slice('A1','A3'),.....)]
In [47]: def mklbl(prefix,n):
....:
return ["%s%s" % (prefix,i)
....:
for i in range(n)]
In [48]: miindex = pd.MultiIndex.from_product([mklbl('A',4),
....:
mklbl('B',2),
....:
mklbl('C',4),
....:
mklbl('D',2)])
....:
In [49]: micolumns = pd.MultiIndex.from_tuples([('a','foo'),('a','bar'),
....:
('b','foo'),('b','bah')],
....:
names=['lvl0', 'lvl1'])
....:
In [50]: dfmi = pd.DataFrame(np.arange(len(miindex)*len(micolumns)).reshape((len(miindex),len(mic
olumns))),
....:
index=miindex,
....:
columns=micolumns).sort_index().sort_index(axis=1)
....:
In [51]: dfmi
Out[51]:
lvl0
a
lvl1
bar
A0 B0 C0 D0
1
D1
5
C1 D0
9
D1
13
C2 D0
17
D1
21
C3 D0
25
...
...
A3 B1 C0 D1 229
C1 D0 233
D1 237
C2 D0 241
D1 245
C3 D0 249
D1 253
b
bah
3
7
11
15
19
23
27
...
231
235
239
243
247
251
255
foo
0
4
8
12
16
20
24
...
228
232
236
240
244
248
252
foo
2
6
10
14
18
22
26
...
230
234
238
242
246
250
254
[64 rows x 4 columns]
基本的使用切片、列表和标签进行多索引切片:
pyhton
In [52]: dfmi.loc[(slice('A1','A3'), slice(None), ['C1', 'C3']), :]
Out[52]:
lvl0
a
b
lvl1
bar foo bah foo
A1 B0 C1 D0
73
72
75
74
D1
77
76
79
78
C3 D0
89
88
91
90
D1
93
92
95
94
B1 C1 D0 105 104 107 106
D1 109 108 111 110
C3 D0 121 120 123 122
...
... ... ... ...
A3 B0 C1 D1 205 204 207 206
C3 D0 217 216 219 218
D1 221 220 223 222
B1 C1 D0 233 232 235 234
D1 237 236 239 238
C3 D0 249 248 251 250
D1 253 252 255 254
[24 rows x 4 columns]
你可以使用pandas.IndexSlice来使用 : 而不是 slice(None) 获得一种更自然便捷的语法:
In [53]: idx = pd.IndexSlice
In [54]: dfmi.loc[idx[:, :, ['C1', 'C3']], idx[:, 'foo']]
Out[54]:
lvl0
a
b
lvl1
foo foo
A0 B0 C1 D0
8
10
D1
12
14
C3 D0
24
26
D1
28
30
B1 C1 D0
40
42
D1
44
46
C3 D0
56
58
...
... ...
A3 B0 C1 D1 204 206
C3 D0 216 218
D1 220 222
B1 C1 D0 232 234
D1 236 238
C3 D0 248 250
D1 252 254
[32 rows x 2 columns]
使用这个方法可以同时在多个轴上进行非常复杂的选择:
In [55]: dfmi.loc['A1', (slice(None), 'foo')]
Out[55]:
lvl0
a
b
lvl1
foo foo
B0 C0 D0
64
66
D1
68
70
C1 D0
72
74
D1
76
78
C2 D0
80
82
D1
84
86
C3 D0
88
90
...
... ...
B1 C0 D1 100 102
C1 D0 104 106
D1 108 110
C2 D0 112 114
D1 116 118
C3 D0 120 122
D1 124 126
[16 rows x 2 columns]
In [56]: dfmi.loc[idx[:, :, ['C1', 'C3']], idx[:, 'foo']]
Out[56]:
lvl0
a
b
lvl1
foo foo
A0 B0 C1 D0
8
10
D1
12
14
C3 D0
24
26
D1
28
30
B1 C1 D0
40
42
D1
44
46
C3 D0
56
58
...
... ...
A3 B0 C1 D1 204 206
C3 D0 216 218
D1 220 222
B1 C1 D0 232 234
D1 236 238
C3 D0 248 250
D1 252 254
[32 rows x 2 columns]
使用布尔索引器,你可以提供与值相关的选择:
In [57]: mask = dfmi[('a', 'foo')] > 200
In [58]: dfmi.loc[idx[mask, :, ['C1', 'C3']], idx[:, 'foo']]
Out[58]:
lvl0
a
b
lvl1
foo foo
A3 B0 C1 D1 204 206
C3 D0 216 218
D1 220 222
B1 C1 D0 232 234
D1 236 238
C3 D0 248 250
D1 252 254
你还可以指定 .loc 的 axis 参数来解释单个轴上传递的切片器 :
In [59]: dfmi.loc(axis=0)[:, :, ['C1', 'C3']]
Out[59]:
lvl0
a
b
lvl1
bar foo bah foo
A0 B0 C1 D0
9
8
11
10
D1
13
12
15
14
C3 D0
25
24
27
26
D1
29
28
31
30
B1 C1 D0
41
40
43
42
D1
45
44
47
46
C3 D0
57
56
59
58
...
... ... ... ...
A3 B0 C1 D1 205 204 207 206
C3 D0 217 216 219 218
D1 221 220 223 222
B1 C1 D0 233 232 235 234
D1 237 236 239 238
C3 D0 249 248 251 250
D1 253 252 255 254
[32 rows x 4 columns]
另外,你可以用下面的方法设置数值:
In [60]: df2 = dfmi.copy()
In [61]: df2.loc(axis=0)[:, :, ['C1', 'C3']] = ‑10
In [62]: df2
Out[62]:
lvl0
a
lvl1
bar
A0 B0 C0 D0
1
D1
5
C1 D0 ‑10
D1 ‑10
C2 D0
17
D1
21
C3 D0 ‑10
...
...
A3 B1 C0 D1 229
C1 D0 ‑10
D1 ‑10
C2 D0 241
D1 245
C3 D0 ‑10
D1 ‑10
b
bah
3
7
‑10
‑10
19
23
‑10
...
231
‑10
‑10
243
247
‑10
‑10
foo
0
4
‑10
‑10
16
20
‑10
...
228
‑10
‑10
240
244
‑10
‑10
foo
2
6
‑10
‑10
18
22
‑10
...
230
‑10
‑10
242
246
‑10
‑10
[64 rows x 4 columns]
你也可以使用一个对齐的对象的右侧(来设置数值):
In [63]: df2 = dfmi.copy()
In [64]: df2.loc[idx[:, :, ['C1', 'C3']], :] = df2 * 1000
In [65]: df2
Out[65]:
lvl0
a
lvl1
bar
A0 B0 C0 D0
1
D1
5
C1 D0
9000
D1
13000
C2 D0
17
D1
21
C3 D0
25000
...
...
A3 B1 C0 D1
229
C1 D0 233000
D1 237000
C2 D0
241
D1
245
C3 D0 249000
D1 253000
foo
0
4
8000
12000
16
20
24000
...
228
232000
236000
240
244
248000
252000
b
bah
3
7
11000
15000
19
23
27000
...
231
235000
239000
243
247
251000
255000
foo
2
6
10000
14000
18
22
26000
...
230
234000
238000
242
246
250000
254000
[64 rows x 4 columns]
13.2.2
截面
DataFrame
的 xs 方法带有一个 level 参数,以使得在 MultiIndex 的特定级别上选择数据更容易:
In [66]: df
Out[66]:
A
B
C
first second
bar
one
0.895717 0.410835 ‑1.413681
two
0.805244 0.813850 1.607920
baz
one
‑1.206412 0.132003 1.024180
two
2.565646 ‑0.827317 0.569605
foo
one
1.431256 ‑0.076467 0.875906
two
1.340309 ‑1.187678 ‑2.211372
qux
one
‑1.170299 1.130127 0.974466
two
‑0.226169 ‑1.436737 ‑2.006747
In [67]: df.xs('one', level='second')
Out[67]:
A
B
C
first
bar
0.895717 0.410835 ‑1.413681
baz
‑1.206412 0.132003 1.024180
foo
1.431256 ‑0.076467 0.875906
qux
‑1.170299 1.130127 0.974466
# using the slicers
In [68]: df.loc[(slice(None),'one'),:]
Out[68]:
A
B
C
first second
bar
one
0.895717 0.410835 ‑1.413681
baz
one
‑1.206412 0.132003 1.024180
foo
one
1.431256 ‑0.076467 0.875906
qux
one
‑1.170299 1.130127 0.974466
你有可以通过传入 axis 参数,用 xs() 来列上进行选择:
In [69]: df = df.T
In [70]: df.xs('one', level='second', axis=1)
Out[70]:
first
bar
baz
foo
qux
A
0.895717 ‑1.206412 1.431256 ‑1.170299
B
0.410835 0.132003 ‑0.076467 1.130127
C
‑1.413681 1.024180 0.875906 0.974466
# using the slicers
In [71]: df.loc[:,(slice(None),'one')]
Out[71]:
first
bar
baz
foo
qux
second
one
one
one
one
A
0.895717 ‑1.206412 1.431256 ‑1.170299
B
0.410835 0.132003 ‑0.076467 1.130127
C
‑1.413681 1.024180 0.875906 0.974466
xs()
也允许用多个键进行选择:
In [72]: df.xs(('one', 'bar'), level=('second', 'first'), axis=1)
Out[72]:
first
bar
second
one
A
0.895717
B
0.410835
C
‑1.413681
# using the slicers
In [73]: df.loc[:,('bar','one')]
Out[73]:
A
0.895717
B
0.410835
C
‑1.413681
Name: (bar, one), dtype: float64
传入 drop_level=False 来保留被选择的级别:
In [74]: df.xs('one', level='second', axis=1, drop_level=False)
Out[74]:
first
bar
baz
foo
qux
second
one
one
one
one
A
0.895717 ‑1.206412 1.431256 ‑1.170299
B
0.410835 0.132003 ‑0.076467 1.130127
C
‑1.413681 1.024180 0.875906 0.974466
对比 drop_level=True 的情况:
In [75]: df.xs('one', level='second', axis=1, drop_level=True)
Out[75]:
first
bar
baz
foo
qux
A
0.895717 ‑1.206412 1.431256 ‑1.170299
B
0.410835 0.132003 ‑0.076467 1.130127
C
‑1.413681 1.024180 0.875906 0.974466
重建索引和数据对齐的高级方法
reindex 和 align 方法添加了 level 参数,这对于在一个级别上广播数据很有用。下面一个示例:
13.2.3
In [76]: midx = pd.MultiIndex(levels=[['zero', 'one'], ['x','y']],
....:
labels=[[1,1,0,0],[1,0,1,0]])
....:
In [77]: df = pd.DataFrame(np.random.randn(4,2), index=midx)
In [78]: df
Out[78]:
0
1
1.519970 ‑0.493662
0.600178 0.274230
0.132885 ‑0.023688
2.410179 1.450520
one
y
x
zero y
x
In [79]: df2 = df.mean(level=0)
In [80]: df2
Out[80]:
one
zero
0
1
1.060074 ‑0.109716
1.271532 0.713416
In [81]: df2.reindex(df.index, level=0)
Out[81]:
0
1
one y 1.060074 ‑0.109716
x 1.060074 ‑0.109716
zero y 1.271532 0.713416
x 1.271532 0.713416
# aligning
In [82]: df_aligned, df2_aligned = df.align(df2, level=0)
In [83]: df_aligned
Out[83]:
0
1
one y 1.519970 ‑0.493662
x 0.600178 0.274230
zero y 0.132885 ‑0.023688
x 2.410179 1.450520
In [84]: df2_aligned
Out[84]:
0
1
one y 1.060074 ‑0.109716
x 1.060074 ‑0.109716
zero y 1.271532 0.713416
x 1.271532 0.713416
13.2.4
使用 swaplevel﴾﴿ 交换索引级别
swaplevel
函数可以切换两个级别的顺序:
In [85]: df[:5]
Out[85]:
0
1
1.519970 ‑0.493662
0.600178 0.274230
0.132885 ‑0.023688
2.410179 1.450520
one
y
x
zero y
x
In [86]: df[:5].swaplevel(0, 1, axis=0)
Out[86]:
0
1
y one
1.519970 ‑0.493662
x one
0.600178 0.274230
y zero 0.132885 ‑0.023688
x zero 2.410179 1.450520
使用 reorder_levels﴾﴿ 对索引级别重新排序
reorder_levels 函数泛化了 swaplevel 函数,允许你一次性排列索引层级:
13.2.4
In [87]: df[:5].reorder_levels([1,0], axis=0)
Out[87]:
0
1
y one
1.519970 ‑0.493662
x one
0.600178 0.274230
y zero 0.132885 ‑0.023688
x zero 2.410179 1.450520
排序
为了让有 MultiIndex 的对象能够高效的索引和切片, MultiIndex 需要排序。与任何索引一样,你可以使用 sort_index :
13.3 MultiIndex
In [88]: import random; random.shuffle(tuples)
In [89]: s = pd.Series(np.random.randn(8), index=pd.MultiIndex.from_tuples(tuples))
In [90]: s
Out[90]:
qux two
0.206053
bar one
‑0.251905
foo one
‑2.213588
baz one
1.063327
qux one
1.266143
baz two
0.299368
foo two
‑0.863838
bar two
0.408204
dtype: float64
In [91]: s.sort_index()
Out[91]:
bar one
‑0.251905
two
0.408204
baz one
1.063327
two
0.299368
foo one
‑2.213588
two
‑0.863838
qux one
1.266143
two
0.206053
dtype: float64
In [92]: s.sort_index(level=0)
Out[92]:
bar one
‑0.251905
two
0.408204
baz one
1.063327
two
0.299368
foo one
‑2.213588
two
‑0.863838
qux one
1.266143
two
0.206053
dtype: float64
In [93]: s.sort_index(level=1)
Out[93]:
bar one
‑0.251905
baz one
1.063327
foo one
‑2.213588
qux one
1.266143
bar two
0.408204
baz two
0.299368
foo two
‑0.863838
qux two
0.206053
dtype: float64
如果 MultiIndex 级别被命名,可以传入到 sort_index 中:
In [94]: s.index.set_names(['L1', 'L2'], inplace=True)
In [95]: s.sort_index(level='L1')
Out[95]:
L1
L2
bar one
‑0.251905
two
0.408204
baz one
1.063327
two
0.299368
foo one
‑2.213588
two
‑0.863838
qux one
1.266143
two
0.206053
dtype: float64
In [96]: s.sort_index(level='L2')
Out[96]:
L1
L2
bar one
‑0.251905
baz one
1.063327
foo one
‑2.213588
qux one
1.266143
bar two
0.408204
baz two
0.299368
foo two
‑0.863838
qux two
0.206053
dtype: float64
在高维对象中,如果轴有多重索引,你可以通过级别排序他们。
In [97]: df.T.sort_index(level=1, axis=1)
Out[97]:
one
zero
one
zero
x
x
y
y
0 0.600178 2.410179 1.519970 0.132885
1 0.274230 1.450520 ‑0.493662 ‑0.023688
即使数据没有排序,索引也能工作,不过效率不高(会抛出一个 PerformanceWarning )。这种操作会返回数据的副本,而不是视图。
In [98]: dfm = pd.DataFrame({'jim': [0, 0, 1, 1],
....:
'joe': ['x', 'x', 'z', 'y'],
....:
'jolie': np.random.rand(4)})
....:
In [99]: dfm = dfm.set_index(['jim', 'joe'])
In [100]: dfm
Out[100]:
jolie
jim joe
0
x
0.490671
x
0.120248
1
z
0.537020
y
0.110968
In [4]: dfm.loc[(1, 'z')]
PerformanceWarning: indexing past lexsort depth may impact performance.
Out[4]:
jolie
jim joe
1
z
0.64094
另外,如果尝试索引没有按词法排序的内容,会引发如下的错误:
In [5]: dfm.loc[(0,'y'):(1, 'z')]
UnsortedIndexError: 'Key length (2) was greater than MultiIndex lexsort depth (1)'
Index
的 is_lexsorted 方法会返回该 Index 是否已经排序, lexsort_depth 属性返回排序的深度:
In [101]: dfm.index.is_lexsorted()
Out[101]: False
In [102]: dfm.index.lexsort_depth
Out[102]: 1
In [103]: dfm = dfm.sort_index()
In [104]: dfm
Out[104]:
jolie
jim joe
0
x
0.490671
x
0.120248
1
y
0.110968
z
0.537020
In [105]: dfm.index.is_lexsorted()
Out[105]: True
In [106]: dfm.index.lexsort_depth
Out[106]: 2
现在,选择操作按照预期执行:
In [107]: dfm.loc[(0,'y'):(1, 'z')]
Out[107]:
jolie
jim joe
1
y
0.110968
z
0.537020
方法
与Numpy的ndarray类似,Pandas的Index、Series和DataFrame也提供了 take 方法,在给定的索引上演着给定的轴检索元素。给定的索引必
须是一个由整数位置组成的列表或ndarray。 take 也接受负整数,表示从索引对象的结尾向前数的位置。
13.4 take
In [108]: index = pd.Index(np.random.randint(0, 1000, 10))
In [109]: index
Out[109]: Int64Index([214, 502, 712, 567, 786, 175, 993, 133, 758, 329], dtype='int64')
In [110]: positions = [0, 9, 3]
In [111]: index[positions]
Out[111]: Int64Index([214, 329, 567], dtype='int64')
In [112]: index.take(positions)
Out[112]: Int64Index([214, 329, 567], dtype='int64')
In [113]: ser = pd.Series(np.random.randn(10))
In [114]: ser.iloc[positions]
Out[114]:
0
‑0.179666
9
1.824375
3
0.392149
dtype: float64
In [115]: ser.take(positions)
Out[115]:
0
‑0.179666
9
1.824375
3
0.392149
dtype: float64
对于DataFrame,给定的索引应该是一个1维的列表或ndarray,指定行列位置:
In [116]: frm = pd.DataFrame(np.random.randn(5, 3))
In [117]: frm.take([1, 4, 3])
Out[117]:
0
1
2
1 ‑1.237881 0.106854 ‑1.276829
4 0.629675 ‑1.425966 1.857704
3 0.979542 ‑1.633678 0.615855
In [118]: frm.take([0, 2], axis=1)
Out[118]:
0
2
0 0.595974 0.601544
1 ‑1.237881 ‑1.276829
2 ‑0.767101 1.499591
3 0.979542 0.615855
4 0.629675 1.857704
特别需要注意,pandas对象的 take 方法没有计划设计在布尔索引上工作,可能会返回意想不到的结果:
In [119]: arr = np.random.randn(10)
In [120]: arr.take([False, False, True, True])
Out[120]: array([‑1.1935, ‑1.1935, 0.6775, 0.6775])
In [121]: arr[[0, 1]]
Out[121]: array([‑1.1935,
0.6775])
In [122]: ser = pd.Series(np.random.randn(10))
In [123]: ser.take([False, False, True, True])
Out[123]:
0
0.233141
0
0.233141
1
‑0.223540
1
‑0.223540
dtype: float64
In [124]: ser.iloc[[0, 1]]
Out[124]:
0
0.233141
1
‑0.223540
dtype: float64
最后,关于性能的一个小提示是,因为 take 方法处理的输入范围很窄,所以它可以提供比花式索引更好的性能。
索引类型
我们在前面几节中已经非常广泛地讨论了 MultiIndex 。这里展示的是 DatetimeIndex 、 PeriodIndex 和 TimedeltaIndex 。
下面的小节中,我们将重点介绍其他一些索引类型。
13.5
13.5.1
类别索引
CategoricalIndex
索引和存储。
是一种支持索引重复值的有用的方法。这是一个 Categorical 的容器, 允许对包含大量重复元素的索引进行有效的
In [125]: from pandas.api.types import CategoricalDtype
In [126]: df = pd.DataFrame({'A': np.arange(6),
.....:
'B': list('aabbca')})
.....:
In [127]: df['B'] = df['B'].astype(CategoricalDtype(list('cab')))
In [128]: df
Out[128]:
A B
0 0 a
1 1 a
2 2 b
3 3 b
4 4 c
5 5 a
In [129]: df.dtypes
Out[129]:
A
int64
B
category
dtype: object
In [130]: df.B.cat.categories
Out[130]: Index(['c', 'a', 'b'], dtype='object')
设置索引将会创建一个 CategoricalIndex
In [131]: df2 = df.set_index('B')
In [132]: df2.index
Out[132]: CategoricalIndex(['a', 'a', 'b', 'b', 'c', 'a'], categories=['c', 'a', 'b'], ordered=Fa
lse, name='B', dtype='category')
使用 __getitem__ 、 .iloc 、 .loc 进行索引的工作方式与具有重复项的 Index 类似。 索引器必须在类别中,否则操作将引发
KeyError 。
In [133]: df2.loc['a']
Out[133]:
A
B
a 0
a 1
a 5
索引之后 CategoricalIndex 会被保留
In [134]: df2.loc['a'].index
Out[134]: CategoricalIndex(['a', 'a', 'a'], categories=['c', 'a', 'b'], ordered=False, name='B',
dtype='category')
对索引排序将按照类别的顺序进行(回想一下,我们用 CategoricalDtype(list('cab')) 创建索引,因此排序后为 cab )
In [135]: df2.sort_index()
Out[135]:
A
B
c 4
a 0
a 1
a 5
b 2
b 3
对索引的Groupby操作也将保留索引的性质.
In [136]: df2.groupby(level=0).sum()
Out[136]:
A
B
c 4
a 6
b 5
In [137]: df2.groupby(level=0).sum().index
Out[137]: CategoricalIndex(['c', 'a', 'b'], categories=['c', 'a', 'b'], ordered=False, name='B',
dtype='category')
重新索引操作将根据传递的索引器的类型返回结果索引 。传入一个列表将会返回一个普通的旧索引; 使用 Categorical 索引将返回一个
CategoricalIndex ,根据传入的 Categorical 的dtype进行索引 。这允许你使用不在类别中的值任意索引它们,类似于重新索引任何
pandas索引。
In [138]: df2.reindex(['a','e'])
Out[138]:
A
B
a 0.0
a 1.0
a 5.0
e NaN
In [139]: df2.reindex(['a','e']).index
Out[139]: Index(['a', 'a', 'a', 'e'], dtype='object', name='B')
In [140]: df2.reindex(pd.Categorical(['a','e'],categories=list('abcde')))
Out[140]:
A
B
a 0.0
a 1.0
a 5.0
e NaN
In [141]: df2.reindex(pd.Categorical(['a','e'],categories=list('abcde'))).index
Out[141]: CategoricalIndex(['a', 'a', 'a', 'e'], categories=['a', 'b', 'c', 'd', 'e'], ordered=Fa
lse, name='B', dtype='category')
警告: 对 CategoricalIndex 的变形和比较操作必须具有相同的类别,否则将引发 TypeError 。
In [9]: df3 = pd.DataFrame({'A' : np.arange(6),
'B' : pd.Series(list('aabbca')).astype('category')})
In [11]: df3 = df3.set_index('B')
In [11]: df3.index
Out[11]: CategoricalIndex([u'a', u'a', u'b', u'b', u'c', u'a'], categories=[u'a', u
'b', u'c'], ordered=False, name=u'B', dtype='category')
In [12]: pd.concat([df2, df3]
TypeError: categories must match existing categories when appending
13.5.2 Int64Index
和 RangeIndex
警告: 在0.18.0版中已经阐明了使用浮点数对基于整数的索引进行索引,有关该更改的摘要,请参照此处。
是pandas中的基础索引。这是一个实现了排序、可切片的集合的可变数组。在0.18.0版本之前, Int64Index 将为所有
对象提供默认索引。
RangeIndex 是0.18.0版本添加的 Int64Index 的子类,现在为所有 NDFrame 对象提供默认索引。 RangeIndex 是 Int64Index 的一
个优化版本,它可以表示一个单调有序的集合。这类似于Python的[range类型] (https://docs.python.org/3/library/stdtypes.html#typesseqrange)。
Int64Index
NDFrame
13.5.3 FloatIndex
默认情况下,在创建索引时传递浮点值或混合整数-浮点值时将自动创建一个 Float64Index ,这使得纯粹的基于标签的切片范式在使用
[] 、 ix 和 loc 进行标量索引和切片时工作一样。
In [142]: indexf = pd.Index([1.5, 2, 3, 4.5, 5])
In [143]: indexf
Out[143]: Float64Index([1.5, 2.0, 3.0, 4.5, 5.0], dtype='float64')
In [144]: sf = pd.Series(range(5), index=indexf)
In [145]: sf
Out[145]:
1.5
0
2.0
1
3.0
2
4.5
3
5.0
4
dtype: int64
和 .loc 的的标量选择将会一直基于标签。整型索引会匹配相等的浮点型索引(例如: 3 等价于 3.0 )。
[] j
In [146]: sf[3]
Out[146]: 2
In [147]: sf[3.0]
Out[147]: 2
In [148]: sf.loc[3]
Out[148]: 2
In [149]: sf.loc[3.0]
Out[149]: 2
唯一的位置索引方式是通过 iloc
In [150]: sf.iloc[3]
Out[150]: 3
当使用 [] 、 ix 、 loc 时,切片主要针对索引的值,而当使用 iloc 时,切片总是基于位置的。当切片是布尔值是会有例外,这种情况下
切片总是基于位置的。
In [151]: sf[2:4]
Out[151]:
2.0
1
3.0
2
dtype: int64
In [152]: sf.loc[2:4]
Out[152]:
2.0
1
3.0
2
dtype: int64
In [153]: sf.iloc[2:4]
Out[153]:
3.0
2
4.5
3
dtype: int64
在float索引中,允许使用float进行切片
In [154]: sf[2.1:4.6]
Out[154]:
3.0
2
4.5
3
dtype: int64
In [155]: sf.loc[2.1:4.6]
Out[155]:
3.0
2
4.5
3
dtype: int64
在非float索引中,使用float进行切片会触发 TypeError
In [1]: pd.Series(range(5))[3.5]
TypeError: the label [3.5] is not a proper indexer for this index type (Int64Index)
In [1]: pd.Series(range(5))[3.5:4.5]
TypeError: the slice start [3.5] is not a proper indexer for this index type (Int64Index)
警告: 在0.18.0版本中已经删除了使用 .iloc 的标量浮点值索引器,因此下面的代码将引发一个 TypeError
In [3]: pd.Series(range(5)).iloc[3.0]
TypeError: cannot do positional indexing on <class 'pandas.indexes.range.RangeIndex'>
with these indexers [3.0] of <type 'float'>
这里有一个使用这种类型的索引的典型示例。假设你有一个有点不规则的类似 timedelta 的索引格式,但是数据被记录为浮点值,比如被记
录为毫秒偏移量。
In [156]: dfir = pd.concat([pd.DataFrame(np.random.randn(5,2),
.....:
index=np.arange(5) * 250.0,
.....:
columns=list('AB')),
.....:
pd.DataFrame(np.random.randn(6,2),
.....:
index=np.arange(4,10) * 250.1,
.....:
columns=list('AB'))])
.....:
In [157]: dfir
Out[157]:
A
B
0.0
0.997289 ‑1.693316
250.0 ‑0.179129 ‑1.598062
500.0
0.936914 0.912560
750.0 ‑1.003401 1.632781
1000.0 ‑0.724626 0.178219
1000.4 0.310610 ‑0.108002
1250.5 ‑0.974226 ‑1.147708
1500.6 ‑2.281374 0.760010
1750.7 ‑0.742532 1.533318
2000.8 2.495362 ‑0.432771
2250.9 ‑0.068954 0.043520
所有选择操作,始终将基于值进行。
In [158]: dfir[0:1000.4]
Out[158]:
A
B
0.0
0.997289 ‑1.693316
250.0 ‑0.179129 ‑1.598062
500.0
0.936914 0.912560
750.0 ‑1.003401 1.632781
1000.0 ‑0.724626 0.178219
1000.4 0.310610 ‑0.108002
In [159]: dfir.loc[0:1001,'A']
Out[159]:
0.0
0.997289
250.0
‑0.179129
500.0
0.936914
750.0
‑1.003401
1000.0
‑0.724626
1000.4
0.310610
Name: A, dtype: float64
In [160]: dfir.loc[1000.4]
Out[160]:
A
0.310610
B
‑0.108002
Name: 1000.4, dtype: float64
您可以检索前1秒(1000 ms)的数据
In [161]: dfir[0:1000]
Out[161]:
A
B
0.0
0.997289 ‑1.693316
250.0 ‑0.179129 ‑1.598062
500.0
0.936914 0.912560
750.0 ‑1.003401 1.632781
1000.0 ‑0.724626 0.178219
如果你需要基于整型的选择操作,应该使用 iloc
In [162]: dfir.iloc[0:5]
Out[162]:
A
B
0.0
0.997289 ‑1.693316
250.0 ‑0.179129 ‑1.598062
500.0
0.936914 0.912560
750.0 ‑1.003401 1.632781
1000.0 ‑0.724626 0.178219
13.5.4 IntervalIndex
版本新增。
0.20.0
和它自己的dtype, interval 以及 Interval 标量类型一起,允许在pandas中对区间表示法提供优秀的支持。
IntervalIndex 允许一些唯一索引,并且也被用来当做 cut() 和 qcut() 的返回类型。
IntervalIndex
警告: 这些索引行为是暂时的,可能在将来的pandas版本中发生变化。
IntervalIndex
可以在Series和DataFrame中作为索引。
In [163]: df = pd.DataFrame({'A': [1, 2, 3, 4]},
.....:
index=pd.IntervalIndex.from_breaks([0, 1, 2, 3, 4]))
.....:
In [164]: df
Out[164]:
A
(0, 1] 1
(1, 2] 2
(2, 3] 3
(3, 4] 4
使用 .loc 沿着一个间隔的边缘进行基于标签的索引会按照你预期的那样工作:选择那个特定的间隔。
In [165]: df.loc[2]
Out[165]:
A
2
Name: (1, 2], dtype: int64
In [166]: df.loc[[2, 3]]
Out[166]:
A
(1, 2] 2
(2, 3] 3
如果你选择了包含在某间隔内的标签,也会选择该间隔。
In [167]: df.loc[2.5]
Out[167]:
A
3
Name: (2, 3], dtype: int64
In [168]: df.loc[[2.5, 3.5]]
Out[168]:
A
(2, 3] 3
(3, 4] 4
Interval
和 IntervalIndex 被用在 cut 和 qcut 中:
In [169]: c = pd.cut(range(4), bins=2)
In [170]: c
Out[170]:
[(‑0.003, 1.5], (‑0.003, 1.5], (1.5, 3.0], (1.5, 3.0]]
Categories (2, interval[float64]): [(‑0.003, 1.5] < (1.5, 3.0]]
In [171]: c.categories
Out[171]:
IntervalIndex([(‑0.003, 1.5], (1.5, 3.0]]
closed='right',
dtype='interval[float64]')
另外, IntervalIndex 允许将其他数据与这些相同的bin进行分割,用NaN表示与其他dtype相似的缺失值。
In [172]: pd.cut([0, 3, 5, 1], bins=c.categories)
Out[172]:
[(‑0.003, 1.5], (1.5, 3.0], NaN, (‑0.003, 1.5]]
Categories (2, interval[float64]): [(‑0.003, 1.5] < (1.5, 3.0]]
生成间隔范围
如果我们需要固定频率的间隔,可以使用 interval_range() 函数使用 start , end 和 periods 的各种组合来创建
IntervalIndex 。对于数字区间,默认的间隔频率为为1,对于类似于时间的区间,默认的间隔频率为1天。
13.5.5
In [173]: pd.interval_range(start=0, end=5)
Out[173]:
IntervalIndex([(0, 1], (1, 2], (2, 3], (3, 4], (4, 5]]
closed='right',
dtype='interval[int64]')
In [174]: pd.interval_range(start=pd.Timestamp('2017‑01‑01'), periods=4)
Out[174]:
IntervalIndex([(2017‑01‑01, 2017‑01‑02], (2017‑01‑02, 2017‑01‑03], (2017‑01‑03, 2017‑01‑04], (201
7‑01‑04, 2017‑01‑05]]
closed='right',
dtype='interval[datetime64[ns]]')
In [175]: pd.interval_range(end=pd.Timedelta('3 days'), periods=3)
Out[175]:
IntervalIndex([(0 days 00:00:00, 1 days 00:00:00], (1 days 00:00:00, 2 days 00:00:00], (2 days 00
:00:00, 3 days 00:00:00]]
closed='right',
dtype='interval[timedelta64[ns]]')
freq
参数用来指定非默认的间隔频率, 并可以利用各种频率别名与日期时间类似的间隔:
In [176]: pd.interval_range(start=0, periods=5, freq=1.5)
Out[176]:
IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0], (6.0, 7.5]]
closed='right',
dtype='interval[float64]')
In [177]: pd.interval_range(start=pd.Timestamp('2017‑01‑01'), periods=4, freq='W')
Out[177]:
IntervalIndex([(2017‑01‑01, 2017‑01‑08], (2017‑01‑08, 2017‑01‑15], (2017‑01‑15, 2017‑01‑22], (201
7‑01‑22, 2017‑01‑29]]
closed='right',
dtype='interval[datetime64[ns]]')
In [178]: pd.interval_range(start=pd.Timedelta('0 days'), periods=3, freq='9H')
Out[178]:
IntervalIndex([(0 days 00:00:00, 0 days 09:00:00], (0 days 09:00:00, 0 days 18:00:00], (0 days 18
:00:00, 1 days 03:00:00]]
closed='right',
dtype='interval[timedelta64[ns]]')
另外, closed 参数用来指定间隔的那一边封闭,默认情况下,间隔的右边封闭:
In [179]: pd.interval_range(start=0, end=4, closed='both')
Out[179]:
IntervalIndex([[0, 1], [1, 2], [2, 3], [3, 4]]
closed='both',
dtype='interval[int64]')
In [180]: pd.interval_range(start=0, end=4, closed='neither')
Out[180]:
IntervalIndex([(0, 1), (1, 2), (2, 3), (3, 4)]
closed='neither',
dtype='interval[int64]')
版本新增特性。
指定 start , end 和 periods 将生成从 start 到 end (包含 end )的均匀间隔的范围,在所得的 IntervalIndex 中其周期为
periods 。
0.23.0
In [181]: pd.interval_range(start=0, end=6, periods=4)
Out[181]:
IntervalIndex([(0.0, 1.5], (1.5, 3.0], (3.0, 4.5], (4.5, 6.0]]
closed='right',
dtype='interval[float64]')
In [182]: pd.interval_range(pd.Timestamp('2018‑01‑01'), pd.Timestamp('2018‑02‑28'), periods=3)
Out[182]:
IntervalIndex([(2018‑01‑01, 2018‑01‑20 08:00:00], (2018‑01‑20 08:00:00, 2018‑02‑08 16:00:00], (20
18‑02‑08 16:00:00, 2018‑02‑28]]
closed='right',
dtype='interval[datetime64[ns]]')
13.6
其他关于索引的FAQ
整数索引
带有整数轴标签的基于标签的索引是一个棘手的问题。在邮件列表以及Python社区的大量成员中都进行了广泛的讨论。在Pandas中,我们的
普遍观点是,标签比整数位置重要。因此,对于整数轴索引,使用 .loc 这样的标准工具只能进行基于标签的索引。下面的代码将会触发异
常:
13.6.1
s = pd.Series(range(5))
s[‑1]
df = pd.DataFrame(np.random.randn(5, 4))
df
df.loc[‑2:]
这样做是为了防止歧义和细微的bug(许多用户报告说,在进行API更改以停止“回退”基于位置的索引时发现了错误)。
非单调索引需要完全匹配
如果 Series 或 DataFrame 的索引是单调递增或递减的,那么基于标签的切片边界可以在索引范围之外,很像是普通的Python列表的索引
切片。索引是否单调,可以用 is_monotonic_increasing 和 is_monotonic_decreasing 属性来判断。
13.6.2
In [183]: df = pd.DataFrame(index=[2,3,3,4,5], columns=['data'], data=list(range(5)))
In [184]: df.index.is_monotonic_increasing
Out[184]: True
# no rows 0 or 1, but still returns rows 2, 3 ﴾both of them﴿, and 4:
In [185]: df.loc[0:4, :]
Out[185]:
data
2
0
3
1
3
2
4
3
# slice is are outside the index, so empty DataFrame is returned
In [186]: df.loc[13:15, :]
Out[186]:
Empty DataFrame
Columns: [data]
Index: []
另一方面,如果索引不是单调的,那么切片的两个边界必须是索引的唯一成员:
In [187]: df = pd.DataFrame(index=[2,3,1,4,3,5], columns=['data'], data=list(range(6)))
In [188]: df.index.is_monotonic_increasing
Out[188]: False
# OK because 2 and 4 are in the index
In [189]: df.loc[2:4, :]
Out[189]:
data
2
0
3
1
1
2
4
3
# 0 is not in the index
In [9]: df.loc[0:4, :]
KeyError: 0
# 3 is not a unique label
In [11]: df.loc[2:3, :]
KeyError: 'Cannot get right slice bound for non‑unique label: 3'
和 Index.is_monotonic_decreasing() 只是用来检查索引的弱单调性,如果想要检查严格
单调性,可以结合 Index.is_unique() 一起使用:
Index.is_monotonic_increasing()
In [190]: weakly_monotonic = pd.Index(['a', 'b', 'c', 'c'])
In [191]: weakly_monotonic
Out[191]: Index(['a', 'b', 'c', 'c'], dtype='object')
In [192]: weakly_monotonic.is_monotonic_increasing
Out[192]: True
In [193]: weakly_monotonic.is_monotonic_increasing & weakly_monotonic.is_unique
Out[193]: False
包含结束端点
标准的Python序列切片,结束端点是不含在内的,而Pandas基于标签的切片,结束端点是 包含 在内的。这样做的主要原因是,通常无法轻易
确定索引中特定标签之后的“后继”元素或下一个元素。 例如,考虑以下系列:
13.6.3
In [194]: s = pd.Series(np.random.randn(6), index=list('abcdef'))
In [195]: s
Out[195]:
a
0.112246
b
0.871721
c
‑0.816064
d
‑0.784880
e
1.030659
f
0.187483
dtype: float64
假设我们想要从 `c` 到 `e` 的切片,使用整数切片可以这样实现:
In [196]: s[2:5]
Out[196]:
c
‑0.816064
d
‑0.784880
e
1.030659
dtype: float64
但是,如果你只有 c 和 e ,确定索引中的下一个元素可能有些复杂。比如,下面的代码将不能正常工作:
s.loc['c':'e'+1]
一个非常常见的用例是将时间序列限定为在两个特定日期开始和结束。为了实现这一点,我们进行了设计,使基于标签的切片包括两个端点:
In [197]: s.loc['c':'e']
Out[197]:
c
‑0.816064
d
‑0.784880
e
1.030659
dtype: float64
这绝对是“实用性胜过纯粹性”的事情,但是如果你期望基于标签的切片以与标准Python整数切片一样的方式工作,则需要提防这一点。
索引可能偷偷地修改Series dtype
不同的索引操作可能偷偷地修改Series的dtype
13.6.4
In [198]: series1 = pd.Series([1, 2, 3])
In [199]: series1.dtype
Out[199]: dtype('int64')
In [200]: res = series1.reindex([0, 4])
In [201]: res.dtype
Out[201]: dtype('float64')
In [202]: res
Out[202]:
0
1.0
4
NaN
dtype: float64
In [203]: series2 = pd.Series([True])
In [204]: series2.dtype
Out[204]: dtype('bool')
In [205]: res = series2.reindex_like(series1)
In [206]: res.dtype
Out[206]: dtype('O')
In [207]: res
Out[207]:
0
True
1
NaN
2
NaN
dtype: object
这是因为上面的重建索引操作会静默插入 NaN ,并且 dtype 会相应更改。这在使用 numpy ufuncs 时(比如 numpy.logical_and )会
引起一些问题。
到the old issue查看更多细节讨论。
第十四章 Pandas 计算工具
14.1
统计函数
变化百分比
Series 、 DataFrame 和 Panel 都有一个方法 pct_change() 来计算给定周期的变化百分比(计算之前使用 fill_method 来填充缺
失值)。
14.1.1
In [1]: ser = pd.Series(np.random.randn(8))
In [2]: ser.pct_change()
Out[2]:
0
NaN
1
‑1.602976
2
4.334938
3
‑0.247456
4
‑2.067345
5
‑1.142903
6
‑1.688214
7
‑9.759729
dtype: float64
In [3]: df = pd.DataFrame(np.random.randn(10, 4))
In [4]: df.pct_change(periods=3)
Out[4]:
0
1
2
3
0
NaN
NaN
NaN
NaN
1
NaN
NaN
NaN
NaN
2
NaN
NaN
NaN
NaN
3 ‑0.218320 ‑1.054001 1.987147 ‑0.510183
4 ‑0.439121 ‑1.816454 0.649715 ‑4.822809
5 ‑0.127833 ‑3.042065 ‑5.866604 ‑1.776977
6 ‑2.596833 ‑1.959538 ‑2.111697 ‑3.798900
7 ‑0.117826 ‑2.169058 0.036094 ‑0.067696
8 2.492606 ‑1.357320 ‑1.205802 ‑1.558697
9 ‑1.012977 2.324558 ‑1.003744 ‑0.371806
14.1.2
协方差
Series.cov()
可以用来计算Series之间的协方差(不包含缺失值)
In [5]: s1 = pd.Series(np.random.randn(1000))
In [6]: s2 = pd.Series(np.random.randn(1000))
In [7]: s1.cov(s2)
Out[7]: 0.00068010881743110871
类似地, DataFrame.cov() 用来两两计算DataFrame中的Series之间的协方差,也会忽略缺失值。
注意:假设缺失值是随机缺失的,将导致对协方差矩阵的估计是无偏的。但是,对于许多应用而言,此估计可能不可接受,因
为不能保证所估计的协方差矩阵是正半定的。 这可能导致估计的相关性具有大于1的绝对值和/或不可逆协方差矩阵。 有关更
多详细信息,请参见协方差矩阵的估计。
In [8]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=['a', 'b', 'c', 'd', 'e'])
In [9]: frame.cov()
Out[9]:
a
b
c
d
e
a 1.000882 ‑0.003177 ‑0.002698 ‑0.006889 0.031912
b ‑0.003177 1.024721 0.000191 0.009212 0.000857
c ‑0.002698 0.000191 0.950735 ‑0.031743 ‑0.005087
d ‑0.006889 0.009212 ‑0.031743 1.002983 ‑0.047952
e 0.031912 0.000857 ‑0.005087 ‑0.047952 1.042487
DataFrame.cov
还支持可选的 min_periods 关键字,该关键字为每个列对指定所需的最小观察数,以获得有效结果。
In [10]: frame = pd.DataFrame(np.random.randn(20, 3), columns=['a', 'b', 'c'])
In [11]: frame.loc[frame.index[:5], 'a'] = np.nan
In [12]: frame.loc[frame.index[5:10], 'b'] = np.nan
In [13]: frame.cov()
Out[13]:
a
b
a 1.123670 ‑0.412851
b ‑0.412851 1.154141
c 0.018169 0.305260
c
0.018169
0.305260
1.301149
In [14]: frame.cov(min_periods=12)
Out[14]:
a
b
c
a 1.123670
NaN 0.018169
b
NaN 1.154141 0.305260
c 0.018169 0.305260 1.301149
相关系数
相关系数可以通过 corr() 方法计算。使用 method 参数,可以选择不同的计算相关系数的方法。
14.1.3
方法名
默认)
pearson (
kendall
spearman
描述
皮尔森相关系数
肯德尔等级相关系数
斯皮尔曼等级相关系数
所有这些方法目前都是使用成对的完整观测值计算的。 维基百科上有涉及上述相关系数的文章:
皮尔森相关系数
肯德尔等级相关系数
斯皮尔曼等级相关系数
注意:请在协方差部分中查看与这种计算相关矩阵的方法有关的说明
In [15]: frame = pd.DataFrame(np.random.randn(1000, 5), columns=['a', 'b', 'c', 'd', 'e'])
In [16]: frame.iloc[::2] = np.nan
# Series with Series
In [17]: frame['a'].corr(frame['b'])
Out[17]: 0.013479040400098794
In [18]: frame['a'].corr(frame['b'], method='spearman')
Out[18]: ‑0.0072898851595406371
# Pairwise correlation of DataFrame columns
In [19]: frame.corr()
Out[19]:
a
b
c
d
e
a 1.000000 0.013479 ‑0.049269 ‑0.042239 ‑0.028525
b 0.013479 1.000000 ‑0.020433 ‑0.011139 0.005654
c ‑0.049269 ‑0.020433 1.000000 0.018587 ‑0.054269
d ‑0.042239 ‑0.011139 0.018587 1.000000 ‑0.017060
e ‑0.028525 0.005654 ‑0.054269 ‑0.017060 1.000000
注意,计算相关系数时,非数字列会被自动排除。类似于 cov , corr 也支持 min_periods 关键字:
In [20]: frame = pd.DataFrame(np.random.randn(20, 3), columns=['a', 'b', 'c'])
In [21]: frame.loc[frame.index[:5], 'a'] = np.nan
In [22]: frame.loc[frame.index[5:10], 'b'] = np.nan
In [23]: frame.corr()
Out[23]:
a
b
a 1.000000 ‑0.121111
b ‑0.121111 1.000000
c 0.069544 0.051742
c
0.069544
0.051742
1.000000
In [24]: frame.corr(min_periods=12)
Out[24]:
a
b
c
a 1.000000
NaN 0.069544
b
NaN 1.000000 0.051742
c 0.069544 0.051742 1.000000
在DataFrame上实现了一个相关的方法 coorwith() ,计算包含在不同DataFrame对象中的类似标签的Series之间的相关系数。
In [25]: index = ['a', 'b', 'c', 'd', 'e']
In [26]: columns = ['one', 'two', 'three', 'four']
In [27]: df1 = pd.DataFrame(np.random.randn(5, 4), index=index, columns=columns)
In [28]: df2 = pd.DataFrame(np.random.randn(4, 4), index=index[:4], columns=columns)
In [29]: df1.corrwith(df2)
Out[29]:
one
‑0.125501
two
‑0.493244
three
0.344056
four
0.004183
dtype: float64
In [30]: df2.corrwith(df1, axis=1)
Out[30]:
a
‑0.675817
b
0.458296
c
0.190809
d
‑0.186275
e
NaN
dtype: float64
数据排名
rank() 方法计算出一个数据排名,并为组固定赋值平均排名(默认情况下)
14.1.4
In [31]: s = pd.Series(np.random.np.random.randn(5), index=list('abcde'))
In [32]: s['d'] = s['b'] # so there's a tie
In [33]: s.rank()
Out[33]:
a
5.0
b
2.5
c
1.0
d
2.5
e
4.0
dtype: float64
rank()
还是一个DataFrame方法,既可以对行(axis=0)也可以对列(axis=1)的进行排名。 NaN 数据会被排除在外。
In [34]: df = pd.DataFrame(np.random.np.random.randn(10, 6))
In [35]: df[4] = df[2][:5] # some ties
In [36]: df
Out[36]:
0
1
2
3
4
5
0 ‑0.904948 ‑1.163537 ‑1.457187 0.135463 ‑1.457187 0.294650
1 ‑0.976288 ‑0.244652 ‑0.748406 ‑0.999601 ‑0.748406 ‑0.800809
2 0.401965 1.460840 1.256057 1.308127 1.256057 0.876004
3 0.205954 0.369552 ‑0.669304 0.038378 ‑0.669304 1.140296
4 ‑0.477586 ‑0.730705 ‑1.129149 ‑0.601463 ‑1.129149 ‑0.211196
5 ‑1.092970 ‑0.689246 0.908114 0.204848
NaN 0.463347
6 0.376892 0.959292 0.095572 ‑0.593740
NaN ‑0.069180
7 ‑1.002601 1.957794 ‑0.120708 0.094214
NaN ‑1.467422
8 ‑0.547231 0.664402 ‑0.519424 ‑0.073254
NaN ‑1.263544
9 ‑0.250277 ‑0.237428 ‑1.056443 0.419477
NaN 1.375064
In [37]: df.rank(1)
Out[37]:
0
1
2
3
0 4.0 3.0 1.5 5.0
1 2.0 6.0 4.5 1.0
2 1.0 6.0 3.5 5.0
3 4.0 5.0 1.5 3.0
4 5.0 3.0 1.5 4.0
5 1.0 2.0 5.0 3.0
6 4.0 5.0 3.0 1.0
7 2.0 5.0 3.0 4.0
8 2.0 5.0 3.0 4.0
9 2.0 3.0 1.0 4.0
4
1.5
4.5
3.5
1.5
1.5
NaN
NaN
NaN
NaN
NaN
5
6.0
3.0
2.0
6.0
6.0
4.0
2.0
1.0
1.0
5.0
有一个可选的参数 ascending (默认为true),当它为False时,数据按照倒序排列,越大的数排名越小。
rank 支持不同的绑定断点方法,通过 method 参数指定:
average :平均排名绑定到组
min :最小排名绑定到组
max :最大排名绑定到组
first : 按它们在数组中出现的顺序分配排名
rank
窗口函数
为了处理数据,我们提供了许多窗口函数来计算通用窗口或滚动统计函数。其中包括: count (计数)、 sum (求和)、 mean (平均
值)、 median (中位数)、 correlation (相关系数)、 variance (方差)、 covariance (协方差)、 standard
deviation (标准差)、 skewness (偏度)和 kurtosis (峰度)
rolling() 和 expanding() 函数可以直接使用在DataFrameGroupBy对象上,从groupby文档查看细节。
14.2
注意:窗口统计的API和GroupBy对象的工作方式非常相似,查看这里
我们通过相应对象的 Rolling 、 Expanding 、 EWM 处理 滚动 、 扩展 和 指数加权 数据
In [38]: s = pd.Series(np.random.randn(1000), index=pd.date_range('1/1/2000', periods=1000))
In [39]: s = s.cumsum()
In [40]: s
Out[40]:
2000‑01‑01
2000‑01‑02
2000‑01‑03
2000‑01‑04
2000‑01‑05
2000‑01‑06
2000‑01‑07
‑0.268824
‑1.771855
‑0.818003
‑0.659244
‑1.942133
‑1.869391
0.563674
...
2002‑09‑20
‑68.233054
2002‑09‑21
‑66.765687
2002‑09‑22
‑67.457323
2002‑09‑23
‑69.253182
2002‑09‑24
‑70.296818
2002‑09‑25
‑70.844674
2002‑09‑26
‑72.475016
Freq: D, Length: 1000, dtype: float64
这些是通过Series和DataFrame上的方法创建的
In [41]: r = s.rolling(window=60)
In [42]: r
Out[42]: Rolling [window=60,center=False,axis=0]
这些对象提供了可用的方法和属性,可以通过tab自动补全
In [14]: r.
r.agg
r.skew
r.aggregate
r.std
r.apply
r.sum
r.corr
r.var
r.count
r.exclusions
r.max
r.median
r.name
r.cov
r.kurt
r.mean
r.min
r.quantile
通常,这些方法都有同样的接口,都接受下面的参数:
windows:移动窗口的大小
min_periods: 需要的非空数据点的阈值(否则结果为NA)
center:布尔值, 是否在中心设置标签(默认为False)
然后我们可以调用这些 rolling 对象的方法,返回类似索引的对象 :
In [43]: r.mean()
Out[43]:
2000‑01‑01
NaN
2000‑01‑02
NaN
2000‑01‑03
NaN
2000‑01‑04
NaN
2000‑01‑05
NaN
2000‑01‑06
NaN
2000‑01‑07
NaN
...
2002‑09‑20
‑62.694135
2002‑09‑21
‑62.812190
2002‑09‑22
‑62.914971
2002‑09‑23
‑63.061867
2002‑09‑24
‑63.213876
2002‑09‑25
‑63.375074
2002‑09‑26
‑63.539734
Freq: D, Length: 1000, dtype: float64
In [44]: s.plot(style='k‑‑')
Out[44]: <matplotlib.axes._subplots.AxesSubplot at 0x1a28c48a90>
In [45]: r.mean().plot(style='k')
Out[45]: <matplotlib.axes._subplots.AxesSubplot at 0x1a28c48a90>
它们也可以应用于DataFrame对象。这实际上只是将移动窗口操作符应用于所有DataFrame的列上的语法糖
In [46]: df = pd.DataFrame(np.random.randn(1000, 4),
....:
index=pd.date_range('1/1/2000', periods=1000),
....:
columns=['A', 'B', 'C', 'D'])
....:
In [47]: df = df.cumsum()
In [48]: df.rolling(window=60).sum().plot(subplots=True)
Out[48]:
array([<matplotlib.axes._subplots.AxesSubplot object at 0x1a28d30588>,
<matplotlib.axes._subplots.AxesSubplot object at 0x1a28d563c8>,
<matplotlib.axes._subplots.AxesSubplot object at 0x1a28d7e588>,
<matplotlib.axes._subplots.AxesSubplot object at 0x1a28da7748>], dtype=object)
方法概览
我们提供了常用的几个统计函数
14.2.1
Method
mean()
非空观测值的数量
数值之和
数值平均值
median()
Arithmetic median of values
count()
sum()
min()
max()
std()
var()
skew()
kurt()
quantile()
apply()
cov()
corr()
apply()
Description
最小值
最大值
贝塞尔修正样本标准差
均方差(无偏方差)
样本偏度(第三阶矩)
样本峰度(4阶)
分位数
通用的apply函数
无偏协方差(二元)
相关系数(二元)
函数接受一个额外的 func 参数,并且执行通常的滚动计算。 func 参数应该是从ndarray输入中产生单一值的单一函数。
In [49]: mad = lambda x: np.fabs(x ‑ x.mean()).mean()
In [50]: s.rolling(window=60).apply(mad, raw=True).plot(style='k')
Out[50]: <matplotlib.axes._subplots.AxesSubplot at 0x1a28db80f0>
0
You can add this document to your study collection(s)
Sign in Available only to authorized usersYou can add this document to your saved list
Sign in Available only to authorized users(For complaints, use another form )