Python-数据分析教程-一-

84 阅读41分钟

Python 数据分析教程(一)

原文:Python Data Analysis

协议:CC BY-NC-SA 4.0

零、前言

数据分析在自然科学、生物医学和社会科学领域有着丰富的历史。由于围绕数据科学的大肆宣传,数据分析最近在几乎每个行业领域都变得流行起来。数据分析和数据科学试图从数据中提取信息。为此,我们使用了统计学、机器学习、信号处理、自然语言处理和计算机科学的技术。

在本书的第一章中可以找到一个思维导图可视化 Python 软件,它可以用于数据分析。首先值得注意的是,Python 生态系统非常成熟、多样、丰富。它包括著名的软件包,如 NumPy、SciPy 和 matplotlib。这不应该令人惊讶,因为 Python 从 1989 年就出现了。Python 易于学习和使用,比其他编程语言不那么冗长,可读性很强。即使你不懂 Python,你也可以在几天内学会基础知识,尤其是如果你有另一种编程语言的经验。要享受这本书,你不需要更多的基础知识。有很多教授 Python 的书籍、课程和在线教程。

这本书涵盖了什么

第 1 章python 库入门,给出安装 Python 和基础 Python 数据分析库的说明。我们使用 NumPy 创建了一个小应用程序,并用 matplotlib 绘制了一些基本的图。

第二章NumPy 数组,为我们介绍 NumPy 的基础知识和数组。到本章结束时,我们将对 NumPy 数组和相关函数有基本的了解。

第三章Pandas 入门,向我们介绍 Pandas 的基本功能、数据结构和操作。

第四章统计与线性代数,快速概述线性代数与统计函数。

第 5 章检索、处理和存储数据,说明如何获取各种格式的数据,以及如何清理原始数据并存储。

第 6 章数据可视化,概述了如何使用 matplotlib 和 pandas 绘图功能绘制数据。

第 7 章信号处理和时间序列,包含使用太阳黑子周期数据的时间序列和信号处理示例。示例使用了 NumPy/SciPy 以及 statsmodels。

第 8 章使用数据库,提供各种数据库(关系数据库和 NoSQL 数据库)和相关应用编程接口的信息。

第九章分析文本数据和社交媒体,分析文本进行情感分析和话题提取。文中还给出了一个网络分析的小例子。

第 10 章预测性分析和机器学习使用 scikit-learn 以天气预测为运行示例,讲解人工智能。其他 API 用于 scikit-learn 未涵盖的算法。

第 11 章Python 生态系统和云计算之外的环境,给出了各种如何集成非 Python 编写的现有代码的例子。此外,还将演示在云中使用 python。

第 12 章性能调优、性能分析和并发给出了以性能分析和 Cythoning 为关键技术来提高性能的提示。还讨论了多核和分布式系统的相关框架。

附录 A关键概念,给出了关键术语及其描述。

附录 B有用功能,提供了库的关键功能列表,可以作为现成的参考。

附录 C在线资源,为读者提供了进一步探究书中所涵盖主题的链接。

这本书你需要什么

本书中的代码示例应该适用于大多数现代操作系统。对于所有章节,Python > 3.5.0 和 pip3 是必需的。可以从www.python.org/downloads/下载 Python 3.5.x。在此网页上,您可以找到 Windows 和 Mac OS X 的安装程序,以及 Linux、Unix 和 Mac OS X 的源代码存档。您可以在此网页上找到各种操作系统的 python 安装和使用说明:docs.python.org/3/using/ind… python 库:

$ pip3 install <some library>

以下是用于示例的 python 库列表:

  • numpy
  • 我的天啊
  • Pandas
  • matplotlib
  • 伊普提洪伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁
  • Jupyter
  • 笔记本
  • 读行
  • scikit-learn
  • rp2
  • 什么时候
  • statsmodels
  • feed parser(feed parser)
  • 美丽的组合 4
  • xml 格式
  • numexpr
  • 桌子
  • openpyxl
  • xls xwritter
  • xlrd . xlrd . xlrd . xlrd . xlrd . xlrd . xlrd . xlrd . xlrd
  • 矮种马
  • 资料组
  • 皮蒙戈
  • redis
  • python 3 memcache(python 3 内存高速缓存)
  • 卡珊德拉-司机
  • sqllcemy(SQL 语法)
  • 我是 nltk
  • 网络 x
  • 剧院
  • 鼻部 _ 参数化
  • pydot2
  • deap!deap
  • JPipes1
  • gprof 2 点
  • 线条分析器
  • 西顿
  • 细胞 olz
  • joblib
  • 瓶颈
  • 水壶
  • mpi4py

除了 python 库,我们还需要以下软件:

  • 重定向服务器
  • 卡桑德拉
  • Java 8
  • Graphviz(图形 viz)
  • 八度音阶
  • 稀有
  • 大喝
  • 断续器
  • 促进
  • gfortran 先生
  • 平均弹着点

通常,可用的最新版本应该适用于上述库和软件。

列出的一些软件仅用于一个例子;因此,在安装软件之前,请首先检查该示例是否与您相关。

要卸载 pip 安装的 Python 包,请使用以下命令:

   $ pip3 uninstall <some library>

这本书是给谁的

这本书是给有 Python 和数学基础知识,想学习如何使用 Python 库分析数据的人看的。我们试图让事情变得简单,但不可能非常详细地涵盖所有主题。使用汗学院和 Coursera 等在线资源更新您的数学知识可能会对您有用。

我会推荐帕克特出版社的以下书籍供进一步阅读:

  • 用 Python 构建机器学习系统,威利·里歇特和路易斯·佩德罗·科埃略(2013)
  • 学习 Cython 编程,Philip Herron (2013)
  • 学习 NumPy 数组,伊万·伊德里斯(2014)
  • 学习 scikit-learn:Python 中的机器学习,劳尔·加雷塔和吉列尔莫·蒙切奇(2013)
  • 数值和科学计算学习科学,弗朗西斯科·布兰科-席尔瓦(2013)
  • 面向 Python 开发人员的 Matplotlib,Sandro Tosi (2009)
  • 《新手指南-第二版》,伊万·伊德里斯(2013)
  • 伊万·伊德里斯(2012 年)
  • Python 并行编程,扬·帕拉奇(2014)
  • 《Python 数据可视化食谱》,伊戈尔·米洛瓦诺维奇(2013 年)
  • 金融 Python,闫宇星(2014)
  • 用 NLTK 2.0 食谱处理 Python 文本,雅各布·帕金斯(2010)

惯例

在这本书里,你会发现许多区分不同种类信息的文本样式。以下是这些风格的一些例子和对它们的意义的解释。

文本中的码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、伪 URL、用户输入和 Twitter 句柄如下所示:“如果您的当前用户在您的系统上没有足够的权限,可能有必要在此命令前添加sudo

代码块设置如下:

def pythonsum(n): 
   a = list(range(n)) 
   b = list(range(n)) 
   c = [] 

   for i in range(len(a)): 
       a[i] = i ** 2 
       b[i] = i ** 3 
       c.append(a[i] + b[i]) 

   return c

任何命令行输入或输出都编写如下:

$ pip3 install numpy scipy pandas matplotlib jupyter notebook

警告或重要提示会出现在这样的框中。

型式

提示和技巧是这样出现的。

读者反馈

我们随时欢迎读者的反馈。让我们知道你对这本书的看法——你喜欢或不喜欢什么。读者反馈对我们来说很重要,因为它有助于我们开发出你真正能从中获益的标题。要向我们发送一般反馈,只需给 feedback@packtpub.com 发电子邮件,并在邮件主题中提及书名。如果你对某个主题有专业知识,并且对写作或投稿感兴趣,请参见我们位于www.packtpub.com/authors的作者指南。

客户支持

现在,您已经自豪地拥有了一本书,我们有许多东西可以帮助您从购买中获得最大收益。

下载示例代码

你可以从你在www.packtpub.com的账户下载这本书的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。

您可以按照以下步骤下载代码文件:

  1. 使用您的电子邮件地址和密码登录或注册我们的网站。
  2. 将鼠标指针悬停在顶部的 SUPPORT 选项卡上。
  3. 点击代码下载&勘误表
  4. 搜索框中输入图书名称。
  5. 选择要下载代码文件的书籍。
  6. 从您购买这本书的下拉菜单中选择。
  7. 点击代码下载

下载文件后,请确保使用最新版本的解压缩文件夹:

  • 视窗系统的 WinRAR / 7-Zip
  • zipeg/izp/un ARX for MAC
  • 适用于 Linux 的 7-Zip / PeaZip

这本书的代码包也托管在 GitHubhttps://GitHub . com/PacktPublishing/Python-Data-Analysis-第二版。我们还有来自丰富的图书和视频目录的其他代码包,可在github.com/PacktPublis…获得。看看他们!

下载本书的彩色图片

我们还为您提供了一个 PDF 文件,其中包含本书中使用的截图/图表的彩色图像。彩色图像将帮助您更好地理解输出中的变化。您可以从https://www . packtpub . com/sites/default/files/downloads/PitOnDaanalysisSessional _ color images . pdf下载此文件。

勘误表

尽管我们尽了最大努力来确保我们内容的准确性,但错误还是会发生。如果你在我们的某本书里发现一个错误,也许是文本或代码中的错误,如果你能向我们报告,我们将不胜感激。通过这样做,你可以让其他读者免受挫折,并帮助我们改进这本书的后续版本。如果您发现任何勘误表,请访问www.packtpub.com/submit-erra…,选择您的书籍,点击勘误表提交表链接,并输入您的勘误表的详细信息。一旦您的勘误表得到验证,您的提交将被接受,勘误表将上传到我们的网站或添加到该标题勘误表部分下的任何现有勘误表列表中。

要查看之前提交的勘误表,请前往www.packtpub.com/books/conte…并在搜索栏中输入图书名称。所需信息将出现在勘误表部分。

盗版

互联网上版权材料的盗版是所有媒体的一个持续问题。在 Packt,我们非常重视版权和许可证的保护。如果您在互联网上遇到任何形式的我们作品的非法拷贝,请立即向我们提供位置地址或网站名称,以便我们寻求补救。

请联系我们在 copyright@packtpub.com 的链接到可疑的盗版材料。

我们感谢您在保护我们的作者方面的帮助,以及我们为您带来有价值内容的能力。

问题

如果你对这本书的任何方面有问题,你可以联系我们在 questions@packtpub.com,我们将尽最大努力解决这个问题。

一、Python 库入门

欢迎光临!我们开始吧。Python 已经成为数据分析和数据科学事实上的标准语言和平台之一。您将很快看到的思维导图描述了数据分析师和数据科学家使用的 Python 生态系统中的一些可用库。NumPy、SciPy、Pandas 和 Matplotlib 库奠定了 Python 数据分析的基础,现在是 SciPy Stack 1.0(www.scipy.org/stackspec.h…)的一部分。我们将学习如何安装 SciPy Stack 1.0 和 Jupyter Notebook,并编写一些简单的数据分析代码作为热身练习。

以下是 Python 生态系统中可供数据分析师和数据科学家使用的库:

  • NumPy :这是一个通用库,提供数值数组,以及高效操作数组的函数。
  • SciPy :这是一个提供科学和工程相关功能的科学计算库。SciPy 补充并略微重叠 NumPy。NumPy 和 SciPy 在历史上共享他们的代码库,但后来分开了。
  • Pandas:这是一个数据操作库,提供数据结构和操作来操作表格和时间序列数据。
  • Matplotlib :这是一个 2D 绘图库,为生成绘图、图表和图形提供支持。Matplotlib 由 SciPy 使用,支持 NumPy。
  • IPython :这为 Python 提供了强大的交互外壳,为 Jupyter 提供了内核,并支持交互数据可视化。我们将在本章稍后介绍 IPython 外壳。
  • Jupyter Notebook :这提供了一个基于网络的交互式外壳,用于通过实时代码和可视化来创建和共享文档。Jupyter Notebook 通过 IPython 提供的内核支持多个版本的 Python。我们将在本章稍后介绍 Jupyter 笔记本。

其他所需软件的安装说明将在适当的时候在整本书中给出。在这一章的最后,你会发现一些提示,告诉你如果你陷入困境或者不确定解决问题的最佳方法,如何在网上找到更多的信息:

Getting Started with Python Libraries

在本章中,我们将涵盖以下主题:

  • 安装 Python 3
  • 使用 IPython 作为外壳
  • 阅读手册页
  • Jupyter 笔记型电脑
  • NumPy 数组
  • 简单的应用程序
  • 在哪里可以找到帮助和参考资料
  • 列出 Python 库中的模块
  • 使用 matplotlib 可视化数据

安装 Python 3

本书使用的软件基于 Python 3,所以需要安装 Python 3。在一些操作系统上,已经安装了 Python 3。Python 有许多实现,包括商业实现和发行版。在本书中,我们将重点介绍标准 Python 实现,它保证与 NumPy 兼容。

