Python 数据可视化秘籍(一)

原文:Python Data Visualization Cookbook

协议:CC BY-NC-SA 4.0

零、前言

最好的数据是我们能看到和理解的数据。作为开发人员,我们希望创建和构建最全面、最容易理解的可视化。它并不总是简单的;我们需要找到数据,阅读它,清洗它,按摩它,然后使用正确的工具来可视化它。这本书解释了如何用简单明了的方法来阅读、清理数据并将其可视化为信息的过程。

如何读取本地数据、远程数据、CSV、JSON 以及关系数据库中的数据,这本书都有讲解。

使用 matplotlib 可以在 Python 中用简单的一行代码绘制一些简单的图表,但是做更高级的图表需要的知识不仅仅是 Python。我们需要理解信息论和人类感知美学来产生最吸引人的视觉化。

这本书将解释 Python 中 matplotlib 绘图背后的一些实践,使用的统计数据,以及我们应该以最佳方式使用的不同图表功能的使用示例。

这本书是用 Python 2.7、IPython 0.13.2、virtualenv 1.9.1、matplotlib 1.2.1、NumPy 1.7.1 和 SciPy 0.11.0 编写的,代码是在 Ubuntu 12.03 上开发的。

这本书涵盖了什么

第 1 章准备您的工作环境,涵盖了一套关于如何在您的平台上安装所需 Python 包和库的安装方法和建议。

第 2 章了解你的数据,向你介绍常见的数据格式以及如何读写它们,无论是 CSV、JSON、XSL 还是关系数据库。

第三章绘制你的第一个图并定制它们,从绘制简单的图开始,涵盖了一些定制。

第 4 章更多绘图和定制,延续前一章,涵盖更高级的图表和网格定制。

第 5 章制作三维可视化,涵盖三维数据可视化,如三维条、三维直方图,以及 matplotlib 动画。

第 6 章用图像和地图绘制图表,涵盖图像处理、将数据投影到地图上以及创建验证码测试图像。

第 7 章使用正确的图来理解数据,涵盖了一些更高级的绘图技术的解释和配方,如光谱图和相关性。

第 8 章更多关于 matplotlib Gems 的内容,涵盖了甘特图、箱线图、触须图等一系列图表,还说明了如何在 matplotlib 中使用 LaTeX 渲染文本。

这本书你需要什么

对于这本书,您需要在操作系统上安装 Python 2.7.3 或更高版本。这本书是使用 Ubuntu 12.03 的 Python 默认版本(2.7.3)编写的。

本书使用的其他软件包是 IPython,这是一个非常强大、灵活的交互式 Python 环境。这可以使用基于 Linux 的操作系统的包管理器来安装,也可以使用为 Windows 和 Mac 操作系统准备的安装程序来安装。

如果您是 Python 安装和软件安装的新手,非常建议您使用预打包的科学 Python 发行版,如 Anaconda、entnown Python 发行版或 Python(X,Y)。

其他所需软件主要由 Python 包组成,这些包都是使用 Python 安装管理器pip安装的,该管理器本身是使用 Python 的easy_install安装工具安装的。

这本书是给谁的

Python 数据可视化烹饪书是为已经对 Python 编程有了大致了解的开发人员准备的。如果你听说过数据可视化,但不知道从哪里开始,这本书将从一开始就指导你,帮助你理解数据、数据格式、数据可视化,以及如何使用 Python 可视化数据。

你需要了解一些通用的编程概念,任何编程经验都会有所帮助。然而,这本书里的代码几乎是一行行解释的。这本书不需要数学;引入的每个概念都用简单的英语进行了全面的解释,并且有参考资料可以让你对这个主题产生进一步的兴趣。

惯例

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

文本中的码字如下所示:“我们在类DemoPIL中打包了我们的小演示,这样我们可以轻松扩展,同时共享围绕演示函数run_fixed_filters_demo的公共代码。”

代码块设置如下:

def_load_image(self, imfile): self.im = mplimage.imread(imfile)

当我们希望将您的注意力吸引到代码块的特定部分时,相关的行或项目以粗体显示:

# tidy up tick labels size  all_axes = plt.gcf().axes for ax in all_axes:for ticklabel in ax.get_xticklabels()+ ax.get_yticklabels(): ticklabel.set_fontsize(10)

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

$ sudo python setup.py install 

新名词重要词语以粗体显示。你在屏幕上看到的单词,例如在菜单或对话框中,出现在如下文本中:“然后我们为主干图和基线位置设置一个标签,默认为 0

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

型式

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

读者反馈

我们随时欢迎读者的反馈。让我们知道你对这本书的看法——你喜欢或可能不喜欢什么。读者反馈对我们开发您真正能从中获得最大收益的标题非常重要。

要给我们发送一般反馈,只需向<[[email protected]](mailto:[email protected])>发送电子邮件,并通过您的消息主题提及书名。

如果你对某个主题有专业知识,并且对写作或投稿感兴趣,请参阅我们在www.packtpub.com/authors上的作者指南。

客户支持

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

下载示例代码

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

勘误表

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

盗版

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

请通过<[[email protected]](mailto:[email protected])>联系我们,获取疑似盗版资料的链接。

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

问题

如果您对本书的任何方面有问题,可以在<[[email protected]](mailto:[email protected])>联系我们,我们将尽最大努力解决。

一、准备你的工作环境

在本章中,我们将介绍以下食谱:

  • 安装 matplotlib、NumPy 和 SciPy
  • 安装 virtualenv 和 virtualenvwrapper
  • 在 Mac OS X 上安装 matplotlib
  • 在 Windows 上安装 matplotlib
  • 安装用于图像处理的 Python 图像库(PIL)
  • 安装请求模块
  • 在代码中自定义 matplotlib 的参数
  • 为每个项目定制 matplotlib 的参数

简介

本章向读者介绍基本的工具以及它们的安装和配置。这是本书其余部分的必要工作和共同基础。如果您从未使用 Python 进行数据和图像处理及可视化,建议不要跳过这一章。即使您跳过它,您也可以随时返回本章,以防您需要安装一些支持工具或验证您需要什么版本来支持当前的解决方案。

安装 matplotlib、NumPy 和 SciPy

本章描述了在 Linux 下安装 matplotlib 和所需依赖项的几种方式。

做好准备

我们假设你已经安装了 Linux(最好是 Debian/Ubuntu 或者红帽/SciLinux)并且上面安装了 Python。通常,Python 已经安装在提到的 Linux 发行版上,如果没有,它可以通过标准方式轻松安装。我们假设您的工作站上安装了 Python 2.7+版本。

几乎所有的代码都应该使用 Python 3.3+版本,但是因为大多数操作系统仍然提供 Python 2.7(有些甚至是 Python 2.6),我们决定编写 Python 2.7 版本的代码。差别很小,主要是包的版本和一些代码(在 Python 3.3+中,xrange 应该用 range 代替)。

我们还假设您知道如何使用操作系统包管理器来安装软件包,并知道如何使用终端。

在构建 matplotlib 之前,必须满足构建要求。

matplotlib 需要 NumPylibpng 、和 freetype 作为构建依赖项。为了能够从源代码构建 matplotlib,我们必须安装 NumPy。以下是如何做到这一点:

http://www.numpy.org/安装 NumPy(至少 1.4+,或者 1.5+如果你想和 Python 3 一起使用的话)。

NumPy 将为我们提供数据结构和数学函数,用于大数据集。Python 的默认数据结构,如元组、列表或字典,非常适合插入、删除和连接。NumPy 的数据结构支持“矢量化”操作,使用和执行都非常高效。它们是在考虑大数据的情况下实现的,并且依赖于允许高效执行时间的 C 实现。

SciPy,在 NumPy 之上构建,是事实上标准的科学和数字 Python 工具包,包括大量精选的特殊函数和算法,其中大多数实际上是用 C 和 Fortran 实现的,来自著名的 Netlib 存储库(参见http://www.netlib.org)。

执行以下步骤安装 NumPy:

    • libpng 1.2 : PNG 文件支持(需要 zlib)

freetype 1.4+ :真字体支持

 $ sudo apt-get install build-dep python-matplotlib 

Install the required libraries:如果您正在使用红帽或该发行版的变体(Fedora、SciLinux 或 CentOS),您可以使用 yum 来执行相同的安装:

 $ su -c 'yum-builddep python-matplotlib'

检查安装版本:

$ python -c 'import numpy; print numpy.__version__'

安装 Python-NumPy 包:

$ sudo apt-get install python-numpy 

怎么做…

可以通过多种方式安装 matplotlib 及其依赖项:从源代码、预编译的二进制文件、从 OS 包管理器,以及使用预打包的 python 发行版和内置的 matplotlib。

最有可能的最简单的方法是使用你的发行包管理器。对于 Ubuntu,应该是:

# in your terminal, type: $ sudo apt-get install python-numpy python-matplotlib python-scipy 

如果你想在流血的边缘,最好的选择是从源头安装。这条路径包括几个步骤:获取源代码、构建需求以及配置、编译和安装。

按照以下步骤,通过从代码主机www.github.com下载最新源代码:

$ cd ~/Downloads/ $ wget https://github.com/downloads/matplotlib/matplotlib/matplotlib-1.2.0.tar.gz $ tar xzf matplotlib-1.2.0.tar.gz $ cd matplotlib-1.2.0 $ python setup.py build $ sudo python setup.py install 

型式

下载示例代码

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

它是如何工作的…

我们使用标准的 Python 分发实用程序,被称为 Distutils ,从源代码安装 matplotlib。这个过程需要我们预先安装依赖项,正如我们已经在本食谱的准备部分解释的那样。依赖项使用标准的 Linux 打包工具安装。

还有更多…

根据您的数据可视化项目,您可能需要安装更多的可选包。

不不管你在做什么项目,我们都建议安装 IPython——一个支持 PyLab 模式的 Interactive Python shell,在这里你已经有了 matplotlib 和相关的包,比如 NumPy 和 SciPy,已经导入并可以玩了!请参考 IPython 的官方网站,了解如何安装和使用它——尽管它非常简单。

安装 virtualenv 和 virtualenvwrapper

如果你同时在多个项目上工作,或者甚至只是频繁地在它们之间切换,你会发现在系统范围内安装所有的东西并不是最好的选择,并且会在未来在你想要运行软件的不同系统(生产)上带来问题。这不是发现您缺少某个包或者已经安装在生产系统上的包之间存在版本冲突的好时机;因此,virtualenv。

virtualenv 是由 Ian Bicking 启动的一个开源项目,它使开发者能够隔离每个项目的工作环境,以便于不同包版本的维护。

例如,您继承了基于 Django 1.1 和 Python 2.3 的遗留 Django 网站,但同时您正在处理一个必须用 Python 2.6 编写的新项目。这是我通常的情况——根据我正在做的项目,有不止一个必需的 Python 版本(和相关的包)。

virtualenv 使我能够轻松切换到不同的环境,如果我需要切换到另一台机器或将软件部署到生产服务器(或客户端的工作站),也可以轻松复制相同的软件包。

做好准备

要安装 virtualenv,您必须安装 Python 和 pip。Pip 是一个安装和管理 Python 包的工具,它是简单安装的替代品。我们将在本书的大部分内容中使用 pip 进行包管理。Pip 很容易安装,因为 root 会在您的终端中执行以下代码:

# easy_install pip

virtualenv by 本身确实很有用,但是在 virtualenvwrapper 的帮助下,这一切变得很容易做到,也很容易组织很多虚拟环境。参见上的所有功能。

怎么做…

通过执行以下步骤,您可以安装 virtualenv 和 virtualenvwrapper 工具:

您可能想要将以下行添加到您的~/.bashrc文件中:

source /usr/loca/bin/virtualenvwrapper.sh 

现在可以在virt1 :

(virt1)user1:~$ pip install matplotlib 

里面安装我们喜欢的包了

安装 virtualenv 和 virtualenvwrapper:

$ sudo pip virtualenv $ sudo pip virtualenvwrapper # Create folder to hold all our virtual environments and export the path to it. $ export VIRTENV=~/.virtualenvs $ mkdir -p $VIRTENV # We source (ie. execute) shell script to activate the wrappers $ source /usr/local/bin/virtualenvwrapper.sh # And create our first virtual environment $ mkvirtualenv virt1 

少数有用的和最常用的命令如下:

  • mkvirtualenv ENV:此创建名为 ENV 的虚拟环境并激活
  • workon ENV:这激活之前创建的 ENV
  • deactivate:这个让我们脱离了当前的虚拟环境

在 Mac OS X 上安装 matplotlib

在 Mac OS X 上获取 matplotlib 最简单的方法是使用预打包的 python 发行版,如**【entnown Python 发行版】** ( EPD )。去 EPD 网站下载安装最新稳定版你的 OS 就行了。

如果您对 EPD 不满意或者因为其他原因无法使用,例如随其分发的版本,有一种手动(阅读:hard)方式安装 Python、matplotlib 及其依赖项。

做好准备

我们将使用 自制软件项目来简化苹果没有在你的操作系统上安装的所有软件的安装,包括 Python 和 matplotlib。在幕后,家酿是一套自动下载和安装的 Ruby 和 Git。遵循这些说明应该可以让安装工作。首先,我们将安装 Homebrew,然后是 Python,接着是 virtualenv 等工具,然后是 matplotlib 的依赖项(NumPy 和 SciPy),最后是 matplotlib。坚持住,我们走。

怎么做…

  1. 要验证安装是否有效,请在命令行中键入python --version,您应该会在响应中看到2.7.3作为版本号。
  2. 安装 matplot lib:t0]

确认一切正常。调用 Python 并执行以下命令:

import numpy print numpy.__version__ import scipy print scipy.__version__ quit()

Next step is what we really wanted to do all along—install matplotlib:

pip install numpy brew install gfortran pip install scipy 

山狮用户将需要通过执行以下代码来安装 SciPy (0.11)的开发版本:

pip install -e git+https://github.com/scipy/scipy#egg=scipy-dev

现在,安装任何所需的软件包都很容易;例如,virtualenv 和 virtualenvwrapper 都很有用:

pip install virtualenv pip install virtualenvwrapper 

你现在应该已经安装了 pip。如果没有安装,使用easy_install添加 pip:

$ easy_install pip 

现在,您需要更新您的路径(添加到同一行):

export PATH=/usr/local/share/python:/usr/local/bin:$PATH 

You will need to restart the terminal so it picks a new path. Installing Python is as easy as firing up another one-liner:

brew install python --framework --universal 

这也将安装 Python 所需的任何先决条件。

接下来,将自制程序目录添加到您的系统路径中,这样您使用自制程序安装的软件包比其他版本具有更高的优先级。打开~/.bash_profile(或/Users/[your-user-name]/.bash_profile)并在文件末尾添加以下一行:

export PATH=/usr/local/bin:$PATH 

In your Terminal paste and execute the following command:

ruby <(curl -fsSkL raw.github.com/mxcl/homebrew/go)

命令完成后,尝试运行 brew update 或 brew doctor 来验证安装是否正常工作。

在 Windows 上安装 matplotlib

在这个食谱中,我们将演示如何安装 Python 并开始使用 matplotlib 安装。我们假设 Python 不是之前安装的。

做好准备

在 Windows 上安装 matplotlib 有两种方式。更简单的方法是通过安装预打包的 Python 环境,如 EPD、Anaconda 和 Python(x,y)。这是建议安装 Python 的方法,尤其是对于初学者。

第二种方法是使用预编译 matplotlib 的二进制文件和所需的依赖项来安装所有内容。这更困难,因为您必须小心您正在安装的 NumPy 和 SciPy 版本,因为不是每个版本都与最新版本的 matplotlib 二进制文件兼容。这样做的好处是,你甚至可以编译你的 matplotlib 的特定版本或任何具有最新特性的库,即使它们不是由作者提供的。

怎么做…

安装免费或商业 Python 科学发行版的建议方法就像遵循项目网站上提供的步骤一样简单。

如果您只是想开始使用 matplotlib,不想被 Python 版本和依赖关系所困扰,那么您可能想考虑使用 entsure Python 发行版(EPD)。EPD 包含使用 matplotlib 所需的预打包库以及所有必需的依赖项(SciPy、NumPy、IPython 等)。

像往常一样,我们下载 Windows Installer ( *.exe),它将安装我们开始使用 matplotlib 所需的所有代码以及本书中的所有食谱。