可以从www.python.org/downloads/下载 Python 3.5.x。在这个网页上,你可以找到 Windows 和 Mac OS X 的安装程序,以及 Linux、Unix 和 Mac OS X 的源文件库。你可以在docs.python.org/3/using/ind…找到各种操作系统安装和使用 Python 的说明。

我们将在本章安装的软件有 Windows 的二进制安装程序、各种 Linux 发行版和 Mac OS X。如果您愿意,也有源代码发行版。您需要在系统上安装 Python 3.5.x 或更高版本。Python 2.7 的日落日期从 2015 年移动到 2020 年,因此 Python 2.7 将被支持和维护到 2020 年。出于这些原因,我们为 Python 3 更新了这本书。

安装数据分析库

我们将学习如何在 Windows、Linux 和 Mac OS X 上安装和设置 NumPy、SciPy、Pandas、Matplotlib、IPython 和 Jupyter Notebook,让我们详细了解一下这个过程。我们将使用pip3来安装库。从 3.4 版本开始,默认情况下,Python 安装中包含了pip3

在 Linux 或者 Mac OS X 上

要安装基础库,请运行以下命令行指令:

$ pip3 install numpy scipy pandas matplotlib jupyter notebook 

如果您的当前用户在您的系统上没有足够的权限,可能有必要将sudo添加到该命令之前。

在窗户上

在撰写本书时,我们在 Windows 10 虚拟机上安装了以下软件作为先决条件:

www.lfd.uci.edu/~gohlke/pyt…为您的 Windows 平台下载并安装适当的预建 NumPy 和 Scipy 二进制文件:

  • 我们下载了numpy-1 . 12 . 0+mkl-cp36-cp36m-win _ amd 64 . whlscipy-0 . 18 . 1-cp36-cp36m-win _ amd 64 . whl
  • 下载后,我们执行pip3 install Downloads\numpy-1.12.0+mkl-cp36-cp36m-win_amd64.whlpip3 install Downloads\scipy-0.18.1-cp36-cp36m-win_amd64.whl命令

安装这些先决条件后,要安装其余的基础库,请运行以下命令行指令:

$ pip3 install pandas matplotlib jupyter

型式

安装 Jupyter 使用这些命令,安装所有需要的软件包,比如笔记本和 IPython。

使用 IPython 作为外壳

数据分析师、数据科学家和工程师习惯于实验。IPython 是由科学家在实验的基础上创造出来的。IPython 提供的交互环境与 Matlab、Mathematica 和 Maple 提供的交互计算环境相当。

以下是 IPython 外壳的功能列表:

  • 选项卡完成,这有助于您找到命令
  • 历史机制
  • 内嵌编辑
  • 能够用%run调用外部 Python 脚本
  • 访问系统命令
  • 对 Python 调试器和分析器的访问

以下列表描述了如何使用 IPython 外壳:

  • Starting a session: To start a session with IPython,enter the following instruction on the command line:

    $ ipython3
    Python 3.5.2 (default, Sep 28 2016, 18:08:09) 
    Type "copyright", "credits" or "license" for more information.
     IPython 5.1.0 -- An enhanced Interactive Python.
    ?         -> Introduction and overview of IPython's features.
    %quickref -> Quick reference.
    help      -> Python's own help system.
    object?   -> Details about 'object', use 'object??' for extra 
                         details.
    In [1]: quit()
    
    

    型式

    quit()功能或 Ctrl + D 退出 IPython 外壳。

  • Saving a session: We might want to be able to go back to our experiments. In IPython, it is easy to save a session for later use with the following command:

    In [1]: %logstart
    Activating auto-logging. Current session state plus future 
     input saved:
     Filename : ipython_log.py
     Mode : rotate
     Output logging : False
     Raw input log : False
     Timestamping : False
    State : active
    
    

    日志记录可以按如下方式关闭:

    In [9]: %logoff
    Switching logging OFF
    
    
  • Executing a system shell command: Execute a system shell command in the default IPython profile by prefixing the command with the ! symbol. For instance, the following input will get the current date:

    In [1]: !date
    
    

    事实上,任何以!为前缀的行都会被发送到系统外壳。我们还可以存储命令输出,如下所示:

    In [2]: thedate = !date
    In [3]: thedate
    
    
  • Displaying history: We can show the history of our commands with the %hist command. For example:

    In [1]: a = 2 + 2
    In [2]: a
    Out[2]: 4
    In [3]: %hist
    a = 2 + 2
    a
    %hist
    
    

    这是命令行界面 ( CLI )环境中的常见功能。我们也可以通过-g开关搜索历史,如下所示:

    In [5]: %hist -g a = 2
     1: a = 2 + 2
    
    

我们看到了一些所谓的神奇功能在发挥作用。这些功能以%字符开始。如果魔法功能单独在一行上使用,%前缀是可选的。

阅读手册页

在 IPython 中导入库时,我们可以用help命令打开库函数的手动页面。不必知道函数的名称。我们可以键入几个字符,然后让标签完成它的工作。例如,让我们浏览arange()功能的可用信息。

我们可以通过以下两种方式浏览可用信息:

  • Calling the help function: Type in help( followed by a few characters of the function and press the Tab key. A list of functions will appear. Select the function from the list using the arrow keys and press the Enter key. Close the help function call with )  and press the Enter key.

    Reading manual pages

  • Querying with a question mark: Another option is to append a question mark to the function name. You will then, of course, need to know the function name, but you don't have to type help, for example:

    In [3]: numpy.arange?
    
    

    标签完成依赖于readline,所以你需要确定它已经安装。通过输入以下命令,可以安装pip:

    $ pip3 install readline
    
    

    问号提供了来自文档字符串的信息。

Jupyter 笔记本电脑

Jupyter Notebook,以前被称为 IPython Notebooks,它提供了一个工具,可以创建和共享带有特殊格式的文本、图表和 Python 代码的网页。请通过以下链接查看这些笔记本系列:

通常,笔记本被用作教育工具,或者演示 Python 软件。我们可以从普通 Python 代码或特殊笔记本格式导入或导出笔记本。笔记本可以在本地运行,或者我们可以通过运行专用的笔记本服务器使它们在线可用。某些云计算解决方案,如 Wakari 和 PiCloud,允许您在云中运行笔记本电脑。云计算是第 11 章Python 生态系统外的环境和云计算的主题之一。

要启动与 Jupyter 笔记本的会话,请在命令行中输入以下指令:

$ jupyter-notebook

这将启动笔记本服务器,并打开一个网页,显示将从中执行命令的文件夹的内容。然后可以选择新建 | Python 3 在 Python 3 中开始一个新的笔记本。

也可以打开本书代码包中提供的ch-01.ipynbch-01笔记本文件包含我们稍后将描述的简单应用程序的代码。

NumPy 数组

完成 NumPy 的安装后,是时候看一下 NumPy 数组了。在数值运算方面,NumPy 数组比 Python 列表更高效。事实上,NumPy 数组是经过大量优化的专用对象。NumPy 代码比同等的 Python 代码需要更少的显式循环。这是基于向量化的。

如果我们回到高中数学,那么我们应该记住标量和向量的概念。例如,数字 2 是一个标量。当我们将 2 加 2 时,我们执行的是标量加法。我们可以用一组标量组成一个向量。在 Python 编程术语中,我们将有一个一维数组。当然,这个概念可以扩展到更高的维度。在两个数组上执行操作,如加法,可以简化为一组标量操作。在直接 Python 中,我们将这样做,循环遍历第一个数组中的每个元素,并将其添加到第二个数组中的相应元素中。然而,这比数学中的方式更冗长。在数学中,我们把两个向量的相加当作一次运算。NumPy 数组也是这样做的,并且使用低级 C 例程进行了某些优化,使这些基本操作更加高效。我们将在第 2 章NumPy 数组中详细介绍 NumPy 数组。

简单的应用

假设我们要加上两个向量,分别叫做ab向量这个词在这里是数学意义上的,意思是一维数组。我们将在第 4 章统计学和线性代数中学习表示矩阵的专用 NumPy 数组。向量a保存整数0n 的平方;例如,如果n等于3a包含014。向量b保存整数0n 的立方,所以如果n等于3,那么向量b等于01或者8。使用普通的 Python 你会怎么做?在我们想出一个解决方案后,我们将把它与 NumPy 等效方案进行比较。

以下函数使用不带 NumPy 的纯 Python 解决了向量加法问题:

def pythonsum(n): 
   a = list(range(n)) 
   b = list(range(n)) 
   c = [] 

   for i in range(len(a)): 
       a[i] = i ** 2 
       b[i] = i ** 3 
       c.append(a[i] + b[i]) 

   return c 

下面是一个用 NumPy 解决向量加法问题的函数:

def numpysum(n): 
  a = numpy.arange(n) ** 2 
  b = numpy.arange(n) ** 3 
  c = a + b 
  return c 

注意numpysum()不需要for循环。我们还使用了 NumPy 中的arange()函数,该函数为我们创建了一个 NumPy 数组,其中包含从0n 的整数。arange()功能已导入;这就是为什么它的前缀是numpy

有趣的部分来了。我们之前提到,在数组操作方面,NumPy 更快。不过,Numpy 要快多少?下面的程序将通过测量numpysum()pythonsum()功能的经过时间(以微秒计)来显示。它还打印向量和的最后两个元素。让我们检查一下是否使用 Python 和 NumPy 得到了相同的答案:

#!/usr/bin/env/python 

import sys 
from datetime import datetime 
import numpy as np 

""" 
This program demonstrates vector addition the Python way. 
Run the following from the command line: 

  python vectorsum.py n 

Here, n is an integer that specifies the size of the vectors. 

The first vector to be added contains the squares of 0 up to n. 
The second vector contains the cubes of 0 up to n. 
The program prints the last 2 elements of the sum and the elapsed  time: 
""" 

def numpysum(n): 
   a = np.arange(n) ** 2 
   b = np.arange(n) ** 3 
   c = a + b 

   return c 

def pythonsum(n): 
   a = list(range(n)) 
   b = list(range(n)) 
   c = [] 

   for i in range(len(a)): 
       a[i] = i ** 2 
       b[i] = i ** 3 
       c.append(a[i] + b[i]) 

   return c 

size = int(sys.argv[1]) 

start = datetime.now() 
c = pythonsum(size) 
delta = datetime.now() - start 
print("The last 2 elements of the sum", c[-2:]) 
print("PythonSum elapsed time in microseconds", delta.microseconds) 

start = datetime.now() 
c = numpysum(size) 
delta = datetime.now() - start 
print("The last 2 elements of the sum", c[-2:]) 
print("NumPySum elapsed time in microseconds", delta.microseconds) 

100020003000向量元素的程序输出如下:

$ python3 vectorsum.py 1000
The last 2 elements of the sum [995007996, 998001000]
PythonSum elapsed time in microseconds 976
The last 2 elements of the sum [995007996 998001000]
NumPySum elapsed time in microseconds 87
$ python3 vectorsum.py 2000
The last 2 elements of the sum [7980015996, 7992002000]
PythonSum elapsed time in microseconds 1623
The last 2 elements of the sum [7980015996 7992002000]
NumPySum elapsed time in microseconds 143
$ python3 vectorsum.py 4000
The last 2 elements of the sum [63920031996, 63968004000]
PythonSum elapsed time in microseconds 3417
The last 2 elements of the sum [63920031996 63968004000]
NumPySum elapsed time in microseconds 237

显然,NumPy 比同等的正常 Python 代码快得多。有一点是肯定的;无论是否使用 NumPy,我们都会得到相同的结果。但是,打印的结果在表示上有所不同。注意numpysum()函数的结果没有逗号。怎么会这样显然,我们处理的不是 Python 列表,而是 NumPy 数组。我们将在第 2 章NumPy 数组中了解更多关于 NumPy 数组的信息。

哪里可以找到帮助和推荐人

下表列出了我们在本章中讨论的 Python 数据分析库的文档网站。

| **包装** | **描述** | | NumPy 和 SciPy | NumPy 和 SciPy 的主要文档网站在[http://docs.scipy.org/doc/](http://docs.scipy.org/doc/)。通过此网页,您可以浏览 NumPy 和 SciPy 用户指南和参考指南,以及几个教程。 | | Pandas | [http://pandas.pydata.org/pandas-docs/stable/](http://pandas.pydata.org/pandas-docs/stable/)。 | | Matplotlib | [http://matplotlib.org/contents.html](http://matplotlib.org/contents.html)。 | | 伊普提洪伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁 | [http://ipython . readthedocs . io/en/stable/](http://ipython.readthedocs.io/en/stable/)。 | | Jupyter 笔记型电脑 | [http://jupyter-notebook.readthedocs.io/en/latest/](http://jupyter-notebook.readthedocs.io/en/latest/) . |

流行的堆栈溢出软件开发论坛有数百个问题,分别标记为 NumPy、SciPy、Pandas、Matplotlib、IPython 和 Jupyter Notebook。要查看它们,请转到http://stackoverflow.com/questions/tagged/<your-tag-word-here>

如果你真的遇到了问题,或者你想随时了解这些库的发展,你可以订阅他们各自的讨论邮件列表。每天的电子邮件数量因列表而异。积极参与这些库开发的开发人员回答了邮件列表中的一些问题。

对于 IRC 用户,在irc://irc.freenode.net上有一个 IRC 频道。这个频道叫做#scipy,但是你也可以问 NumPy 问题,因为 SciPy 用户也有 NumPy 的知识,因为 SciPy 是基于 NumPy 的。SciPy 频道始终至少有 50 名成员。

列出 Python 库中的模块

ch-01.ipynb文件包含查看 NumPy、SciPy、Pandas 和 Matplotlib 库中模块的代码。不要担心理解代码,只是现在尝试运行它。您可以修改这段代码来查看其他库中的模块。

使用 Matplotlib 可视化数据

我们将在后面的章节中学习可视化数据。现在,让我们尝试加载两个样本数据集并构建一个基本图。首先,安装 sklearn 库,我们将使用以下命令从中加载数据:

$ pip3 install scikit-learn 

使用以下命令导入数据集:

from sklearn.datasets import load_iris 
from sklearn.datasets import load_boston 

导入 Matplotlib 绘图模块:

from matplotlib import pyplot as plt 
%matplotlib inline 

加载iris数据集,打印数据集描述,将第 1 列(萼片长度)绘制为x,将第 2 列(萼片宽度)绘制为y:

iris = load_iris() 
print(iris.DESCR) 
data=iris.data 
plt.plot(data[:,0],data[:,1],".") 

生成的图将如下图所示:

Visualizing data using Matplotlib

加载 boston 数据集,打印数据集描述,将第 3 列(非零售业务占比)标为x,第 5 列(一氧化氮浓度)标为y,图上各点标有 + 符号:

boston = load_boston()
print(boston.DESCR)
data=boston.data
plt.plot(data[:,2],data[:,4],"+")

生成的图将如下图所示:

Visualizing data using Matplotlib

总结

在这一章中,我们安装了 NumPy、SciPy、Pandas、Matplotlib、IPython 和 Jupyter Notebook,所有这些都将在本书中使用。我们让一个向量加法程序工作,并了解了 NumPy 如何提供卓越的性能。此外,我们还探索了可用的文档和在线资源。我们执行代码来查找库中的模块,并加载了一些示例数据集来使用 Matplotlib 绘制一些基本图。

在下一章第二章NumPy 数组中,我们将在 NumPy 的兜帽下看一看,探索一些基本概念,包括数组和数据类型。

二、NumPy 数组

现在,我们已经研究了一个利用 SciPy 堆栈中的基础数据分析库的真实例子,是时候了解 NumPy 数组了。本章让您了解 NumPy 数组的基础知识。在本章的最后,您将对 NumPy 数组和相关函数有一个基本的了解。

我们将在本章中讨论的主题如下:

  • NumPy 数组对象
  • 创建多维数组
  • 选择 NumPy 数组元素
  • 数字类型
  • 一维切片和索引
  • 操作数组形状
  • 创建数组视图和副本
  • 花式索引
  • 用位置列表索引
  • 用布尔值索引 NumPy 数组
  • 广播 NumPy 数组

您可能想打开 Jupyter 笔记本中的ch-02.ipynb文件,按照本章中的示例进行操作,或者将它们键入您自己的新笔记本中。

NumPy 数组对象

NumPy 提供了一个名为ndarray的多维数组对象。NumPy 数组是固定大小的类型化数组。Python 列表是异构的,因此列表的元素可以包含任何对象类型,而 NumPy 数组是同构的,只能包含一种类型的对象。一ndarray由两部分组成,如下:

  • 存储在连续内存块中的实际数据
  • 描述实际数据的元数据

由于实际数据存储在连续的内存块中,因此将大数据集加载为ndarray,它会受到足够大的连续内存块的可用性的影响。NumPy 中的大多数数组方法和函数不会影响实际数据,只修改元数据。

我们在前面的章节中已经发现了如何通过应用arange()函数来产生数组。实际上,我们制作了一个一维数组,其中包含一组数字。ndarray可以有多个维度。

NumPy 数组的优势

通常,NumPy 数组是同构的(有一个特定的记录数组类型是异构的),数组中的项目必须是相同的类型。这样做的好处是,如果我们知道数组中的项目属于同一类型,就很容易确定数组所需的存储大小。NumPy 数组可以执行向量化操作,处理一个完整的数组,这与 Python 列表不同,在 Python 列表中,您通常必须循环遍历列表并对每个元素执行操作。NumPy 数组是从 0 开始索引的,就像 Python 中的列表一样。NumPy 利用优化的 C 语言应用编程接口,使数组操作特别快。

我们将再次用arange()子程序进行数组。在本章中,您将看到 Jupyter 笔记本会话的片段,其中 NumPy 已经使用指令import numpy as np导入。以下是获取数组数据类型的方法:

In: a = np.arange(5) 
In: a.dtype 
Out: dtype('int64')

数组a的数据类型是int64(至少在我的电脑上是这样的),但是如果你使用的是 32 位 Python,你可能会得到int32作为输出。在这两种情况下,我们都在处理整数(64 位或 32 位)。除了数组的数据类型之外,知道它的形状也很重要。第 1 章Python 库入门中的例子演示了如何创建向量(实际上是一维 NumPy 数组)。数学中通常使用向量,但大多数时候我们需要更高维的对象。让我们找出几分钟前产生的向量的形状:

In: a 
Out: array([0, 1, 2, 3, 4]) 
In: a.shape 
Out: (5,) 

可以看到,向量有五个分量,取值范围从04。数组的shape属性是一个元组;在这种情况下,1元素的元组,它保存每个维度中的长度。

创建多维数组

现在我们知道了如何创建向量,我们将创建多维 NumPy 数组。生成矩阵后,我们将再次需要显示它,如以下代码片段所示:

  1. 创建多维数组,如下所示:

            In: m = np.array([np.arange(2), np.arange(2)]) 
            In: m 
            Out: 
            array([[0, 1], 
                   [0, 1]]) 
    
    
  2. 我们可以将数组形状显示如下:

            In: m.shape 
            Out: (2, 2) 
    
    

我们用arange()子程序做了一个 2x2 数组。array()函数根据传递给它的对象创建一个数组。对象必须是一个数组,例如 Python 列表。在前面的例子中,我们传递了一个数组列表。对象是array()功能唯一需要的参数。NumPy 函数往往有一堆带有预定义默认选项的可选参数。

选择 NumPy 数组元素

有时,我们会希望选择一个数组的特定组成部分。我们将了解如何做到这一点,但首先,让我们再次制作一个 2x2 矩阵:

In: a = np.array([[1,2],[3,4]]) 
In: a 
Out: 
array([[1, 2], 
       [3, 4]]) 

这次通过给array()函数一个列表来创建矩阵。我们现在将一次选择矩阵中的每一项,如下面的代码片段所示。回想一下,索引号从0开始:

In: a[0,0] 
Out: 1 
In: a[0,1] 
Out: 2 
In: a[1,0] 
Out: 3 
In: a[1,1] 
Out: 4 

如您所见,选择数组元素相当简单。对于数组a,我们只使用符号a[m,n],其中mn是数组中项目的索引。请看下图,以供参考:

Selecting NumPy array elements

数值类型

Python 有integer类型、float类型、complex类型;尽管如此,这还不足以进行科学计算。实际上,我们仍然需要更多精度不同的数据类型,因此需要不同的类型存储大小。因此,NumPy 有更多的数据类型。大多数 NumPy 数学类型以数字结尾。该数字表示与类型相关的位数。下表(改编自 NumPy 用户指南)概述了 NumPy 数字类型:

|

类型

|

描述

| | --- | --- | | bool | 布尔(TrueFalse)存储为一位 | | inti | 平台整数(通常为int32int64) | | int8 | 字节(-128 到 127) | | int16 | 整数(-32768 到 32767) | | int32 | 整数(-2 ** 31 至 2 ** 31 -1) | | int64 | 整数(-2 ** 63 至 2 ** 63 -1) | | uint8 | 无符号整数(0 到 255) | | uint16 | 无符号整数(0 到 65535) | | uint32 | 无符号整数(0 到 2 ** 32 - 1) | | uint64 | 无符号整数(0 到 2 ** 64 - 1) | | float16 | 半精密浮子;符号位、5 位指数和 10 位尾数 | | float32 | 单精度浮动;符号位、8 位指数和 23 位尾数 | | float64float | 双精度浮动;符号位、11 位指数和 52 位尾数 | | complex64 | 复数,由两个 32 位浮点(实部和虚部)表示 | | complex128complex | 复数,由两个 64 位浮点(实部和虚部)表示 |

对于每种数据类型,都有一个匹配的转换函数:

In: np.float64(42) 
Out: 42.0 
In: np.int8(42.0) 
Out: 42 
In: np.bool(42) 
Out: True 
In: np.bool(0) 
Out: False 
In: np.bool(42.0) 
Out: True 
In: np.float(True) 
Out: 1.0 
In: np.float(False) 
Out: 0.0 

许多函数都有一个数据类型参数,它通常是可选的:

In: np.arange(7, dtype= np.uint16) 
Out: array([0, 1, 2, 3, 4, 5, 6], dtype=uint16)

请注意,不允许将复数转换为整数,这一点很重要。试图做到这一点会引发TypeError:

In: np.int(42.0 + 1.j)Traceback (most recent call last):
<ipython-input-24-5c1cd108488d> in <module>()
----> 1 np.int(42.0 + 1.j)
TypeError: can't convert complex to int

复数到浮点数的转换也是如此。顺便说一下,j分量是一个复数的虚部系数。即便如此,你也可以把一个浮点数转换成复数;例如,complex(1.0)。复数的实数和虚数可以分别用real()imag()函数取出。

数据类型对象

数据类型对象是numpy.dtype类的实例。同样,数组也有数据类型。确切地说,NumPy 数组中的每个元素都有相同的数据类型。数据类型对象可以告诉您数据的大小(以字节为单位)。字节大小由dtype类的itemsize属性给出:

In: a.dtype.itemsize 
Out: 8 

字符代码

包含字符代码是为了向后兼容数字。NumPy 的前身是 NumPy。不建议使用它,但这里提供了代码,因为它会在不同的位置弹出。你应该用dtype对象代替。下表列出了几种不同的数据类型和与之相关的字符代码:

|

类型

|

字符代码

| | --- | --- | | integer | i | | unsigned integer | u | | single precision float | f | | double precision float | d | | bool | b | | complex | D | | string | S | | unicode | U | | void | V |

看看下面的代码,生成一个单精度浮点数组:

In: arange(7, dtype='f') 
Out: array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.], dtype=float32) 

同样,下面的代码创建了一个复数数组:

In: arange(7, dtype='D') 
Out: array([ 0.+0.j,  1.+0.j,  2.+0.j,  3.+0.j,  4.+0.j,  5.+0.j,   6.+0.j]) 

数据类型构造函数

我们有多种方法来创建数据类型。以浮点数据为例(看看本书代码包中的dtypeconstructors.py):

  • 我们可以使用通用 Python float,如下面几行代码所示:

            In: np.dtype(float) 
            Out: dtype('float64') 
    
    
  • 我们可以用字符代码指定一个单精度浮点数:

            In: np.dtype('f') 
            Out: dtype('float32') 
    
    
  • 我们可以使用双精度浮点数,其字符代码为:

            In: np.dtype('d') 
            Out: dtype('float64') 
    
    
  • We can pass the dtype constructor a two character code. The first character stands for the type; the second character is a number specifying the number of bytes in the type (the numbers 2, 4, and 8 correspond to floats of 16, 32, and 64 bits, respectively):

            In: np.dtype('f8') 
            Out: dtype('float64') 
    
    

    通过应用sctypeDict.keys(),可以找到所有完整数据类型代码的(截断的)列表:

    In: np.sctypeDict.keys() 
    Out: dict_keys(['?', 0, 'byte', 'b', 1, 'ubyte', 'B', 2, 'short', 'h', 3, 'ushort', 'H', 4, 'i', 5, 'uint', 'I', 6, 'intp', 'p', 7, 'uintp', 'P', 8, 'long', 'l', 'L', 'longlong', 'q', 9, 'ulonglong', 'Q', 10, 'half', 'e', 23, 'f', 11, 'double', 'd', 12, 'longdouble', 'g', 13, 'cfloat', 'F', 14, 'cdouble', 'D', 15, 'clongdouble', 'G', 16, 'O', 17, 'S', 18, 'unicode', 'U', 19, 'void', 'V', 20, 'M', 21, 'm', 22, 'bool8', 'Bool', 'b1', 'float16', 'Float16', 'f2', 'float32', 'Float32', 'f4', 'float64', 'Float64', 'f8', 'float128', 'Float128', 'f16', 'complex64', 'Complex32', 'c8', 'complex128', 'Complex64', 'c16', 'complex256', 'Complex128', 'c32', 'object0', 'Object0', 'bytes0', 'Bytes0', 'str0', 'Str0', 'void0', 'Void0', 'datetime64', 'Datetime64', 'M8', 'timedelta64', 'Timedelta64', 'm8', 'int64', 'uint64', 'Int64', 'UInt64', 'i8', 'u8', 'int32', 'uint32', 'Int32', 'UInt32', 'i4', 'u4', 'int16', 'uint16', 'Int16', 'UInt16', 'i2', 'u2', 'int8', 'uint8', 'Int8', 'UInt8', 'i1', 'u1', 'complex_', 'int0', 'uint0', 'single', 'csingle', 'singlecomplex', 'float_', 'intc', 'uintc', 'int_', 'longfloat', 'clongfloat', 'longcomplex', 'bool_', 'unicode_', 'object_', 'bytes_', 'str_', 'string_', 'int', 'float', 'complex', 'bool', 'object', 'str', 'bytes', 'a']) 
    
    