还有一个免费的科学项目 Python(x,y)(http://code.google.com/p/pythonxy/)针对 Windows 32 位系统,包含所有解析的依赖项,是一个轻松(而且免费!)在 Windows 上安装 matplotlib 的方式。因为 Python(x,y)与 Python 模块安装程序兼容,所以可以很容易地用其他 Python 库扩展。在安装 Python(x,y)之前,系统上不应存在 Python 安装。

让我简短地解释一下,我们将如何使用预编译的 Python、NumPy、SciPy 和 matplotlib 二进制文件来安装 matplotlib。首先,我们使用针对我们平台(x86 或 x86-64)的官方 MSI Installer 下载并安装标准 Python。之后下载 NumPy 和 SciPy 的官方二进制文件,先安装。当您确定 NumPy 和 SciPy 安装正确后,我们将下载 matplotlib 的最新稳定版本二进制文件,并按照官方说明进行安装。

还有更多…

请注意,许多示例没有包含在 Windows 安装程序中。如果您想尝试演示,请下载 matplotlib 源代码并查看示例子目录。

安装 Python 图像库(PIL)进行图像处理

Python 图像库 ( PIL )使用 Python 实现图像处理,具有广泛的文件格式支持,对于图像处理足够强大。

PIL 的一些流行特征是快速访问数据、点操作、过滤、图像大小调整、旋转和任意仿射变换。例如,直方图方法允许我们获得关于图像的统计数据。

PIL 还可以用于其他目的,例如批处理、图像存档、创建缩略图、图像格式之间的转换以及打印图像。

PIL 读大量的格式,而写支持(有意地)限于最常用的交换和表示格式。

怎么做…

最简单也是最推荐的方法是使用平台的包管理器。对于 Debian/Ubuntu 使用以下命令:

$ sudo apt-get build-dep python-imaging $ sudo pip install http://effbot.org/downloads/Imaging-1.1.7.tar.gz 

它是如何工作的…

这样我们就满足了所有使用apt-get系统的构建依赖,同时也安装了 PIL 最新的稳定版本。一些旧版本的 Ubuntu 通常不提供最新版本。

在 RedHat/SciLinux 上:

# yum install python-imaging# yum install freetype-devel# pip install PIL

还有更多…

有一本很好的在线手册,特别是针对 PIL 的。可以在http://www.pythonware.com/library/pil/handbook/index.htm阅读,也可以从http://www.pythonware.com/media/data/pil-handbook.pdf下载 PDF 版本。

还有一个 PIL 叉,枕头,其主要目的是解决安装问题。枕头可以在http://pypi.python.org/pypi/Pillow找到,安装方便。

在 Windows 上,也可以使用二进制安装文件安装 PIL。通过从http://www.pythonware.com/products/pil/执行.exe在您的 Python 站点包中安装 PIL。

现在,如果您希望在虚拟环境中使用 PIL,请手动将PIL.pth文件和位于C:\Python27\Lib\site-packages的 PIL 目录复制到您的 virtualenv 站点包目录中。

安装请求模块

我们现在需要的大多数数据都可以通过 HTTP 或类似的协议获得,所以我们需要一些东西来获取它。Python 库请求使这项工作变得简单。

尽管 Python 附带了 urllib2 模块来处理远程资源并支持 HTTP 功能,但它需要大量工作来完成基本任务。

请求模块带来了新的应用编程接口,使网络服务的使用变得无缝和无痛苦。许多 HTTP 1.1 的东西被隐藏起来,只有当你需要它的行为不同于默认的时候才会暴露出来。

怎么做…

使用 pip 是安装请求的最佳方式。使用以下命令进行同样的操作:

$ pip install requests 

就这样。如果您不需要每个项目的请求,或者想要支持每个项目的不同请求版本,这也可以在 virtualenv 中完成。

为了让您快速取得成功,这里有一个关于如何使用请求的小例子:

import requests r = requests.get('http://github.com/timeline.json')print r.content 

它是如何工作的…

我们将GET HTTP 请求发送到位于www.github.com的一个 URI,该请求返回一个 JSON 格式的 GitHub 上的活动时间表(您可以在https://github.com/timeline上看到该时间表的 HTML 版本)。成功读取响应后,r对象包含响应的内容和其他属性(响应代码、cookies 集、头元数据,甚至是我们为了获得该响应而发送的请求)。

在代码中自定义 matplotlib 的参数

我们在本书中最常用的图书馆是 matplotlib 它提供绘图功能。大多数属性的默认值已经在 matplotlib 的配置文件中设置,称为.rc 文件。这个配方描述了如何从我们的应用代码中修改 matplotlib 属性。

做好准备

正如我们已经说过的,matplotlib 配置是从配置文件中读取的。这个文件为 matplotlib 的某些属性提供了一个设置永久默认值的地方,几乎可以设置 matplotlib 中的所有属性。

怎么做…

在代码执行过程中,有两种方法可以更改参数:使用参数字典(rcParams)或调用matplotlib.rc()命令。前者使我们能够将已经存在的字典加载到rcParams中,而后者使我们能够使用关键字参数的元组来调用函数。

如果要恢复动态变化的参数,可以使用matplotlib.rcdefaults()调用恢复标准 matplotlib 设置。

以下两个代码示例说明了前面解释的行为:

matplotlib.rcParams示例:

import matplotlib as mp mpl.rcParams['lines.linewidth']=2 mpl.rcParams['lines.color']='r'

matplotlib.rc()呼叫示例:

import matplotlib as mpl mpl.rc('lines', linewidth=2, color='r')

这两个例子在语义上是相同的。在第二个示例中,我们定义所有后续的图将具有线宽为 2 点的线。前面代码的最后一条语句定义了这条语句后面的每一行的颜色都是红色,除非我们通过本地设置覆盖它。请参见以下示例:

import matplotlib.pyplot as plt import numpy as np t = np.arange(0.0,1.0,0.01) s = np.sin(2* np.pi * t)# make line red plt.rcParams['lines.color']='r' plt.plot(t,s) c = np.cos(2* np.pi * t)# make line thick plt.rcParams['lines.linewidth']= '3 plt.plot(t,c) plt.show()

它是如何工作的…

首先,我们导入matplotlib.pyplot和 NumPy,允许我们绘制正弦和余弦图。在绘制第一个图形之前,我们使用plt.rcParams['lines.color'] = 'r'将线条颜色明确设置为红色。

接下来,我们转到第二个图(余弦函数),使用plt.rcParams['lines.linewidth'] = '3'显式设置线宽为 3 点。

如果要重置具体设置,应该调用matplotlib.rcdefaults()

定制每个项目的 matplotlib 参数

这个食谱解释了 matplotlib 使用的各种配置文件在哪里,以及为什么我们想要使用其中一个。此外,我们解释了这些配置文件中的内容。

做好准备

如果您不想在每次使用 matplotlib 时都将其配置为代码的第一步(就像我们在上一个食谱中所做的那样),这个食谱将解释如何为不同的项目配置不同的 matplotlib 默认配置。这样,您的代码就不会被配置数据弄得乱七八糟,而且,您可以轻松地与同事甚至其他项目共享配置模板。

怎么做…

如果您的工作项目总是对 matplotlib 中的某些参数使用相同的设置,您可能不希望每次添加新的图形代码时都设置它们。相反,您需要的是代码之外的一个永久文件,它为 matplotlib 参数设置默认值。

matplotlib 通过其matplotlibrc配置文件支持这一点,该文件包含 matplotlib 的大部分可变属性。

它是如何工作的…

这个文件可以存在三个不同的地方,它的位置定义了它的用途。它们是:

  • 当前工作目录:这是你的代码运行的地方。这是为可能包含当前项目代码的当前目录定制 matplotlib 的地方。文件名为matplotlibrc
  • 每个用户。matplotlib/matplotlibrc :这通常是在用户的$HOME目录下(在 Windows 下,这是你的Documents and Settings目录)。您可以使用matplotlib.get_configdir()命令找到您的配置目录。检查下一个命令。
  • 每个安装配置文件:这通常在您的 python 站点包中。这是一个系统范围的配置,但是每次重新安装 matplotlib 时都会被覆盖;因此,最好使用每个用户配置文件进行更持久的自定义。到目前为止,对我来说,最好的用法是,如果我弄乱了用户的配置文件,或者如果我需要新的配置来为不同的项目进行定制,就将它用作默认模板。

下面的一行代码将打印配置目录的位置,并且可以从 shell 运行。

$ python -c 'import matplotlib as mpl; print mpl.get_configdir()'

配置文件包含以下设置:

  • axes:处理面和边缘颜色、刻度尺寸和网格显示。
  • backend:设置目标输出:TkAggGTKAgg
  • figure:处理 dpi、边缘颜色、图表尺寸和子图设置。
  • font:查看字体系列、字体大小和样式设置。
  • grid:处理网格颜色和线条设置。
  • legend:指定内部的图例和文本将如何显示。
  • lines:检查线条(颜色、样式、宽度等)和标记设置。
  • patch:面片是填充 2D 空间的图形对象,比如多边形、圆形;设置线宽、颜色、抗锯齿等。
  • savefig:保存的图形有单独的设置。例如,制作白色背景的渲染文件。
  • text:这看起来像文本颜色的,如何解释文本(纯文本和乳胶标记)等等。
  • verbose:它检查 matplotlib 在运行时给出了多少信息:静默、有帮助、调试和令人讨厌的调试。
  • xticksyticks:设置 x 轴和 y 轴的大刻度和小刻度的颜色、大小、方向和标签大小。

还有更多…

如果您对每个提到的设置(以及一些我们在这里没有提到的设置)的更多细节感兴趣,最好去 matplotlib 项目的网站,那里有最新的 API 文档。如果没有帮助,用户和开发列表总是留下问题的好地方。有关有用的在线资源,请参见本书的背面。

二、了解您的数据

在这一章中,我们将涵盖以下食谱:

  • 从 CSV 导入数据
  • 从微软 Excel 文件导入数据
  • 从固定宽度数据文件导入数据
  • 从制表符分隔的文件导入数据
  • 从 JSON 资源导入数据
  • 将数据导出到 JSON、CSV 和 Excel
  • 从数据库导入数据
  • 从异常值中清除数据
  • 分块读取文件
  • 读取流数据源
  • 将图像数据导入 NumPy 数组
  • 生成受控随机数据集
  • 平滑真实数据中的噪声

简介

本章介绍从各种格式导入和导出数据的基本知识。还介绍了清理数据的方法,例如标准化值、添加缺失数据、实时数据检查,以及使用一些类似的技巧来为可视化正确准备数据。

从 CSV 导入数据

在这个食谱中,我们将使用在数据的狂野世界中会遇到的最常见的文件格式,CSV。它代表逗号分隔值,几乎解释了所有的格式。(文件还有一个标题部分,但是那些值也是逗号分隔的。)

Python 有一个叫csv的模块,支持各种方言的 CSV 文件的读写。方言很重要,因为没有标准的 CSV,不同的应用实现 CSV 的方式略有不同。文件的方言几乎总是可以通过第一次查看文件来识别。

做好准备

这个食谱我们需要的是 CSV 文件本身。我们将使用您可以从ch02-data.csv下载的样本 CSV 数据。

我们假设示例数据文件与读取它的代码在同一个文件夹中。

怎么做…

下面的代码示例演示如何从 CSV 文件导入数据。我们将:

  1. 打开ch02-data.csv文件进行读取。
  2. 先看标题。
  3. 阅读其余的行。
  4. 如果出现错误,引发异常。

阅读完所有内容后,打印标题和其余行。

import csv filename ='ch02-data.csv' data =[]try:withopen(filename)as f: reader = csv.reader(f) header = reader.next() data =[row for row in reader]except csv.Error as e:print"Error reading CSV file at line %s: %s"%(reader.line_num, e) sys.exit(-1)if header:print header print'=================='for datarow in data:print datarow 

它是如何工作的…

首先,我们导入csv模块,以便能够访问所需的方法。然后,我们使用with复合语句打开带有数据的文件,并将其绑定到对象f。上下文管理器with语句在我们完成对资源的操作后,释放我们对关闭资源的关心。这是处理资源类文件的一种非常方便的方式,因为它确保在对资源执行代码块后释放资源(例如,关闭文件)。

然后,我们使用返回reader对象的csv.reader()方法,允许我们迭代读取文件的所有行。每一行都只是一个值列表,并在循环中打印。

读取第一行有些不同,因为它是文件的标题,描述了每一列中的数据。这对于 CSV 文件来说并不是强制性的,有些文件没有标题,但它们确实是提供关于数据集的最少元数据的好方法。但有时,您会发现单独的文本甚至 CSV 文件只是用作元数据,描述数据的格式和附加数据。

检查第一行是什么样子的唯一方法是打开文件并进行视觉检查(例如,查看文件的前几行)。这可以在 Linux 上使用 bash 命令(如head)高效地完成,如下所示:

$ head some_file.csv 

在数据迭代过程中,我们将第一行保存在header中,而每隔一行添加到data列表中。

如果在读取过程中出现任何错误,csv.reader()将生成一个错误,我们可以捕捉到该错误并打印出有用的消息给用户,以帮助检测错误。

还有更多…

如果你想了解csv模块的背景和推理,可以在http://www.python.org/dev/peps/pep-0305/获得 PEP 定义的文档 CSV 文件 API

如果我们有更大的文件要加载,通常最好使用众所周知的库,如 NumPy 的loadtxt(),可以更好地处理大型 CSV 文件。

基本用法很简单,如下面的代码片段所示:

import numpy data = numpy.looadtxt('ch02-data.csv', dtype='string', delimiter=',')

请注意,我们需要定义一个分隔符来指示 NumPy 适当地分隔我们的数据。函数numpy.loadtxt()比类似的函数numpy.genfromtxt()稍快,但后者可以更好地处理丢失的数据,并且您可以提供函数来表示在处理某些列加载的数据文件时要做什么。

目前在 Python 2.7.x 中,csv模块不支持 Unicode,必须将读取的数据显式转换为 UTF-8 或 ASCII 可打印。Python CSV 官方文档提供了如何解决数据编码问题的好例子。

在 Python 3.3 和更高版本中,默认情况下支持 Unicode,不存在此类问题。

从微软 Excel 文件导入数据

虽然微软 Excel 支持一些图表制作,但有时候你需要更灵活更强大的可视化,需要将数据从现有的电子表格导出到 Python 中进一步使用。

从 Excel 文件中导入数据的一种常见方法是将数据从 Excel 导出到 CSV 格式的文件中,并使用前面配方中描述的工具使用 Python 从 CSV 文件中导入数据。如果我们有一个或两个文件(并且安装了微软 Excel 或 OpenOffice.org),这是一个相当简单的过程,但是如果我们正在自动化许多文件的数据管道(作为正在进行的数据处理工作的一部分),我们不能手动将每个 Excel 文件转换成 CSV。所以,我们需要一种读取任何 Excel 文件的方法。

Python 对通过项目www.python-excel.org读写 Excel 文件有不错的支持。这种支持是以读写不同模块的形式提供的,是平台无关的;换句话说,我们不必为了读取 Excel 文件而在 Windows 上运行。

微软 Excel 文件格式随着时间的推移而改变,不同的 Python 库支持不同的版本。XLRD 的最新稳定版本在撰写本文时是 0.90,它支持阅读。xlsx文件。

做好准备

首先我们需要安装所需的模块。对于这个例子,我们将使用模块xlrd。我们将在我们的虚拟环境中使用pip

$ mkvirtualenv xlrdexample (xlrdexample)$ pip install xlrd 

安装成功后,使用样品文件ch02-xlsxdata.xlsx

怎么做…

下面的代码示例演示如何从已知的 Excel 文件中读取示例数据集。我们将:

  1. 打开文件工作簿。
  2. 按名称查找工作表。
  3. 使用行数(nrows)和列数(ncols)读取单元格。

出于演示目的,我们只打印读取的数据集。

import xlrd file='ch02-xlsxdata.xlsx' wb = xlrd.open_workbook(filename=file) ws = wb.sheet_by_name('Sheet1') dataset =[]for r inxrange(ws.nrows): col =[]for c inrange(ws.ncols): col.append(ws.cell(r, c).value) dataset.append(col)from pprint import pprint pprint(dataset)

它是如何工作的…

让我们试着解释一下xlrd使用的简单对象模型。在顶层,我们有一个工作簿(Python 类xlrd.book.Book),它由一个或多个工作表(xlrd.sheet.Sheet)组成,每个工作表都有一个单元格(xlrd.sheet.Cell),我们可以从中读取值。

我们使用open_workbook()从一个文件中加载一个工作簿,该文件返回包含工作簿所有信息的xlrd.book.Book实例,例如工作表。我们使用sheet_by_name()访问工作表;如果我们需要所有的表,我们可以使用 sheets(),它返回一个xlrd.sheet.Sheet实例的列表。xlrd.sheet.Sheet类有许多列和行作为属性,我们可以使用这些属性来推断循环的范围,以便使用方法 cell()访问工作表中的每个特定单元格。有一个xrld.sheet.Cell类,虽然不是我们想直接用的东西。

请注意,日期是作为浮点数存储的,而不是作为单独的数据类型存储的,但是xlrd模块能够检查该值,并尝试推断数据是否实际上是日期。因此,我们可以检查单元格的单元格类型,以获得 Python 日期对象。如果数字格式字符串看起来像日期,模块xlrd将返回xlrd.XL_CELL_DATE作为单元格类型。下面是演示这一点的代码片段:

from datetime import datetime from xlrd import open_workbook, xldate_as_tuple … cell = sheet.cell(1,0)print cell print cell.value print cell.ctype if cell.ctype == xlrd.XL_CELL_DATE: date_value = xldate_as_tuple(cell.value, book.datemode)print datetime(*date_value)

此字段仍有问题,因此请参考官方文档和邮件列表,以防您需要大量的日期工作。

还有更多…

xlrd的一个简洁的特性是它能够只加载内存中需要的部分文件。有一个on_demand参数可以在调用open_workbook时传递值True,这样工作表只有在被请求时才会被加载。例如:

book = open_workbook('large.xls', on_demand=True)

在这一节中,我们没有提到编写 Excel 文件,部分是因为将有一个单独的方法,部分是因为有一个不同的模块——T0。您将在本章的将数据导出到 JSON、CSV 和 Excel 配方中了解更多信息。

如果您需要前面解释的模块和示例中没有涉及到的具体用法,这里列出了 PyPi 上的其他 Python 模块,这些模块可能会帮助您处理电子表格:http://pypi.python.org/pypi?:action=browse&c = 377

从固定宽度的数据文件导入数据

事件日志文件和时间序列数据文件是数据可视化的常见来源。有时,我们可以用 CSV 方言读取制表符分隔的数据,但有时它们没有被任何特定的字符分隔。相反,字段的宽度是固定的,我们可以推断出匹配和提取数据的格式。

实现这一点的一种方法是逐行读取文件,然后使用字符串操作函数将字符串拆分成单独的部分。这种方法看起来很简单,如果性能不是问题,应该首先尝试。

如果性能更重要或者要解析的文件很大(几百兆字节),使用 Python 模块struct(http://docs.python.org/library/struct.html)可以加快速度,因为该模块是用 C 实现的,而不是用 Python 实现的。

做好准备

由于模块struct是 Python 标准库的一部分,我们不需要安装任何额外的软件来实现这个配方。

怎么做…

我们将使用具有一百万行固定宽度记录的预生成数据集。以下是示例数据:

… 207152670398435680411695324270531801466959270421533831670088597261315325444920138359697328651524421074004769531360921567802830421421342037064593625911780546 … 

该数据集是使用可在本章ch02-generate_f_data.py的存储库中找到的代码生成的。

现在我们可以读取数据了。我们可以使用下面的代码示例。我们将:

  1. 定义要读取的数据文件。
  2. 定义如何读取数据的掩码。
  3. 使用掩码逐行读取,将每行解包到单独的数据字段中。

将每行打印为单独的字段。

import struct import string datafile ='ch02-fixed-width-1M.data'# this is where we define how to# understand line of data from the file mask='9s14s5s'withopen(datafile,'r')as f:for line in f: fields = struct.Struct(mask).unpack_from(line)print'fields: ',[field.strip()for field in fields]

它是如何工作的…

我们根据之前在数据文件中看到的内容定义格式掩码。要查看该文件,我们可以使用 Linux shell 命令,如headmore,或类似的命令。

字符串格式用于定义要提取的数据的预期布局。我们使用格式字符来定义我们期望的数据类型。因此,如果将掩码定义为9s15s5s,我们可以将其理解为“一个宽度为 9 个字符的字符串,后跟一个 15 个字符的字符串,再后跟一个 5 个字符的字符串。”

一般来说,c定义字符(C 中的char类型)或长度为 1 的字符串,s定义字符串(C 中的char[]类型),d定义浮点数(C 中的double类型)等等。完整表格可在 Python 官方网站http://docs . Python . org/library/struct . html # format-characters查阅。

然后我们逐行读取文件,并根据指定的格式提取(T0 方法)该行。因为我们的字段前(或后)可能有多余的空格,所以我们使用strip()来去除每个提取的字段。

对于解包,我们使用了使用struct.Struct类的面向对象 ( OO )方法,但是我们也可以使用非对象方法,其中的线是:

fields = struct.unpack_from(mask, line)

唯一不同的是模式的用法。如果我们要使用相同的格式掩码来执行更多的处理,那么面向对象的方法可以避免我们在每次调用中使用这种格式。此外,它使我们能够在将来继承struct.Struct类,为特定需求扩展或提供额外的功能。

从制表符分隔的文件导入数据

平面数据文件的另一种非常常见的格式是制表符分隔的文件。这也可以来自 Excel 导出,但也可以是一些自定义软件的输出,我们必须从这些软件中获取输入。

好的一点是,通常这种格式可以以几乎与 CSV 文件相同的方式读取,因为 Python 模块csv支持所谓的方言,这使我们能够使用相同的原理来读取类似文件格式的变体——其中之一是制表符分隔格式。

做好准备

我们已经能够读取 CSV 文件。如果没有,请先参考从 CSV 配方导入数据。

怎么做…

我们将重新使用中的代码,从 CSV 配方中导入数据,我们只需要改变我们使用的方言。

import csv filename ='ch02-data.tab' data =[]try:withopen(filename)as f: reader = csv.reader(f, dialect=csv.excel_tab) header = reader.next() data =[row for row in reader]except csv.Error as e:print"Error reading CSV file at line %s: %s"%(reader.line_num, e) sys.exit(-1)if header:print header print'==================='for datarow in data:print datarow 

它是如何工作的…

基于方言的方法与我们已经在中从 CSV 配方导入数据的非常相似,除了我们实例化csv读取器对象的一行,给它参数dialect并指定我们想要的'excel_tab'方言。

还有更多…

如果数据是“脏的”,也就是说,如果某些行不是以新的行字符结束,而是有额外的\t(制表符)标记,基于 CSV 的方法将不起作用。所以我们需要在拆分之前单独清洗特殊线路。样本“脏”制表符分隔的文件可以在ch02-data-dirty.tab中找到。下面的代码示例在读取数据时清除数据:

datafile ='ch02-data-dirty.tab'withopen(datafile,'r')as f:for line in f:# remove next comment to see line before cleanup# print 'DIRTY: ', line.split('\t')# we remove any space in line start or end line = line.strip()# now we split the line by tab delimiterprint line.split('\t')

我们还看到还有另一种方法——使用split('\t')函数。

有时使用csv模块方法相对于split()的优势在于,我们可以通过更改方言并使用文件扩展名(.csv.tab)或一些其他方法(例如,使用csv.Sniffer类)检测方言来重新使用相同的代码进行读取。

从 JSON 资源导入数据

这个食谱将告诉我们如何读取 JSON 数据格式。此外,我们将在本食谱中使用远程资源。它会给食谱增加一点点复杂性,但它会让它变得更有用,因为在现实生活中,我们会遇到比本地更多的远程资源。

JavaScript 对象符号 ( JSON )作为一种独立于平台的格式被广泛用于系统或应用之间的数据交换。

在这种情况下,资源是我们可以读取的任何东西,无论是文件还是网址端点(可以是远程进程/程序的输出,也可以只是远程静态文件)。简而言之,我们不在乎谁生产了一种资源,如何生产;我们只需要它采用已知的格式,比如 JSON。

做好准备

为了开始使用这个配方,我们需要在虚拟环境中安装并导入 requests模块(在PYTHONPATH中)。我们已经在第 1 章准备您的工作环境中安装了该模块。

我们还需要互联网连接,因为我们将阅读远程资源。

怎么做…

下面的代码示例从 GitHub(http://github.com)站点读取并解析最近的活动时间线。我们将为此执行以下步骤:

  1. 定义 GitHub URL 来读取 JSON 格式。
  2. 使用requests模块从网址获取内容。
  3. 以 JSON 的形式阅读内容。

对于 JSON 对象中的每个条目,读取每个存储库的网址值。

import requests url ='https://github.com/timeline.json' r = requests.get(url) json_obj = r.json() repos =set()for entry in json_obj:try: repos.add(entry['repository']['url'])except KeyError as e:print"No key %s. Skipping..."%(e)from pprint import pprint pprint(repos)

它是如何工作的…

首先,我们使用requests模块获取远程资源。这非常简单,因为requests模块提供了一个简单的 API 来定义 HTTP 动词,所以我们只需要发出一个get()方法调用。这个方法检索数据并请求元数据,将其包装在Response对象中,以便我们可以检查它。对于这个食谱,我们只对Response.json()方法感兴趣,它会自动读取内容(可在Response.content获得),并将其解析为 JSON,然后将其加载到 JSON 对象中。

现在我们有了 JSON 对象,我们可以处理数据了。为了做到这一点,我们需要了解数据是什么样子的。我们可以通过使用我们最喜欢的网络浏览器或命令行工具(如wgetcurl)打开 JSON 资源来实现这种理解。

另一种方法是从 IPython 获取数据,并以交互方式进行检查。我们可以通过从 IPython 运行我们的程序(使用%run program_name.py)来实现这一点。执行之后,我们剩下程序产生的所有变量。使用%who%whos列出它们。

无论我们使用什么方法,我们都会获得关于 JSON 数据结构的知识,并能够看到该结构中我们感兴趣的部分。

JSON 对象基本上只是一个 Python 字典(或者更复杂的话,一个字典的字典),我们可以使用一个众所周知的基于键的符号来访问它的一部分。我们得到了最近更新的参考entry['repository']['url']库的网址列表。

entry['repository']['url']匹配实际 JSON 文件中的该部分:

… "repository":{..."url":"https://github.com/ipython/ipython",...}, … 

我们现在可以看到嵌套结构如何对应于 Python 代码中的多维键索引。

还有更多…

JSON 格式(由 RFC 4627 指定;参考http://tools.ietf.org/html/rfc4627.html)最近变得非常流行,因为它比 XML 更具可读性,也不那么冗长。因此,就传输数据所需的语法而言,它更简单。它在网络应用领域非常受欢迎,因为它是 JavaScript 的 T2 本地语言,JavaScript 是当今大多数丰富的互联网应用所使用的语言。

Python JSON 模块的功能比我们这里展示的还要多;例如,我们可以专门化基本的JSONEncoder / JSONDecoder类,将我们的 Python 数据转换为 JSON 格式。经典的例子使用这种方法来 JSON-ify 用于复数的 Python 内置类型。

对于简单的定制,我们不必子类化JSONDecoder / JSONEncoder类,因为一些参数可以解决我们的问题。

例如,json.loads()将解析一个浮点数为 Python 类型float,大部分时间都是对的。然而,有时 JSON 文件中的浮点值代表价格值,这最好用十进制表示。我们可以指示json解析器将浮点解析为十进制。例如,我们有这个 JSON 字符串:

jstring ='{"name":"prod1","price":12.50}'

接下来是这两行代码:

from decimal import Decimal json.loads(jstring, parse_float=Decimal)

前面两行代码将生成以下输出:

{u'name':u'prod1',u'price': Decimal('12.50')}

将数据导出到 JSON、CSV、Excel

而作为数据可视化的生产者,我们大多使用别人的数据;导入和读取数据是主要活动。我们确实需要编写或导出我们生产或处理的数据,无论数据是供我们或他人当前或未来使用。

我们将演示如何使用前面提到的 Python 模块来导入、导出和将数据写入各种格式,如 JSON、CSV 和 XLSX。

出于演示目的,我们使用从固定宽度数据文件导入数据的预生成数据集。

做好准备

对于 Excel 编写部分,我们需要通过执行以下命令来安装xlwt模块(在我们的虚拟环境中):

$ pip install xlwt 

怎么做…

我们将展示一个包含我们想要演示的所有格式的代码示例:CSV、JSON 和 XLSX。程序的主要部分接受输入并调用适当的函数来转换数据。我们将遍历代码的不同部分,解释它的用途。

最后,我们有主代码入口点,在这里我们从命令行解析类似参数的文件,以导入数据并将其导出为所需的格式。

if __name__ =='__main__':# parse input arguments parser = argparse.ArgumentParser() parser.add_argument("import_file",help="Path to a fixed-width data file.") parser.add_argument("export_format",help="Export format: json, csv, xlsx.") args = parser.parse_args()if args.import_file isNone:print>> sys.stderr,"You myst specify path to import from." sys.exit(1)if args.export_format notin('csv','json','xlsx'):print>> sys.stderr,"You must provide valid export file format." sys.exit(1)# verify given path is accesible fileifnot os.path.isfile(args.import_file):print>> sys.stderr,"Given path is not a file: %s"% args.import_file sys.exit(1)# read from formated fixed-width file data = import_data(args.import_file)# export data to specified format# to make this Unix-lixe pipe-able# we just print to stdoutprint write_data(data, args.export_format)

我们单独为每个数据格式(CSV、JSON 和 XLSX)指定单独实现。

defwrite_csv(data):'''Transforms data into csv. Returns csv as string. '''# Using this to simulate file IO,# as csv can only write to files. f = StringIO.StringIO() writer = csv.writer(f)for row in data: writer.writerow(row)# Get the content of the file-like objectreturn f.getvalue()defwrite_json(data):'''Transforms data into json. Very straightforward. ''' j = json.dumps(data)return j defwrite_xlsx(data):'''Writes data into xlsx file. '''from xlwt import Workbook book = Workbook() sheet1 = book.add_sheet("Sheet 1") row =0for line in data: col =0for datum in line:print datum sheet1.write(row, col, datum) col +=1 row +=1# We have hard limit here of 65535 rows# that we are able to save in spreadsheet.if row >65535:print>> sys.stderr,"Hit limit of # of rows in one sheet (65535)."break# XLS is special case where we have to# save the file and just return 0 f = StringIO.StringIO() book.save(f)return f.getvalue()

然后,定义读写数据的适当函数。

defimport_data(import_file):''' Imports data from import_file. Expects to find fixed width row Sample row: 161322597 0386544351896 0042 ''' mask ='9s14s5s' data =[]withopen(import_file,'r')as f:for line in f:# unpack line to tuple fields = struct.Struct(mask).unpack_from(line)# strip any whitespace for each field# pack everything in a list and add to full dataset data.append(list([f.strip()for f in fields]))return data defwrite_data(data, export_format):'''Dispatches call to a specific transformer and returns data set. Exception is xlsx where we have to save data in a file. '''if export_format =='csv':return write_csv(data)elif export_format =='json':return write_json(data)elif export_format =='xlsx':return write_xlsx(data)else:raise Exception("Illegal format defined")

导入所需的模块。

import os import sys import argparse try:import cStringIO as StringIO except:import StringIO import struct import json import csv 

它是如何工作的…

简单来说,我们导入固定宽度数据集(定义在中,从固定宽度数据文件中导入数据),然后将其导出到stdout,这样我们就可以在文件中捕捉到它,或者将其作为另一个程序的输入。

我们从命令行调用程序员,给两个强制参数:输入文件名和导出数据格式(JSON、CSV 和 XLSX)。

如果我们成功解析了这些参数,我们会将输入文件的读取发送到函数import_data(),该函数返回 Python 数据结构(列表列表),我们可以轻松操作该数据结构以获得适当的导出格式。

我们在write_data()函数中路由我们的请求,在那里我们只需将调用转发给适当的函数(例如write_csv())。

对于 CSV,我们获得csv.writer()实例,用于编写我们迭代的每一行数据。

我们只返回给定的字符串,因为我们将把这个输出从我们的程序重定向到另一个程序(或者只是猫在一个文件中)。

这个例子不需要 JSON 导出,因为json模块为我们提供了dump()方法,可以愉快地读取我们的 Python 结构。就像 CSV 一样,我们只需将这个输出返回并转储到stdout

Excel 导出需要更多的代码,因为我们需要为 Excel 工作簿和保存数据的工作表创建一个更复杂的模型。这项活动之后是类似的迭代方法。我们有两个循环,外部循环遍历源数据集中迭代的每一行,而内部循环遍历给定行中的每个字段。

在完成所有这些之后,我们将Book实例保存到一个类似文件的流中,我们可以返回到stdout并在读取文件和 web 服务使用的文件中使用它。

还有更多…

当然,这只是我们可以导出的一小组可能的数据格式。修改行为相当容易。基本上有两个地方需要改变:导入和导出功能。如果我们想导入一种新的数据源,导入函数需要改变。

如果我们想添加一个新的导出格式,我们需要首先添加函数,这些函数将返回一个格式的数据流。然后,我们需要更新write_data()函数来添加新的elif分支,让它调用我们新的write_*函数。

我们还可以做的一件事是将它做成一个 Python 包,这样我们就可以在更多的项目中重用它。在这种情况下,我们希望使import更加灵活,并可能为import添加一些更多的配置功能。

从数据库导入数据

很多时候,我们在数据分析和可视化方面的工作是在数据管道的消费者端。我们通常使用已经产生的数据,而不是自己产生数据。例如,现代应用在关系数据库(或其他数据库)中保存不同的数据集,我们使用这些数据库并生成漂亮的图形。

这个食谱将向您展示如何使用 Python 中的 SQL 驱动程序来访问数据。

我们将使用一个 SQLite 数据库演示这个方法,因为它需要最少的设置工作,但是界面类似于大多数其他基于 SQL 的数据库引擎(MySQL 和 PostgreSQL)。然而,这些数据库引擎支持的 SQL 方言存在差异。此示例使用简单的 SQL 语言,并且应该可以在大多数常见的 SQL 数据库引擎上重现。

做好准备

为了能够执行这个食谱,我们需要安装 SQLite 库。

$ sudo apt-get install sqlite3 

默认情况下,Python 支持 SQLite,所以我们不需要安装任何与 Python 相关的东西。只需在 IPython 中激发以下代码片段来验证一切都在那里:

import sqlite3 sqlite3.version sqlite3.sqlite_version 

我们得到类似如下的输出:

In [1]:import sqlite3 In [2]: sqlite3.version Out[2]:'2.6.0' In [3]: sqlite3.sqlite_version Out[3]:'3.6.22'

这里,sqlite3.version获取 Python sqlite3模块的版本,sqlite_version返回系统 SQLite 库版本。

怎么做…

为了能够读取数据库,我们需要:

  1. 连接到数据库引擎(或者在 SQLite 的情况下连接到文件)。
  2. 对选定的表运行查询。
  3. 读取数据库引擎返回的结果。

我不会尝试在这里教 SQL,因为有很多关于这个特定主题的书。但是为了清楚起见,我们将在这个代码示例中解释 SQL 查询:

SELECT ID, Name, Population FROM City ORDER BY Population DESC LIMIT 1000

IDNamePopulation是表City的列(字段),我们从中选择数据。ORDER BY告诉数据库引擎按照Population列对我们的数据进行排序,DESC表示降序。LIMIT允许我们只获得找到的第一个 1000 条记录。

对于本例,我们将使用world.sql示例表,其中保存了世界上的城市名称和人口。这个表有 5000 多个条目。

我们的桌子是这样的:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_13.jpg

首先,我们需要将这个 SQL 文件导入到 SQLite 数据库中。以下是如何做到这一点:

import sqlite3 import sys iflen(sys.argv)<2:print"Error: You must supply at least SQL script."print"Usage: %s table.db ./sql-dump.sql"%(sys.argv[0]) sys.exit(1) script_path = sys.argv[1]iflen(sys.argv)==3: db = sys.argv[2]else:# if DB is not defined# create memory database db =":memory:"try: con = sqlite3.connect(db)with con: cur = con.cursor()withopen(script_path,'rb')as f: cur.executescript(f.read())except sqlite3.Error as err:print"Error occured: %s"% err 

这将读取 SQL 文件,并针对打开的 SQLite db文件执行 SQL 语句。如果我们不指定文件名,SQLite 会在内存中创建数据库。然后一行行地执行这些语句。

如果我们遇到任何错误,我们会捕捉异常并将错误消息打印给用户。

将数据导入数据库后,我们就可以查询数据并进行一些处理。下面是从数据库文件中读取数据的代码:

import sqlite3 import sys iflen(sys.argv)!=2:print"Please specify database file." sys.exit(1) db = sys.argv[1]try: con = sqlite3.connect(db)with con: cur = con.cursor() query ='SELECT ID, Name, Population FROM City ORDER BY Population DESC LIMIT 1000' con.text_factory =str cur.execute(query) resultset = cur.fetchall()# extract column names col_names =[cn[0]for cn in cur.description]print"%10s %30s %10s"%tuple(col_names)print"="*(10+1+30+1+10)for row in resultset:print"%10s %30s %10s"% row except sqlite3.Error as err:print"[ERROR]:", err 

它是如何工作的…

首先,我们验证用户是否提供了数据库文件路径。这只是一个快速检查,我们可以继续进行其余的代码。

然后,我们尝试连接到数据库;如果失败,我们捕捉sqlite3.Error并打印给用户。

如果连接成功,我们使用con.cursor()获得一个光标。游标是一种类似迭代器的结构,它使我们能够遍历从数据库返回的结果集的记录。

我们定义一个通过连接执行的查询,并使用cur.fetchall()获取结果集。如果我们只期望一个结果,我们会只使用fetchone()

cur.description的列表理解允许我们获取列名。description是一个只读属性,只返回列名所需的内容,所以我们只从每个列的 7 项元组中获取第一项。

然后,我们使用简单的字符串格式打印带有列名的表格标题。之后,我们迭代resultset并以类似的方式打印每一行。

还有更多…

数据库是当今最常见的数据来源。我们无法在这份简短的食谱中呈现所有内容,但我们可以建议进一步的研究方向。

官方 Python 文档是寻找如何使用数据库的解释的第一个地方。最常见的数据库是开源数据库,如 MySQL、PostgreSQL 和 SQLite,另一端是企业数据库系统,如微软的 SQL、甲骨文和 Sybase。大多数情况下,Python 支持它们,并且接口总是抽象的,所以如果底层数据库发生变化,您不必更改程序,但是可能需要进行一些调整。这取决于您是否使用了特定数据库系统的细节。例如,Oracle 支持一种特定的语言 PL/SQL,这种语言不是标准的 SQL,如果您的数据库从 Oracle 更改为 MS SQL,有些东西将不起作用。同样,SQLite 不支持来自 MySQL 数据类型或数据库引擎类型(MyISAM 和 InnoDB)的细节。这些事情可能很烦人,但是让你的代码依赖于标准的 SQL(http://en.wikipedia.org/wiki/SQL:2011)会让你的代码从一个数据库系统移植到另一个系统。

从异常值中清除数据

这个食谱描述了如何处理来自现实世界的数据集,以及如何在可视化之前清理它们。

我们将展示几种技术,本质上不同,但是有相同的目标,那就是清理数据。

然而,清洁不应该是全自动的。在我们应用任何强大的现代算法来清理数据之前,我们需要理解给定的数据,并且能够理解什么是异常值以及数据点代表什么。这不是一个可以在食谱中定义的东西,因为它依赖于大量的领域,如统计数据、领域知识和良好的眼光(然后是一些运气)。

做好准备

我们将使用我们已经知道的标准 Python 模块,因此不需要额外的安装。

在这个食谱中,我将引入一个新的术语,MAD。统计学中的中值绝对偏差 ( MAD )代表定量数据的单变量(拥有一个变量)样本的可变性的度量。这是统计离差的一种度量。它属于一组稳健的统计数据,因此对异常值更有弹性。

怎么做…

这里有一个例子,展示了如何使用 MAD 来检测数据中的异常值。我们将为此执行以下步骤:

  1. 生成正态分布的随机数据。
  2. 加入一些异常值。
  3. 使用功能is_outlier()检测异常值。

绘制两个数据集(xfiltered)以查看差异。

import numpy as np import matplotlib.pyplot as plt defis_outlier(points, threshold=3.5):""" Returns a boolean array with True if points are outliers and False otherwise. Data points with a modified z-score greater than this # value will be classified as outliers. """# transform into vectoriflen(points.shape)==1: points = points[:,None]# compute median value  median = np.median(points, axis=0)# compute diff sums along the axis diff = np.sum((points - median)**2, axis=-1) diff = np.sqrt(diff)# compute MAD med_abs_deviation = np.median(diff)# compute modified Z-score# http://www.itl.nist.gov/div898/handbook/eda/section4/eda43.htm#Iglewicz modified_z_score =0.6745* diff / med_abs_deviation # return a mask for each outlierreturn modified_z_score > threshold # Random data x = np.random.random(100)# histogram buckets buckets =50# Add in a few outliers x = np.r_[x,-49,95,100,-100]# Keep valid data points# Note here that# "~" is logical NOT on boolean numpy arrays filtered = x[~is_outlier(x)]# plot histograms plt.figure() plt.subplot(211) plt.hist(x, buckets) plt.xlabel('Raw') plt.subplot(212) plt.hist(filtered, buckets) plt.xlabel('Cleaned') plt.show()

请注意,在 NumPy 中,~运算符被重载为逻辑运算符,而不是布尔数组。例如,假设我们在pylab模式下启动 IPython。

$ ipython –pylab 

我们得到:

In [1]:~numpy.array(False) Out[1]:True

我们应该会看到两个不同的直方图,第一个直方图几乎什么都没有显示——除了一个桶中最大的异常值——第二个直方图显示了多样化的数据桶,因为我们移除了异常值。

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_01.jpg

识别异常值的另一种方法是目视检查您的数据。为了做到这一点,我们可以创建散点图,在散点图中,我们可以很容易地发现中央群体之外的值。我们也可以做一个方框图,里面会显示的中位数,中位数上下的四分位数,以及距离这个方框较远的点。

该框从数据的下四分位数延伸到上四分位数,中间有一条线。触须从方框中延伸出来,显示数据的范围。飞人点是那些超过胡须末端的点。

这里有一个例子来证明:

from pylab import*# fake up some data spread= rand(50)*100 center = ones(25)*50# generate some outliers high and low flier_high = rand(10)*100+100 flier_low = rand(10)*-100# merge generated data set data = concatenate((spread, center, flier_high, flier_low),0) subplot(311)# basic plot# 'gx' defining the outlier plotting properties boxplot(data,0,'gx')# compare this with similar scatter plot subplot(312) spread_1 = concatenate((spread, flier_high, flier_low),0) center_1 = ones(70)*25 scatter(center_1, spread_1) xlim([0,50])# and with another that is more appropriate for# scatter plot subplot(313) center_2 = rand(70)*50 scatter(center_2, spread_1) xlim([0,50]) show()

我们可以然后看到代表异常值的 x 形标记:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_02.jpg

我们还可以看到,在散点图中显示类似数据集的第二个图不是很直观,因为 x 轴的所有值都在 25,我们并没有真正区分内联和外联。

第三个图,我们在 x 轴上生成的值为分布在从 0 到 50 的范围内,让我们更清楚地看到不同的值,我们可以看到哪些值是 y 轴上的异常值。

在下面的代码示例中,我们看到了相同的数据(在本例中是均匀分布的)如何以非常不同的方式显示自己,有时还会欺骗性地传达一些不真实的信息:

# generate uniform data points x =1e6*rand(1000) y = rand(1000) figure()# crate first subplot subplot(211)# make scatter plot scatter(x, y)# limit x axis xlim(1e-6,1e6)# crate second subplot subplot(212)# make scatter plot scatter(x,y)# but make x axis logarithmic xscale('log')# set same x axis limit xlim(1e-6,1e6) show()

这是结果输出:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_03.jpg

如果我们有一个缺失值的数据集呢?我们可以使用 NumPy 加载器来补偿丢失的值,或者我们可以编写代码来用我们需要的值替换现有的值,以便进一步使用。

假设我们想要说明美国地理地图上的一些数据集,并且可能在数据集中有不一致的州名值。例如,我们有代表美国俄亥俄州的值OHOhioOHIOUS-OHOH-USA。在这种情况下,我们必须手动检查数据集,或者将其加载到电子表格处理器(如微软 Excel 或 OpenOffice.org Calc)中。有时候,用 Python 打印所有的行就足够简单了。如果文件是 CSV 或者类 CSV,我们可以用任何文本编辑器打开,直接检查数据。

在我们总结了数据中的内容之后,我们可以编写 Python 代码来对这些相似的值进行分组,并用一个将使进一步处理一致的值来替换它们。通常的方法是使用readlines()读入文件的行,并使用标准的 Python 字符串操作函数来执行操作。

还有更多…

有一些特殊的产品,包括商业和非商业的(比如 OpenRefine—https://github.com/OpenRefine,它们围绕“肮脏”的实时数据集上的转换提供了一些自动化。

尽管如此,还是需要手工操作,这取决于数据有多嘈杂,以及我们对数据的理解有多深刻。

如果你想了解更多关于清理异常值和清理一般数据的信息,请寻找统计模型和抽样理论。

分块读取文件

Python 非常擅长处理读写文件或类似文件的对象。例如,如果你试图加载大文件,比如几百 MB,假设你有一台至少有 2gb RAM 的现代机器,Python 将能够毫无问题地处理它。它不会试图一次加载所有内容,而是聪明地玩,根据需要加载。

因此,即使有合适的文件大小,做一些像下面的代码这样简单的事情也能直接工作:

withopen('/tmp/my_big_file','r')as bigfile:for line in bigfile:# line based operation, like 'print line'

但是,如果我们想跳转到文件中的某个特定位置或进行其他非顺序读取,我们将需要使用手工方法并使用 IO 功能,如seek()tell()read()next(),这些功能为大多数用户提供了足够的灵活性。这些函数中的大多数只是绑定到 C 实现(并且是特定于操作系统的),所以它们速度很快,但是它们的行为会根据我们运行的操作系统而有所不同。

怎么做…

根据我们的目标,处理大文件有时可以分块管理。例如,您可以读取 1000 行,并使用 Python 标准的基于迭代器的方法来处理它们。

import sys filename = sys.argv[1]# must pass valid file namewithopen(filename,'rb')as hugefile: chunksize =1000 readable =''# if you want to stop after certain number of blocks# put condition in the whilewhile hugefile:# if you want to start not from 1st byte# do a hugefile.seek(skipbytes) to skip# skipbytes of bytes from the file start start = hugefile.tell()print"starting at:", start file_block =''# holds chunk_size of linesfor _ inxrange(start, start + chunksize): line = hugefile.next() file_block = file_block + line print'file_block',type(file_block), file_block readable = readable + file_block # tell where are we in file# file IO is usually buffered so tell()# will not be precise for every read. stop = hugefile.tell()print'readable',type(readable), readable print'reading bytes from %s to %s'%(start, stop)print'read bytes total:',len(readable)# if you want to pause read between chucks# uncomment following line#raw_input()

我们从 Python 命令行解释器调用这段代码,给出文件名路径作为第一个参数。

$ python ch02-chunk-read.py myhugefile.dat 

它是如何工作的…

我们希望能够读取行块进行处理,而不读取内存中的整个文件。

我们打开文件,在内部for循环中读入行。我们移动文件的方式是在文件对象上调用next()。这个函数从文件中读取一行,并将文件指针移动到下一行。在循环执行期间,我们在file_block变量中添加行。为了简化示例代码,我们不做任何处理,只是添加file_block来完成输出变量readable

我们在执行过程中做一些打印,只是为了说明某些变量的当前状态。

while循环中的最后一个注释行raw_input()可以取消注释,我们可以暂停执行并读取上面打印的行。

还有更多…

当然,这个方法只是读取大(巨大)文件的可能方法之一。其他方法可能包括特定的 Python 或 C 库,但它们都取决于我们打算如何处理数据以及我们希望如何处理数据。

像 MapReduce 范例这样的并行方法最近变得非常流行,因为我们可以以较低的价格获得更多的处理能力和内存。

多处理有时也是一种可行的方法,因为 Python 有很好的库支持来创建和管理带有多个库的线程,例如multiprocessingthreadingthread

如果处理巨大的文件对于一个项目来说是一个重复的过程,我们建议构建您的数据管道,这样每次您需要在输出端以特定的格式准备好数据时,您就不必去源并手动完成它。

读取流数据源

如果来源的数据是连续的呢?如果我们需要读取连续数据怎么办?这个方法将展示一个简单的解决方案,它将适用于许多常见的现实场景,尽管它不是通用的,如果您在应用中遇到特殊情况,您将需要修改它。

怎么做…

在这个食谱中,我们将向您展示如何读取一个总是变化的文件并打印输出。我们将使用常见的 Python 模块来实现这一点。

import time import os import sys iflen(sys.argv)!=2:print>> sys.stderr,"Please specify filename to read" filename = sys.argv[1]ifnot os.path.isfile(filename):print>> sys.stderr,"Given file: \"%s\" is not a file"% filename withopen(filename,'r')as f:# Move to the end of file filesize = os.stat(filename)[6] f.seek(filesize)# endlessly loopwhileTrue: where = f.tell()# try reading a line line = f.readline()# if empty, go backifnot line: time.sleep(1) f.seek(where)else:# , at the end prevents print to add newline, as readline()# already read that.print line,

它是如何工作的…

代码的核心在while True:循环内部。这个循环从未停止(除非我们通过按下键盘上的 Ctrl + C 来中断它)。我们首先移动到正在读取的文件的末尾,然后尝试读取一行。如果没有行,说明我们使用seek()检查后,文件中没有添加任何内容。所以,我们睡一秒钟,然后再试一次。

如果有一个非空行,我们打印出来,并抑制新的行字符。

还有更多…

我们可能想读最后一行*。我们可以通过接近文件的结尾来做到这一点。我们可以通过寻找文件,也就是file.seek(filesize – N * avg_line_len)去那里。这里,avg_line_len应该是该文件中平均行长度的近似值(大约 1,024)。然后,我们可以使用readlines()从那个点读取行,然后只打印列表中的[-N]行。*

*这个例子中的想法可以用于各种解决方案。例如,输入必须是类似文件的对象或远程 HTTP 可访问的资源。因此,可以从远程服务中读取输入,并持续解析它和更新实时图表,例如,或者更新中间队列、缓冲区或数据库。

一个特定的模块对于流处理非常有用— io。它是从 2.6 版本开始的 Python 中的,是作为文件模块的替代而构建的,并且是 Python 3.x 中的默认接口

在一些更复杂的数据管道中,我们需要启用某种消息队列,在这里,我们的传入连续数据必须排队一段时间才能被接受。这使得我们作为数据的消费者,能够在超负荷的情况下暂停处理。在公共消息总线上拥有数据使项目中的其他客户端能够使用相同的数据,并且不会干扰我们的软件。

将图像数据导入 NumPy 数组

我们将在演示如何使用 Python 的库如 NumPySciPy 来进行图像处理。

在科学的计算中,图像通常被视为 n 维数组。它们通常是二维数组;在我们的示例中,它们被表示为 NumPy 数组数据结构。因此,在这些结构上执行的功能和操作被视为矩阵操作。

从这个意义上说,图像并不总是二维的。对于医学或生物科学,图像是更高维度的数据结构,例如 3D(以 z 轴作为深度或时间轴)或 4D(以三个空间维度和一个时间维度作为第四维度)。我们不会在这个食谱中使用这些。

我们可以使用各种技术导入图像;它们都取决于你想用图像做什么。此外,这还取决于你所使用的工具的更大的生态系统,以及你运行你的项目的平台。

在这个食谱中,我们将演示几种在 Python 中使用图像处理的方法,主要与科学处理有关,较少涉及图像处理的艺术方面。

做好准备

在本食谱的一些例子中,我们使用了 SciPy 库,如果您已经安装了 NumPy,那么您已经安装了该库。如果没有,可以通过执行以下命令,使用操作系统的软件包管理器轻松安装:

$ sudo apt-get install python-scipy 

对于 Windows 用户,我们建议使用预打包的 Python 环境,例如 EPD,我们在第 1 章准备您的工作环境中讨论过。

如果您想使用官方源代码发行版安装这些,请确保您已经安装了系统依赖项,例如:

  • BLAS 和 LAPACK: libblasliblapack
  • c 和 Fortran 编译器:gccgfortran

怎么做…

无论谁在数字信号处理领域工作过,甚至上过这方面或相关学科的大学课程,都一定遇到过 Lena 的图像,这是事实上的标准图像,用于演示图像处理算法。

SciPy 包含这个已经打包在misc.模块中的图像,所以我们重用那个图像真的很简单。这是如何阅读和展示这张图片:

import scipy.misc import matplotlib.pyplot as plt # load already prepared ndarray from scipy lena = scipy.misc.lena()# set the default colormap to gray plt.gray() plt.imshow(lena) plt.colorbar() plt.show()

这应该会打开一个新窗口,图中以灰色调和轴显示莉娜的图像。颜色条显示了图中的数值范围;这里显示 0-黑色到 255-白色。

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_04.jpg

此外,我们可以用下面的代码来检查这个对象:

print lena.shape print lena.max()print lena.dtype 

前面代码的输出如下:

(512,512)245 dtype('int32')

这里我们看到的图像是:

  • 宽 512 点,高 512 点
  • 整个数组(即图像)中的最大值是 254
  • 每个点都表示为一个小端 32 位长整数

我们也可以使用 Python 图像库 ( PIL )读取图像,我们在第 1 章准备您的工作环境中安装了该图像。

import numpy import Image import matplotlib.pyplot as plt bug = Image.open('stinkbug.png') arr = numpy.array(bug.getdata(), numpy.uint8).reshape(bug.size[1], bug.size[0],3) plt.gray() plt.imshow(arr) plt.colorbar() plt.show()

我们应该看到类似莉娜的形象如下:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_05.jpg

如果我们已经在利用一个使用 PIL 作为默认图像加载器的现有系统,这将非常有用。

它是如何工作的…

除了加载图像之外,我们真正想做的是使用 Python 来处理图像。我们希望能够加载一个由 RGB 通道组成的真实图像,将其转换为一个通道ndarray,然后使用数组切片来放大图像的部分。下面的代码演示了我们如何使用 NumPy 和 matplotlib 来做到这一点。

import matplotlib.pyplot as plt import scipy import numpy bug = scipy.misc.imread('stinkbug1.png')# if you want to inspect the shape of the loaded image# uncomment following line#print bug.shape# the original image is RGB having values for all three# channels separately. We need to convert that to greyscale image# by picking up just one channel.# convert to gray bug = bug[:,:,0]

bug[:,:,0]叫做 阵切片。这个 NumPy 特性允许我们选择多维数组的任何部分。例如,让我们看一个一维数组:

>>> a = array(5,1,2,3,4)>>> a[2:3] array([2])>>> a[:2] array([5,1])>>> a[3:] array([3,4])

对于多维数组,我们用逗号(,)分隔每个维度。例如:

>>> b = array([[1,1,1],[2,2,2],[3,3,3]])# matrix 3 x 3>>> b[0,:]# pick first row array([1,1,1])>>> b[:,0]# we pick the first column array([1,2,3])

看看下面的代码:

# show original image plt.figure() plt.gray() plt.subplot(121) plt.imshow(bug)# show 'zoomed' region zbug = bug[100:350,140:350]

这里我们放大整个图像的特定部分。记住图像只是一个表示为 NumPy 数组的多维数组。这里的缩放意味着从这个矩阵中选择一系列的行和列。所以我们从 100 到 250 行和 140 到 350 列中选择一个部分矩阵。请记住,索引从 0 开始,因此坐标 100 处的行是第 101 行。

plt.subplot(122) plt.imshow(zbug) plt.show()

这将显示如下:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_06.jpg

还有更多…

对于大的图像,我们建议使用numpy.memmap进行图像的记忆映射。这将加快处理图像数据的速度。例如:

import numpy file_name ='stinkbug.png' image = numpy.memmap(file_name, dtype=numpy.uint8, shape =(375,500))

在这里,我们将一个大文件的一部分加载到内存中,以 NumPy 数组的形式访问它。这是非常有效的,允许我们将文件数据结构作为标准的 NumPy 数组进行操作,而无需将所有内容加载到内存中。参数形状定义了从file_name参数加载的数组的形状,它是一个类似文件的对象。请注意,这是一个与 Python 的mmap参数(http://docs.python.org/2/library/mmap.html)相似的概念,但在一个非常重要的方面有所不同——NumPy 的memmap属性返回一个类似数组的对象,而 Python 的mmap返回一个类似文件的对象。因此,我们使用它们的方式非常不同,但在每个环境中都非常自然。

有一些专门的包只是专注于图像处理,比如 sci kit-image(http://scikit-image.org/);这基本上是一个免费的图像处理算法集合,建立在 NumPy/SciPy 库之上。如果你想做边缘检测,去除图像中的噪声,或者寻找轮廓,scikit 是用来寻找算法的工具。最好的开始方式是查看示例库,找到示例图像和代码(http://scikit-image.org/docs/dev/auto_examples/)。

生成受控随机数据集

在这个食谱中,我们将展示生成随机数序列和单词序列的不同方法。有些示例使用标准 Python 模块,有些使用 NumPy/SciPy 函数。

我们将进入一些统计术语,但我们将解释每个术语,这样您就不必在阅读本食谱时随身携带统计参考书。

我们使用常见的 Python 模块生成人工数据集。通过这样做,我们能够理解分布、方差、抽样和类似的统计术语。更重要的是,我们可以利用这些假数据来了解我们的统计方法是否能够发现我们想要发现的模型。我们可以这样做,因为我们提前知道模型,并通过在已知数据上应用它来验证我们的统计方法。在现实生活中,我们没有这种能力,总是有一定比例的不确定性我们必须假设,让位于错误。

做好准备

为了练习这些例子,我们不需要在系统上安装任何新的东西。掌握一些统计学知识是有用的,尽管不是必需的。

为了更新我们的统计知识,这里有一个小词汇表,我们将在这一章和后面的章节中使用。

  • 分布或概率分布 :这将统计实验的结果与该实验发生的概率联系起来。
  • 标准偏差:这是一个数值,表示个体与群体相比在上的差异。如果它们的变化更大,标准偏差就会很大,反之亦然——如果所有的单个实验在整个组中大致相同,标准偏差就会很小。
  • 方差 :等于标准偏差的平方。
  • 人口或统计人口 :这是一个所有潜在可观察案例的集合,例如,世界上所有学生的所有成绩,如果我们有兴趣得到世界学生平均成绩的话。
  • 样本:这是人群的一个子集。我们无法获得世界上所有学生的所有成绩,所以我们只能收集数据样本并对其进行建模。

怎么做…

我们可以使用 Python 的模块random生成一个简单的随机样本。这里有一个例子:

import pylab import random SAMPLE_SIZE =100# seed random generator# if no argument provided# uses system current time random.seed()# store generated random values here real_rand_vars =[]# pick some random values real_rand_vars =[random.random()for val inxrange(SIZE)]# create histogram from data in 10 buckets pylab.hist(real_rand_vars,10)# define x and y labels pylab.xlabel("Number range") pylab.ylabel("Count")# show figure pylab.show()

这是一个均匀分布的样本。当我们运行这个例子时,我们应该会看到类似于下面的图:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_07.jpg

尝试将SAMPLE_SIZE设置为一个大数字(比如10000),看看直方图的表现。

如果我们希望值的范围不是从 0 到 1,而是从 1 到 6(例如,模拟单骰子投掷),我们可以使用random.randint(min, max);这里,minmax分别是包含下限和包含上限。如果你想生成的是浮点数而不是整数,有一个random.uniform(min, max)函数来提供。

以类似的方式,并使用相同的工具,我们可以生成虚构的价格增长数据的时间序列图,带有一些随机噪声。

import pylab import random # days to generate data for duration =100# mean value mean_inc =0.2# standard deviation std_dev_inc =1.2# time series x =range(duration) y =[] price_today =0for i in x: next_delta = random.normalvariate(mean_inc, std_dev_inc) price_today += next_delta y.append(price_today) pylab.plot(x,y) pylab.xlabel("Time") pylab.xlabel("Time") pylab.ylabel("Value") pylab.show()

这个代码定义了一系列 100 个数据点(虚构的日子)。对于接下来的每一天,我们从从mean_incstd_dev_inc的正态分布(random.normalvariate())中选择一个随机值,并将该值添加到昨天的价格值(price_today)中。

如果我们想要更多的控制,我们可以使用不同的分布。下面的代码演示并可视化了不同的分布。我们将对单独的代码段进行注释。我们首先导入所需的模块,并定义一些直方图桶。我们还创建了一个保存直方图的图形。

# coding: utf-8import random import matplotlib import matplotlib.pyplot as plt SAMPLE_SIZE =1000# histogram buckets buckets =100 plt.figure()# we need to update font size just for this example matplotlib.rcParams.update({'font.size':7})

为了布局所有需要的图,我们为所有直方图定义了一个 6 乘 2 的子图网格。第一个图是正态分布随机变量。

plt.subplot(621) plt.xlabel("random.random")# Return the next random floating point number in the range [0.0, 1.0). res =[random.random()for _ inxrange(1, SAMPLE_SIZE)] plt.hi 

对于第二个图,我们绘制了一个均匀分布的随机变量。

plt.subplot(622) plt.xlabel("random.uniform")# Return a random floating point number N such that a <= N <= b for a <= b and b <= N <= a for b < a.# The end-point value b may or may not be included in the range depending on floating-point rounding in the equation a + (b-a) * random(). a =1 b = SAMPLE_SIZE res =[random.uniform(a, b)for _ inxrange(1, SAMPLE_SIZE)] plt.hist(res, buckets)

第三个图是三角形分布。

plt.subplot(623) plt.xlabel("random.triangular")# Return a random floating point number N such that low <= N <= high and with the specified # mode between those bounds. The low and high bounds default to zero and one. The mode # argument defaults to the midpoint between the bounds, giving a symmetric distribution. low =1 high = SAMPLE_SIZE res =[random.triangular(low, high)for _ inxrange(1, SAMPLE_SIZE)] plt.hist(res, buckets)

第四个图是贝塔分布。参数的条件是α和β应该大于零。返回值的范围在 0 和 1 之间。

plt.subplot(624) plt.xlabel("random.betavariate") alpha =1 beta =10 res =[random.betavariate(alpha, beta)for _ inxrange(1, SAMPLE_SIZE)] plt.hist(res, buckets)

第五个图显示了指数分布。lambd是 1.0 除以所需平均值。它应该是非零的。(该参数将被称为 lambda,但在 Python 中这是一个保留字。)如果lambd为正,返回值范围从 0 到正无穷大,如果lambd为负,返回值范围从负无穷大到 0。

plt.subplot(625) plt.xlabel("random.expovariate") lambd =1.0/((SAMPLE_SIZE +1)/2.) res =[random.expovariate(lambd)for _ inxrange(1, SAMPLE_SIZE)] plt.hist(res, buckets)

我们的下一个图是伽马分布,其中参数的条件是α和β大于 0。概率分布函数为:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_12.jpg

伽马分布的代码如下:

plt.subplot(626) plt.xlabel("random.gammavariate") alpha =1 beta =10 res =[random.gammavariate(alpha, beta)for _ inxrange(1, SAMPLE_SIZE)] plt.hist(res, buckets)

对数正态分布是我们的下一个图。如果你取这个分布的自然对数,你会得到一个均值mu和标准差sigma的正态分布。mu可以有任意值,sigma必须大于零。

plt.subplot(627) plt.xlabel("random.lognormvariate") mu =1 sigma =0.5 res =[random.lognormvariate(mu, sigma)for _ inxrange(1, SAMPLE_SIZE)] plt.hist(res, buckets)

下一个图是正态分布,其中mu是平均值,sigma是标准差。

plt.subplot(628) plt.xlabel("random.normalvariate") mu =1 sigma =0.5 res =[random.normalvariate(mu, sigma)for _ inxrange(1, SAMPLE_SIZE)] plt.hist(res, buckets)

最后一个图是帕累托分布。alpha是形状参数。

plt.subplot(629) plt.xlabel("random.paretovariate") alpha =1 res =[random.paretovariate(alpha)for _ inxrange(1, SAMPLE_SIZE)] plt.hist(res, buckets) plt.tight_layout() plt.show()

这是一个很大的代码示例,但是基本上,我们根据各种分布选择了 1000 个随机数。这些是不同统计分支(经济学、社会学、生物科学等)中使用的常见分布。

我们应该看到基于所使用的分布算法的直方图的差异。花点时间了解以下九个绘图:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_08.jpg

使用seed()初始化伪随机发生器,因此random()产生相同的预期随机值。这有时很有用,比预先生成随机数据并将其保存到文件中要好。后一种技术并不总是可行的,因为它需要在文件系统上保存(可能是大量的)数据。

如果您想防止您随机生成的序列的任何重复性,我们建议使用random.SystemRandom,它下面使用os.urandomos.urandom提供更多熵源。如果使用这个随机生成器界面,seed()setstate() 没有效果;因此,这些样品是不可复制的。

如果我们想有一些随机词,最简单的方法(在 Linux 上)可能是使用/usr/share/dicts/words。我们可以在下面的例子中看到这是如何做到的:

import random withopen('/usr/share/dict/words','rt')as f: words = f.readlines() words =[w.rstrip()for w in words]for w in random.sample(words,5):print w 

这个解决方案只适用于 Unix,不会在 Windows 上运行(不过会在 Mac OS 上运行)。对于 Windows,您可以使用从各种免费来源构建的文件(古登堡计划、维基词典、英国国家语料库或彼得·诺维格博士的http://norvig.com/big.txt)。

平滑真实数据中的噪声

在这个食谱中,我们引入了一些高级算法来帮助清理来自真实世界来源的数据。这些算法在信号处理领域是众所周知的,我们不会深入数学,而只是举例说明它们是如何以及为什么工作的,以及它们可以用于什么目的。

做好准备

来自不同现实生活传感器的数据通常不平滑和干净,并且包含一些我们通常不想在图表和绘图上显示的噪声。我们希望图表清晰明了,能够显示信息,让观众花最少的力气去解读。

我们不需要安装任何新软件,因为我们将使用一些已经熟悉的 Python 包:NumPy、SciPy 和 matplotlib。

怎么做…

基本算法基于使用滚动窗口(例如卷积)。该窗口滚动数据,用于计算该窗口的平均值。

对于我们的离散数据,我们使用 NumPy 的 convolve函数;它返回两个一维序列的离散线性卷积。我们还使用了 NumPy 的linspace功能,它为指定的时间间隔生成一系列均匀间隔的数字。

函数ones定义了一个数组或矩阵(例如多维数组),其中每个元素都有值1。这有助于生成用于平均的窗口。

它是如何工作的…

平滑我们正在处理的数据中的噪声的一个简单而天真的技术是对某个窗口(样本)求平均值,并绘制给定窗口的平均值,而不是所有数据点的平均值。这是更高级算法的基础。

from pylab import*from numpy import*defmoving_average(interval, window_size):'''Compute convoluted window for given size ''' window = ones(int(window_size))/float(window_size)return convolve(interval, window,'same') t = linspace(-4,4,100) y = sin(t)+ randn(len(t))*0.1 plot(t, y,"k.")# compute moving average y_av = moving_average(y,10) plot(t, y_av,"r")#xlim(0,1000) xlabel("Time") ylabel("Value") grid(True) show()

这里,我们展示了与原始数据点(绘制为点)相比,平滑线的外观:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_09.jpg

按照这个想法,我们可以跳到一个更高级的例子,并使用现有的 SciPy 库使这个窗口平滑工作得更好。

我们将要演示的方法是基于缩放窗口与信号(即数据点)的卷积(函数求和)。这个信号是以一种巧妙的方式准备的,在两端添加相同信号的副本,但反射它,因此我们将边界效应降至最低。这段代码基于 SciPy Cookbook 的例子,可以在这里找到:http://www.scipy.org/Cookbook/SignalSmooth

import numpy from numpy import*from pylab import*# possible window type WINDOWS =['flat','hanning','hamming','bartlett','blackman']# if you want to see just two window type, comment previous line,# and uncomment the following one# WINDOWS = ['flat', 'hanning']defsmooth(x, window_len=11, window='hanning'):""" Smooth the data using a window with requested size. Returns smoothed signal. x -- input signal window_len -- lenght of smoothing window window -- type of window: 'flat', 'hanning', 'hamming', 'bartlett', 'blackman' flat window will produce a moving average smoothing. """if x.ndim !=1:raise ValueError,"smooth only accepts 1 dimension arrays."if x.size < window_len:raise ValueError,"Input vector needs to be bigger than window size."if window_len <3:return x ifnot window in WINDOWS:raise ValueError("Window is one of 'flat', 'hanning', 'hamming', ""'bartlett', 'blackman'")# adding reflected windows in front and at the end s=numpy.r_[x[window_len-1:0:-1], x, x[-1:-window_len:-1]]# pick windows type and do averagingif window =='flat':#moving average w = numpy.ones(window_len,'d')else:# call appropriate function in numpy w =eval('numpy.'+ window +'(window_len)')# NOTE: length(output) != length(input), to correct this:# return y[(window_len/2-1):-(window_len/2)] instead of just y. y = numpy.convolve(w/w.sum(), s, mode='valid')return y # Get some evenly spaced numbers over a specified interval. t = linspace(-4,4,100)# Make some noisy sinusoidal x = sin(t) xn = x + randn(len(t))*0.1# Smooth it y = smooth(x)# windows ws =31 subplot(211) plot(ones(ws))# draw on the same axes hold(True)# plot for every windowsfor w in WINDOWS[1:]:eval('plot('+w+'(ws) )')# configure axis properties axis([0,30,0,1.1])# add legend for every window legend(WINDOWS) title("Smoothing windows")# add second plot subplot(212)# draw original signal plot(x)# and signal with added noise plot(xn)# smooth signal with noise for every possible windowing algorithmfor w in WINDOWS: plot(smooth(xn,10, w))# add legend for every graph l=['original signal','signal with noise'] l.extend(WINDOWS) legend(l) title("Smoothed signal") show()

我们应该看下面两个图,看看开窗算法如何影响噪声信号。上图表示可能的窗口算法,下图显示从原始信号到加噪信号的所有可能结果,甚至每个窗口算法的平滑信号。尝试对可能的窗口类型进行评论,只留下一两个以获得更好的理解。

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_10.jpg

还有更多…

另一个非常流行的信号平滑算法是中值滤波。该滤波器的主要思想是逐个条目地遍历信号条目,用相邻条目的中值替换每个条目。这个想法使得这个过滤器既快速又适用于等一维数据集,也适用于二维数据集(如图像)。

在下面的例子中,我们使用了 SciPy 信号工具箱中的实现:

import numpy as np import pylab as p import scipy.signal as signal # get some linear data x = np.linspace (0,1,101)# add some noisy signal x[3::10]=1.5 p.plot(x) p.plot(signal.medfilt(x,3)) p.plot(signal.medfilt(x,5)) p.legend(['original signal','length 3','length 5']) p.show ()

我们在下图中看到,窗口越大,我们的信号与原始信号相比失真越大,但看起来越平滑:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_02_11.jpg

还有很多方法可以平滑从外部来源接收的数据(信号)。这在很大程度上取决于你工作的区域和信号的性质。许多算法是专门针对特定信号的,可能并没有针对您遇到的每种情况的通用解决方案。

然而,有一个重要的问题:“什么时候你不应该平滑一个信号?”不应该平滑信号的一种常见情况是在统计过程之前,例如最小二乘曲线拟合,因为所有的平滑算法都至少有轻微的损耗,并且会改变信号形状。此外,平滑的噪声可能被误认为是实际信号。*

三、绘制您的第一个绘图并自定义它们

在这一章中,我们将更详细地介绍 matplotlib 的大部分可能性。我们将涵盖:

  • 定义绘图类型-条形图、折线图和堆叠图
  • 绘制简单的正弦和余弦曲线
  • 定义轴长度和限制
  • 定义绘图线样式、特性和格式字符串
  • 设置记号、标签和网格
  • 添加图例和标注
  • 将脊椎移向中心
  • 制作直方图
  • 用误差线制作条形图
  • 让饼图有价值
  • 用填充区域绘图
  • 用彩色标记绘制散点图

简介

虽然我们已经使用 matplotlib 绘制了我们的第一个图,但是我们没有详细讨论它们是如何工作的,如何设置它们,或者使用 matplotlib 有什么可能性。我们探索和练习最常见的数据可视化类型:折线图、条形图、直方图、饼图及其变体。

matplotlib 是一个强大的工具箱,它几乎满足了我们对 2D 的所有需求,也满足了一些 3D 绘图需求。作者打算让你学习 matplotlib 的最好方法是通过例子。当我们需要画一个绘图时,我们会寻找一个类似的例子,并试图改变它以适应我们的需要。这样,我们也准备给大家呈现一些有用的例子,相信这个例子会帮助大家找到一个最符合自己需要的绘图。

定义绘图类型–条形图、折线图和堆叠图

在这个食谱中,我们将展示不同的基本绘图以及它们的用途。这里描述的大部分图都是日常使用的,其中一些图为理解更多数据可视化高级概念提供了基础。

做好准备

我们从matplotlib.pyplot库中的一些常见图表开始,仅包含样本数据集,以开始基本的图表制作,并为以下食谱奠定基础。

怎么做…

我们从在 IPython 中创建一个简单的图开始。IPython 非常棒,因为它允许我们交互式地更改绘图,并立即看到结果。

然后输入 matplotlib plot代码:

In [1]: plot([1,2,3,2,3,2,2,1]) Out[1]:[<matplotlib.lines.Line2D at 0x412fb50>]

通过在命令提示符下键入以下命令启动 IPython:

$ ipython --pylab 

该图应在新窗口中打开,显示图的默认外观和一些支持信息:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_01.jpg

matplotlib 中的基本绘图包含以下元素:

  • x 轴和 y 轴:这是水平轴和垂直轴。
  • x 和 y 刻度:这些是表示轴段的小刻度。可以有大票和小票。
  • x 和 y 刻度标签:代表特定轴上的值。
  • 绘图区:这是实际绘图的地方。

您会注意到我们提供给plot()的值是 y 轴值。plot()提供 x 轴的默认值;它们是从 0 到 7 的线性值(y 值的数量减 1)。

现在,尝试为 x 轴添加值;作为plot()函数的第一个参数,同样在同一个 IPython 会话中,键入:

In [2]: plot([4,3,2,1],[1,2,3,4]) Out[2]:[<matplotlib.lines.Line2D at 0x31444d0>]

型式

注意 IPython 如何计数输入和输出线(In [2]Out [2])。这将帮助我们记住我们在当前会话中的位置,并启用更高级的功能,例如将会话的一部分保存在 Python 文件中。在数据分析过程中,使用 IPython 进行原型制作是获得令人满意的解决方案,然后将特定会话保存到文件中的最快方法,如果需要重现相同的绘图,可以稍后执行。

这将更新绘图如下:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_02.jpg

我们在这里看到如何 matplotlib 扩展 y 轴以适应新的值范围,并自动改变第二条绘图线的颜色以使我们能够区分新的绘图。

除非我们关闭 hold属性(通过调用hold(False)),否则所有后续的图都将绘制在相同的轴上。这是 IPython 中pylab模式的默认行为,而在常规 Python 脚本中,hold默认为关闭。

让我们打包一些更常见的图,并在同一数据集上进行比较。您可以在 IPython 中键入它,或者从单独的 Python 脚本中运行它:

from matplotlib.pyplot import*# some simple data x =[1,2,3,4] y =[5,4,3,2]# create new figure figure()# divide subplots into 2 x 3 grid# and select #1 subplot(231) plot(x, y)# select #2 subplot(232) bar(x, y)# horizontal bar-charts subplot(233) barh(x, y)# create stacked bar charts subplot(234) bar(x, y)# we need more data for stacked bar charts y1 =[7,8,5,3] bar(x, y1, bottom=y, color ='r')# box plot subplot(235) boxplot(x)# scatter plot subplot(236) scatter(x,y) show()

这就是应该如何将从变成的图形:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_03.jpg

它是如何工作的…

借助figure()我们创建一个新的图形。如果我们提供一个字符串参数,比如sample charts,它将是一个窗口的后端标题。如果我们用相同的参数(也可以是数字)调用figure()函数,我们将激活相应的图形,并对该图形执行以下所有绘图。

接下来,我们使用subplot(231)调用将图分成 2 乘 3 的网格。我们可以用subplot(3, 2, 1)来称呼这个,其中第一个参数是行数,第二个是列数,第三个代表图号。

我们继续使用简单的调用创建垂直条形图(bar())和水平条形图(barh())来创建一个常见的图表类型。对于堆叠条形图,我们需要将两个条形图调用绑定在一起。我们通过使用参数bottom = y将第二个条形图与前一个连接起来。

使用boxplot()调用创建方框图,其中方框从下四分位数延伸到上四分位数,线条位于中间值。我们将很快回到方块图。

我们最后创建一个散点图,让您了解基于点的数据集。当我们在一个数据集中有数千个数据点时,这可能更适合使用,但是在这里,我们想说明同一数据集的不同表示。

还有更多…

我们现在可以回到方框图,因为我们需要解释最重要的显示选项。

对于初学者,我们可以添加从框延伸的胡须来表示数据集的整个范围。方框和须图主要用于表示一个或多个数据集中数据的变化;它们很容易比较和易读。在同一个图中,它们可以表示五个统计数据:

  • 最小值:在数据集中
  • 第二个四分位数:低于这个四分位数的是给定数据集的 25%
  • 中值:这是数据集的中值
  • 第三个四分位数:高于这个四分位数的是给定数据集的上 25%
  • 最大值:这是给定数据集的最大值

为了说明这种行为,我们将演示在方框图和直方图中绘制相同的数据集,如以下代码所示:

from pylab import* dataset =[113,115,119,121,124,124,125,126,126,126,127,127,128,129,130,130,131,132,133,136] subplot(121) boxplot(dataset, vert=False) subplot(122) hist(dataset) show()

这将为我们提供以下图表:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_04.jpg

在之前的比较中,我们可以观察到两个不同图表中相同数据集的表示中的差异。左侧的指向上述五个统计值,而右侧的(直方图)显示给定范围内数据集的分组。

绘制简单的正弦和余弦图

本食谱将复习绘制数学函数的基础知识以及与数学图形相关的几件事,例如在标签和曲线上写希腊符号。

做好准备

我们最常用的图形是线图命令,它在图形图上绘制给定的(x,y)坐标。

怎么做…

我们从开始,计算同一线性区间内的正弦和余弦函数——从π到π,中间有 256 个点——并在同一曲线上绘制正弦(x)和余弦(x)的值:

import matplotlib.pyplot as pl import numpy as np x = np.linspace(-np.pi, np.pi,256, endpoint=True) y = np.cos(x) y1 = np.sin(x) pl.plot(x,y) pl.plot(x, y1) pl.show()

这将为我们提供以下图表:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_05.jpg

按照这个简单的图,我们可以定制更多,以提供更多信息,并更精确地了解轴和边界:

from pylab import*import numpy as np # generate uniformly distributed# 256 points from -pi to pi, inclusive x = np.linspace(-np.pi, np.pi,256, endpoint=True)# these are vectorised versions# of math.cos, and math.sin in built-in Python maths# compute cos for every x y = np.cos(x)# compute sin for every x y1 = np.sin(x)# plot cos plot(x, y)# plot sin plot(x, y1)# define plot title title("Functions $\sin$ and $\cos$")# set x limit xlim(-3.0,3.0)# set y limit ylim(-1.0,1.0)# format ticks at specific values xticks([-np.pi,-np.pi/2,0, np.pi/2, np.pi],[r'$-\pi$',r'$-\pi/2$',r'$0$',r'$+\pi/2$',r'$+\pi$']) yticks([-1,0,+1],[r'$-1$',r'$0$',r'$+1$']) show()

这应该会给我们一个稍微好一点的图表:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_06.jpg

我们看到我们使用了这样的表达方式$\sin$,或者$-\pi$来写数字中的希腊字母。这是 LaTex 语法,我们将在后面的章节中进一步探讨。在这里,我们只是说明了让你的数学图表对某些受众来说更易读是多么容易。

定义轴长度和极限

这个配方将围绕极限和长度展示各种有用的轴属性,我们可以在 matplotlib 中进行配置。

做好准备

对于这个食谱,我们想激发 IPython:

$ ipython --pylab 

怎么做…

开始尝试轴的各种属性。只需调用一个空的axis()函数就会返回轴的默认值:

In [1]: axis() Out[1]:(0.0,1.0,0.0,1.0)

请注意,如果您处于交互模式并使用窗口后端,将显示一个带有空轴的图形。

这里的数值分别代表xminxmaxyminymax。同样,我们可以设置 x 轴和 y 轴的值:

In [2]: l =[-1,1,-10,10] In [3]: axis(l) Out[3]:[-1,1,-10,10]

同样,如果您处于交互模式,这将更新相同的数字。此外,我们还可以使用关键字参数(**kwargs)单独更新任何值,只需将xmax设置为某个值。

它是如何工作的…

如果我们不使用axis()或其他设置,matplotlib 将自动使用允许我们在一个图中看到所有数据点的最小值。如果我们将axis()限制设置为小于数据集中的最大值,matplotlib 将按照指示执行,并且我们不会看到图上的所有点。这可能是一个混乱甚至错误的来源,我们认为我们看到了我们画的一切。避免这种情况的一种方法是调用autoscale() ( matplotlib.pyploy.autoscale()),它将计算轴的最佳大小以适合要显示的数据。

如果要给同一个图形添加新的轴,可以使用matplotlib.pyploy.axes()。我们通常想在这个默认调用中添加一些属性;例如,rect—可以用归一化单位(0,1)表示属性leftbottomwidthheight—也可能是axisbg,指定坐标轴的背景色。

我们还可以为添加的轴设置其他属性,如sharex / sharey,它接受其他轴实例的值,并与其他轴共享当前轴(x/y)。或者定义我们是否要使用极轴的参数polar

添加新的轴可能很有用;例如,如果需要紧密耦合同一数据的不同视图来说明其属性,可以将多个图表组合在一个图上。

如果我们想在当前图形中只添加一行,可以使用matplotlib.pyploy.axhline()matplotlib.pyplot.axvline()。功能axhilne()axvline()将分别为给定的 x 和 y 数据值绘制横轴和纵轴。它们共享相似的参数,最重要的是 y 位置、xmin,以及用于axhline()和 x 位置、yminxmax,以及用于axvline()ymax

让我们看一下它的外观,在同一个 IPython 会话中继续:

In [3]: axhline() Out[3]:<matplotlib.lines.Line2D at 0x414ecd0> In [4]: axvline() Out[4]:<matplotlib.lines.Line2D at 0x4152490> In [5]: axhline(4) Out[5]:<matplotlib.lines.Line2D at 0x4152850>

我们应该有一个像下面这样的图:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_07.jpg

这里我们看到只是在没有参数的情况下调用这些函数让它们取默认值,为y=0 ( axhline())画一条水平线,为x=0 ( axvline())画一条垂直线。

与此类似的是两个相关的函数,允许我们在轴上添加水平跨度(矩形)。这些是matplotlib.pyplot.axhspan()matplotlib.pyplot.axspan()。功能axhspan()yminymax作为定义水平跨度有多宽的必需参数。与此类似,axvspan()xminxmax来定义垂直跨度的宽度。

还有更多…

默认情况下,图形中的网格处于关闭状态,但可以轻松打开和自定义。对matplotlib.pyplot.grid()的默认调用将切换网格的可见性。其他控制参数如下:

  • which:定义要绘制的网格刻度类型(可以是majorminorboth)
  • axis:定义画哪组网格线(可以是bothxy)

轴通常通过matplotlib.pyplot.axis()控制。在内部,轴由几个 Python 类表示,父类是matplotlib.axes.Axes,它包含了大多数操作轴的方法。单个轴由matplotlib.axis.Axis类表示,其中 x 轴使用matplotlib.axis.XAxis,y 轴使用matplotlib.axis.YAxis类。

我们不需要使用这些来执行我们的配方,但重要的是要知道如果我们对更高级的轴控制感兴趣,以及当我们达到通过matplotlib.pyplot命名空间可用的极限时,应该从哪里寻找。

定义绘图线样式、特性和格式字符串

这个食谱展示了我们如何改变各种线条属性,例如样式、颜色或宽度。根据所呈现的信息,适当设置线条,并使其足够清晰以适合目标观众(如果观众是较年轻的人群,我们可能希望用更鲜艳的颜色来瞄准他们;如果他们年龄较大,我们可能希望使用更多对比色)可以在几乎不引人注意和给观众留下很大影响之间产生差异。

做好准备

尽管我们强调了从美学角度调整演示文稿的重要性,但我们首先必须学会如何做。

如果你对配色没有特别的眼光,有免费的商业在线工具可以为你生成颜色集。其中最著名的是彩色啤酒 2,可以在 http://colorbrewer2.org/ T2 找到。

一些严肃的研究已经在数据可视化中使用了颜色,但是解释这个理论超出了本书的范围。如果你每天都在使用更高级的可视化工具,那么这个主题的材料是必读的。

怎么做…

让我们学习如何更改线条属性。我们可以用不同的方法改变我们的绘图。

首先也是最常见的是通过将关键字参数传递给函数来定义行,如plot():

plot(x, y, linewidth=1.5)

因为对plot()的调用会返回行实例(matplotlib.lines.Line2D),所以我们可以在该实例上使用一组 setter 方法来设置各种属性:

line,= plot(x, y) line.set_linewidth(1.5)

使用 MATLAB 的人会觉得有必要使用第三种方式配置线属性——使用 setp()功能:

lines = plot(x, y) setp(lines,'linewidth',1.5)

另一种使用setp()的方式是:

setp(lines, linewidth=1.5)

无论你喜欢用什么方式配置线路,选择一种方法在整个项目中保持一致(或者至少一个文件)。这种的方式,当你(或者将来是你的另一个人)回到代码时,会更容易理解和改变它。

它是如何工作的…

我们可以为一条线更改的所有属性都包含在matplotlib.lines.Line2D类中。我们在下表中列出了其中的一些:

|

财产

|

值类型

|

描述

|
| — | — | — |
| alpha | float | 设置用于混合的 alpha 值;并非所有后端都支持。 |
| colorc | 任何 matplotlib 颜色 | 设置线条的颜色。 |
| dashes | 以点为单位的开/关油墨顺序 | 设置破折号序列,即以磅为单位的带开/关墨水的破折号序列。如果seq为空或者如果seq = (None, None)linestyle将被设置为solid。 |
| label | 任何字符串 | 将自动图例的标签设置为s。 |
| linestylels | [ '-' &#124; '–' &#124; '-.' &#124; ':' &#124; 'steps' &#124; ...] | 设置线条的线条样式(也接受绘图样式)。 |
| linewidthlw | float以点数表示的值 | 以磅为单位设置线宽。 |
| marker | [ 7 &#124; 4 &#124; 5 &#124; 6 &#124; 'o' &#124; 'D' &#124; 'h' &#124; 'H' &#124; '_' &#124; '' &#124; 'None' &#124; ' ' &#124; None &#124; '8' &#124; 'p' &#124; ',' &#124; '+' &#124; '.' &#124; 's' &#124; '*' &#124; 'd' &#124; 3 &#124; 0 &#124; 1 &#124; 2 &#124; '1' &#124; '3' &#124; '4' &#124; '2' &#124; 'v' &#124; '<' &#124; '>' &#124; '^' &#124; '&#124;' &#124; 'x' &#124; '$...$' &#124; tuple &#124; Nx2 array ] | 设置线条标记。 |
| markeredgecolormec | 任何 matplotlib 颜色 | 设置标记边缘颜色。 |
| markeredgewidthmew | float以点数表示的值 | 以磅为单位设置标记边缘宽度。 |
| markerfacecolormfc | 任何 matplotlib 颜色 | 设置标记面颜色。 |
| markersizems | float | 以磅为单位设置标记的大小。 |
| solid_capstyle | ['butt' &#124; 'round' &#124; 'projecting'] | 为实线样式设置帽样式。 |
| solid_joinstyle | ['miter' &#124; 'round' &#124; 'bevel'] | 设置实线样式的连接样式。 |
| visible | [True &#124; False] | 设置艺术家的可见性。 |
| xdata | np.array | 为 x 设置数据np.array。 |
| ydata | np.array | 设置数据np.array为 y。 |
| Zorder | Any number | 为艺术家设置 z 轴顺序。先画出Zorder值较低的艺术家。如果 x 轴和 y 轴水平向右,垂直于屏幕顶部,则 z 轴是向观看者延伸的轴。所以 0 值会在屏幕上,1,上面一层,以此类推。 |

以下表格显示了一些线型:

|

线条样式

|

描述

|
| — | — |
| '-' | 固体 |
| '--' | 虚线 |
| '-.' | 虚线点 |
| ':' | 有点的 |
| 'None', ' ', '' | 什么都不画 |

下表显示了线标记:

|

标记

|

描述

|
| — | — |
| 'o' | 圆 |
| 'D' | 钻石 |
| 'h' | 六边形 1 |
| 'H' | 六边形 2 |
| '_' | 水平线 |
| '', 'None', ' ', None | 没有任何东西 |
| '8' | 八角形 |
| 'p' | 五边形 |
| ',' | 像素 |
| '+' | 加 |
| '.' | 要点 |
| 's' | 平方 |
| '*' | 星星 |
| 'd' | 薄钻石 |
| 'v' | 三角形向下 |
| '<' | 三角形 _ 左侧 |
| '>' | 三角形 _ 右 |
| '^' | 三角形向上 |
| '&#124;' | 垂直线 |
| 'x' | X |

颜色

我们可以通过调用matplotlib.pyplot.colors()获得【matplotlib 支持的所有颜色;这将给出:

|

别名

|

颜色

|
| — | — |
| b | 蓝色 |
| g | 格林(姓氏);绿色的 |
| r | 红色 |
| c | 蓝绿色 |
| m | 品红 |
| y | 黄色 |
| k | 黑色 |
| w | 怀特(姓氏) |

这些颜色可以在不同的 matplotlib 函数中使用,这些函数接受颜色参数。

如果这些基本的颜色不够——随着我们的进步,它们将不够——我们可以使用另外两种方式来定义颜色值。我们可以使用一个 HTML 十六进制字符串:

color ='#eeefff'

我们也可以使用合法的 HTML 颜色名称('red''chartreuse')。我们还可以传递一个归一化为[0, 1]的 RGB 元组:

color =(0.3,0.3,0.4)

参数颜色被一系列函数接受,例如title():

title('Title in a custom color', color='#123456')

背景颜色

通过为matplotlib.pyplot.axes()matplotlib.pyplot.subplot()等功能提供axisbg,我们可以定义轴的背景颜色:

subplot(111, axisbg=(0.1843,0.3098,0.3098))

设置刻度、标签和网格

在这个配方中,我们将继续设置轴和线属性,并为我们的图形和图表添加更多数据。

做好准备

让我们了解一下图表和子图。

在 matplotlib 中,figure() 用于显式创建图形,表示用户界面窗口。图形是通过调用plot()或类似的函数隐式创建的。这对于简单的图表来说很好,但是具有能力来显式地创建图形并获得对其实例的引用对于更高级的使用非常有用。

一个图表包含一个或多个子图。子图允许我们在一个规则的网格中安排绘图。我们已经使用了subplot(),其中我们指定了行数和列数以及我们所指的图的数量。

如果我们想要更多的控制,我们需要使用matplotlib.axes.Axes类的轴实例。它们允许我们在图中的任何位置放置绘图。一个例子是把一个小的绘图放在一个大的绘图里。

怎么做…

刻度是数字的一部分。它们由刻度定位器(刻度出现的地方)和显示刻度如何出现的刻度格式化器组成。有大有小。默认情况下,次要刻度不可见。更重要的是,大刻度和小刻度可以相互独立地格式化和定位。

我们可以使用matplotlib.pyplot.locator_params()来控制刻度定位器的行为。即使刻度位置通常是自动确定的,我们也可以控制刻度的数量,如果我们想的话,可以使用紧凑的视图,例如当图较小时。

from pylab import*# get current axis ax = gca()# set view to tight, and maximum number of tick intervals to 10 ax.locator_params(tight=True, nbins =10)# generate 100 normal distribution values ax.plot(np.random.normal(10,.1,100)) show()

这应该会给我们以下图表:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_08.jpg

我们看到 x 轴和 y 轴是如何划分的,显示了哪些值。我们本可以使用定位器类实现相同的设置。这里我们说的是“将主定位器设置为为 10 的倍数”:

ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(10))

同样可以指定刻度格式化程序。格式化程序指定值(通常是数字)的显示方式。例如,matplotlib.ticker.FormatStrFormatter简单地指定'%2.1f''%1.1f cm'作为用于跑马灯标签的字符串。

让我们看一个使用日期的例子。

matplotlib 将浮点值中的日期表示为自世界协调时 0001-01-01 加 1 后经过的天数。所以,0001-01-01 世界协调时 06:00 是 1.25。

然后我们可以使用诸如matplotlib.dates.date2num()matplotlib.dates.num2date()matplotlib.dates.drange()等辅助函数在不同的表示之间转换日期。

让我们看另一个例子:

from pylab import*import matplotlib as mpl import datetime fig = figure()# get current axis ax = gca()# set some daterange start = datetime.datetime(2013,01,01) stop = datetime.datetime(2013,12,31) delta = datetime.timedelta(days =1)# convert dates for matplotlib dates = mpl.dates.drange(start, stop, delta)# generate some random values values = np.random.rand(len(dates)) ax = gca()# create plot with dates ax.plot_date(dates, values, linestyle='-', marker='')# specify formater date_format = mpl.dates.DateFormatter('%Y-%m-%d')# apply formater ax.xaxis.set_major_formatter(date_format)# autoformat date labels# rotates labels by 30 degrees by default# use rotate param to specify different rotation degree# use bottom param to give more room to date labels fig.autofmt_xdate() show()

前面的代码将给出下面的图表:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_09.jpg

添加图例和标注

图例和标注在上下文中清晰地解释了数据图。通过给每个绘图分配一个关于它所代表的数据的简短描述,我们在读者(观众)的头脑中启用了一个更简单的心智模型。这个食谱将展示如何在我们的图形上标注特定的点,以及如何创建和定位数据图例。

做好准备

你有多少次看着图表想知道数据代表什么?更多的时候,报纸和其他日报和周刊会制造不包含适当传说的绘图,这样读者就可以自由地解读这些表象。这给读者造成了歧义,增加了出错的可能性。

怎么做…

让我们用下面的例子演示如何添加图例和标注:

from matplotlib.pyplot import*# generate different normal distributions x1 = np.random.normal(30,3,100) x2 = np.random.normal(20,2,100) x3 = np.random.normal(10,3,100)# plot them plot(x1, label='plot') plot(x2, label='2nd plot') plot(x3, label='last plot')# generate a legend box legend(bbox_to_anchor=(0.,1.02,1.,.102), loc=3, ncol=3, mode="expand", borderaxespad=0.)# annotate an important value annotate("Important value",(55,20), xycoords='data', xytext=(5,38), arrowprops=dict(arrowstyle='->')) show()

在代码之前的将给出如下图:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_10.jpg

我们所做的是为每个图分配一个字符串标签,这样legend()将尝试并确定在图例框中添加什么。

我们通过定义loc参数来设置图例框的位置。这是可选的,但是我们希望指定一个最不可能在绘图线上绘制图例框的位置。

它是如何工作的…

下表给出了所有位置参数字符串:

|

线

|

数值

|
| — | — |
| upper right | 1 |
| upper left | 2 |
| lower left | 3 |
| lower right | 4 |
| right | 5 |
| center left | 6 |
| center right | 7 |
| lower center | 8 |
| upper center | 9 |
| center | 10 |

要不在图例中显示标签,请将标签设置为_nolegend_

对于图例,我们用ncol = 3定义列数,用lower left设置位置。我们指定了一个边界框(bbox_to_anchor)从位置(0., 1.02)开始,宽度为1,高度为0.102。这些是标准化的轴坐标。参数modeNoneexpand,允许图例框水平扩展填充轴区域。参数borderaxespad定义轴和图例边框之间的填充。

对于标注,我们已经定义了要在坐标xy上绘制的字符串。坐标系被指定为与数据坐标系相同;因此,坐标系为xycoord = 'data'。文本的起始位置由xytext的值定义。

一个箭头从xytext画到xy坐标,并且arrowprops字典可以为该箭头定义许多属性。对于这个例子,我们使用arrowstyle来定义箭头样式。

将脊椎移向中央

这个食谱将演示如何将脊椎移到中间。

脊线定义数据区边界;它们连接轴刻度标记。有四根刺。我们可以把它们放在任何我们想放的地方;默认情况下,它们被放置在轴的边界上,因此我们看到数据图周围有一个框。

怎么做…

要将刺移到绘图中心,我们需要去掉两个刺,使其隐藏(将color设置为none)。之后,我们移动另外两个坐标(0,0)。坐标在数据空间坐标中指定。

下面的代码显示了如何做到这一点:

import matplotlib.pyplot as plt import numpy as np x = np.linspace(-np.pi, np.pi,500, endpoint=True) y = np.sin(x) plt.plot(x, y) ax = plt.gca()# hide two spines ax.spines['right'].set_color('none') ax.spines['top'].set_color('none')# move bottom and left spine to 0,0 ax.spines['bottom'].set_position(('data',0)) ax.spines['left'].set_position(('data',0))# move ticks positions ax.xaxis.set_ticks_position('bottom') ax.yaxis.set_ticks_position('left') plt.show()

这就是绘图会是什么样子:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_11.jpg

它是如何工作的…

这段代码依赖于绘制的图,因为我们正在将脊线移动到位置(0,0),并且在(0,0)位于图中间的间隔上绘制正弦函数。

然而,这展示了如何将脊椎移动到一个特定的位置,以及如何去掉我们不想展示的脊椎。

还有更多…

此外,spines 可以被限制在数据结束的地方,例如,使用set_smart_bounds(True)调用。在这种情况下,matplotlib 试图以一种复杂的方式设置界限,例如,处理倒排界限,或者在数据超出视图范围时剪切要查看的行。

制作直方图

直方图很简单,但是将正确的数据放入其中很重要。我们现在将讨论 2D 的直方图。

直方图用于可视化数据分布的估计。一般来说,我们在谈到直方图时会用到几个术语。垂直矩形表示数据点在特定时间间隔内的频率,称为箱。以固定的间隔创建面元,因此直方图的总面积等于数据点的数量。

直方图可以显示数据的相对频率,而不是使用数据的绝对值。在这种情况下,总面积等于 1。

直方图通常在图像处理软件中用作可视化图像属性的一种方式,例如特定颜色通道中的光分布。此外,这些图像直方图可用于计算机视觉算法中,以检测峰值,帮助边缘检测、图像分割等。

第 5 章制作三维可视化中,我们有处理三维直方图的食谱。

做好准备

箱的数量是我们想要得到的值,但是很难得到正确的值,因为没有关于最佳箱数量的严格规则。关于如何计算箱数有不同的理论,最简单的是基于天花板函数的理论,其中箱数( k )等于天花板*(max(x)–min(x)/h)*,其中 x 是绘制的数据集, h 是所需的箱宽度。这只是一种选择,因为正确显示数据所需的箱数取决于实际数据分布。

怎么做…

我们用一组参数创建一个名为matplotlib.pyploy.hist()的直方图。以下是一些最有用的方法:

  • bins:这要么是整数个箱,要么是给出箱的序列。默认为10
  • range:这是仓位的范围,如果仓位是按顺序给定的,则不使用。离群值被忽略,默认为None
  • normed:如果该值为True,直方图值归一化,形成概率密度。默认为False
  • histtype:这是默认的条形直方图。其他选项包括:
    • barstacked:对于多个数据,这给出了堆叠视图直方图。
    • step:这将创建一个未填充的线图。
    • stepfilled:这将创建默认填充的线图。默认为bar
  • align:这将使料箱边缘之间的条居中。默认为mid。其他数值有leftright
  • color:指定直方图的颜色。它可以是单个值,也可以是一系列颜色。如果指定了多个数据集,颜色序列将以相同的顺序使用。如果未指定,则使用默认的线条颜色序列。
  • orientation:这允许通过将orientation设置为horizontal来创建水平直方图。默认为vertical

以下代码演示了hist()的用法:

import numpy as np import matplotlib.pyplot as plt mu =100 sigma =15 x = np.random.normal(mu, sigma,10000) ax = plt.gca()# the histogram of the data ax.hist(x, bins=35, color='r') ax.set_xlabel('Values') ax.set_ylabel('Frequency') ax.set_title(r'$\mathrm{Histogram:}\ \mu=%d,\ \sigma=%d$'%(mu, sigma)) plt.show()

这为我们的数据样本创建了一个整洁的红色直方图:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_12.jpg

它是如何工作的…

我们从生成一些正态分布的数据开始。直方图以指定的箱数(35 个)绘制,并通过将normed设置为True(或1)进行归一化;我们将color设置为red ( r)。

之后,我们为绘图设置标签和标题。在这里,我们使用编写 LaTeX 表达式的能力来编写数学符号,并将其与 Python 格式字符串混合在一起。

用误差线制作条形图

在本食谱中,我们将展示如何创建条形图以及如何绘制误差线。

做好准备

为了可视化数据集中的测量不确定性或指示误差,我们可以使用误差线。误差线可以很容易地给出数据集无误差的概念。它们可以显示一个标准偏差、一个标准误差或 95%的置信区间。这里没有标准,所以一定要明确说明误差线显示什么值(误差)。实验科学中的大多数论文应该包含误差条来表示数据的准确性。

怎么做…

即使只有两个参数是强制的— leftheight —我们经常想要使用更多的参数。以下是我们可以使用的一些参数:

  • width:这给出了条的宽度。默认值为0.8
  • bottom:如果指定了bottom,则该值加到高度上。默认为None
  • edgecolor:这给出了条边的颜色。
  • ecolor:指定任意误差线的颜色。
  • linewidth:这给出了条边的宽度;特殊值为None(使用默认值)和0(不显示条边时)。
  • orientation:这有两个值verticalhorizontal
  • xerryerr:用于在条形图上生成误差线。

一些可选参数(coloredgecolorlinewidthxerryerr)可以是单个值或长度与小节数相同的序列。

它是如何工作的…

让我们用一个例子来说明这一点:

import numpy as np import matplotlib.pyplot as plt # generate number of measurements x = np.arange(0,10,1)# values computed from "measured" y = np.log(x)# add some error samples from standard normal distribution xe =0.1* np.abs(np.random.randn(len(y)))# draw and show errorbar plt.bar(x, y, yerr=xe, width=0.4, align='center', ecolor='r', color='cyan', label='experiment #1');# give some explainations plt.xlabel('# measurement') plt.ylabel('Measured values') plt.title('Measurements') plt.legend(loc='upper left') plt.show()

前面的代码将绘制以下内容:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_13.jpg

为了能够绘制误差线,我们需要一些措施(x);对于计算出的每一个度量值(y,我们引入了误差(xe)。

我们使用 NumPy 来生成和计算值;标准分布对于演示目的来说已经足够好了,但是如果你碰巧提前知道你的数据分布,你总是可以制作一些原型可视化,并尝试不同的布局来找到呈现信息的最佳选项。

另一个有趣的选择,如果我们正在准备黑白介质的可视化是阴影;它可以具有以下值:

|

阴影值

|

描述

|
| — | — |
| / | 对角线阴影线 |
| \ | 后对角线 |
| &#124; | 垂直阴影线 |
| - | 水平的 |
| + | 交叉的 |
| x | 交叉对角线 |
| o | 小圆 |
| 0 | 大圈 |
| . | 光点图形 |
| * | 星形图案 |

还有更多…

我们刚才使用的是误差线,称为对称误差线。如果数据集的性质使得误差在两个方向上(负的和正的)不相同,我们也可以使用不对称误差线分别指定它们。

我们所要做的不同是使用两元素列表(如 2D 数组)来指定xerryerr,其中第一个列表包含负误差的值,第二个列表包含正误差的值。

让饼图有价值

饼图在很多方面都很特别,最重要的是它们显示的数据集总和必须达到 100%,否则它们就完全无效。

做好准备

饼图代表数字比例,其中每个线段的弧长与其代表的数量成比例。

它们很紧凑,看起来很有美感,但它们一直被批评为难以比较。饼图的另一个不符合其最大利益的特性是,饼图以特定的角度(视角)呈现,而线段使用特定的颜色,这会扭曲我们的感知,并影响我们对所呈现信息的结论。

我们将在这里展示使用饼图呈现数据的不同方式。

怎么做…

首先,我们创建一个所谓的分解的饼图:

from pylab import*# make a square figure and axes figure(1, figsize=(6,6)) ax = axes([0.1,0.1,0.8,0.8])# the slices will be ordered# and plotted counter-clockwise. labels ='Spring','Summer','Autumn','Winter'# fractions are either x/sum(x) or x if sum(x) <= 1 x =[15,30,45,10]# explode must be len(x) sequence or None explode=(0.1,0.1,0.1,0.1) pie(x, explode=explode, labels=labels, autopct='%1.1f%%', startangle=67) title('Rainy days by season') show()

如果饼图位于方形图中,并且有方形轴,则看起来最佳。

饼图总和的分数定义为x/sum(x),如果是sum(x) <= 1,则定义为x。我们通过定义一个爆炸序列来获得爆炸效果,其中每一项代表偏移每条弧线的半径分数。我们使用autopct参数来格式化将要在弧内绘制的标签;它们可以是格式字符串或可调用函数。

我们还可以使用布尔阴影参数为饼图添加阴影效果。

如果我们不指定startangle,分数将从 x 轴(角度 0)逆时针开始排序。如果我们将90指定为startangle的值,那么饼图将从 y 轴开始。

这是生成的饼图:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_14.jpg

绘制填充区域

在本食谱中,我们将向您展示如何填充曲线下或两条不同曲线之间的区域。

做好准备

库 matplotlib 允许我们用颜色填充曲线之间和之下的区域,这样我们就可以向观众显示该区域的价值。有时,读者(观众)理解给定的专业是必要的。

怎么做…

下面是一个如何填充两个轮廓之间区域的示例:

from matplotlib.pyplot import figure, show, gca import numpy as np x = np.arange(0.0,2,0.01)# two different signals are measured y1 = np.sin(2*np.pi*x) y2 =1.2*np.sin(4*np.pi*x) fig = figure() ax = gca()# plot and# fill between y1 and y2 where a logical condition is met ax.plot(x, y1, x, y2, color='black') ax.fill_between(x, y1, y2, where=y2>=y1, facecolor='darkblue', interpolate=True) ax.fill_between(x, y1, y2, where=y2<=y1, facecolor='deeppink', interpolate=True) ax.set_title('filled between') show()

它是如何工作的…

在我们为预定义的时间间隔生成随机信号后,我们使用规则的plot()绘制这两个信号。然后我们用必需属性和强制属性来调用fill_between()

fill_between() 功能是使用x作为拾取y值(y1y2)的位置,然后以特定定义的颜色绘制多边形。

我们用where参数指定一个条件来填充曲线,该参数接受布尔值(可以是表达式),因此只有在满足where条件时才会进行填充。

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_15.jpg

还有更多…

与其他绘图功能类似,该功能也接受更多参数,例如,hatch(指定填充图案而不是颜色)和线条选项(linewidthlinestyle)。

还有fill_betweenx(),启用类似的填充特征,但在水平曲线之间也是如此。

更通用的功能fill() 提供了用颜色或阴影填充任何多边形的能力。

用彩色标记绘制散点图

如果你有两个变量,并想找出它们之间的相关性,散点图可能是点模式的解决方案。

这种类型的绘图也非常适合作为多维数据的更高级可视化的起点,例如,绘制散点图矩阵。

做好准备

散点图显示两组数据的值。数据可视化是作为没有线连接的点的集合来完成的。它们中的每一个都有由变量值决定的坐标。一个变量是受控的(独立变量),而另一个变量是测量的(因变量),通常绘制在 y 轴上。

怎么做…

下面是一个代码示例,绘制了两个图:一个具有不相关的数据,另一个具有强正相关:

import matplotlib.pyplot as plt import numpy as np # generate x values x = np.random.randn(1000)# random measurements, no correlation y1 = np.random.randn(len(x))# strong correlation y2 =1.2+ np.exp(x) ax1 = plt.subplot(121) plt.scatter(x, y1, color='indigo', alpha=0.3, edgecolors='white', label='no correl') plt.xlabel('no correlation') plt.grid(True) plt.legend() ax2 = plt.subplot(122, sharey=ax1, sharex=ax1) plt.scatter(x, y2, color='green', alpha=0.3, edgecolors='grey', label='correl') plt.xlabel('strong correlation') plt.grid(True) plt.legend() plt.show()

在这里,我们还使用了更多的参数,例如color用于设置绘图的颜色,marker用于用作点标记(默认为circle)、alpha (alpha 透明度)、edgecolors(标记边缘的颜色)和label(用于图例框)。

这些是我们得到的图:

https://github.com/OpenDocCN/freelearn-ds-zh/raw/master/docs/py-data-vis-cb/img/3367OS_03_16.jpg

它是如何工作的…

散点图通常用于识别两个变量之间的潜在关联,并且通常在拟合回归函数之前绘制。它给出了相关性的良好视觉图像,特别是对于非线性关系。matplotlib 提供了scatter()功能来绘制xy一维数组,一维数组的长度与散点图相同。

Read more

springboot超市综合运营管理系统-计算机毕业设计源码34827

springboot超市综合运营管理系统-计算机毕业设计源码34827

摘  要 随着电子商务的蓬勃发展和消费者需求的多样化,超市运营面临着越来越复杂的管理任务。为了提高超市管理的效率和服务质量,传统的人工管理方式已经无法满足现代超市的运营需求,亟需一种智能化、系统化的管理平台。本文提出并设计了一种基于SpringBoot与Vue技术的超市综合运营管理系统,旨在通过数字化手段提升超市的运营效率,实现商品管理、订单处理、库存监控、会员管理等多方面的自动化与智能化。系统包括用户端、员工端和管理员端,涵盖了超市商城、商品库存管理、收银信息管理、订单管理、会员管理等核心功能,能够有效整合超市内部资源,简化运营流程,提高用户体验。采用SpringBoot作为后端开发框架,Vue.js作为前端开发框架,结合MySQL数据库进行数据存储,确保系统的高性能和稳定性。系统设计通过模块化架构,保证了良好的扩展性和维护性。在实际开发和测试过程中,系统能够高效完成商品管理、订单处理、会员管理等任务,且操作界面简洁易用,满足了不同用户的需求,提升了超市的运营管理水平和顾客购物体验。 关键词:超市综合运营管理系统开发;Spring Boot;MySQL;Vue.js; Ab

By Ne0inhk
视频续播功能实现 - 断点续看从前端到 Spring Boot 后端

视频续播功能实现 - 断点续看从前端到 Spring Boot 后端

🌷 古之立大事者,不惟有超世之才,亦必有坚忍不拔之志 🎐 个人CSND主页——Micro麦可乐的博客 🐥《Docker实操教程》专栏以最新的Centos版本为基础进行Docker实操教程,入门到实战 🌺《RabbitMQ》专栏19年编写主要介绍使用JAVA开发RabbitMQ的系列教程,从基础知识到项目实战 🌸《设计模式》专栏以实际的生活场景为案例进行讲解,让大家对设计模式有一个更清晰的理解 🌛《开源项目》本专栏主要介绍目前热门的开源项目,带大家快速了解并轻松上手使用 🍎 《前端技术》专栏以实战为主介绍日常开发中前端应用的一些功能以及技巧,均附有完整的代码示例 ✨《开发技巧》本专栏包含了各种系统的设计原理以及注意事项,并分享一些日常开发的功能小技巧 💕《Jenkins实战》专栏主要介绍Jenkins+Docker的实战教程,让你快速掌握项目CI/CD,是2024年最新的实战教程 🌞《Spring Boot》专栏主要介绍我们日常工作项目中经常应用到的功能以及技巧,代码样例完整 👍《Spring Security》专栏中我们将逐步深入Spring Security的各个

By Ne0inhk
政安晨【人工智能项目随笔】OpenClaw网关与子节点完整配对指南——从零构建分布式AI助手网络

政安晨【人工智能项目随笔】OpenClaw网关与子节点完整配对指南——从零构建分布式AI助手网络

政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正! 目录 1.前言:从单机助手到分布式AI助手 2. 概念解析:OpenClaw网关与子节点 2.1 网关(Gateway) 2.2 子节点(Node) 2.3 通信机制 2.4 安全模型 3. 架构设计:为什么要使用子节点 3.1 场景驱动:从需求到架构 场景一:计算资源隔离 场景二:物理设备控制 场景三:能力扩展 3.2 拓扑结构 3.3 数据流设计 4.

By Ne0inhk
Scrapling+OpenClaw:2026年最强本地AI数据管道,爬虫直接对接智能体

Scrapling+OpenClaw:2026年最强本地AI数据管道,爬虫直接对接智能体

今年开年给一个做户外用品的老客户搭AI选品智能体,踩了我做爬虫+AI这几年最憋屈的一个坑。 客户的需求很明确:做一套完全本地化的竞品监控选品系统,每天自动爬3个垂直平台的新品数据,洗干净、结构化之后直接喂给选品智能体,能自动做价格带分析、卖点拆解、库存预警,所有数据绝对不能出本地服务器——毕竟竞品监控的核心数据,一旦泄露就是商业事故。 最开始我搭的传统方案,四个模块拆得明明白白: 1. 用Playwright+BeautifulSoup手搓爬虫,写了快600行规则适配3个平台的页面; 2. 用Python写了一套数据清洗脚本,去重、格式标准化、过滤广告; 3. 用Milvus搭本地向量库,把清洗后的数据转成向量入库; 4. 用LangChain搭选品智能体,对接向量库做分析和问答。 结果上线不到两周,问题全炸了:先是其中一个平台前端大改版,类名全换成了随机哈希,爬虫直接废了,熬了两个通宵重写规则;然后是清洗脚本和向量入库的格式对不上,智能体检索出来的数据全是错的;最头疼的是,客户要加一个新的数据源,我要从头到尾改一遍爬虫、清洗、入库的代码,前前后后花了快一周。 也是那

By Ne0inhk