数据类型属性

dtype类有许多有用的属性。例如,我们可以通过dtype的属性获取数据类型的字符代码信息:

In: t = np.dtype('Float64') 
In: t.char 
Out: 'd' 

type 属性对应于数组元素的对象类型:

In: t.type 
Out: numpy.float64 

dtypestr属性给出了数据类型的字符串表示。它以一个表示字符顺序的字符开始,如果合适的话,然后是一个字符代码,后面是一个对应于每个数组项所需字节数的数字。这里的字节序是指字节在 32 或 64 位字中的排列方式。在大端顺序中,最重要的字节首先被存储,由>指示。按照小端顺序,首先存储最低有效字节,由<表示,如以下代码行所示:

In: t.str 
Out: '<f8' 

一维切片和索引

一维 NumPy 数组的切片就像标准 Python 列表的切片一样工作。让我们定义一个包含数字 0、1、2 等直到 8 的数组。我们可以从索引 3 到 7 中选择数组的一部分,它提取数组 3 到 6 的元素:

In: a = np.arange(9) 
In: a[3:7] 
Out: array([3, 4, 5, 6]) 

我们可以从 0 到 7 的索引中选择元素,增量为 2:

In: a[:7:2] 
Out: array([0, 2, 4, 6]) 

就像在 Python 中一样,我们可以使用负索引并反转数组:

In: a[::-1] 
Out: array([8, 7, 6, 5, 4, 3, 2, 1, 0]) 

操纵数组形状

我们已经了解了reshape()功能。另一个重复的任务是展平数组。在这种情况下,展平需要将多维数组转换为一维数组。让我们创建一个数组 b,我们将使用它来练习进一步的示例:

In: b = np.arange(24).reshape(2,3,4) 

In: print(b) 

Out: [[[ 0,  1,  2,  3], 
        [ 4,  5,  6,  7], 
        [ 8,  9, 10, 11]], 

       [[12, 13, 14, 15], 
        [16, 17, 18, 19], 
        [20, 21, 22, 23]]]) 

我们可以使用以下函数来操作数组形状:

  • 拉威尔:我们可以通过ravel()功能来完成,如下所示:

            In: b 
            Out: 
            array([[[ 0,  1,  2,  3], 
                    [ 4,  5,  6,  7], 
                    [ 8,  9, 10, 11]], 
                   [[12, 13, 14, 15], 
                    [16, 17, 18, 19], 
                    [20, 21, 22, 23]]]) 
            In: b.ravel() 
            Out: 
            array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,  13, 
            14, 15, 16, 17, 18, 19, 20, 21, 22, 23]) 
    
    
  • 展平:适当命名的功能flatten()ravel()相同。然而,flatten()总是分配新的内存,而ravel返回数组的视图。这意味着我们可以直接操作数组如下:

            In: b.flatten() 
            Out: 
            array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,  13, 
            14, 15, 16, 17, 18, 19, 20, 21, 22, 23]) 
    
    
  • 用元组设置形状:除了reshape()函数,我们还可以直接用元组定义形状,如下图所示:

            In: b.shape = (6,4) 
            In: b 
            Out: 
            array([[ 0,  1,  2,  3], 
                   [ 4,  5,  6,  7], 
                   [ 8,  9, 10, 11], 
                   [12, 13, 14, 15], 
                   [16, 17, 18, 19], 
                   [20, 21, 22, 23]]) 
    
    
  • 正如您所理解的,前面的代码立即改变了数组。现在,我们有一个 6x4 数组。

  • 转置:线性代数中,矩阵转置很常见。换位是一种转换数据的方式。对于二维表,换位意味着行变成列,列变成行。我们也可以通过使用以下代码来做到这一点:

            In: b.transpose() 
            Out: 
            array([[ 0,  4,  8, 12, 16, 20], 
                   [ 1,  5,  9, 13, 17, 21], 
                   [ 2,  6, 10, 14, 18, 22], 
                   [ 3,  7, 11, 15, 19, 23]]) 
    
    
  • 调整的大小:resize()方法的工作原理与reshape()方法相同,但改变了它所作用的数组:

            In: b.resize((2,12)) 
            In: b 
            Out: 
            array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11], 
                   [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]]) 
    
    

堆叠数组

数组可以水平、深度或垂直堆叠。为了这个目标,我们可以使用vstack()dstack()hstack()column_stack()row_stack()concatenate()功能。首先,让我们设置一些数组:

In: a = np.arange(9).reshape(3,3) 
In: a 
Out: 
array([[0, 1, 2], 
       [3, 4, 5], 
       [6, 7, 8]]) 
In: b = 2 * a 
In: b 
Out: 
array([[ 0,  2,  4], 
       [ 6,  8, 10], 
       [12, 14, 16]]) 

如前所述,我们可以使用以下技术堆叠数组:

  • 水平堆叠:从水平堆叠开始,我们将形成一个ndarrays元组,交给hstack()函数来堆叠数组。如下所示:

            In: np.hstack((a, b)) 
            Out: 
            array([[ 0,  1,  2,  0,  2,  4], 
                   [ 3,  4,  5,  6,  8, 10], 
                   [ 6,  7,  8, 12, 14, 16]]) 
    
    
  • We can attain the same thing with the concatenate() function, which is shown as follows:

            In: np.concatenate((a, b), axis=1) 
            Out: 
            array([[ 0,  1,  2,  0,  2,  4], 
                   [ 3,  4,  5,  6,  8, 10], 
                   [ 6,  7,  8, 12, 14, 16]]) 
    
    

    下图描述了水平堆叠:

    Stacking arrays

  • Vertical stacking: With vertical stacking, a tuple is formed again. This time it is given to the vstack() function to stack the arrays. This can be seen as follows:

            In: np.vstack((a, b)) 
            Out: 
            array([[ 0,  1,  2], 
                   [ 3,  4,  5], 
                   [ 6,  7,  8], 
                   [ 0,  2,  4], 
                   [ 6,  8, 10], 
                   [12, 14, 16]]) 
    
    

    concatenate()功能给出相同的结果,轴参数固定为0。这是 axis 参数的默认值,如以下代码所示:

              In: np.concatenate((a, b), axis=0) 
              Out: 
              array([[ 0,  1,  2], 
                   [ 3,  4,  5], 
                   [ 6,  7,  8], 
                   [ 0,  2,  4], 
                   [ 6,  8, 10], 
                   [12, 14, 16]]) 
    
    

    垂直堆放见下图:

    Stacking arrays

  • 深度堆叠:首先,深度堆叠使用dstack()和元组,当然。这需要沿着第三个轴(深度)堆叠数组列表。例如,我们可以将图像数据的 2D 数组堆叠在彼此之上,如下所示:

            In: np.dstack((a, b)) 
            Out: 
            array([[[ 0,  0], 
                    [ 1,  2], 
                    [ 2,  4]], 
                   [[ 3,  6], 
                    [ 4,  8], 
                    [ 5, 10]], 
                   [[ 6, 12], 
                    [ 7, 14], 
                    [ 8, 16]]]) 
    
    
  • Column stacking: The column_stack() function stacks 1D arrays column-wise. This is shown as follows:

            In: oned = np.arange(2) 
            In: oned 
            Out: array([0, 1]) 
            In: twice_oned = 2 * oned 
            In: twice_oned 
            Out: array([0, 2]) 
            In: np.column_stack((oned, twice_oned)) 
            Out: 
            array([[0, 0], 
                   [1, 2]]) 
    
    

    2D 数组的堆叠方式与hstack()函数的堆叠方式相同,如下面几行代码所示:

              In: np.column_stack((a, b)) 
              Out: 
              array([[ 0,  1,  2,  0,  2,  4], 
                     [ 3,  4,  5,  6,  8, 10], 
                     [ 6,  7,  8, 12, 14, 16]]) 
              In: np.column_stack((a, b)) == np.hstack((a, b)) 
              Out: 
              array([[ True,  True,  True,  True,  True,  True], 
                     [ True,  True,  True,  True,  True,  True], 
                     [ True,  True,  True,  True,  True,  True]],  
                     dtype=bool) 
    
    

    是的,你猜对了!我们使用==运算符比较了两个数组。

  • Row stacking: NumPy, naturally, also has a function that does row-wise stacking. It is named row_stack() and for 1D arrays, it just stacks the arrays in rows into a 2D array:

            In: np.row_stack((oned, twice_oned)) 
            Out: 
            array([[0, 1], 
                   [0, 2]]) 
    
    

    2D 数组的row_stack()函数结果等于vstack()函数结果:

              In: np.row_stack((a, b)) 
              Out: 
              array([[ 0,  1,  2], 
                     [ 3,  4,  5], 
                     [ 6,  7,  8], 
                     [ 0,  2,  4], 
                     [ 6,  8, 10], 
                     [12, 14, 16]]) 
              In: np.row_stack((a,b)) == np.vstack((a, b)) 
              Out: 
              array([[ True,  True,  True], 
                     [ True,  True,  True], 
                     [ True,  True,  True], 
                     [ True,  True,  True], 
                     [ True,  True,  True], 
                     [ True,  True,  True]], dtype=bool) 
    
    

拆分 NumPy 数组

数组可以垂直、水平或深度拆分。涉及的功能有hsplit()vsplit()dsplit()split()。我们可以将数组拆分成相同形状的数组,或者指出拆分应该发生的位置。让我们详细看看每个函数:

  • Horizontal splitting: The following code splits a 3x3 array on its horizontal axis into three parts of the same size and shape:

            In: a 
            Out: 
            array([[0, 1, 2], 
                   [3, 4, 5], 
                   [6, 7, 8]]) 
            In: np.hsplit(a, 3) 
            Out: 
            [array([[0], 
                   [3], 
                   [6]]), 
             array([[1], 
                   [4], 
                   [7]]), 
             array([[2], 
                   [5], 
                   [8]])] 
    
    

    将它比作对split()函数的调用,还有一个额外的参数axis=1:

              In: np.split(a, 3, axis=1) 
              Out: 
              [array([[0], 
                     [3], 
                     [6]]), 
               array([[1], 
                     [4], 
                     [7]]), 
               array([[2], 
                     [5], 
                     [8]])] 
    
    
  • Vertical splitting: vsplit() splits along the vertical axis:

            In: np.vsplit(a, 3) 
            Out: [array([[0, 1, 2]]), array([[3, 4, 5]]), 
                  array([[6, 7,  8]])] 
    
    

    带有axis=0split()功能也沿垂直轴拆分:

              In: np.split(a, 3, axis=0) 
              Out: [array([[0, 1, 2]]), array([[3, 4, 5]]), 
                    array([[6, 7,  8]])] 
    
    
  • 深度分裂:不出所料,dsplit()函数会深度分裂。我们需要一组等级3来开始:

            In: c = np.arange(27).reshape(3, 3, 3) 
            In: c 
            Out: 
            array([[[ 0,  1,  2], 
                    [ 3,  4,  5], 
                    [ 6,  7,  8]], 
                   [[ 9, 10, 11], 
                    [12, 13, 14], 
                    [15, 16, 17]], 
                   [[18, 19, 20], 
                    [21, 22, 23], 
                    [24, 25, 26]]]) 
            In: np.dsplit(c, 3) 
            Out: 
            [array([[[ 0], 
                    [ 3], 
                    [ 6]], 
                   [[ 9], 
                    [12], 
                    [15]], 
                   [[18], 
                    [21], 
                    [24]]]), 
             array([[[ 1], 
                    [ 4], 
                    [ 7]], 
                   [[10], 
                    [13], 
                    [16]], 
                   [[19], 
                    [22], 
                    [25]]]), 
             array([[[ 2], 
                    [ 5], 
                    [ 8]], 
                   [[11], 
                    [14], 
                    [17]], 
                   [[20], 
                    [23], 
                    [26]]])] 
    
    

NumPy 数组属性

让我们借助一个例子来了解更多关于 NumPy 数组属性的信息。让我们创建一个数组b,我们将使用它来练习进一步的示例:

In: b = np.arange(24).reshape(2, 12) 
In: b 
Out: 
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11], 
       [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]]) 

除了shapedtype属性外,ndarray还有许多其他属性,如下表所示:

  • ndim属性给出了维数,如下面的代码片段所示:

            In: b.ndim 
            Out: 2 
    
    
  • size属性保存元素的计数。如下所示:

            In: b.size 
            Out: 24 
    
    
  • itemsize属性返回数组中每个元素的字节数,如下面的代码片段所示:

            In: b.itemsize 
            Out: 8 
    
    
  • 如果您需要数组所需的完整字节数,可以查看nbytes。这只是itemsizesize属性的产物:

            In: b.nbytes 
            Out: 192 
            In: b.size * b.itemsize 
            Out: 192 
    
    
  • T属性的结果与transpose()函数相同,如下所示:

            In: b.resize(6,4) 
            In: b 
            Out: 
            array([[ 0,  1,  2,  3], 
                   [ 4,  5,  6,  7], 
                   [ 8,  9, 10, 11], 
                   [12, 13, 14, 15], 
                   [16, 17, 18, 19], 
                   [20, 21, 22, 23]]) 
            In: b.T 
            Out: 
            array([[ 0,  4,  8, 12, 16, 20], 
                   [ 1,  5,  9, 13, 17, 21], 
                   [ 2,  6, 10, 14, 18, 22], 
                   [ 3,  7, 11, 15, 19, 23]]) 
    
    
  • 如果数组的秩小于 2,我们将只获得数组的视图:

            In: b.ndim 
            Out: 1 
            In: b.T 
            Out: array([0, 1, 2, 3, 4]) 
    
    
  • NumPy 中的复数用j表示。例如,我们可以生成一个复数数组,如下所示:

            In: b = np.array([1.j + 1, 2.j + 3]) 
            In: b 
            Out: array([ 1.+1.j,  3.+2.j]) 
    
    
  • real属性向我们返回数组的实数部分,或者如果数组只包含实数,则返回数组本身:

            In: b.real 
            Out: array([ 1.,  3.]) 
    
    
  • imag属性保存数组的虚部:

            In: b.imag 
            Out: array([ 1.,  2.]) 
    
    
  • 如果数组包含复数,那么数据类型也会自动复杂:

            In: b.dtype 
            Out: dtype('complex128') 
            In: b.dtype.str 
            Out: '<c16' 
    
    
  • flat属性返回一个numpy.flatiter对象。这是获得一个flatiter对象的唯一手段;我们无法访问flatiter构造器。flat迭代器使我们能够像遍历平面数组一样遍历数组,如下面的代码片段所示:

            In: b = np.arange(4).reshape(2,2) 
            In: b 
            Out: 
            array([[0, 1], 
                   [2, 3]]) 
            In: f = b.flat 
            In: f 
            Out: <numpy.flatiter object at 0x103013e00> 
            In: for item in f: print(item) 
            Out:  
            0 
            1 
            2 
            3 
    
    
  • 可以直接获得具有flatiter对象的元素:

            In: b.flat[2] 
            Out: 2 
    
    
  • 另外,您可以获得多个元素,如下所示:

            In: b.flat[[1,3]] 
            Out: array([1, 3]) 
    
    
  • 可以设置flat属性。设置flat属性的值会覆盖整个数组的值:

            In: b.flat = 7 
            In: b 
            Out: 
            array([[7, 7], 
                   [7, 7]]) 
    
    
  • We can also obtain selected elements as follows:

            In: b.flat[[1,3]] = 1 
            In: b 
            Out: 
            array([[7, 1], 
                   [7, 1]]) 
    
    

    下图说明了ndarray的各种属性:

    NumPy array attributes

转换数组

我们可以用tolist()函数将 NumPy 数组转换成 Python 列表。以下是简要说明:

  • 转换为列表:

            In: b 
            Out: array([ 1.+1.j,  3.+2.j]) 
            In: b.tolist() 
            Out: [(1+1j), (3+2j)] 
    
    
  • astype()函数将数组转换为指定数据类型的数组:

            In: b 
            Out: array([ 1.+1.j,  3.+2.j]) 
            In: b.astype(int) 
            /usr/local/lib/python3.5/site-packages/ipykernel/__main__.py:1:  
            ComplexWarning: Casting complex values to real discards the 
            imaginary part
            ... 
            Out: array([1, 3]) 
            In: b.astype('complex') 
            Out: array([ 1.+1.j,  3.+2.j]) 
    
    

当从复数类型转换为整数类型时,我们去掉了虚部。astype()函数也将数据类型的名称作为字符串。

前面的代码这次不会显示警告,因为我们使用了正确的数据类型。

创建数组视图和副本

在关于ravel()的例子中,提出了一些观点。视图不应与数据库视图的构造混淆。NumPy 世界中的视图不是只读的,您没有保护底层信息的可能性。了解我们何时处理共享数组视图以及何时拥有数组数据的副本至关重要。例如,一个数组的切片将产生一个视图。这意味着,如果您将切片分配给一个变量,然后更改底层数组,该变量的值将会改变。我们将从 SciPy 包中的人脸图片创建一个数组,然后在最后阶段创建一个视图并修改它:

  1. 获取人脸图像:

            face = scipy.misc.face() 
    
    
  2. 创建面数组的副本:

            acopy = face.copy() 
    
    
  3. 创建数组视图:

            aview = face.view() 
    
    
  4. flat迭代器将视图中的所有值设置为0:

            aview.flat = 0 
    
    

最后的结果是只有一张图片描绘了这个模型。其他的全部被审查,如下图所示:

Creating array views and copies

请参考本教程的以下代码,其中显示了数组视图和副本的行为:

import scipy.misc 
import matplotlib.pyplot as plt 

face = scipy.misc.face() 
acopy = face.copy() 
aview = face.view() 
aview.flat = 0 
plt.subplot(221) 
plt.imshow(face) 
plt.subplot(222) 
plt.imshow(acopy) 
plt.subplot(223) 
plt.imshow(aview) 
plt.show() 

如您所见,通过改变程序末尾的视图,我们修改了原始的 Lena 数组。这导致了三张蓝色(或者黑色,如果你正在阅读这本书的印刷版)的图片。复制的数组没有改变。重要的是要记住视图不是只读的。

花式索引

花式索引是不涉及整数或切片的索引,是常规的索引。在本教程中,我们将练习花式索引,将 Lena 照片的对角线值设置为0。这将沿着对角线画黑线,穿过它们。

以下是该示例的代码:

import scipy.misc 
import matplotlib.pyplot as plt 

face = scipy.misc.face() 
xmax = face.shape[0] 
ymax = face.shape[1] 
face=face[:min(xmax,ymax),:min(xmax,ymax)] 
xmax = face.shape[0] 
ymax = face.shape[1] 
face[range(xmax), range(ymax)] = 0 
face[range(xmax-1,-1,-1), range(ymax)] = 0 
plt.imshow(face) 
plt.show() 

以下是前面代码的简要说明:

  1. Set the values of the first diagonal to 0.

    要将对角线值设置为0,我们需要为 xy 值(笛卡尔坐标系中的坐标)指定两个不同的范围:

            face[range(xmax), range(ymax)] = 0 
    
    
  2. Set the values of the other diagonal to 0.

    要设置另一条对角线的值,我们需要一组不同的范围,但规则保持不变:

              face[range(xmax-1,-1,-1), range(ymax)] = 0 
    
    

    在最后一个阶段,我们画出对角线被划掉的下图:

    Fancy indexing

我们为 x 值和 y 值指定了不同的范围。这些范围用于索引 Lena 数组。花式索引是基于内部 NumPy 迭代器对象完成的。执行以下三个步骤:

  1. 迭代器对象被创建。
  2. 迭代器对象绑定到数组。
  3. 数组元素通过迭代器访问。

用位置列表索引

让我们应用ix_()功能对 Lena 照片进行洗牌。下面是本例的代码,没有注释。食谱的最终代码可以在本书的代码包ix.py中找到:

import scipy.misc 
import matplotlib.pyplot as plt 
import numpy as np 

face = scipy.misc.face() 
xmax = face.shape[0] 
ymax = face.shape[1] 

def shuffle_indices(size): 
   arr = np.arange(size) 
   np.random.shuffle(arr) 

   return arr 

xindices = shuffle_indices(xmax) 
np.testing.assert_equal(len(xindices), xmax) 
yindices = shuffle_indices(ymax) 
np.testing.assert_equal(len(yindices), ymax) 
plt.imshow(face[np.ix_(xindices, yindices)]) 
plt.show() 

该函数从多个序列生成一个网格。我们将参数作为一维序列提交,函数返回一组 NumPy 数组,例如,如下所示:

In : ix_([0,1], [2,3]) 
Out: 
(array([[0],[1]]), array([[2, 3]])) 

要用位置列表索引 NumPy 数组,请执行以下步骤:

  1. Shuffle the array indices.

    numpy.random子包的shuffle()功能做一个随机索引号的数组。该函数就地修改数组:

              def shuffle_indices(size): 
                 arr = np.arange(size) 
                 np.random.shuffle(arr) 
    
              return arr 
    
    
  2. 绘制混合指数,如以下代码所示:

            plt.imshow(face[np.ix_(xindices, yindices)]) 
    
    

我们得到的是一个完全混乱的莉娜:

Indexing with a list of locations

用布尔值索引 NumPy 数组

布尔索引是基于布尔数组的索引,属于花式索引家族。因为布尔索引是一种花哨的索引,所以它的工作方式本质上是相同的。

以下是该段的代码(参见本书代码包中的boolean_indexing.py):

import scipy.misc 
import matplotlib.pyplot as plt 
import numpy as np 

face = scipy.misc.face() 
xmax = face.shape[0] 
ymax = face.shape[1] 
face=face[:min(xmax,ymax),:min(xmax,ymax)] 

def get_indices(size): 
   arr = np.arange(size) 
   return arr % 4 == 0 

face1 = face.copy()  
xindices = get_indices(face.shape[0]) 
yindices = get_indices(face.shape[1]) 
face1[xindices, yindices] = 0 
plt.subplot(211) 
plt.imshow(face1) 
face2 = face.copy()  
face2[(face > face.max()/4) & (face < 3 * face.max()/4)] = 0 
plt.subplot(212) 
plt.imshow(face2) 
plt.show() 

前面的代码意味着索引是在一个特殊的迭代器对象的帮助下进行的。

以下步骤将简要解释前面的代码:

  1. Image with dots on the diagonal.

    这在某种程度上类似于花式索引部分。这次我们选择图片对角线上的模 4 点:

              def get_indices(size):
               arr = np.arange(size)
               return arr % 4 == 0
    

    然后,我们只使用这个选择并绘制点:

              face1 = face.copy()  
              xindices = get_indices(face.shape[0]) 
              yindices = get_indices(face.shape[1]) 
              face1[xindices, yindices] = 0 
              plt.subplot(211) 
              plt.imshow(face1) 
    
    
  2. Set to 0 based on value.

    选择最大值四分之一到四分之三之间的数组值,并将其设置为0:

              face2[(face > face.max()/4) & (face < 3 * face.max()/4)] =  0 
    
    

    带有两张新图片的图表如下所示:

    Indexing NumPy arrays with Booleans

广播 NumPy 数组

NumPy 试图执行一个过程,即使操作数没有相同的形状。

在这个食谱中,我们将把一个数组和一个标量相乘。标量被扩展为数组操作数的形状,然后执行乘法。这里描述的过程叫做广播。以下是该配方的全部代码:

import scipy.io.wavfile as sw 
import matplotlib.pyplot as plt 
import urllib 
import numpy as np 

request = urllib.request.Request('http://www.thesoundarchive.com/austinpowers/smashingbaby.wav') 
response = urllib.request.urlopen(request) 
print(response.info()) 
WAV_FILE = 'smashingbaby.wav' 
filehandle = open(WAV_FILE, 'wb') 
filehandle.write(response.read()) 
filehandle.close() 
sample_rate, data = sw.read(WAV_FILE) 
print("Data type", data.dtype, "Shape", data.shape) 

plt.subplot(2, 1, 1) 
plt.title("Original") 
plt.plot(data) 

newdata = data * 0.2 
newdata = newdata.astype(np.uint8) 
print("Data type", newdata.dtype, "Shape", newdata.shape) 

sw.write("quiet.wav", sample_rate, newdata) 

plt.subplot(2, 1, 2) 
plt.title("Quiet") 
plt.plot(newdata)2 

plt.show() 

我们将下载一个声音文件,并创建一个更安静的新版本:

  1. Reading a WAV file.

    我们将使用标准的 Python 代码来下载一个声音文件,奥斯汀·鲍尔斯大声喊道:“粉碎,宝贝”SciPy 有一个wavfile子包,可以让你加载音频数据或者生成 WAV 文件。如果安装了 SciPy,那么我们应该已经有了这个子包。read()功能提供数据数组和采样率。在本练习中,我们只关注数据:

              sample_rate, data = scipy.io.wavfile.read(WAV_FILE) 
    
    
  2. Plot the original WAV data.

    用 Matplotlib 绘制原始 WAV 数据,给子图起标题Original:

              plt.subplot(2, 1, 1) 
              plt.title("Original") 
              plt.plot(data) 
    
    
  3. Create a new array.

    现在,我们将使用 NumPy 产生一个静音样本。只需要用一个常数乘以一个新数组,就可以得到一个更小的值。这就是广播的诀窍所在。最后,由于 WAV 格式,我们希望确保我们拥有与原始数组中相同的数据类型:

              newdata = data * 0.2 
              newdata = newdata.astype(np.uint8) 
    
    
  4. Write to a WAV file.

    这个新数组可以保存在一个新的 WAV 文件中,如下所示:

              scipy.io.wavfile.write("quiet.wav", 
                  sample_rate, newdata) 
    
    
  5. Plot the new WAV data.

    用 Matplotlib 绘制新的数据数组,如下所示:

              plt.subplot(2, 1, 2) 
              plt.title("Quiet") 
              plt.plot(newdata) 
              plt.show() 
    
    

    结果是原始 WAV 文件数据和具有较小值的新数组的图表,如下图所示:

    Broadcasting NumPy arrays

总结

在这一章中,我们发现了一堆关于 NumPy 的基础知识——数据类型和数组。数组有各种描述它们的属性。您了解到其中一个属性是数据类型,在 NumPy 中,它由一个完整的对象表示。

与标准 Python 列表相比,NumPy 数组可以以有效的方式进行切片和索引。NumPy 数组具有处理多维度的额外能力。

可以通过多种方式修改数组的形状,例如堆叠、调整大小、整形和拆分。本章介绍了大量用于形状操作的便利功能。

已经掌握了基础知识,现在是时候使用第 4 章统计和线性代数中的常用函数进行数据分析了。这包括使用主要的统计和数字函数。

鼓励读者阅读参考资料一节中提到的书籍,以便更详细、更深入地探索 NumPy。

参考文献

  • I. Idris, NumPy Cookbook -第二版, Packt 出版,2015。
  • I. Idris,学习 NumPy 数组, Packt 出版,2014。
  • I. Idris, Numpy:初学者指南-第三版, Packt 出版,2015。
  • 长度(梁辉)秦和 t .杜塔, NumPy Essentials, Packt 出版,2016。

三、Pandas 入门

Pandas 以面板数据(计量经济学术语)和 Python 数据分析命名,是一个流行的开源 Python 库。在本章中,我们将学习 Pandas 的基本功能、数据结构和操作。

官方 Pandas 文件坚持用小写字母命名项目“T1”Pandas“T2”。Pandas 项目坚持的另一个惯例是import pandas as pd进口声明。

我们将在本文中遵循这些约定。

在本章中,我们将安装和探索 Pandas。然后,我们将熟悉 Pandas 的两个核心数据结构——数据框架和系列。之后,您将学习如何对这些数据结构中包含的数据执行类似于 SQL 的操作。Pandas 有统计工具,包括时间序列例程,其中一些将被演示。我们将研究的主题如下:

  • 安装和探索 Pandas
  • Pandas 数据帧
  • Pandas 系列
  • Pandas 中的数据查询
  • Pandas 数据帧的统计
  • Pandas 数据帧的数据聚合
  • 连接和附加数据帧
  • 正在加入数据框
  • 处理缺失值
  • 处理日期
  • 数据透视表

安装和探索 Pandas

Pandas 的最小依赖集要求如下:

  • NumPy :这是我们安装的基本数值数组包,在前面的章节中已经详细介绍过了
  • python-dateutil :这是一个日期处理库
  • pytz :这处理时区定义

这份清单是最起码的;可选依赖项的更长列表可以位于pandas.pydata.org/pandas-docs…。我们可以使用二进制安装程序,借助我们的操作系统包管理器,或者通过检查代码从源代码,通过 PyPI 用pipeasy_install安装 Pandas。二进制安装程序可以从pandas.pydata.org/getpandas.h…下载。

pip安装 Pandas 的命令如下:

$ pip3 install pandas rpy2

rpy2是 R 的接口,因为rpy被弃用,所以需要。如果您的用户帐户没有足够的权限,您可能需要在前面的命令前面加上sudo

正如我们在第 1 章Python 库入门中看到的,我们可以打印 Pandas 的版本和子包。该程序为 Pandas 打印了以下输出:

pandas version 0.19.0
pandas.api
pandas.compat DESCRIPTION compat  Cross-compatible functions for Python 2 and 3\. Key items to import for 2/3 compatible code: * iterators: range(), map(), 
pandas.computation
pandas.core
pandas.formats
pandas.indexes
pandas.io
pandas.msgpack DESCRIPTION # coding: utf-8 # flake8: noqa PACKAGE CONTENTS _packer _unpacker _version exceptions CLASSES ExtType(builtins.tuple) ExtType cl 
pandas.rpy DESCRIPTION # GH9602 # deprecate rpy to instead directly use rpy2 PACKAGE CONTENTS base common mass vars FILE /usr/local/lib/python3.5/site- 
pandas.sparse
pandas.stats
pandas.tests
pandas.tools
pandas.tseries
pandas.types
pandas.util

不幸的是,Pandas 子包的文档缺乏信息描述;然而,子包名称的描述性足以让我们了解它们的内容。

Pandas 数据帧

Pandas 数据框架是一种带标签的二维数据结构,在精神上类似于谷歌工作表或微软 Excel 中的工作表,或者关系数据库表。Pandas 数据框中的列可以是不同的类型。顺便说一下,类似的概念最初是在 R 编程语言中发明的。(更多信息请参考www.r-tutor.com/r-introduct…)。可以通过以下方式创建数据框:

  • 使用另一个数据帧。
  • 使用 NumPy 数组或具有二维形状的数组组合。
  • 同样,我们可以从另一个 Pandas 数据结构创建一个数据帧,称为系列。我们将在下一节中了解系列。
  • 数据帧也可以从文件中生成,例如 CSV 文件。
  • 来自一维结构的字典,如一维 NumPy 数组、列表、字典或 Pandas 系列。

例如,我们将使用可以从www.exploredata.net/Downloads/W…检索的数据。原始数据文件比较大,列比较多,我们就用一个编辑好的文件代替,只包含前九列,叫做WHO_first9cols.csv;文件在这本书的代码包里。这是前两行,包括标题:

Country,CountryID,Continent,Adolescent fertility rate (%),Adult literacy rate (%),Gross national income per capita (PPP international $),Net primary school enrolment ratio female (%),Net primary school enrolment ratio male (%),Population (in thousands) total
Afghanistan,1,1,151,28,,,,26088

在接下来的步骤中,我们将了解 Pandas 数据帧及其属性:

  1. To kick off, load the data file into a DataFrame and print it on the screen:

              from pandas.io.parsers import read_csv 
    
              df = read_csv("WHO_first9cols.csv") 
              print("Dataframe", df) 
    
    

    打印输出是数据框的摘要。它太长,无法完全显示,所以我们将只抓取最后几行:

              199                          21732.0   
              200                          11696.0   
              201                          13228.0
    
              [202 rows x 9 columns]
    
  2. The DataFrame has an attribute that holds its shape as a tuple, similar to ndarray. Query the number of rows of a DataFrame as follows:

            print("Shape", df.shape) 
            print("Length", len(df)) 
    
    

    我们获得的值符合上一步的打印输出:

            Shape (202, 9) 
            Length 202 
    
    
  3. Check the column's header and data types with the other attributes:

            print("Column Headers", df.columns) 
            print("Data types", df.dtypes) 
    
    

    我们接收特殊数据结构中的列标题:

              Column Headers Index([u'Country', u'CountryID', u'Continent',  
              u'Adolescent fertility rate (%)', u'Adult literacy rate (%)', 
              u'Gross national income per capita (PPP international $)', 
              u'Net primary school enrolment ratio female (%)', 
              u'Net primary school enrolment ratio male (%)', 
              u'Population (in thousands) total'], dtype='object') 
    
    

    数据类型打印如下:

    The Pandas DataFrames

  4. The Pandas DataFrame has an index, which is like the primary key of relational database tables. We can either specify the index or have Pandas create it automatically. The index can be accessed with a corresponding property, as follows:

            Print("Index", df.index) 
    
    

    索引帮助我们快速搜索项目,就像本书中的索引一样。在我们的例子中,索引是从0开始的数组的包装,每行增加一个:

              Index Int64Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 
              13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 
              28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 
              43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 
              58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 
              73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 
              88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...], 
              dtype='int64')
    
  5. 有时,我们希望迭代数据框架的底层数据。如果我们使用 Pandas 迭代器,对列值进行迭代可能效率很低。提取底层的 NumPy 数组并使用它们要好得多。Pandas 数据框也有一个属性可以帮助你做到这一点:

            print("Values", df.values) 
    
    

请注意,有些值在输出中被指定为nan,表示“不是数字”。这些值来自输入数据文件中的空字段:

Values [['Afghanistan' 1 1 ..., nan nan 26088.0] 
 ['Albania' 2 2 ..., 93.0 94.0 3172.0] 
 ['Algeria' 3 3 ..., 94.0 96.0 33351.0]
 ..., 
 ['Yemen' 200 1 ..., 65.0 85.0 21732.0] 
 ['Zambia' 201 3 ..., 94.0 90.0 11696.0] 
 ['Zimbabwe' 202 3 ..., 88.0 87.0 13228.0]]

前面的代码可以在 Python 笔记本ch-03.ipynb中找到,可以在本书的代码包中找到。

Pandas 系列

PandasSeries数据结构是一个带有标签的一维异构数组。我们可以创建 PandasSeries数据结构如下:

  • 使用 Python 格言
  • 使用 NumPy 数组
  • 使用单个标量值

当创建一个系列时,我们可以给构造函数一个轴标签的列表,这通常被称为索引。索引是一个可选参数。默认情况下,如果我们使用 NumPy 数组作为输入数据,Pandas 将通过从 0 开始自动递增索引来索引值。如果传递给构造函数的数据是 Python 字典,排序后的字典键将成为索引。在标量值作为输入数据的情况下,我们需要提供索引。对于索引中的每个新值,将重复标量输入值。Pandas 系列和数据框架接口的特性和行为借鉴了 NumPy 数组和 Python 字典,例如切片、使用键的查找函数和向量化操作。对DataFrame列执行查找会返回Series。我们将通过返回上一节并再次加载 CSV 文件来演示Series的这一特性和其他特性:

  1. We will start by selecting the Country column, which happens to be the first column in the datafile. Then, show the type of the object currently in the local scope:

            country_col = df["Country"] 
            print("Type df", type(df)) 
            print("Type country col", type(country_col)) 
    
    

    我们现在可以确认,当我们选择数据框的一列时,我们得到了一个系列:

              Type df <class 'pandas.core.frame.DataFrame'>
              Type country col  <class 'pandas.core.series.Series'>
    

    如果需要,您可以打开 Python 或 IPython shell,导入 Pandas,并使用dir()函数查看上一次打印输出中找到的类的函数和属性列表。但是,请注意,在这两种情况下,您都会得到一长串函数。

  2. The Pandas Series data structure shares some of the attributes of DataFrame, and also has a name attribute. Explore these properties as follows:

            print("Series shape", country_col.shape) 
            print("Series index", country_col.index) 
            print("Series values", country_col.values) 
            print("Series name", country_col.name) 
    
    

    输出(为了节省空间而被截断)如下所示:

              Series shape (202,)
              Series index Int64Index([0, 1, 2, 3, 4, 5, 
              6, 7, 8, 9, 10, 11, 12, ...], dtype='int64')
              Series values ['Afghanistan' ... 'Vietnam' 'West Bank and          
              Gaza' 'Yemen' 'Zambia' 'Zimbabwe']
              Series name Country
    
  3. To demonstrate the slicing of a Series, select the last two countries of the Country Series and print the type:

            print("Last 2 countries", country_col[-2:]) 
            print("Last 2 countries type", type(country_col[-2:])) 
    
    

    切片产生另一个系列,如下所示:

              Last 2 countries
              200      Zambia
              201    Zimbabwe
              Name: Country, dtype: object
              Last 2 countries type <class 'pandas.core.series.Series'>
    
  4. NumPy functions can operate on Pandas DataFrame and Series. We can, for instance, apply the NumPy sign() function, which yields the sign of a number. 1 is returned for positive numbers, -1 for negative numbers, and 0 for zeros. Apply the function to the DataFrame's last column, which happens to be the population for each country in the dataset:

            last_col = df.columns[-1] 
            print("Last df column signs:\n", last_col, 
            np.sign(df[last_col]), "\n") 
    
    

    为了节省空间,输出在这里被截断,如下所示:

              Last df column signs Population (in thousands) total 0     1
              1     1
              [TRUNCATED]
              198   NaN
              199     1
              200     1
              201     1
              Name: Population (in thousands) total, Length: 202, dtype: 
              float64
    

请注意,指数 198 的人口值为NaN。匹配记录如下:West Bank and Gaza,199,1,,,,,,

我们可以在数据帧、序列和 NumPy 数组之间执行各种数值操作。如果我们得到 Pandas 系列的基本 NumPy 数组,并从该系列中减去该数组,我们可以合理地预期以下两个结果:

  • 用零和至少一个 NaN 填充的数组(我们在上一步中看到了一个 NaN)
  • 我们也可以期望只得到零

NumPy 函数的规则是为涉及 nan 的大多数操作生成 nan,如以下 IPython 会话所示:

In: np.sum([0, np.nan])
Out: nan

编写以下代码来执行减法:

print np.sum(df[last_col] - df[last_col].values) 

该代码片段产生第二个选项预测的结果:

0.0

请参考本书代码包中的ch-03.ipynb文件。

查询 Pandas 的数据

由于 Pandas 数据框的结构类似于关系数据库,因此我们可以将从数据框中读取数据的操作视为查询。在这个例子中,我们将从 Quandl 检索年太阳黑子数据。我们可以使用 Quandl API 或者从http://www . Quandl . com/SIDC/黑子 _ A-黑子-数字-年度手动下载数据作为 CSV 文件。如果您想安装应用编程接口,您可以通过从pypi.python.org/pypi/Quandl下载安装程序或运行以下命令来完成:

$ pip3 install Quandl

使用该应用编程接口是免费的,但仅限于每天 50 次应用编程接口调用。如果您需要更多的 API 调用,您将不得不请求一个身份验证密钥。本教程中的代码没有使用键。将代码更改为使用密钥或读取下载的 CSV 文件应该很简单。如果您有困难,请参考第 1 章 中的哪里可以找到帮助和参考资料部分,或者在docs.python.org/2/的 Python 文档中搜索。

没有进一步的前言,让我们来看看如何查询 Pandas 数据框中的数据:

  1. 作为第一步,我们显然必须下载数据。导入 Quandl API 后,得到如下数据:

            import quandl 
    
            # Data from 
            http://www.quandl.com/SIDC/SUNSPOTS_A-Sunspot-Numbers-Annual 
            # PyPi url https://pypi.python.org/pypi/Quandl 
            sunspots = quandl.get("SIDC/SUNSPOTS_A") 
    
    
  2. The head() and tail() methods have a purpose similar to that of the Unix commands with the same name. Select the first n and last n records of a DataFrame, where n is an integer parameter:

            print("Head 2", sunspots.head(2) ) 
            print("Tail 2", sunspots.tail(2)) 
    
    

    这给了我们太阳黑子数据的前两行和后两行(为了简洁起见,我们这里没有显示所有的列;您的输出将包含数据集的所有列):

              Head 2             Number
              Year              
              1700-12-31         5
              1701-12-31        11
    
              [2 rows x 1 columns]
              Tail 2             Number
              Year              
              2012-12-31    57.7
              2013-12-31    64.9
    
              [2 rows x 1 columns]
    

    请注意,我们只有一栏记录每年太阳黑子的数量。日期是数据框索引的一部分。

  3. The following is the query for the last value using the last date:

            last_date = sunspots.index[-1] 
            print("Last value", sunspots.loc[last_date]) 
    
    

    您可以使用上一步的结果检查以下输出:

              Last value Number    64.9
              Name: 2013-12-31 00:00:00, dtype: float64
    
  4. Query the date with date strings in the YYYYMMDD format as follows:

            print("Values slice by date:\n", sunspots["20020101": 
            "20131231"]) 
    
    

    这给出了从 2002 年到 2013 年的记录:

              Values slice by date             Number
              Year              
              2002-12-31   104.0
              [TRUNCATED]
              2013-12-31    64.9
    
              [12 rows x 1 columns]
    
  5. A list of indices can be used to query as well:

            print("Slice from a list of indices:\n", sunspots.iloc[[2, 4, 
            -4, -2]]) 
    
    

    前面的代码选择了下列行:

              Slice from a list of indices             Number
              Year              
              1702-12-31    16.0
              1704-12-31    36.0
              2010-12-31    16.0
              2012-12-31    57.7
    
              [4 rows x 1 columns]
    
  6. To select scalar values, we have two options. The second option given here should be faster. Two integers are required, the first for the row and the second for the column:

            print("Scalar with Iloc:", sunspots.iloc[0, 0]) 
            print("Scalar with iat", sunspots.iat[1, 0]) 
    
    

    这给了我们数据集的第一个和第二个标量值:

              Scalar with Iloc 5.0
              Scalar with iat 11.0
    
  7. Querying with Booleans works much like the Where clause of SQL. The following code queries for values larger than the arithmetic mean. Note that there is a difference between when we perform the query on the whole DataFrame and when we perform it on a single column:

            print("Boolean selection", sunspots[sunspots > 
            sunspots.mean()]) 
            print("Boolean selection with column label:\n",          
            sunspots[sunspots['Number of Observations'] > sunspots['Number 
            of Observations'].mean()]) 
    
    

    值得注意的区别是,第一个查询生成所有的行,有些行不符合 NaN 值的条件。第二个查询只返回值大于平均值的行:

                Boolean selection             Number
                Year                      
                1700-12-31     NaN
                [TRUNCATED]
                1759-12-31    54.0              
                               ...
    
               [314 rows x 1 columns]
               Boolean selection with column label             Number
               Year              
               1705-12-31    58.0
               [TRUNCATED]
               1870-12-31   139.1               
                              ...
    
               [127 rows x 1 columns]
    

前面的示例代码在本书代码包的ch_03.ipynb文件中。

Pandas 数据帧统计

Pandas 数据框架有十几种统计方法。下表列出了这些方法,以及每种方法的简短描述:

| **方法** | **描述** | | `describe` | 此方法返回一个带有描述性统计信息的小表。 | | `count` | 此方法返回非 NaN 项的数量。 | | `mad` | 该方法计算平均绝对偏差,这是一种类似于标准偏差的稳健度量。 | | `median` | 此方法返回中位数。这相当于第 50 个百分点的值。 | | `min` | 此方法返回最低值。 | | `max` | 此方法返回最高值。 | | `mode` | 此方法返回模式,这是最常出现的值。 | | `std` | 此方法返回标准偏差,用于测量离差。它是方差的平方根。 | | `var` | 此方法返回方差。 | | `skew` | 此方法返回偏斜度。偏斜度表示分布对称。 | | `kurt` | 此方法返回峰度。峰度表示分布形状。 |

使用与前面示例相同的数据,我们将演示这些统计方法。完整的脚本在本书的代码包ch-03.ipynb中:

import quandl 

# Data from http://www.quandl.com/SIDC/SUNSPOTS_A-Sunspot-Numbers-Annual 
# PyPi url https://pypi.python.org/pypi/Quandl 
sunspots = quandl.get("SIDC/SUNSPOTS_A") 
print("Describe", sunspots.describe(),"\n") 
print("Non NaN observations", sunspots.count(),"\n") 
print("MAD", sunspots.mad(),"\n") 
print("Median", sunspots.median(),"\n") 
print("Min", sunspots.min(),"\n") 
print("Max", sunspots.max(),"\n") 
print("Mode", sunspots.mode(),"\n") 
print("Standard Deviation", sunspots.std(),"\n") 
print("Variance", sunspots.var(),"\n") 
print("Skewness", sunspots.skew(),"\n") 
print("Kurtosis", sunspots.kurt(),"\n") 

以下是脚本的输出:

Statistics with Pandas DataFrames

Statistics with Pandas DataFrames

Pandas 数据帧的数据聚合

数据聚合是关系数据库领域中使用的一个术语。在数据库查询中,我们可以按一列或多列中的值对数据进行分组。然后,我们可以对每个组执行各种操作。Pandas 数据框也有类似的功能。我们将生成保存在 Python 字典中的数据,然后使用这些数据创建 Pandas 数据框架。然后,我们将练习 Pandas 聚合功能:

  1. Seed the NumPy random generator to make sure that the generated data will not differ between repeated program runs. The data will have four columns:

    • Weather(一串)
    • Food(也是一串)
    • Price(随机浮动)
    • Number(1 到 9 之间的随机整数)

    用例是,我们有某种消费者购买研究的结果,结合天气和市场定价,我们计算价格的平均值,并跟踪样本大小和参数:

                import pandas as pd
                from numpy.random import seed
                from numpy.random import rand
                from numpy.random import rand_int
                import numpy as np
    
                seed(42)
    
                df = pd.DataFrame({'Weather' : ['cold', 'hot', 'cold',   
                'hot', 'cold', 'hot', 'cold'],
                'Food' : ['soup', 'soup', 'icecream', 'chocolate',
                'icecream', 'icecream', 'soup'],
                'Price' : 10 * rand(7), 'Number' : rand_int(1, 9,)})
                 print(df)
    

    您应该会得到类似如下的输出:

                      Food  Number     Price Weather
               0       soup       8  3.745401    cold
               1       soup       5  9.507143     hot
               2   icecream       4  7.319939    cold
               3  chocolate       8  5.986585     hot
               4   icecream       8  1.560186    cold
               5   icecream       3  1.559945     hot
               6       soup       6  0.580836    cold
    
               [7 rows x 4 columns]
    

    请注意,列标签来自 Python 字典的词汇顺序键。词汇或词典顺序基于字符串中字符的字母顺序。

  2. Group the data by the Weather column and then iterate through the groups as follows:

            weather_group = df.groupby('Weather') 
    
            i = 0 
    
            for name, group in weather_group: 
               i = i + 1 
               print("Group", i, name) 
               print(group) 
    
    

    我们有两种天气,热的和冷的,所以我们有两组:

               Group 1 cold
                      Food  Number     Price Weather
               0      soup       8  3.745401    cold
               2  icecream       4  7.319939    cold
               4  icecream       8  1.560186    cold
               6      soup       6  0.580836    cold
    
               [4 rows x 4 columns]
               Group 2 hot
                       Food  Number     Price Weather
               1       soup       5  9.507143     hot
               3  chocolate       8  5.986585     hot
               5   icecream       3  1.559945     hot
    
               [3 rows x 4 columns]
    
  3. The weather_group variable is a special Pandas object that we get as a result of the groupby() method. This object has aggregation methods, which are demonstrated as follows:

            print("Weather group first\n", weather_group.first()) 
            print("Weather group last\n", weather_group.last()) 
            print("Weather group mean\n", weather_group.mean()) 
    
    

    前面的代码片段打印了每组的第一行、最后一行和平均值:

               Weather group first
                        Food  Number     Price
               Weather                        
               cold     soup       8  3.745401
               hot      soup       5  9.507143
    
               [2 rows x 3 columns]
               Weather group last
                        Food  Number     Price
               Weather                            
               cold         soup       6  0.580836
               hot      icecream       3  1.559945
    
               [2 rows x 3 columns]
               Weather group mean           Number     Price
               Weather                    
               cold     6.500000  3.301591
               hot      5.333333  5.684558
    
               [2 rows x 2 columns]
    
  4. Just as in a database query, we are allowed to group on multiple columns. The groups attribute will then tell us the groups that are formed, as well as the rows in each group:

            wf_group = df.groupby(['Weather', 'Food']) 
            print("WF Groups", wf_group.groups) 
    
    

    对于天气和食物价值的每种可能组合,都会创建一个新的组。每行的成员资格由其索引值表示,如下所示:

            WF Groups {('hot', 'chocolate'): [3], ('cold', 'icecream'):
            [2, 4], ('hot', 'icecream'): [5], ('hot', 'soup'): [1],  
            ('cold', 'soup'): [0, 6]} 
    
    
  5. Apply a list of NumPy functions on groups with the agg() method:

            print("WF Aggregated\n", wf_group.agg([np.mean, np.median])) 
    
    

    显然,我们可以应用更多的函数,但是它看起来比下面的输出更混乱:

    WF Aggregated                    Number             Price                                            
                                       mean  median      mean    median
    Weather Food
    cold    icecream                      6       6  4.440063  4.440063        
            soup                          7       7  2.163119  2.163119
    hot     chocolate                     8       8  5.986585  5.986585              
            icecream                      3       3  1.559945  1.559945           
            soup                          5       5  9.507143  9.507143
    
    [5 rows x 4 columns]
    

完整的数据聚合示例代码在ch-03.ipynb文件中,可以在本书的代码包中找到。

连接和追加数据帧

Pandas 数据框架允许类似于数据库表的内部和外部连接的操作。我们还可以追加和连接行。为了练习追加和连接行,我们将重用上一节中的数据帧。让我们选择前三行:

print("df :3\n", df[:3]) 

检查这些是否确实是前三行:

df :3
       Food  Number     Price Weather
0      soup       8  3.745401    cold
1      soup       5  9.507143     hot
2  icecream       4  7.319939    cold

concat()函数连接数据帧。例如,我们可以将由三行组成的数据框连接到其余的行,以便重新创建原始数据框:

print("Concat Back together\n", pd.concat([df[:3], df[3:]])) 

串联输出如下所示:

Concat Back together
        Food  Number     Price Weather
0       soup       8  3.745401    cold
1       soup       5  9.507143     hot
2   icecream       4  7.319939    cold
3  chocolate       8  5.986585     hot
4   icecream       8  1.560186    cold
5   icecream       3  1.559945     hot
6       soup       6  0.580836    cold

[7 rows x 4 columns]

要追加行,使用append()功能:

print("Appending rows\n", df[:3].append(df[5:])) 

结果是一个DataFrame,原始DataFrame的前三行和最后两行附加在它上面:

Appending rows
       Food  Number     Price Weather
0      soup       8  3.745401    cold
1      soup       5  9.507143     hot
2  icecream       4  7.319939    cold
5  icecream       3  1.559945     hot
6      soup       6  0.580836    cold

[5 rows x 4 columns]

连接数据帧

为了演示加入,我们将使用两个 CSV 文件- dest.csvtips.csv。背后的用例是我们在经营一家出租车公司。每次乘客在目的地下车时,我们都会在dest.csv文件中添加一行司机的员工编号和目的地:

EmpNr,Dest5,The Hague3,Amsterdam9,Rotterdam

有时候司机会得到提示,所以我们希望在tips.csv文件中注册(如果这看起来不现实,请随意想出自己的故事):

EmpNr,Amount5,109,57,2.5

Pandas 中类似数据库的连接可以通过merge()函数或join()数据框方法来完成。join()方法默认连接到指数,这可能不是你想要的。在关系数据库查询语言 SQL 中,我们有内部连接、左外部连接、右外部连接和完全外部连接。

对于联接条件中指定的列,当且仅当值匹配时,内部联接从两个表中选择行。外部联接不需要匹配,并且可能返回更多的行。更多关于连接的信息可以在en.wikipedia.org/wiki/Join_%…找到。

Pandas 支持所有这些连接类型,但我们只看一下内部连接和完全外部连接:

  • A join on the employee number with the merge() function is performed as follows:

            print("Merge() on key\n", pd.merge(dests, tips, on='EmpNr')) 
    
    

    这就产生了一个内部连接:

              Merge() on key   
                 EmpNr       Dest  Amount
              0      5  The Hague      10
              1      9  Rotterdam       5
    
              [2 rows x 3 columns]
    
  • Joining with the join() method requires providing suffixes for the left and right operands:

            print("Dests join() tips\n", dests.join(tips, lsuffix='Dest',  
            rsuffix='Tips')) 
    
    

    此方法调用联接索引值,因此结果不同于 SQL 内部联接:

               Dests join() tips
                  EmpNrDest       Dest  EmpNrTips  Amount
               0          5  The Hague          5    10.0
               1          3  Amsterdam          9     5.0
               2          9  Rotterdam          7     2.5
    
               [3 rows x 4 columns]
    
  • An even more explicit way to execute an inner join with merge() is as follows:

            print("Inner join with merge()\n", pd.merge(dests, tips, 
            how='inner')) 
    
    

    输出如下:

               Inner join with merge()
                  EmpNr       Dest  Amount
               0      5  The Hague      10
               1      9  Rotterdam       5
    
               [2 rows x 3 columns]
    

    要使这成为完全的外部连接,只需要一个小的改变:

                print("Outer join\n", pd.merge(dests, tips, how='outer')) 
    
    

    外部连接添加具有NaN值的行:

                Outer join
                   EmpNr       Dest  Amount
                0      5  The Hague    10.0
                1      3  Amsterdam     NaN
                2      9  Rotterdam     5.0
                3      7        NaN     2.5
    
                [4 rows x 3 columns]
    

在关系数据库查询中,这些值会被设置为NULL。演示代码在本书代码包的ch-03.ipynb文件中。

处理缺失值

我们经常在数据记录中遇到空字段。我们最好接受这一点,并学习如何以稳健的方式处理这类问题。真实的数据不仅可能有缺口,还可能有错误的值,例如,由于测量设备故障。在 Pandas 中,缺少的数值将被指定为NaN,对象将被指定为None,而datetime64对象将被指定为NaT。带有NaN值的算术运算的结果也是NaN。描述性统计方法,如求和和平均,表现不同。正如我们在前面的例子中观察到的,在这种情况下,NaN值被视为零值。然而,如果在求和过程中所有的值都是NaN,返回的总和仍然是NaN。在聚合操作中,我们分组的列中的NaN值被忽略。我们将再次将WHO_first9cols.csv文件加载到数据框中。请记住,该文件包含空字段。我们只选择前三行,包括CountryNet primary school enrolment ratio male (%)列的标题如下:

df = df[['Country', df.columns[-2]]][:2] 
print("New df\n", df) 

我们得到一个具有两个 NaN 值的数据帧:

New df
       Country  Net primary school enrolment ratio male (%)
0  Afghanistan                                          NaN
1      Albania                                           94

[2 rows x 2 columns]

Pandasisnull()功能检查缺失值,如下所示:

print("Null Values\n", pd.isnull(df)) 

我们的数据帧输出如下:

Null Values
  Country Net primary school enrolment ratio male (%)
0   False                                        True
1   False                                       False

为了计算每一列的NaN值的数量,我们可以对isnull()返回的布尔值求和。这是因为在求和过程中,True值被视为 1,False值被视为 0:

Total Null Values
Country                                        0
Net primary school enrolment ratio male (%)    1
dtype: int64

同样,我们可以使用数据框notnull()方法检查任何存在的非缺失值:

print("Not Null Values\n", df.notnull()) 

notnull()方法的结果与isnull()功能相反:

Not Null Values
  Country Net primary school enrolment ratio male (%)
0    True                                       False
1    True                                        True

当我们将具有NaN值的数据帧中的值加倍时,乘积仍将包含NaN值,因为加倍是一种算术运算:

print("Last Column Doubled\n", 2 * df[df.columns[-1]]) 

我们将包含数值的最后一列加倍(将字符串值加倍会重复字符串):

Last Column Doubled
0    NaN
1    188
Name: Net primary school enrolment ratio male (%), dtype: float64

然而,如果我们添加一个NaN值,则NaN值获胜:

print("Last Column plus NaN\n", df[df.columns[-1]] + np.nan) 

如你所见,NaN值宣布完全胜利:

Last Column plus NaN
0   NaN1   NaN
Name: Net primary school enrolment ratio male (%), dtype: float64

用标量值替换缺少的值。例如,用fillna()方法替换0(我们不能总是用零替换丢失的值,但有时这已经足够好了):

print("Zero filled\n", df.fillna(0)) 

前一行的效果是将 NaN 值替换为 0:

Zero filled
       Country  Net primary school enrolment ratio male (%)
0  Afghanistan                                            0
1      Albania                                           94

本节的代码在本书代码包的ch-03.ipynb文件中:

处理日期

日期很复杂。想想千年虫,悬而未决的 2038 年问题,时区造成的混乱。一团糟。在处理时间序列数据时,我们自然会遇到日期。Pandas 可以创建日期范围,对时间序列数据重新采样,并执行日期算术运算。

创建从 1900 年 1 月 1 日开始并持续 42 天的日期范围,如下所示:

print("Date range", pd.date_range('1/1/1900', periods=42, freq='D')) 

一月不到 42 天,所以结束日期在二月,因为你可以自己检查:

Date range <class 'pandas.tseries.index.DatetimeIndex'>
[1900-01-01, ..., 1900-02-11]
Length: 42, Freq: D, Timezone: None

Pandas 官方文档中的下表描述了 Pandas 使用的频率:

|

分类代码

|

描述

| | --- | --- | | B | 工作日频率 | | C | 自定义工作日频率(实验性) | | D | 日历日频率 | | W | 每周频率 | | M | 月末频率 | | BM | 营业月末频率 | | MS | 月份开始频率 | | BMS | 营业月开始频率 | | Q | 四分之一结束频率 | | BQ | 业务季度结束频率 | | QS | 四分之一开始频率 | | BQS | 业务季度开始频率 | | A | 年终频率 | | BA | 营业年度结束频率 | | AS | 年度开始频率 | | BAS | 业务年度开始频率 | | H | 每小时频率 | | T | 微小频率 | | S | 其次是频率 | | L | 毫秒 | | U | 微秒 |

Pandas 的日期范围是有限制的。Pandas 中的时间戳(基于 NumPy datetime64数据类型)由一个 64 位整数表示,分辨率为纳秒(十亿分之一秒)。这将合法时间戳限制在大约介于 1677 年和 2262 年之间的日期范围内(并非这些年中的所有日期都有效)。这个范围的确切中点是 1970 年 1 月 1 日。例如,1677 年 1 月 1 日不能用 Pandas 时间戳定义,而 1677 年 9 月 30 日可以,如以下代码片段所示:

try: 
   print("Date range", pd.date_range('1/1/1677', periods=4, freq='D')) 
except: 
   etype, value, _ = sys.exc_info() 
   print("Error encountered", etype, value) 

该代码片段打印以下错误消息:

Date range Error encountered <class 'pandas.tslib.OutOfBoundsDatetime'> Out of bounds nanosecond timestamp: 1677-01-01 00:00:00

给定所有先前的信息,用 PandasDateOffset计算允许的日期范围如下:

offset = DateOffset(seconds=2 ** 33/10 ** 9) 
mid = pd.to_datetime('1/1/1970') 
print("Start valid range", mid - offset) 
print("End valid range", mid + offset') 

我们得到以下范围值:

Start valid range 1969-12-31 23:59:51.410065 
End valid range 1970-01-01 00:00:08.589935

我们可以把字符串列表转换成 Pandas 的日期。当然,不是所有的字符串都可以转换。如果 Pandas 无法转换字符串,通常会报告一个错误。有时,由于不同地区定义日期的方式不同,可能会出现歧义。在这种情况下,请使用格式字符串,如下所示:

print("With format", pd.to_datetime(['19021112', '19031230'], format='%Y%m%d')) 

字符串应该在不发生错误的情况下进行转换:

With format [datetime.datetime(1902, 11, 12, 0, 0) datetime.datetime(1903, 12, 30, 0, 0)]

如果我们尝试转换一个字符串(显然不是日期),默认情况下该字符串不会被转换:

print("Illegal date", pd.to_datetime(['1902-11-12', 'not a date'])) 

不应转换列表中的第二个字符串:

Illegal date ['1902-11-12' 'not a date']

要强制转换,请将coerce参数设置为True:

print("Illegal date coerced", pd.to_datetime(['1902-11-12', 'not a date'], errors='coerce')) 

显然,第二个字符串仍然不能转换为日期,所以我们能给它的唯一有效值是NaT(“不是时间”):

Illegal date coerced <class 'pandas.tseries.index.DatetimeIndex'>
[1902-11-12, NaT]Length: 2, Freq: None, Timezone: None

这个例子的代码在本书的代码包ch-03.ipynb中。

数据透视表

Excel 中使用的透视表汇总数据。到目前为止,我们在本章中看到的 CSV 文件中的数据都在平面文件中。数据透视表从平面文件中为某些列和行聚合数据。聚合操作可以是求和、求平均值、标准差等。我们将从ch-03.ipynb开始重用数据生成代码。Pandas 应用编程接口有一个顶级的pivot_table()函数和一个相应的数据框架方法。使用aggfunc参数,我们可以指定聚合函数,比如说,使用 NumPy sum()函数。cols参数告诉 Pandas 要聚合的列。在Food列上创建一个透视表,如下所示:

print(pd.pivot_table(df, cols=['Food'], aggfunc=np.sum)) 

我们得到的数据透视表包含每种食物的总数:

Food    chocolate   icecream      soup
Number   8.000000  15.000000  19.00000
Price    5.986585  10.440071  13.83338

[2 rows x 3 columns]

前面的代码可以在本书的代码包ch-03.ipynb中找到。

总结

在这一章中,我们重点介绍了 Pandas——一个 Python 数据分析库。这是一个关于 Pandas 的基本特征和数据结构的入门教程。我们看到了 Pandas 的大量功能是如何模仿关系数据库表的,允许我们高效地查询、聚合和操作数据。NumPy 和 Pandas 很好地合作,使得进行基本的统计分析成为可能。在这一点上,你可能会想 Pandas 是我们进行数据分析所需要的一切。然而,数据分析不仅仅是表面现象。

已经掌握了基础知识,现在是时候使用第 4 章统计和线性代数中的常用函数进行数据分析了。这包括使用主要的统计和数字函数。

鼓励读者阅读参考资料部分提到的书籍,以更详细和更深入地探索 Pandas。

参考文献

  1. 泰德·佩特罗Pandas 食谱帕克特出版**2017
  2. F .安东尼掌握 Pandas帕克特出版**2015
  3. M. Heydt ,*掌握 Pandas 财经,*T4】Packt 出版, 2015
  4. t . haukPandas 数据密集型应用-操作指南Packt Publishing2013
  5. m . Heydt学习 PandasPackt 出版**2015