Python-数据可视化学习手册-全-
Python 数据可视化学习手册(全)
零、前言
大家好,我是查德·亚当斯,欢迎来到学习 Python 数据可视化。在本书中,我们将介绍使用 Python 编程语言用代码生成动态图表和通用图形的基础知识。我们将使用 pygal 库,一个为 Python 编写的简单而强大的图形库,来探索我们可以为各种数据创建的不同类型的图表。
我们还将回顾 Python 语言本身,讨论如何处理文件输入/输出,并介绍如何处理数据。然后,我们将把这些数据解析成图表,创建一个动态图表应用。我们还将接触更多流行(和更高级)的库,如 matplotlib 和 Plotly,并使用这些库构建图表,探索它们的特性。
通过这本书,我们将探索和构建数据可视化,使用科学、金融、医疗和制药行业的许多流行制图应用中使用的基本工具集。
这本书涵盖了什么
第一章、设置你的开发环境,将讨论 Python 在 Windows、Mac 和 Ubuntu 上的安装过程。我们将回顾 Python 的 easy_install 和 pip 包管理器,并讨论为 Python 安装第三方库时的常见问题。
第 2 章、Python reviewer,将快速回顾大多数 Python 开发者工具带中的 Python 语言和常用库。我们还将通过用代码创建自定义图形来轻松构建图表,并了解如何将文件保存到文件系统中。
第 3 章、pygal 入门将介绍 pygal 库的基础知识,这是一个简单的图表库,可以在 HTML5 就绪的 SVG 文件中生成图表。我们将使用该库构建一些基本图表,其中一些包括折线图、条形图和散点图。
第 4 章、高级图表,将涵盖 pygal 库中更复杂的图表,如箱线图、雷达图和世界地图图。
第 5 章、调整 pygal ,将讨论我们可以给 pygal 图表的可选设置,例如调整字体大小以及标签和图例的定位。我们还将使用 pygal 图书馆中的法国国家地图作为示例。
第 6 章、导入动态数据将详细介绍使用 Python 语言及其内置库从 Web 中提取数据的细节,并涵盖解析 XML、JSON 和 JSONP 数据。
第 7 章、综合所有,将构建一个简单的图表,将我们从过去的章节中学到的知识纳入其中,并使用来自网络的数据构建一个基于 pygal 的动态图表。
第 8 章、更多资源,将回顾一些非常受欢迎的图表库,如 matplotlib 和 Plotly,为每个库浏览构建示例图表,并涵盖进一步阅读的资源。
附录、参考资料和资源,将列出一些流行的 Python 数据可视化库以及一些有用的实用程序。
这本书你需要什么
您将需要运行 Python 2.7 32 位或 Python 2.7 64 位的 Windows、Mac 或 Ubuntu 系统。您需要在此系统上拥有管理员权限。您还需要一个 Python 文本编辑器,如 Eclipse 或带有 Python 工具的 Visual Studio。对于第 8 章、进一步资源,你还需要 Python 3.4 或更高版本。Python 2.7 和 3.4 可以并排安装。
这本书是给谁的
如果您是 Python 语言的新手,并且正在考虑使用 Python 构建图表,这是一个很好的入门资源。如果您已经完成了一些 Python 开发,但还没有涉足图形和图表,那么本书中有大量关于创建图形和图表的信息。
惯例
在这本书里,你会发现许多区分不同种类信息的文本风格。以下是这些风格的一些例子,以及对它们的含义的解释。
文本中的码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、伪 URL、用户输入和 Twitter 句柄如下所示:“创建一个名为PyREADME.txt的文本文件,并将其保存到项目的目录中。”
代码块设置如下:
def main():
print("Hello, World")
main()
任何命令行输入或输出都编写如下:
sudo pip install pygal
新名词和重要词语以粗体显示。你在屏幕上看到的单词,例如在菜单或对话框中,出现在如下文本中:“点击两个窗口上的确定,保存并重新启动你的电脑。”
注
警告或重要提示会出现在这样的框中。
型式
提示和技巧是这样出现的。
读者反馈
我们随时欢迎读者的反馈。让我们知道你对这本书的看法——你喜欢或可能不喜欢什么。读者反馈对我们开发您真正能从中获得最大收益的标题非常重要。
要给我们发送一般反馈,只需向<[feedback@packtpub.com](mailto:feedback@packtpub.com)>发送电子邮件,并通过您的消息主题提及书名。
如果你对某个主题有专业知识,并且对写作或投稿感兴趣,请参阅我们在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,我们非常重视版权和许可证的保护。如果您在互联网上遇到任何形式的我们作品的非法拷贝,请立即向我们提供位置地址或网站名称,以便我们寻求补救。
请通过<[copyright@packtpub.com](mailto:copyright@packtpub.com)>联系我们,获取疑似盗版资料的链接。
我们感谢您在保护我们作者方面的帮助,以及我们为您带来有价值内容的能力。
问题
如果您对本书的任何方面有问题,可以在<[questions@packtpub.com](mailto:questions@packtpub.com)>联系我们,我们将尽最大努力解决。
一、设置您的开发环境
简介
在本章中,我们将回顾如何在 Windows、Mac 和 Ubuntu Linux 上设置 Python 2.7 32 位版本。我们将遍历 Python 解释器,并构建几个 Hello-World 风格的 Python 应用,以确保我们的代码正常工作。本章的 Windows 部分将主要介绍这一点,但其他操作系统部分将会重复这一点。
我们还将回顾如何安装和使用 easy_install 和 pip ,这两个是 Python 开发中常用的包管理器。我们还将回顾如何安装 lxml ,这是一个流行的 xml 解析器和编写器,我们将在后面的章节中需要它。
在 Windows 上设置 Python
如果你对 Python 相当陌生,你可能听说过 Python 没有合适的构建工具在 Windows 上运行,或者 Python 针对基于 Unix 的系统进行了优化,比如 Mac OS X 和 Linux 变体。在某种程度上,这是真的;大多数库,包括本书中介绍的库,如果您在非 Windows 的操作系统上,会工作得更好,也更容易安装。
我想在这一部分花一点额外的时间,以防你,读者,在阅读这本书的时候,想使用 Windows 作为你的开发操作系统。首先,我想解释一下为什么众所周知 Windows 与 Python 开发人员之间存在问题。通常,不是语言导致了问题,也不是缺少编辑。事实上,Windows 甚至有更多高质量的 Python 编辑器,包括带有 Python 工具的 Visual Studio,以及更多文本编辑器选项,如 Notepad++。
困扰开发人员的真正问题是库的兼容性,特别是那些引用基于 C 的代码来实现直接使用 Python 语言无法实现的结果的 Python 库。与 Mac OS X 或 Linux 变体不同,Windows 不包含 C 编译器作为操作系统的一部分。通常,当 Python 库作者提到 Windows“缺少构建工具”时,这通常指的是不包括 C 编译器的 Windows。
另一个问题是命令提示符;在 Python 开发中,通常使用终端或使用 Windows 命令中的命令提示符来安装库和资产。安装库的两个常用命令是easy_install和pip。如果你不熟悉,easy_install 是一个基于命令行的 Python 包管理器。它使用 Python eggs(一个特定于 easy_install 的重命名的.zip文件)来捆绑库的脚本和所需文件。easy_install 包管理器也是一个较旧的包管理器,已经在 Python 工具带中使用了很长时间。通常使用 easy_install 可以找到旧的 Python 库。下面的截图向您展示了 PyPI 网站:

另一个命令叫做 pip,也被称为 Python 包索引 ( PyPi )。尽管 easy_install 是由社区驱动的,但 PyPi 是 Python 软件基金会的官方包管理器,该组织负责更新和处理 Python 语言。该网站还托管第三方软件包。
下面的截图向您展示了 Python 网站:

注
较新的库通常使用 pip 创建,原因有二。第一,pip 比 easy_install 有更多的特性,第二,pip 库可以在 Python 的官方包网站库中搜索,位于https://pypi.python.org/pypi。
安装
让我们从在你的 Windows 机器上安装 Python 开始。对于这本书,我将使用 Windows 8.1,尽管如果您运行的是 Windows 7 或 Windows Vista,这个工作流应该没问题。首先,打开你选择的浏览器,导航到http://www.python.org/。

在主页上,你应该会看到一个如前面截图所示的的下载链接。对于 Windows,我们正在寻找 Python 版本 2.7+(32 位版本)。继续点击该链接,您将进入下载页面:

在下载页面,你会想要下载 Windows x86 MSI 安装程序。我们想要 32 位安装程序,而不是 64 位安装程序。这将确保与即将到来的章节中的包的最佳兼容性。下面的截图显示了 Python 在 Windows 上的一般安装窗口(此处显示的是 64 位版本的 Python,用于演示目的):

一旦你下载了安装程序,双击安装程序运行它。遵循向导,保留默认值,特别是前面截图中显示的 Python 安装路径。让安装程序完成安装并重新启动系统。
重启系统后,如果你在 Windows 8 的桌面上,右键点击开始屏幕图标,点击系统。然后点击高级系统设置(如果你在 Windows 7 或 Vista 中,可以通过导航到控制面板 | 所有控制面板项目 | 系统找到这个),如下图截图所示:

一旦完成,你就要点击环境变量,如前面截图所示,在系统变量下寻找路径。这些变量允许命令提示符知道它可以访问系统中任何地方的哪些程序。我们要编辑路径,如下图截图所示,选择路径,点击编辑:

当编辑菜单可见时,在变量值的末尾键入C:\Python27;C:\Python27\Lib\site-packages\;C:\Python27\Scripts\;(包括前面的分号来区分路径)。在两个窗口中点击确定保存更改并重新启动电脑。

现在,让我们测试一下你的 Python 安装!打开命令提示符,小写输入python,按进入。假设安装程序工作正常,您应该会看到命令提示符路径光标位置更改到>>>之前,如下图所示:

您现在在 Python 解释器中;在这里,您可以运行简单的单行脚本,如以下命令:
print('Hello Reader!')
型式
下载示例代码
您可以从您在http://www.packtpub.com的账户中下载您购买的所有 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问http://www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。

下一个行将输出Hello Reader!,向控制台显示您的 Python 脚本打印,下面的>>>等待您的下一个命令。也可以处理2 + 2等命令,点击进入,下一行会看到4。
让我们尝试将一个变量保存到提示中;在下一行键入以下命令:
authorsName = 'Chad'
按进入。然后,输入以下命令,再次按进入:
print(authorsName)
输出显示在下一张截图中:

现在,您的命令提示符看起来像前面的截图。请注意结果行中的Chad是authorsName Python 变量的输出。这意味着您已经正确安装了 Python 编译器!通过测试函数对象、数学对象和变量对象,我们已经确认 Python 可以在窗口上工作。
经过测试,您可以通过退出编译器从 Python 编译器返回到标准命令提示符。只需键入exit(0)即可退出 Python 实例。
探索 Windows 中的 Python 安装
既然已经查看了 Windows 上的命令行,我们需要在开始编写代码之前了解一些其他的事情。让我们从 Python 和任何库在您的机器上的安装位置开始。打开 Windows 资源管理器,导航至C:\Python27,如下图截图所示:

在Python27目录中,可以看到python.exe文件;这是我们的系统变量中的路径寻找运行 Python 脚本和命令的应用。该文件夹还包含 Python 需要运行的其他库,包括从 easy_install 或 pip 下载的库。
通过导航到C:\Python27\Lib\site-packages,可以找到第三方库。默认情况下,通过 pip 或 easy_install 下载的任何库和任何第三方依赖项都将安装在此目录中。
接下来,让我们为这本书下拉几个我们需要的库。默认情况下,Python 的 Windows Installer 包含 Windows pip 上的 Python 2.7 和 easy_install。首先,我们需要lxml图书馆。现在,在 Windows 上,lxml库是非常流行的基于 C 的 XML 解析器和 Python 库的编写器库,并且由于其基于 C 的实现而与 Windows 系统不兼容是出了名的。我们先安装lxml库,再拉可能依赖这个的包,从lxml开始,如下图截图所示:

lxml 确实有 pip 和 easy_install 两种风格;然而,由于它是基于 C 的,我们需要在 https://pypi.python.org/pypi/lxml/3.3.3 找到的 T4 视窗安装程序。抓取lxml-3.3.3.win32-py2.7.exe文件或更新的 2.7 版本库,运行安装程序。一旦安装完成,我们可以通过导航到site-packages目录并检查是否已经创建了任何名为lxml的新文件夹来确认安装。安装后,site-packages目录应该如下图所示:

lxml安装完成后,我们将设置 easy_install 和 pip。首先,让我们下载 easy_install 并安装它。步骤如下:
-
将您选择的浏览器导航到 https://pypi.python.org/pypi/setuptools T2。
-
然后,下载
ez_setup.py文件。 -
Save the file to
C:\Python27\ez_setup.py. You can find the file on the page here, as shown in the following screenshot:![Exploring the Python installation in Windows]()
现在,以管理员权限再次打开您的命令提示符,然后键入以下命令,并按进入:
cd c:\Python27
接下来,输入以下命令并按进入:
python ez_setup.py
完成后,命令提示符应该如下图所示:

现在,让我们测试 easy_install 并同时安装 pip!再次,打开命令提示符设置您的目录,就像您之前做的那样:
cd c:\Python27
然后,输入以下命令,按进入:
easy_install pip
如果成功,您的命令提示符应该如下图所示:

做完这些,让我们来测试一下 pip!我们想尝试安装一个名为BeautifulSoup的库。这是一个通用的 Python 库,用于废弃 HTML 内容。我们不会使用BeautifulSoup但是我们需要测试 pip 安装,BeautifulSoup是一个适用于大多数安装的好库。要在控制台打开且路径仍指向您的C:\Python27目录时安装BeautifulSoup,请键入以下命令:
pip install beautifulsoup
您将在最后看到一条消息,如下图所示:

Python 编辑器
我们现在已经安装了构建 Python 脚本所需的必要库和框架,所以让我们选择一个代码编辑器。对于第一次(甚至是资深的 Python)开发人员,我推荐一个 IDE 作为首选编辑器,而不是纯文本编辑器。这主要有两个原因。第一,一个典型的集成开发环境包括某种代码提示,让开发人员知道有哪些 Python 包可用,甚至安装在开发人员的系统上。第二,大多数好的 ide 包括 Python 特定的代码文档模板和帮助编写大型代码库的助手。
比较流行的 ide 之一是带有 PyDev 的 Eclipse 它是免费的,是 Python 非常好的入门 IDE。我们将在接下来的章节中针对其他平台更深入地介绍 Eclipse,但是如果您打算在 Windows 上使用 Eclipse,请确保为您的 Windows 版本安装最新的 Java 运行时和 JDK 安装程序。请继续阅读,通过 PyDev 了解更多关于 Eclipse 的信息。
如果您来自. NET 背景或总体上更喜欢 Visual Studio,请查看 Visual Studio 的 Python 工具。这允许您在 Visual Studio 项目中运行 Python 代码,并且能够将 Python 代码保存在 Team Foundation Server(微软的源代码控制系统)中。下面的截图显示了 Visual Studio 网站的 Python 工具:

要为 Visual Studio 安装 Python 工具,请从http://pytools.codeplex.com/获取安装程序(如上图所示)。另外,如果你没有 Visual Studio,Python 工具可以安装在 Visual Studio for Desktop 或 Visual Studio for Web 上,这是微软免费下载的。你可以在网站下载快递版。
型式
如果您打算使用快速版本,我建议您下载 Visual Studio 网络快速,因为我们将在本书的后面使用一些 HTML 和 CSS。
下面的截图显示了 IronPython 网站:

你可能还会在 http://ironpython.net/的 T2 注意到铁蟒。IronPython 是针对 Windows 优化的 Python,可以访问。NET 库,这意味着你可以访问。NET 属性,如System.Windows.Forms。
对于这本书,我们将使用 CPython(通常指没有添加任何内容的普通 Python 库)。请记住,一些用 Python 编写的库可能在 IronPython 中工作,也可能不工作,这取决于它的依赖关系。
在进入操作系统 x 之前,让我们在 Visual Studio 中使用 Python 工具构建一个快速的 Python 脚本。在下面的截图中,您将看到新项目窗口。请注意名为 Python 应用的普通(CPython)选项,以及其他项目类型,如姜戈和铁 Python 。本书需要 Python 应用。

安装好 Visual Studio 的 Python 工具后,打开 Visual Studio,在 Python 下新建一个项目,选择 Python 应用,命名为Pyname,如前面的截图所示。右键单击Pyname项目,然后单击属性。将你的解释器设置为 Python 2.7 ,点击工具栏中的保存,如下图截图所示:

现在,看看解决方案资源管理器并扩展您的 Python 环境 | Python 32 位 2.7 。您将能够看到我们安装的第三方库现在在 Visual Studio 中可见,如下图所示(此处显示的是 64 位版本的 Python,用于演示目的):

让我们编写我们之前使用的脚本,并在 Visual Studio 中运行它。在Pyname.py文件中输入以下内容:
authorName = ('Chad')
print(authorName)
现在点击启动,你会看到屏幕上打印着Chad的命令提示符自动启动。成功;你刚刚在 Visual Studio 中写了 Python!
在本节中,我们讨论了以下主题:
- 在 Windows 中安装 Python
- 安装简易安装和 pip
- 安装
lxml,一个常见的 Python 库
在 Mac OS X 上设置 Python
从开始,Python 变得更容易安装。如果你在苹果电脑上,许多人认为 Python 是最好的运行工具,因为它包含了构建工具和编译器。在我们安装 Python 之前,知道 OS X 在操作系统中包含 Python 是很重要的。然而,一个问题是,它没有包括基本安装程序所做的一切。此外,OS X 锁定了一些在 Unix 系统中常见的命令行功能,这些功能可能会导致一些 Python 模块和库出现问题。
在这一节中,我们将回顾在 OS X 使用 PyDev 3.0 的 Eclipse IDE,并回顾使用 easy_install 和使用 OSX 的 pip 。首先到https://www.python.org/下载 2.7.7(或更高)32 位.dmg安装程序安装 Python。

安装完成后,打开终端并测试 easy_install。由于默认情况下包含 easy_install,因此我们可以使用 easy_install 来安装 pip。在控制台中键入以下命令:
sudo easy_install pip
请记住,使用控制台中的sudo会提示您输入管理员密码。根据您的版本,您的输出可能会提到您已经安装了;没关系,这意味着您的 Python 包管理器已经准备好了。现在,尝试测试 Python 编译器。在终端中,键入python并按回车键。
这应该看起来像下面的截图;请注意解释器中的版本号,以确认哪个版本是活动的。

现在,让我们测试一下解释器;尝试键入以下命令:
print('Hello Reader!')
输出应为Hello Reader!。现在,让我们试试我们的authorName变量脚本(如下截图所示)来确认 Python 中的变量正在被保存。键入下面截图中显示的两行,它应该类似于下面的示例。如果是,恭喜;Python 及其基础库都安装好了!

安装了 Python ,我们现在可以专注于一个编辑器了。OS X、Aptana 和 Pycharm 有几种 Python IDEs,但是我们将使用的一种(也是 Python 开发人员中最流行的一种)是 Eclipse 的 PyDev。在撰写本文时,Eclipse 开普勒(4.3.2)已经发布,PyDev 版本也已经发布。两者都需要安装 Java 7 和 JDK 7 或更高版本,PyDev 才能正常工作。因此,在安装 Eclipse 和 PyDev 之前,通过访问以下链接来安装最新的 JRE 和 JDK:
- http://Java . com/en/download/
- http://www . Oracle . com/tech network/Java/javase/downloads/index . html
一旦你安装了 Java 运行时和 JDK,重启你的苹果电脑,导航你选择的浏览器到http://www.eclipse.org,下载 Eclipse 开普勒(4.3.2)经典版本(32 位或 64 位,取决于你的系统)。经典版本是 Eclipse 本身,不包含插件或项目类型。完成后,将 Eclipse .zip文件提取到桌面上的一个文件夹中,并打开 Eclipse 应用。第一次启动 Eclipse 时,设置您的工作空间路径并点击确定。Eclipse 将重新启动并重新启动 Eclipse。同样在 Safari 上,我们可能会因为这个站点消息而阻止一个插件。要继续,用户必须点击信任。这是一种安全措施,用于确认用户想要安装外部软件包或插件。点击信任进行安装。
此外,您还需要 JDK 和 Java 7 运行时或更高版本,因为它是当前版本的 PyDev 所必需的。OS X 安装的过程应该是相同的。
现在,加载了 Eclipse,导航到帮助 | Eclipse 市场。然后,在搜索字段中,输入Pydev。您应该会看到如下截图:

点击上的立即安装并按照提示操作,包括通过选择 PyDev 的我同意单选按钮并点击完成来批准证书,然后退出 Eclipse。一旦 Eclipse 重启,您可以通过导航到窗口 | 打开透视图 | 其他 | Pydev 并点击确定来更改 Python 开发的 IDE。
接下来,让我们配置我们的解释器,以便当我们运行 Python 代码时,IDE 可以处理我们的运行请求。最简单的方法是在 Eclipse 中。
导航至窗口 | 偏好设置 | PyDev | 解释器(Python/Jython/iron Python)。
然后,点击解释器窗口中的自动配置,运行自动配置。你的路径将被自动设置。遇到问题可以手动设置,通过导航到库 | 框架 | Python 指向可执行文件。框架 | 版本 | 2.7 | 仓 | 蟒 2.7-32 。
现在,让我们用 Eclipse 编写一些代码。Eclipse 重启后,导航至文件 | 新 | Pydev 项目。

创建一个名为Pyname的项目,如下图所示。接下来,在右侧的项目浏览器中创建一个pyname.py文件。

最后,如下图截图所示输入以下代码,点击运行。如果成功,你会在输出窗口看到Chad。

在这个部分,我们介绍了如何在 OS X 上安装 Python,使用 easy_install 安装 pip,使用终端工作,以及使用 PyDev 设置 Eclipse。
在 Ubuntu 上设置 Python
基于 Linux 的操作系统,比如 Ubuntu,在很多方面都是 Python 的家。Ubuntu 市场是用 Python 编写的,许多 Linux 应用通常都有 Python 代码库。Ubuntu 拥有许多 OS X 使用的相同的终端命令,如果不是相同的命令的话。对于 Ubuntu,我们将重点介绍 Ubuntu 13.10。如果你使用的是 Ubuntu、Lubuntu、Xubuntu 或 Linux Mint 的衍生版本,有几点需要记住。
这些命令中的大多数应该是相同的,根据您的设置有一些小的不同。如果您在加载每个软件组件时遇到问题,请联系您选择的搜索引擎。基于 Debian 或红帽的 Linux 发行版也是如此。
像 OS X 一样,您将需要 Java 7 运行时或更高版本以及 JDK 7 运行时或更高版本。Ubuntu 没有将这些包含在它包含的包管理器中,但是您可以通过命令行安装它们。
好消息是 Ubuntu 13.10 中包含了 Python 2.7,所以我们不需要安装 Python。我们甚至可以通过打开终端,在 Bash 提示符下输入python来测试这一点,如下图截图所示:

然后我们将带入 Python 解释器,解释器将向您显示默认 Python 实例的版本号,在本例中为 2.7.5+。
easy_install 和 pip Python 包管理器通常用于安装和更新我们的包。接下来,获取 easy_install 和 pip,并使用以下命令安装这两个工具:
sudo apt-get install python-setuptools
sudo apt-get install python-pip
请记住,在 Ubuntu 中,sudo命令会在安装python-setuptools和python-pip之前询问密码。如果成功,终端应该会返回如下消息,如下图所示:

接下来,在用 PyDev 安装 Eclipse 之前,让我们下载 Java 7 和 JDK。为此,我们将把它添加到我们的系统包存储库中,并安装 Java。打开终端并键入以下命令:
sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java7-installer
请务必在终端中使用许可协议上的键盘按键,如下图所示:

当完成后,您可以在终端中通过输入Java –version来测试安装。该命令将返回已安装的 Java 版本。
现在,让我们安装 Eclipse 和 PyDev。使用您选择的浏览器导航到http://www.eclipse.org/,然后下载 Eclipse Classic。打开下载文件,打开 Eclipse,选择一个工作空间路径,点击确定。
注
在撰写本文时,在 Ubuntu 中重新构建 Eclipse 菜单有一个错误。如果您遇到此问题,请在线检查命令行修复,因为这可能因更新而异。Ubuntu 14.04 LTS 计划在发行版中解决这个错误。
完成后,通过导航至帮助 | 日蚀市场打开日蚀市场并搜索 PyDev。安装插件并同意证书,然后重新启动 Eclipse。
假设一切安装正确,您将在 Eclipse 的首选项部分看到 PyDev。
关于 Ubuntu 的最后一个注意事项:由于 Ubuntu 有自己的包管理器apt-get,我们也可以使用它为 Python 安装包,例如,使用lxml:
sudo apt-get install python-lxml
请注意我们在我们的包之前添加了一个python-前缀。这个帮助apt-get指定包类型,因为apt-get使用多种语言。
此时,您应该已经为 Python 开发做好了准备。尝试从 OS X 和视窗部分重新创建我们的authorName脚本。
总结
在这一章中,我们讨论了安装 Python 的基础知识以及用于 Windows、Mac OS X 和基于 Linux 的 Python 开发的工具。接下来,准备好工具后,我们将复习一些 Python 编码基础知识,为使用 Python 代码构建图表做准备。
二、Python 复习
在本章中,我们将学习一些 Python 编程基础知识以及 Python 开发中使用的常见 Python 第一和第三方库。在这一章中,我们将使用标准的 2.7 CPython 为 Visual Studio 开发 Python 工具,这对于新的 Python 开发人员来说非常有用。如果你在苹果电脑、Linux Eclipse 或你选择的编辑器上工作,那就没问题了。我们将在 Visual Studio 中使用 pip 和 easy_install,但我也将包括 Mac 和 Linux 命令的注释。
Python 基础知识
让我们从用 Python 工具在 Visual Studio 中创建一个项目(VSPT)开始。转到文件 | 新项目,然后 Python | Python 应用,调用此解决方案Chapter2,如下图截图所示:

接下来,导航至 【解决方案浏览器】,点击添加/删除环境。你会看到如下截图所示的提示。如果您有多个 Python 安装,您将能够选择您想要指定的安装。点击 Python 2.7 环境,点击确定。

那么这是做什么的呢?Python 开发的一个常见问题是使用 pip 或 easy_install 安装在主机系统上的库被保存在项目文件之外。这使得在一个系统上安装不同库的 Python 脚本无法在另一个没有完全相同设置的系统上运行。它还将更新 Visual Studio 的智能感知,以尽可能好地安装任何 Python 库。
现在看看你的解决方案浏览器。你会看到 Python 2.7 已经添加到 Python 环境下。右键点击 Python 2.7 ,点击文件资源管理器中的【打开文件夹】,可以看到复制的环境保存在项目中的什么位置。您将看到一个Lib文件夹,其中有一个名为site-packages的子文件夹,该文件夹是用自己的库结构为第三方模块和库创建的。
现在,让我们从 Python 的规范 Hello World 应用开始编写一些代码。在编辑器中输入以下代码,点击顶部的开始:
print("Hello, World") #This will execute the quickest and fastest, but doesn't scale well in Python.
很多小的 Python 脚本都是这样写的,没有结构也没有包含。随着您的应用越来越大,这个可能会成为一个问题。让我们把它包装成一个函数。再次在编辑器中输入以下代码,点击顶部的开始:
#A simple python function.
def main():
print("Hello, World")
main() #Called after main() is loaded into memory.
这里,我们创建了一个名为main()的函数,并将我们的print语句包含在其中。下一行,我们调用main()触发控制台打印Hello World。如果你来自 C#或 JavaScript 背景,这可能看起来有点好笑,你这样想是对的。Python 是一种非常松散的语言——函数没有大括号,也没有分号来终止一行代码。
这将打字保持在最低限度,这很好,但是对于那些以前从未编写过 Python 代码的人来说,这可能会导致问题。Python 非常具体地说明了它的代码是如何构造的,以便它能够工作。请记住,在这本书的开头,像这样的错误会阻碍开发。让我们看一个例子:
#A simple python function.
def main():
print("Hello, World") '''A function needs to be indented and not be further away more than one line break.'''
main()
通过使用像 Visual Studio 或 Eclipse 这样的 IDE,我们可以看到类似这样的问题,而简单的文本编辑器可能不会显示这些问题。下面是 Visual Studio 显示的缩进问题。将鼠标移到print()方法上,您将获得问题所在的帮助。

到目前为止,这对于一个小的 Python 脚本来说是非常有效的,但是让我们开始将我们的代码转移到一个更加面向对象的结构中。现在,我们想要包装我们的main()函数,并让它用我们的 Python 内置脚本事件之一触发,具体来说,就是我们的__main__事件。请注意双下划线,这表示它是 Python 中的内置事件。以下是在main()功能上触发的__main__事件示例:
#Same function as before.
def main():
print("Hello, World")
if __name__ == '__main__': #Here Python checks if the runtime event __main__ is called if so run the code below.
main()
你可以看到我们为__main__检查__name__事件;如果存在,则执行一个或多个功能。这类似于 C#中的private void函数或 JavaScript 中的window.onload。如果您想要创建自己的 Python 模块库,以这种方式包装函数也很重要,这样每个函数只能在模块完全加载时调用,而不是之前。
现在,让我们添加一个参数,这样我们就可以重用我们的main()函数。在这里,我将把用户名添加到main()函数中,这样我就可以向我们的Hello print语句传递一个字符串:
#Main function with passed in parameter.
def main(readersname):
print("Hello, " + readersname)
if __name__ == '__main__':
main('Chad')
请注意,您可以使用+追加字符串,就像使用 JavaScript 或 C#一样。在 Python 中,您还可以选择字符串格式化程序。下面是前面的代码,其中一个字符串格式化程序传入了我们的readersname参数:
#Here we use string formatting to better construct our strings.
def main(readersname):
print("Hello, %s" % readersname)
if __name__ == '__main__':
main('Chad')
让我们在main()方法中添加另一个参数。这一次,我们将使用一个数字,特别是一个整数。让我们将数字35作为读者已阅读的固定页数传递,并更新我们的print()声明,以包括两者:
#Main function with two passed in parameters.
def main(readersname, amt):
print("Hello, " + readersname + ", you have read " + str(amt) + " pages.")
if __name__ == '__main__':
main('Chad', 35)
运行脚本,输出将是Hello, Chad, you have read 35 pages.。接下来,让我们在这里使用字符串格式,而不是使用字符串连接。我已经使用%i将字符串连接更改为字符串格式化程序,以指示格式是整数:
#Here we use string formatting to better construct our strings.
def main(readersname, amt):
print("Hello, %s, you have read %i" % (readersname, amt))
if __name__ == '__main__':
main('Chad', 35)
字符串格式还可以帮助参数以独特的方式输出。假设我们想将35显示为带小数点的浮点数。我们可以将字符串整数格式化程序%i改为浮点格式化程序%f。看看这个例子:
#Let's format the string to output a float with decimal places.
def main(readersname, amt):
print("Hello, %s, your total pages read are %f." % (readersname, amt))
if __name__ == '__main__':
main('Chad', 50)
如果我们运行 Python 脚本,您将看到输出Hello, Chad, your total pages read are 50.000000.。如我们所见,我们传递的整数值现在是50.000000,浮点格式化程序修改了我们的字符串,没有任何转换代码。如果我们希望它只显示两个小数点呢?好吧,我们可以调整我们的修饰符并指定小数点的数量,如下面的代码所示:
#Let's format the string to output a float with two decimal places.
def main(readersname, amt):
print("Hello, %s, your total pages read are %0.2f." % (readersname, amt))
if __name__ == '__main__':
main('Chad', 50)
如果我们现在运行 Python 脚本,我们的输出如下所示:Hello, Chad, your total pages read are 50.00。
格式化程序也处理浮点数到整数;看看这个代码示例:
def main(readersname, amt):
print("Hello, %s, your total pages read are %i." % (readersname, amt))
if __name__ == '__main__':
main('Chad', 50.652)
现在,让我们看看我们的结果:Hello, Chad, your total pages read are 50.。我们注意到它删除了小数位,尽管.652值应该将我们的 50 整数舍入到 51,但它没有。整数格式化程序只是修剪了值,并没有对值进行舍入。对于整数格式化程序来说,这一点非常重要。
太棒了。现在我们有了一个快速简单的方法,可以将数值转换成浮点数(小数点),如果以后需要转换图表中的数值,还可以再次转换成整数。请记住,Python 是一种动态类型的语言,这意味着所有变量都可以是任何类型,而不需要指定,并且 Python 解释器会根据可用的类型来分配类型。现在我们已经掌握了函数和字符串,让我们来看看一些常见的库,以帮助我们理解文件输入和输出。
导入模块和库
到目前为止,我们所介绍的可以适用于非常小的 Python 脚本,但是我们希望使用预制的库和函数来充分利用 Python,以允许我们编写可维护的代码。在本节中,我们将回顾导入现有 Python 模块和库以及在代码中使用这些函数。
回想一下在第 1 章、设置您的开发环境中,我们介绍了安装 pip 和 easy_install。嗯,pip 至少是一个 Python 库,但是有一点你可能想不起来,那就是在那一章中,我们实际上安装了很多库和模块。如果你记得的话,我们还安装了来自https://www.python.org/的 Python 语言解释器和工具。我们的安装附带了数百个要使用的捆绑库。这些被认为是 Python 的通用发布库。这些是 Python 开发中使用的公共语言库,由 Python 软件基金会测试,用于跨平台开发,这消除了对核心语言中特定于操作系统的开发的需求。
让我们尝试导入sys模块。sys模块提供对解释器使用或维护的一些变量以及与解释器强交互的函数的访问。
要导入模块,请在 Python 脚本的最上面一行键入以下内容。
import sys
请看下面截图中的这一步:

在 Visual Studio 中,您可以通过按下 Ctrl +空格键来调用智能感知。当您键入时,可以越来越多地看到智能感知过滤器。此外,突出显示的内容将显示为该文件编写的 Python 文档字符串,以便为实现这些库的开发人员提供帮助和注释。
如果你在 Eclipse 中,Eclipse PyDev 也提供代码提示,就像 IntelliSense 一样。连键盘快捷键都一样, Ctrl +空格键。现在让我们测试一下我们的导入。由于sys可以查找 Python 参数和环境变量,所以让我们检查一下我们运行的是什么平台。在编辑器中键入以下内容:
import sys
platform = sys.platform;
print("%s" % platform)
现在运行脚本,如果你在基于 Windows 的操作系统中,你的输出将是win32;如果你在 Mac OS X 上,你的输出会是darwin(这里指的是 FreeBSD 的苹果实现,它是 OS X 的核心)。如果你在像 Ubuntu 这样的 Linux 操作系统上,你的输出将是linux2。
接下来,让我们使用sys.version_info检查版本。version_info列表返回当前版本用于该脚本的内容、主要发行号(int)、次要发行号(int)和微发行号(int)的数组。为了测试这一点,让我们运行下面代码中显示的脚本:
import sys
pyversion_major = sys.version_info[0];
pyversion_minor = sys.version_info[1];
pyversion_micro = sys.version_info[2];
print("Python version: %s.%s.%s" % (pyversion_major, pyversion_minor, pyversion_micro))
运行脚本,你的输出应该是Python version: 2.7.6或者 Python 2.7 的更新版本。现在我们已经掌握了导入,让我们从使用os模块及其用户和路径功能的 Python 文件输入/输出的基础开始。
输入和输出
使用 Python 时的一个核心技能是理解输入和输出。如果您来自无法访问文件的客户端 web 开发背景,请在这一部分特别注意,因为在创建图表时,我们需要能够将图表保存到硬盘上。
os模块是 Python 中使用最多的模块之一,主要是因为它如何处理跨平台获取公共文件路径。让我们演示如何读取文件。创建一个名为PyREADME.txt的文本文件,并将其保存到项目目录中。将以下文本复制到PyREADME.txt文件中并保存文件:
Hello Reader,
This copy is being read in Python, and saved as a string to a variable.
保存后,它应该如下图所示:

现在,在您的 Python 编辑器中,我们将通过在最上面的行包含import os来导入os模块,然后添加以下代码。确保你的PyREADME.txt文件和你运行的 Python 脚本在同一个目录下。
import os
#Open the file to be read.
openFile = os.open('PyREADME.txt', os.O_RDONLY )
#Save the file's inner content to a Python variable string. This take two parameters, the file to be opened and how many characters to read.
readmeText = os.read(openFile, 100)
print(readmeText)
如果一切都成功了,您的输出窗口应该会显示下图中的内容。
型式
如果您有问题,请仔细检查您的文件类型,看看您是否有文件扩展名,如PyREADME.txt。

现在让我们回顾一下我们的代码。我们使用open()函数打开我们的文件,我们有两个参数:带扩展名的文件名和r字符串。r字符串告诉open()方法我们在处理文件时有什么权限。打开文件后,我们读取文件并将其打印到控制台。最后,我们关闭我们的openFile;这使我们不会有潜在的内存泄漏,因为我们的文件 I/O 不会关闭,直到我们告诉它关闭。
接下来,让我们创建一个文本文件。我们将以Hello World为内容,命名为content.txt。用这段代码替换您的 Python 文件:
import os
txtContent = 'Hello World'
openFile = open('content.txt', 'w') #Open the file to be written.
readmeText = openFile.write(txtContent) #Write the file's inner content to the text file.
openFile.close() #Close the file.
如果成功,你应该有一个新的文本文件,文件中写着Hello World,如下图所示。您可以在与 Python 脚本相同的目录中找到该文件。让我们回顾一下代码。您会注意到,我们将open()权限的参数更改为w,这意味着只写,并且我们将文件名设置为content.txt到以指示文件的名称(即使在运行脚本之前它不存在)。除此之外,唯一改变的代码是因为我们用openFile.write()替换了openFile.read(),告诉 Python 向文件中写入内容字符串,而不是从文件中输出。

生成图像
现在我们理解了导入和读写文件到我们电脑的硬盘,让我们生成一个带有一些文本的图像。首先要做的是下载一个映像库来使用,因为默认情况下 Python 不包含映像库。其中最常见的是 Python 图像库 ( PIL )。PIL 允许将文本输入打印为图像,并且非常常用于验证码密码系统。
要安装 PIL,我们需要使用 easy_install。如果您有 Mac OS X 或 Linux,命令如下:
sudo easy_install PIL
在 Windows 上,您可以作为 Python 目录中的管理员在命令行中运行以下命令:
easy_install PIL
更好的是,如果你使用 Visual Studio 作为你的编辑器,将你的项目设置为 Python 2.7 并点击 Python 环境 2.7 实例下的安装 Python 包。在 easy_install 下输入pil,勾选以管理员身份运行,如下图截图所示:

如果成功,您应该能够看到环境中包含的pil,如下图所示:

在其他环境中,如 Eclipse,您可以使用以下命令检查您的导入:
from pil import *
这将从 PIL 图书馆导入所有模块。现在我们有了这个设置,我们将重用我们之前创建的content.txt文件,并生成一个包含其内容的图像。由于这有点复杂,我将把这两个步骤移到它们自己的函数中,如下面的代码所示:
from pil import *
import Image
import ImageDraw
import os
def readcontent():
'''Open the file to be read. Note the file's permission is set to read-only.'''
openFile = open('content.txt', 'r')
'''Save the file's inner content to a Python Variable string.'''
readmeText = openFile.read()
'''Close the file to save memory.'''
openFile.close()
'''Return the results to each as a reference variable.'''
return openFile, readmeText
def generateImage():
'''Create our references.'''
img = Image.new("RGBA", (100, 80), "white")
'''Draw the images size and background to the screen.'''
draw = ImageDraw.Draw(img)
'''Position the text with an x/y of 10 x 10, assign it the text value and text color of red.'''
output = draw.text((10, 10), readmeText, fill=(255,0,0,255))
'''Draw the text to the screen.'''
draw = ImageDraw.Draw(img)
'''Save the image.'''
img.save("output.png")
'''Return the results to each as a reference variable.'''
return draw, img, output
'''trigger the read content function.'''
openFile, readmeText = readcontent()
'''Generate our image.'''
draw, img, output = generateImage()
我们现在应该有一个简单的图像,如下图所示。如果您想设置字体大小和字体,请随意修改content.txt文件以获得不同的结果。

型式
通过 easy_install,当前版本的 PIL 有一个 bug。一些基于 C 的代码安装不正确。你可能想看看枕头(PIL 的捆绑版),你可以在这里下载:https://code.google.com/p/rudix/downloads/detail?名称=枕头-1.7.7-0.pkg & can=2 & q 。
使用 svgwrite 创建 SVG 图形
在我们结束这一章之前,让我们来看看如何生成基于矢量和计算机绘制的可缩放线条和形状的 SVG 图形。为此,我们将使用一个名为svgwrite的 Python 库,您可以在这里找到:https://pypi.python.org/pypi/svgwrite。由于这是 PyPi 上的 Python 库,我们可以使用 pip 来安装。
对于使用 VSPT 的 Windows 用户
将您当前的 Python 实例添加到您的解决方案资源管理器中的 Python 环境中,并在安装 Python 包提示符下键入svgwrite,如下图所示:

如果成功,您应该会在您的解决方案资源管理器中看到包,如下图所示。如果您看不到它们,请尝试打开您的 Python 环境,然后在您的解决方案中打开您的 Python 版本:

适用于 Eclipse 或 Windows 上的其他编辑器
在具有管理员权限的命令提示符下键入以下命令:
cd C:\Python27
pip install svgwrite
以下是 Windows 命令提示符的屏幕截图:

适用于 Mac 和 Linux 上的 Eclipse
打开终端,输入以下命令:
sudo pip install svgwrite
我们在这里使用sudo来确保svgwrite的所有东西都已正确安装。接下来,在 PyDev 中创建您的项目,并确保将项目路径设置为src。这将路径设置为它自己的目录,而不是您的 Python 根目录。请看下面的截图,它显示了 OS X Eclipse 中一个新的 PyDev 项目的开始:

在 Eclipse 中,您也可以使用 Python 控制台检查代码。在控制台中,点击窗口右上角的新控制台视图图标,选择 Pydev 控制台,如下图所示:

您也可以在 PyDev 中验证您安装了什么软件包。在您的 PyDev 包浏览器中,展开 python 根目录,转到系统库|2.7/站点包。一旦打开,如果能找到svgwrite,就应该设置好了。在下面的截图中,您可以看到它在我的系统上的外观:

一旦你准备好了,我们将创建一个新的项目和 Python 文件来生成我们的 SVG 文件。首先创建svgwrite的导入(小写),如下代码所示:
import svgwrite
现在让我们引用根svgwrite库,并将其分配给一个变量,我们可以将其作为 SVG 文件输出。为此,我们将使用svgwrite中的Drawing方法。Drawing方法使我们能够创建一个 SVG 文件。这是我们将放下其他对象的地方,如文本、线条、圆圈等。
让我们看看下面的例子:
drawObj = svgwrite.Drawing('username.svg', profile='tiny', width=444, height=300)
这里,我们有一个drawObj变量,我们已经创建了一个svgwrite对象的实例,并使用一些参数调用了Drawing方法。我们的第一个参数是一个字符串,我们在这里声明我们的文件名;在这种情况下,username.svg。请注意,我们没有包含路径,因此对于这个脚本,文件将保存在我们的项目目录中。
profile属性为 SVG 设置基本配置文件。您可以使用两个值:tiny和full。我们也可以用width和height属性设置 SVG 标签的宽度和高度。
现在,我们有了基础绘图对象来绘制形状。我们将为drawObj变量添加一个 SVG 文本节点。请看下面一行:
drawObj.add(drawObj.text('Test', insert=(0, 0), fill='red', font_size=70, font_family='sans-serif', font_weight='bold'))
这里我们有一系列的参数。第一个是我们想要写入节点的文本副本的字符串,下一个是一个映射(映射是一组两个参数)。这个映射为 SVG 节点中的左上角文本块元素设置了 X 和 Y 坐标。
下面是我们这个文本块的fill颜色;在这种情况下,我们有一个值red。如果我们需要它类似于 CSS 中的颜色十六进制,我们也可以在这里使用十六进制值。这里我们还有三个参数:font_size、font_family和font_weight,它们都是不言自明的。font_size参数使用简单的 int 值来增加或减少大小。font_family参数将适用于系统中包含的任何常规字体(不需要文件路径)。而font_weight可以根据所选字体的属性将字体的粗细设置为bold或light。注意,没有font_family参数,font_weight参数将不起作用。
最后,我们将使用save()函数将drawObj变量保存到文件中。这将使用我们添加的参数保存文件。加上这些,这里有一个准备运行的完整脚本。这是我们的save()功能:
drawObj.save()
现在让我们从我们的 IDE 中运行应用。按照上一个代码中显示的drawObj示例检查您的代码,假设没有遇到错误,您应该会看到一个终端(或命令提示符)窗口,其中按回车键继续……如上一个示例所示,表示成功。
我们可以通过进入我们的项目目录,并在我们选择的浏览器中打开我们新生成的username.svg文件来查看:

我们快到了。看起来我们的 SVG 文件很好,但是我们的文本节点偏离了中心。还记得我们的insert地图吗,我们在那里定义了我们的 X 和 Y 坐标?让我们调整一下;此外,如果您正在使用 Ubuntu 或任何其他 Linux 发行版,您可能需要格式化 X 和 Y 坐标以适合您平台的浏览器:
drawObj.add(drawObj.text('Test', insert=(15, 64), fill='red', font_size=70, font_family='sans-serif', font_weight='bold'))
让我们在浏览器中重新运行并刷新我们的 SVG 文件:

有我们的文本,显示作为我们生成的一个 SVG。请注意,我们甚至可以选择文本。由于这是一个文本节点,我们应该能够在网络内容中突出显示甚至搜索它。将输出作为 SVG 给了我们创建图形的一系列用途。
让我们在文本节点周围添加几行,比如 X 和 Y 图表基线,只是为了显示一些基本的绘图。在您的save()功能之前,包括如下例所示的line()功能:
import svgwrite
drawObj = svgwrite.Drawing('username.svg', profile='tiny', width=444, height=300)
drawObj.add(drawObj.text('Test', insert=(15, 64), fill='red', font_size=70, font_family='sans-serif', font_weight='bold'))
drawObj.add(drawObj.line((10, 10), (10, 70), stroke=svgwrite.rgb(0, 0, 0, '%')))
drawObj.add(drawObj.line((10, 70), (370, 70), stroke=svgwrite.rgb(0, 0, 0, '%')))
drawObj.save()
现在让我们重新运行我们的项目看看结果:

现在我们有了一个非常简单的图表的开始,它被导出为一个我们可以在 HTML 中操作的 SVG 文件(使用兼容 SVG 的浏览器)。看看下面的截图。在这里,我们可以使用 Chrome 的网页浏览器来改变fill的颜色:

整洁!所以现在我们可以打印文本和对象到一个 SVG 文件!我们可以在 SVG 中绘制线条、方框和圆圈,当您看到这些时,您开始了解如何从头开始构建图表和图形。让我们让这个脚本更实用一点,就像我们把它用作一个应用一样。让我们重用我们的 Hello World 图像脚本中的文本文件阅读器模块。
在开始这段代码之前,请确保您的content.txt文件位于项目目录的根目录下。接下来,让我们重用我们早期脚本中的readcontent()函数。早期在模块中分解这些代码有助于我们通过复制和粘贴在新项目中重用这些代码!
首先,包括您的导入,它将包含svgwrite,就像之前访问您的文本文件一样:
import svgwrite
def readcontent():
'''Open the file to be read. Note the file's permission is set to read-only.'''
openFile = open('content.txt', 'r')
'''Save the file's inner content to a Python Variable string.'''
readmeText = openFile.read()
'''Close the file to save memory.'''
openFile.close()
'''Return the results to each as a reference variable.'''
return openFile, readmeText
'''trigger the read content function.'''
openFile, readmeText = readcontent()
现在让我们将包装在它自己的函数中,并给它一个参数;在这种情况下,username,要传递我们的content.txt文件的输出。您的 Python 脚本应该类似于以下代码:
import svgwrite
def readcontent():
'''Open the file to be read. Note the file's permission is set to read-only.'''
openFile = open('content.txt', 'r')
readmeText = openFile.read()
'''Save the file's inner content to a Python Variable string.'''
openFile.close()
'''Close the file to save memory.'''
return openFile, readmeText
'''Return the results to each as a reference variable.'''
def createSVGText(usrname):
drawObj = svgwrite.Drawing('username.svg', profile='tiny', width=444, height=300)
drawObj.add(drawObj.text(usrname, insert=(15, 64), fill='red', font_size=70, font_family='sans-serif', font_weight='bold'))
drawObj.add(drawObj.line((10, 10), (10, 70), stroke=svgwrite.rgb(0, 0, 0, '%')))
drawObj.add(drawObj.line((10, 70), (400, 70), stroke=svgwrite.rgb(0, 0, 0, '%')))
drawObj.save()
return drawObj
'''trigger the read content function.'''
openFile, readmeText = readcontent()
'''Grab the 'readmeText' file content and pass that into our createSVGText function.'''
drawObj = createSVGText(readmeText)
重新运行脚本,让我们看看的username.svg文件:

我们到了。我们创建了一个动态脚本,它从本地文本文件中提取数据,并将其导入类似图表的布局中,并在每次运行时动态更新。玩转选项,看看你能做什么,把其他单词输入content.txt文件。
现在这还是一个简单的剧本;显然,如果我们在文本文件中键入一个非常长的字符串,它将溢出 SVG 文件。这只是一个要素。如果您从头开始构建图表,并且需要一切正常工作,该怎么办?我们可以假设这只会变得越来越复杂,最终这就是本章的重点。
总结
总结一下,在本章中,我们回顾了一些基本的 Python 技能、变量、函数和参数;看到了如何导入库以及如何在多个 IDEs 上安装库;并使用 Python 图像库(一个常见的 Python 库)生成 PNG 图形。
我们花了一点时间来理解 Python 中的路径和文件输入/输出,在 Python 代码中读写文件和变量。我们还学习了 SVG 图形:如何使用它们,并使用svgwrite库在 Python 中生成它们。
现在,我们已经用 Python 完成了一些基本的图形生成,并使用了一些基本的图像库,我们准备开始本书的数据可视化部分。
如您现在所知,构建图形,更不用说图表,在没有一些帮助库的情况下可能是一项艰巨的任务。幸运的是,Python 提供了一个又一个经过测试的漂亮的图表工具库,为您的 Python 项目做好了准备。
在下一章中,我们将从一个非常易于使用的、几乎全包的库开始,该库用于构建已准备好高端多媒体内容的 SVG 图表:pygal!
三、pygal 入门
在本章中,我们将从使用 Python 的 pygal 图表库构建一些基本的 SVG 图表开始,并查看常见的图表类型和样式。
为什么用 pygal?
在 Python 开发的世界中,有许多图表库(Matplotlib 和 Plotly 是几个例子),并且已经为其中许多编写了烹饪书风格的书籍。由于这是对数据图表的介绍,我们需要一个简单易用的库,熟悉 Python 图表或 Python 图表的开发人员可以轻松地从这本书中获取代码并构建 Python 应用。下面的截图显示了 pygal 网站和一些图表示例:

这是皮格尔进来的地方;pygal(http://pygal.org/)是一款基于 Python 的 SVG 图表创建器,由 Kozea 社区(http://community.kozea.fr/)开发,如下图所示。这是一个致力于构建高质量开源库的组织(主要基于 Python,但也适用于 HTML5 项目)。

pygal库除了我认为的标准图表之外,还提供了多种图表选项:条形图、折线图和饼图。它包括世界地图、漏斗图、雷达图和方框图等。
它还包括预先构建的主题和样式,如果您不打算这样做,就不必定制它们。此外,由于图表库的输出是 SVG,这使得它成为 HTML5 甚至打印媒体的高度灵活的输出类型。Python 中一些图表库的一个问题是输出默认为具有指定图像大小的 PNG 格式。由于 SVG 是一种矢量图形(一种可缩放而不损失图像质量的图形),因此它可以根据任何需要进行缩放和调整大小,而不会损失质量。
请看下面的http://pygal.org/文档页面截图:

pygal 网站也包含了非常好且易于阅读的文档。第三方 Python 库很常见的一点是,文档可以从一个文档齐全、可在线搜索的 wiki 到一个仅显示如何安装库的简单readme.txt文件。pygal库也不需要太多的依赖,这对于入门书来说是至关重要的,因为一个非常依赖的库可能会给新开发人员或刚接触 pygal 的开发人员带来问题。
许多 Python 框架都有一些非常挑剔的依赖项,您可能需要它们来完成您的项目,但是它们可能适合您的系统,也可能不适合您的系统。

lxml库是 pygal 唯一需要的库,但是它有一些问题,这取决于您在哪个操作系统上运行 Python 代码。在我们介绍 pygal 的安装之前,我鼓励您重读lxml上的注释(特别是如果您运行的是 Windows)。
至此,让我们安装 pygal 并构建一些图表!
使用 pip 安装 pygal
首先首先,如果你还没有安装lxml,如果你在 Windows 上工作,你会想要安装lxml安装程序,如第 1 章、设置你的开发环境所述;否则,下面的命令应该会为你安装lxml。接下来,我们将使用 pip 并使用以下命令为 Windows 和 Mac/Linux 系统安装pygal(注意在 Mac 和 Ubuntu 安装中使用了sudo)。
如果您是 Windows 用户,请键入以下命令:
pip install pygal
如果您是苹果或 Ubuntu 用户,请键入以下命令:
sudo pip install pygal
接下来,用 PyDev 打开 Eclipse,创建一个新的 Python 项目,然后是一个新文件(设置并不重要,因为这是一个测试项目)。项目创建后,创建新文件,称之为importtest.py,并键入以下内容:
import pygal
如果成功,您应该能够按下 Ctrl +空格键,并看到 PyDev 的代码提示拉出系统上安装的所有库。在下面的截图中,可以看到pygal正在我的系统中被识别:

使用 Python 工具为 Visual Studio 安装 pygal
如果您计划在 Visual Studio 中工作以完成本书的剩余部分,这里有一个关于安装的注意事项:首先,如果您还没有安装lxml,如第 1 章、中所述设置您的开发环境,然后在安装 Python 包窗口中用您的 Python 环境运行 easy_install ,如下图截图所示:

如果成功,您的解决方案资源管理器窗口应该如下图所示,包括lxml:

最后,安装pygal库。右键单击环境中的,选择安装 Python 包,这次是pygal,如下图所示:

构建折线图
折线图通常显示特定数据在不同时间间隔的变化情况。在图表中,这是您能制作的最简单的图表,通常带有 x 和 y 轴,图表上的每个轴表示时间、值或其他参数。
让我们建立一个简单的图表,在这种情况下,一个网站在过去两年(2012-2014 年)的点击量。看看下面代码的第一行;这是 Python 解释器用来指定文件字符串编码类型的声明行。另外,你会在line.x_labels上注意到我们使用了一个名为 range()的内联函数。这让我们创建一个数字数组,从最低的数字开始到最高的数字;在下面的例子中,2012和2014将打印为一个数组中的2012、2013、2014。现在,将以下代码复制到项目的主 Python 文件中:
# -*- coding: utf-8 -*-
import pygal
#create a new line chart.
line = pygal.Line()
line.title = 'Website hits in the past 2 years' #set chart title
line.x_labels = map(str, range(2012, 2014)) #set the x-axis labels.
line.add('Page views', [None, 0, 12, 32, 72, 148]) #set values.
line.render_to_file('linechart.svg') #set filename.
下面的截图显示了一个基本的 pygal 折线图输出:

在运行脚本的主项目文件中,您可以看到创建的linechart.svg文件。打开它,你的图表将看起来像前面截图中显示的那样。要找到文件,打开项目所在的目录,找到linechart.svg文件。请注意,您可以将鼠标悬停在这些点上,并获取图表中每个标记的值;这些是pygal图书馆预建的一些功能。
我们也将看到图表的时间线从 2013 年的 0.0 开始。如果你看一下 line.add()语句,第一个参数是None;这在我们的图表中增加了一个间隔,将图表数据推出一点点,而不是迫使图表从 2012 年开始。这是设置图表布局的常见技巧。
另一个特点是,如果您将鼠标悬停在线标签上(在本例中为Page views),整条线将被高亮显示,指示您正在使用该标签查看哪个数据集。pygal库还将检查您的数据,并强调数据轴上的某些行,如 0.0、50.0 和 100.0,以分解一些图表行,从而更容易阅读。
型式
pygal 对line()函数外观的代码提示支持取决于您使用的 IDE。与大多数 Python 库相比,pygal库的编写方式略有不同。该库使用for循环动态生成每个图表类型,该循环检查pygal库中的每个图表类。因此,在 Python 中需要静态硬编码函数的 ide 会抛出一个错误,但在运行时不会中断。换句话说,使用代码提示可能会也可能不会很好地工作,这取决于您使用的编辑器。
堆叠折线图
堆叠折线图的工作方式与传统折线图相似,但它们将多组数据堆叠在一起,以显示一个组的特定值。将以下代码复制到项目的主 Python 文件中,并运行该文件。另外,请注意我们图表上的多个add()功能。由于图表在一个图表中有多个数据集,我们需要为每个数据集创建一个数据集:
# -*- coding: utf-8 -*-
import pygal
#create a new stacked line chart.
line = pygal.StackedLine(fill=True)
line.title = 'Web hits in the past 2 years' #set chart title
line.x_labels = map(str, range(2012, 2014)) #set the x-axis labels.
line.add('Site A', [None, 0, 12, 32, 72, 148]) #set values.
line.add('Site B', [2, 16, 12, 87, 91, 342]) #set values.
line.add('Site C', [42, 55, 84, 88, 90, 171]) #set values.
line.render_to_file('linechart.svg') #set filename.
下面的截图显示了我们脚本的结果:

渲染后,您的堆叠图将看起来像前面截图中显示的那样。打开项目所在的目录,找到linechart.svg文件。注意默认情况下 pygal 是如何覆盖您的原始 SVG 文件的;使用此库时,请记住这一点。另外,您会注意到,当我们声明图表时,我们在StackedLine函数中添加了一个fill=True参数;这是一个图表参数。稍后将对此进行更多介绍,但在这里我们可以看到填充颜色被添加到图表的线条下方。
简单条形图
条形图和折线图一样是典型的,但是它们占据了图表的整个区域。它们还有助于显示信息类别的价值。让我们构建一个简单条形图,将以下代码复制到一个名为bar_chart.py的新文件中,并运行脚本:
# -*- coding: utf-8 -*-
import pygal
#create a new bar chart.
bar = pygal.Bar()
bar.title = 'Searches for term: sleep'
bar.x_labels = map(str, range(2011, 2015))
bar.add('Searches', [81, 88, 88, 100])
bar.render_to_file('bar_chart.svg')
转到您的项目目录,在浏览器中打开bar_chart.svg。请注意,除了提供的数据和定义的图表类型之外,代码没有太大变化(在本例中,它是pygal.Bar())。下面的截图显示了我们脚本的结果:

堆叠条形图
就像折线图一样,堆叠条形图按照数据顺序将不同的条形图重叠在一起。让我们复制下面的代码示例并运行这个脚本:
# -*- coding: utf-8 -*-
import pygal
#Create a new stacked bar chart.
bar = pygal.StackedBar()
bar.title = 'Searches for term: sleep'
bar.x_labels = map(str, range(2011, 2015))
bar.add('Men', [81, 88, 88, 100])
bar.add('Women', [78, 84, 69, 92])
bar.render_to_file('bar_chart.svg')
下面的截图显示了我们脚本的结果:

由于这是一个堆叠值,我们有两组数据;在这种情况下,男人和女人搜索。前面的截图是完整的图表,显示了带有术语“睡眠”的总搜索的分离段值的组合数据集。
水平条形图
对于 pygal 提供的最后一种条形图类型,我们将使用水平图,并重用简单条形图中的数据。水平条形图旨在更好地显示某个时间点的数据。为此,我们将删除我们的x_labels属性,因为我们只想显示一个月,因此删除了年份。现在,复制以下代码并运行脚本:
# -*- coding: utf-8 -*-
import pygal
#create a new bar chart.
bar = pygal.HorizontalBar()
bar.title = 'Searches for term: sleep in April'
bar.add('Searches', [81, 88, 88, 100])
bar.render_to_file('bar_chart.svg')
打开bar_chart.svg文件;结果显示在下面的截图中:

XY 图表
XY 图表是科学数据中用来显示不同点的多个值的典型。它们也可以显示负值。为了便于阅读,它们还覆盖了多组值。让我们用两点建立一个简单的 XY 图表。将以下代码复制到您的 Python 文件中并运行应用,并将您的 SVG 文件输出保存为xy_chart.svg:
# -*- coding: utf-8 -*-
import pygal
xy_chart = pygal.XY()
xy_chart.add('Value 1', [(-50, -30), (100, 45)])
xy_chart.render_to_file("xy_chart.svg")
打开xy_chart.svg文件;结果显示在下面的截图中:

注意pygal如何在 x 和 y 坐标上高亮显示 0 线;同样,这是由pygal库提供的免费样式,用于指示负值。此外,注意add()函数,每个值是如何记为(x,y)坐标,并组合成一个数组的。让我们建立另一个图表,这次有两个图;在这种情况下,我们使用Value 1和Value 2进行构建。复制以下代码并运行它:
# -*- coding: utf-8 -*-
import pygal
xy_chart = pygal.XY()
xy_chart.add('Value 1', [(-50, -30), (100, 45)])
xy_chart.add('Value 2', [(-2, -14), (370, 444)])
xy_chart.render_to_file("xy_chart.svg")
打开xy_chart.svg文件;注意有两条线图,如下图所示:

我们现在将看到如何在 XY 图表中绘制基本线图,但是如果我们在一条线上有多个值呢?我们再写一张 XY 图,三个值,每个值六个点。让我们看看下面的代码并运行它:
# -*- coding: utf-8 -*-
import pygal
xy_chart = pygal.XY()
xy_chart.add('Value 1', [(-50, -30), (100, 45), (120, 56), (168, 102), (211, 192), (279, 211)])
xy_chart.add('Value 2', [(-2, -14), (370, 444), (391, 464), (399, 512), (412, 569), (789, 896)])
xy_chart.add('Value 3', [(2, 10), (142, 164), (184, 216), (203, 243), (208, 335), (243, 201)])
xy_chart.render_to_file("xy_chart.svg")
完成后,打开xy_chart.svg文件;它应该看起来像下面的截图所示:

请注意我们如何轻松地读取每个数据集。我们可以看出Value 2的数值最多的是在偏高的一侧,也可以看出Value 3达到了比Value 1更高的点但是下降的很快,这使得 XY 图对于科学数据来说非常棒。现在,让我们来看看 XY 图表的一种变体,称为散点图。
散点图
散点图的工作原理与 XY 图表相同,但它们没有链接在一起的线条。在pygal库中,这次没有“散点图”功能可以使用。相反,我们只是重用 XY 图表函数并设置一个参数;在这种情况下,stroke等于False(默认为stroke为True)。让我们重复使用上一张图表中的 XY 代码,添加stroke参数,看一下:
# -*- coding: utf-8 -*-
import pygal
xy_chart = pygal.XY(stroke=False)
xy_chart.add('Value 1', [(-50, -30), (100, 45), (120, 56), (168, 102), (211, 192), (279, 211)])
xy_chart.add('Value 2', [(-2, -14), (370, 444), (391, 464), (399, 512), (412, 569), (789, 896)])
xy_chart.add('Value 3', [(2, 10), (142, 164), (184, 216), (203, 243), (208, 335), (243, 201)])
xy_chart.render_to_file("xy_chart.svg")
打开xy_chart.svg文件;它应该像下面的截图所示:

请注意,如果有更多的数据点,这个图表会更容易阅读。通常,使用 XY 图表和散点图的一个很好的经验法则是,如果每个数据集有 10 个以上的点或者有 6 个以上的数据集要显示。在结束本章之前,让我们再看一下pygal库中 XY 图表库的一个变体: DateY 。
日期图表
日期图表的工作方式与任何 XY 图表相同,只有一个例外。每个数据点都与一个日期相关联,而不是 Python 中带有日期的字符串类型,而是 Python 代码中的物理日期时间对象。在我们的 Python 代码中,每个 X 标签都将与一个date对象相关联,Y 要么是整数,要么是我们提供的浮点数。
与我们的散点图不同,DateY 包含自己的函数,并遵循自己的规则。让我们构建一个非常简单的日期图来看看我们正在处理什么。首先,在运行下面的代码之前,先看看datetime库,具体是datetime和timedelta。
datetime库是 Python 的内置库,非常简单。它允许将日期保存到本地机器内部时钟的代码中,并包括将字符串转换为日期以及按时间向前或向后计数的方法。timedelta功能属于datetime图书馆。timedelta()代表的是持续时间,以及两个日期或时间之间的差异,带有基于日期的参数。让我们构建一个名为timedelta.py的快速脚本,并复制以下代码来查看结果:
# -*- coding: utf-8 -*-
import datetime
from time import sleep
start = datetime.datetime.now()
sleep(5) #delay the python script for 5 seconds.
stop = datetime.datetime.now()
elapsed = stop - start
if elapsed > datetime.timedelta(minutes=4):
print "Slept for greater than 4 minutes"
if elapsed > datetime.timedelta(seconds=4):
print "Slept for greater than 4 seconds"
输出显示在下面的截图中。请注意,在设置stop变量的日期之前,我们在脚本中使用sleep()函数设置了5秒的延迟,从而通过了4秒。
型式
time.sleep()函数是一个常见的 Python 函数,对于无法多线程的进程密集型代码非常有用,例如复制或删除硬盘上的文件,或者使用 Python 为网络活动设置延迟。

接下来,让我们写下我们的日期表。对于这个图表,我们将用值给一系列日期加上时间戳;在这种情况下,乘客在给定时间从圣路易斯抵达。让我们编写以下代码并将输出保存为datey_chart.svg:
# -*- coding: utf-8 -*-
import pygal
from datetime import datetime, timedelta
Date_Y = pygal.DateY()
Date_Y.title = "Flights and amount of passengers arriving from St. Louis."
Date_Y.add("Arrival", [
(datetime(2014, 1, 5), 42),
(datetime(2014, 1, 14), 123),
(datetime(2014, 2, 2), 97),
(datetime(2014, 3, 22), 164)
])
Date_Y.render_to_file('datey_chart.svg')
现在,让我们看看下面的图表。我们可以看到一个完整的日期时间与每个数据点相关联,如以及我们的图表值,在本例中是乘客。我们还可以在 x 轴标签上看到时间范围。然而,这有一个问题。看看下面的图表,看看 x 轴上的标签:

注意标签是如何聚集在一起的,如果它们不合适,就会被剪掉。这不是我们担心的问题,因为日期图表有一个可选参数来帮助呈现这些标签。我们可以使用下面代码中显示的参数沿着 x 轴旋转它们:
# -*- coding: utf-8 -*-
import pygal
from datetime import datetime, timedelta
Date_Y = pygal.DateY(x_label_rotation=25)
Date_Y.title = "Flights and amount of passengers arriving from St. Louis."
Date_Y.add("Arrival", [
(datetime(2014, 1, 5), 42),
(datetime(2014, 1, 14), 123),
(datetime(2014, 2, 2), 97),
(datetime(2014, 3, 22), 164)
])
Date_Y.render_to_file('datey_chart.svg')
现在,让我们重新渲染我们的图表,如下图所示。我们可以看到标签的格式有助于提高可读性:

在完成本章之前,让我们再添加一个图表。在这里,我们将捕获由我们的代码调用的两个时间点,一个将被延迟一个睡眠延迟,就像我们的timedelta例子一样;我们将有两个航班在两个不同的时间点到达。
这里,我们将设置两次到达之间的延迟,并为每个数据点设置时间。我们将使用time.sleep()来延迟脚本。运行以下脚本。请记住,由于图表的代码有延迟,SVG 文件将需要277秒来处理:
# -*- coding: utf-8 -*-
import pygal, time
from datetime import datetime
#Set pre-defined arrival dates for compare.
arrival1 = datetime.now()
time.sleep(277)
arrival2 = datetime.now()
delta = arrival2 - arrival1
result = str(delta.seconds) + ' seconds'
Date_Y = pygal.DateY(x_label_rotation=25)
Date_Y.title = "Flights and amount of passengers arriving from St. Louis."
Date_Y.add("Arrival", [
(datetime(2014, 1, 5), 42),
(datetime(2014, 1, 14), 123),
(datetime(2014, 2, 2), 97),
(datetime(2014, 3, 22), 164)
])
Date_Y.add("Arrivals today (time between flights %s)" % result, [
(arrival1, 14),
(arrival2, 47)
])
Date_Y.render_to_file('datey_chart.svg')
现在,我们来看看的结果。由于时间相隔并不太远,你可能想停留在上面,更细致地观察时间过去了多少。根据我们的标签,277秒已经过去:

总结
干得好!您已经使用pygal库完成了第一轮图表!我们回顾了常见的图表,如折线图和条形图、散点图和 XY 图表;了解了如何使用带有 DateY 的图表,包括两个datetime变量之间的比较;并创建了一个模拟现实场景的图表。
如果你纠结这一章,没关系。开始浏览前几个图表(折线图和条形图),用自己的数据创建一些图表,直到你变得更加自信。
在下一章中,我们将介绍pygal库中更高级的图表类型,包括世界地图,以及更复杂的数据。
四、高级图表
在本章中,我们将使用 Python 的 pygal 图表库来探索和构建一些更高级的 SVG 图表。我们还将与 pygal 一起探索世界地图图表,并探索该图表特有的数据类型。
饼图
饼图可以很好地显示一组数据或一组数据的总和,这些数据像饼图一样被分割开来。让我们用一些虚拟数据构建一个简单的饼图。看看下面的代码,并将其合并到您自己的 Python 文件中。请注意,这次我们将文件输出保存到pie_chart.svg:
# -*- coding: utf-8 -*-
import pygal
pie_chart = pygal.Pie()
pie_chart.title = 'Total top tablet sales in 2013 (in %)'
pie_chart.add('iPad & iPad mini', 49.7)
pie_chart.add('Surface Pro 2', 36.3)
pie_chart.add('Surface 2', 24.5)
pie_chart.add('Nexus 7', 17.5)
pie_chart.render_to_file('pie_chart.svg')
下面的截图显示了我们脚本的结果:

正如我们在前面的图表中所看到的,饼图中的每个add()函数都将一个不同的设备作为一个切片添加到饼图中。pie函数还包括基于第一个参数中给出的字符串的标准图例。
堆叠饼图
堆叠饼图的工作原理和听起来一样;它们将数值叠加在饼图的每个部分,让你更深入地了解数据。由于堆叠图表可以包含多个值,这些值并不是整个饼图的一部分,因此我们将构建此图表而不检查错误。让我们用下面的代码示例构建我们的图表;请注意,我们仍在使用Pie()功能,该功能类似于上一章的散点图和 XY 图表。一个限制是 pygal 中的堆叠饼图只接受主值之上的另一个值。我们还可以使用 Nexus 7 数据集上显示的单个值。这在以下代码中显示:
# -*- coding: utf-8 -*-
import pygal
pie_chart = pygal.Pie()
pie_chart.title = 'Total top tablet sales in 2013 (in %)'
pie_chart.add('iPad & iPad mini', [19.7, 21.3])
pie_chart.add('Surface 2 (& Pro 2)', [24.5, 36.3])
pie_chart.add('Nexus 7', 17.5)
pie_chart.render_to_file('pie_chart.svg')
打开pie_chart.svg文件,结果如下截图所示:

在前面的示例中,我们可以看到每个饼图切片中的子层切片。看看我们的代码示例,注意额外的切片来自我们添加到图表对象的每个add()函数的第二个参数中的 Python 列表数组。
雷达图
雷达图非常适合显示一个数据对象的多个变量。雷达图通常看起来像你期望在机场雷达上看到的,向你显示一个中间有零点的地图区域。应用性能是您可以在其中找到雷达图的一个常见地方。它们也被用在体育图表中,显示球员或球队的优势和劣势。对于此图表,我们将为单个项目建立一个预算估计和实际预算支出合并在一个图表中:
# -*- coding: utf-8 -*-
import pygal
radar_chart = pygal.Radar()
radar_chart.title = 'Product Budget Figures'
radar_chart.x_labels = ['Sales', 'Marketing', 'Development', 'Customer support', 'Information Technology', 'Administration']
radar_chart.add('Estimate', [40, 20, 100, 20, 30, 20, 10])
radar_chart.add('Actual Spending', [70, 50, 40, 10, 17, 8, 10])
radar_chart.render_to_file('radar_chart.svg')
打开radar_chart.svg文件,结果如下截图所示:

注意前面代码中显示的x_labels。在这种情况下,我们有Sales、Marketing、Development、Customer support、Information Technology和Administration作为一个数组项。
数组中每个项目的顺序很重要,因为雷达图按照数组的顺序设置每个数据集的值,以逆时针方式将该标签设置到雷达的每个端点。构建雷达图时请记住这一点。
方框图
箱线图,有时称为箱线图和触须图,是一种以类似条形图的方式向您显示数值的低、中、高范围的图表。方框图通常包含一个方框,该方框被定义为数据的高范围,然后在顶部和底部垂直向表示中间值的线逐渐变细,方框图末尾的线处的最小值也称为晶须。
箱线图适用于给定时间或供应范围内的估计值。它们也能很好地向您展示数据分布。箱线图也使用数组来表示数据,就像我们的雷达图一样。让我们用下面的代码构建一个简单的方框图,显示 2014 年初全脂牛奶的成本。确保您在render_to_file()函数中使用box_plot.svg作为文件名,如以下代码所示:
# -*- coding: utf-8 -*-
import pygal
box_plot = pygal.Box()
box_plot.title = 'Cost of Whole Milk in early 2014'
box_plot.add('US Dollars', [2.08, 3.14, 3.89, 3.91, 3.94, 3.98])
box_plot.render_to_file('box_plot.svg')
这是一个非常简单的 pygal 图表;让我们看看我们的例子:

使用方框图,我们可以向图表中添加多个方框图。让我们这样做,通过增加另一个货币市场来看看结果。在这种情况下,我们将使用Pound sterling。复制以下代码,并查看结果:
# -*- coding: utf-8 -*-
import pygal
box_plot = pygal.Box()
box_plot.title = 'Cost of Whole Milk in early 2014'
box_plot.add('US Dollars', [2.08, 3.14, 3.89, 3.91, 3.94, 3.98])
box_plot.add('Pound Sterling', [2.78, 3.84, 1.69, 4.71, 4.84, 4.92])
box_plot.render_to_file('box_plot.svg')
以下截图显示了我们脚本的结果:

现在,通过这个例子,我们可以看到并比较箱线图的中位数,并注意到,通过将鼠标悬停在我们的 SVG 图表上,美元的中位数落在范围的顶部附近并保持不变,而英镑的中位数被设置在箱线图的中高范围内,从而显示出一些变化。
点状图
点图(也称为点图)类似于旧的计算机穿孔卡片。它们是一种非常简单的传递数据集的形式,可以替代饼图或条形图。在 pygal 中,dot_chart类允许根据给定值调整每个点的大小,而无需程序员编写额外的代码。这保持了数据的简单性,但允许图表数据的消费者仍然对数据感兴趣。通常,您可以在数据集中找到点图,也可以在条形图中使用,并且可以更容易地读取小数据集。一些选民登记和/或统计将使用点状图,并将结果打卡到卡片或纸上的一行。
让我们建立一个简单的点图。我们将重用我们的箱线图中的数据集。首先,我们将使用美国货币数据集。将以下代码复制到您选择的编辑器中,并确保您将文件保存为dot_chart.svg:
# -*- coding: utf-8 -*-
import pygal
dot_chart = pygal.Dot()
dot_chart.title = 'Cost of Whole Milk in early 2014'
dot_chart.add('US Dollars', [2.08, 3.14, 3.89, 3.91, 3.94, 3.98])
dot_chart.render_to_file('dot_chart.svg')
型式
点图仅适用于每个数据集不超过 30 个值的少量数据。如果您考虑的数据集大于 20-25,请考虑条形图、折线图或饼图。
打开dot_chart.svg文件,结果如下截图所示:

查看上图,可以看到数值越大,点越大。传统的点图通常不会调整点的大小;它们保持一致的大小。
让我们从方框图示例中添加英镑数据集,并更新变量。我们还将添加月份,以便使用x_labels属性更好地阐明我们的图表,并且我们还将向我们的 x 轴添加旋转。这将为我们的标签添加轻微的旋转,这些标签看起来会与多个数据集相互重叠。复制以下代码:
# -*- coding: utf-8 -*-
import pygal
dot_chart = pygal.Dot(x_label_rotation=45)
dot_chart.title = 'Cost of Whole Milk in early 2014'
dot_chart.x_labels = ['Jan', 'Feb', 'Mar', 'April', 'May', 'June']
dot_chart.add('US Dollars', [2.08, 3.14, 3.89, 3.91, 3.94, 3.98])
dot_chart.add('Pound Sterling', [2.78, 3.84, 1.69, 4.71, 4.84, 4.92])
dot_chart.render_to_file('dot_chart.svg')
打开dot_chart.svg文件,结果如下截图所示:

这里,有了x轴标签,我们现在对跨数据集的数据有了更好的理解。这看起来很好,通过调整点的大小,我们可以看到每个月的一般值。
型式
使用库很容易构建点图,但是从库中从头开始构建点图就不容易了。问题是创建一个大小合适的点,因为每个数据值都需要大量的数学知识。如果你试图用点状图构建或重新设计一个库,请对此预先警告。
漏斗图
漏斗图(也称为漏斗图)是一种突出显示数据中某些阶段的某些共享属性的图表,其中数据集来自多个数据源,但似乎在一个中心点上相互重叠。通常,这些图表旨在向您展示一组数据集的共性。
pygal 漏斗图的一个优势是它们运行良好,显示大数值集的数据,例如,航空和火箭科学,能够测试和读取空速、用于推力的燃料磅数等数据。让我们看一个例子,它有一个类似于前面提到的图表;这里,我们有一个代码示例,它向我们显示了航天飞机在起飞时使用的推力。复制下面的代码,让我们在我们选择的编辑器中运行这个代码示例。再次,确保将此保存在单独的图表名称中,这次是funnel_chart.svg。我们还将向图表中添加一个x_label_rotation属性,以帮助显示我们的数据集:
# -*- coding: utf-8 -*-
import pygal
funnel_chart = pygal.Funnel(x_label_rotation=40)
funnel_chart.title = 'Amount of thrust used in a space shuttle at takeoff (in lbs)'
funnel_chart.x_labels = ['Pre-takeoff', '5 min', ' 10 min', '15 min', '20 min']
funnel_chart.add('Main Engine', [7000000, 6115200, 5009600, 4347400, 2341211])
funnel_chart.add('Engine #1', [1285000, 1072000, 89000, 51600, 12960])
funnel_chart.add('Engine #3 & #4 (mid-size)', [99000, 61600, 21960, 17856, 11235])
funnel_chart.render_to_file('funnel_chart.svg')
打开funnel_chart.svg文件,结果如下截图所示:

看看我们的图表。在 x 轴上,Main Engine结束,Engine #1接管,当我们从发射到达不同阶段时,我们可以看到推力逐渐减小。然后,我们看到Main Engine分离,然后我们看到燃烧的Engine #1,接着是Engine #3 & #4,在Engine #1分离之后。这当然向我们展示了航天飞机在发射时的指向和飞往太空时的初始推力输出。
仪表图
仪表图以类似于汽车速度计的图形方式显示数据。它们也适用于多个数据集,但不适用于单个数据集。在下面的代码中,我们有一个仪表图的例子,这是一个非常简单的 pygal 图。这一次,我们将使用一些新的数据;在这种情况下,我们将使用一个数据集来表示航天飞机从发射到 20 分钟的速度。
让我们看看下面代码片段中的示例代码和图表。将代码复制到您选择的编辑器中,并确保将文件保存到gauge_chart.svg:
# -*- coding: utf-8 -*-
import pygal
gauge_chart = pygal.Gauge()
gauge_chart.title = 'Speed of space shuttle during takeoff'
gauge_chart.x_labels = ['Pre-takeoff', '5 min', ' 10 min', '15 min', '20 min']
gauge_chart.add('Pre-takeoff', 0)
gauge_chart.add('5 min', 96)
gauge_chart.add('10 min', 167)
gauge_chart.add('15 min', 249)
gauge_chart.add('20 min', 339)
gauge_chart.render_to_file('gauge_chart.svg')
打开gauge_chart.svg文件,结果如下截图所示:

在我们的浏览器中查看gauge_chart.svg,我们可以看到一个简单的仪表图。但是,请注意,速度值是浮动的。如果我们正在处理具有多个小数点的复杂浮点数,我们可以简化该图表,并使用我们的Gauge()函数中的human_readable=True来修剪这些浮点数,如以下代码所示:
# -*- coding: utf-8 -*-
import pygal
gauge_chart = pygal.Gauge(human_readable=True)
gauge_chart.title = 'Speed of space shuttle during takeoff'
gauge_chart.x_labels = ['Pre-takeoff', '5 min', ' 10 min', '15 min', '20 min']
gauge_chart.add('Pre-takeoff', 0)
gauge_chart.add('5 min', 96)
gauge_chart.add('10 min', 167)
gauge_chart.add('15 min', 249)
gauge_chart.add('20 min', 339)
gauge_chart.render_to_file('gauge_chart.svg')
现在,让我们更新我们的图表,看看结果。

这个看起来不错;使用human_readable=True将有助于修剪我们图表中的长值,并有助于防止重叠。请注意前面截图中的值;现在,我们的图表标签中的任何十进制值都会被修剪。
型式
仪表图的一个简洁的用户界面特征是,如果您将鼠标悬停在与 SVG 兼容的浏览器中仪表标签的某个值上,将会出现一条虚线,指示与数据集值的接近程度。
金字塔图
通常金字塔图是用来显示大量数据值较高的数据,如人口数据、投票率、选举结果等。
让我们试着建立一个图表;现在,这个 pygal 图表看起来会有点不同。金字塔图在有大量数据的情况下看起来最好,在这种情况下,我已经打印出了一堆数据,你可以用来练习。对于这个图表,我将在另一个数组内部创建一个数组,每个括号集作为一个子数组。这将被称为miles_traveled。
接下来,我将创建另一个数组,谢天谢地没有那么大,叫做craft_type。这将保留一个代表航天飞机类型的字符串数组,它等于我们的子阵列数量,在本例中为Apollo Rockets、Russian rockets、US Space Shuttles和Satellites。我们还将在for循环中使用一个名为zip()的内置 Python 函数来迭代我们的miles_traveled。zip()函数允许用户返回元组列表或只有两个值的小数组:
# -*- coding: utf-8 -*-
import pygal
#Array of miles each with a subarray of miles traveled.
miles_traveled = [(364383, 359443, 360172, 345780, 333968, 326914, 323053, 312576, 302015, 301277, 309874, 318295, 323396, 332736, 330759, 335267, 345096, 352685, 368067, 381521, 380145, 378724, 388045, 382303, 373469, 365184, 342869, 316928, 285137, 273553, 250861, 221358, 195884, 179321, 171010, 162594, 152221, 148843, 143013, 135887, 125824, 121493, 115913, 113738, 105612, 99596, 91609, 83917, 75688, 69538, 62999, 58864, 54593, 48818, 44739, 41096, 39169, 36321, 34284, 32330, 31437, 30661, 31332, 30334, 23600, 21999, 20187, 19075, 16574, 15091, 14977, 14171, 13687, 13155, 12558, 11600, 10827, 10436, 9851, 9794, 8787, 7993, 6901, 6422, 5506, 4839, 4144, 3433, 2936, 2615),
(349909, 340550, 342668, 346788, 319010, 312898, 308153, 296752, 289639, 290466, 296190, 303451, 309786, 317436, 315487, 316696, 325772, 331694, 345815, 354696, 354899, 351727, 354579, 341702,
336421, 321116, 292261, 261874, 242407, 229488, 208939, 184147, 162662, 147361, 140424, 134336, 126929, 125404, 122764, 116004, 105590, 100813, 95021, 90950, 85036, 79391, 72952, 66022, 59126, 52716, 46582, 42772, 38509, 34048, 30887, 28053, 26152, 23931, 22039, 20677, 19869, 19026, 18757, 18308, 14458, 13685, 12942, 12323, 11033, 10183, 10628, 10803, 10655, 10482, 10202, 10166, 9939, 10138, 10007, 10174, 9997, 9465, 9028, 8806, 8450, 7941, 7253, 6698, 6267, 5773),
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 81, 312, 1319, 2987, 5816, 10053, 16045, 24240, 35066, 47828, 62384, 78916, 97822, 112799, 124414, 130658, 140789, 153951, 168560, 179996, 194471, 212006, 225209, 228886, 239690, 245974, 253459, 255455, 260715, 259980, 256481, 252222, 249467, 240268, 238465, 238167, 231361, 223832, 220459, 222512, 220099, 219301, 221322, 229783, 239336, 258360, 271151, 218063, 213461, 207617, 196227, 174615, 160855, 165410, 163070, 157379, 149698, 140570, 131785, 119936, 113751, 106989, 99294, 89097, 78413, 68174, 60592, 52189, 43375, 35469, 29648, 24678, 20365),
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 344, 1478, 3901, 7878, 12899, 19948, 29108, 42475, 58287, 74163, 90724, 108375, 125886, 141559, 148061, 152871, 159725, 171298, 183536, 196136, 210831, 228757, 238731, 239616, 250036, 251759, 259593, 261832, 264864, 264702, 264070, 258117, 253678, 245440, 241342, 239843, 232493, 226118, 221644, 223440, 219833, 219659, 221271, 227123, 232865, 250646, 261796, 210136, 201824, 193109, 181831, 159280, 145235, 145929, 140266, 133082, 124350, 114441, 104655, 93223, 85899, 78800, 72081, 62645, 53214, 44086, 38481, 32219, 26867, 21443, 16899, 13680, 11508),
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7, 11, 13, 31, 34, 38, 35, 45, 299, 295, 218, 247, 252, 254, 222, 307, 316, 385, 416, 463, 557, 670, 830, 889, 1025, 1149, 1356, 1488, 1835, 1929, 2130, 2362, 2494, 2884, 3160, 3487, 3916, 4196, 4619, 5032, 5709, 6347, 7288, 8139, 9344, 11002, 12809, 11504, 11918, 12927, 13642, 13298, 14015, 15751, 17445, 18591, 19682, 20969, 21629, 22549, 23619, 25288, 26293, 27038, 27039, 27070, 27750, 27244, 25905, 24357, 22561, 21794, 20595),
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 9, 10, 20, 34, 49, 84, 97, 368, 401, 414, 557, 654, 631, 689, 698, 858, 1031, 1120, 1263, 1614, 1882, 2137, 2516, 2923, 3132, 3741, 4259, 4930, 5320, 5948, 6548, 7463, 8309, 9142, 10321, 11167, 12062, 13317, 15238, 16706, 18236, 20336, 23407, 27024, 32502, 37334, 34454, 38080, 41811, 44490, 45247, 46830, 53616, 58798, 63224, 66841, 71086, 73654, 77334, 82062, 87314, 92207, 94603, 94113, 92753, 93174, 91812, 87757, 84255, 79723, 77536, 74173)]
#Array of miles each with a sub array of miles traveled.
craft_type = ['Apollo Rockets', 'Russian Rockets', 'US Space Shuttles', 'Satellites']
pyramid_chart = pygal.Pyramid()
pyramid_chart.title = 'Miles traveled of earth spacecraft'
#loop thru miles_traveled for each sub-array and add them to a craft_type as a data set.
for type, miles in zip(craft_type, miles_traveled):
pyramid_chart.add(type, miles)
pyramid_chart.render_to_file('pyramid_chart.svg')
现在,让我们在下一张截图中看看我们的新金字塔图。我们可以看到相当多的数据。每一条水平线都向你显示了每次任务的飞行里程数,随着发射时间的延长,我们可以看到一些早期形式的太空旅行的飞行里程逐渐减少,卫星和航天飞机使用了更多的飞行里程。
打开pyramid_chart.svg文件,结果如下截图所示:

世界地图图表
我认为世界地图图表不需要太多介绍。这是一个分成国家的世界地图,输出到一个 SVG 文件中。世界地图图表是pygal库的一个奇妙特性,也是我喜欢这个 Python 图表库的部分原因。这是因为在我们今天生活的现代 HTML5 移动世界中,没有多少 Python 图表库将地图作为一项功能,更不用说那些使用 SVG 输出,使地图图表 pygal 产生非常便携的图表文件的库了。
使用 pygal 库,一个简单的世界地图很容易用一点虚拟数据构建。让我们构建一个简单的世界地图,仅突出显示United States和China作为示例。将以下代码复制到您选择的编辑器中,运行您的 Python 脚本,让我们看看结果。另外,确保将输出的 SVG 文件保存为world_map.svg。
# -*- coding: utf-8 -*-
import pygal
worldmap_chart = pygal.Worldmap()
worldmap_chart.title = 'Highlighting China and the United States'
worldmap_chart.add('China', ['cn'])
worldmap_chart.add('United States', ['us'])
#Render file.
worldmap_chart.render_to_file('world_map.svg')
打开world_map.svg文件,结果如下截图所示:

因此,在前面的截图中,我们有了我们的输出世界地图,我们可以验证China和United States都被突出显示了。我们还可以看到夏威夷和阿拉斯加在United States中被恰当地突出显示。让我们回顾一下我们的代码,看看与其他图表相比,世界地图有什么不同。
看看我们的world_map变量的add()函数,看看其中传递的两个参数,如下面的代码所示:
worldmap_chart.add('China', ['cn'])
worldmap_chart.add('United States', ['us'])
请注意,我们的add()函数的工作方式类似于我们过去的图表;但是,这一次,我们传递的是一个字符串,而不是数组中的数字,在本例中,是一个带有字符串的单个项目数组。这个字符串实际上是一个两个字母的国家代码,由于它是一个标准的国家代码,pygal 可以为我们地图上的特定国家设置值。
回到我们add()方法中的数组,如果我们为单个add()函数向数组中添加多个国家会发生什么?让我们修改我们的图表,以便突出显示多个国家。
这次我们将United States改名,标签改为U.S. Allies。让我们把这些盟友和我们的us国家代码加在一起,看看会发生什么。此外,我将把我们的数组分解为每个国家/地区代码的一行(每个国家/地区代码在代码包中被移到新的一行),这样我们就可以很容易地阅读或更新我们的代码:
# -*- coding: utf-8 -*-
import pygal
worldmap_chart = pygal.Worldmap()
worldmap_chart.title = 'United States Allies and China'
worldmap_chart.add('China', ['cn'])
worldmap_chart.add('U.S. Allies', ['al','be','bg','ca','hr','cz','dk','ee','ff','de','hu','is','it','lv','lt','lu','nl','no','pl','pt','ro','si','sk','tr','us','uk'])
#Render file.
worldmap_chart.render_to_file('world_map.svg')
打开world_map.svg文件,结果如下截图所示:

还不错;我们可以很容易地看到图表用相同的颜色突出显示我们的U.S. Allies,而China在单独的数据集上用另一种颜色突出显示。
型式
世界地图是很棒的 SVG 图表。需要注意的一点是,世界地图是非常复杂的 SVG 图像,所以考虑一下你自己的图表将包含的数据量,避开极其复杂的数据集。一些用动画渲染 SVG 的移动平台在部署时可能会变得迟缓。
总结
说完这些,这一章就结束了。在这一章中,我们介绍了 pygal 库的其余图表、特性以及更高级的图表和非常复杂的数据集的正确使用。此时,考虑用自己的数据构建自己的图表。实验并玩结果;你玩库玩得越多,你就越能理解应该如何组织数据。在下一章中,我们将开始学习 pygal 主题、可选特性和定制。
五、调整 pygal
在本章中,我们将介绍如何应用主题和使用pygal库中使用的一些可选功能。我们还将研究图表的主题和样式。
国家图表
pygal库有一个图表,我们在过去的章节中没有复习过;这是提出话题和讨论图表的好时机。它叫做国家地图。像世界地图图表一样,它显示了一个国家更深层次的详细区域;不幸的是,在写这本书的时候,它只限于法国这个国家,它的工作原理与世界地图非常相似。
如果你还记得我们的介绍章节,你可能还记得我们关于 Kozea 的讨论,Kozea 是起源于法国的开源社区,它开发了pygal图书馆。由于他们来自法国,他们为自己的国家创建了一张地图,包括被称为省和地区的分区。法国的地区类似于州,部门类似于一个州的县。
让我们看一下这张地图的一些示例代码,然后我们可以使用 pygal 框架的一些额外特性来构建它。首先,让我们构建一个使用部门的简单示例。创建一个新的 Python 文件,并将以下代码复制到您选择的编辑器中。请注意这和我们的世界地图代码在第 4 章高级图表中的相似之处。请务必使用france_map.svg作为文件输出:
# -*- coding: utf-8 -*-
import pygal
france_chart = pygal.FrenchMap_Departments()
france_chart.title = 'Sample departments'
france_chart.add('Data-set 1', ['17'])
france_chart.add('Data-set 2', ['27'])
france_chart.add('Data-set 3', ['38'])
france_chart.add('Data-set 4', ['42'])
france_chart.add('Data-set 5', ['19'])
france_chart.render_to_file('france_map.svg')
打开france_map.svg文件,结果会是如下截图所示:

让我们来看看我们的france_chart型。请注意,我们在图表类型中使用了以Departments为后缀的pygal.FrenchMap_Departments()。在这个图表中,有两种模式——一种用于部门,另一种用于地区。使用以下代码查看如何创建基于区域的图表。这次注意后缀:
# -*- coding: utf-8 -*-
import pygal
france_chart = pygal.FrenchMap_Regions()
france_chart.title = 'Sample Regions'
france_chart.add('Centre', ['24'])
france_chart.add('Lorraine', ['41'])
france_chart.add('Picardy', ['22'])
france_chart.add('Upper Normandy', ['23'])
france_chart.add('Corsica', ['94'])
france_chart.render_to_file('france_map.svg')
打开france_map.svg文件,结果会是如下截图所示:

仔细观察代码,我们可能会想为什么我们没有使用地区缩写,或者问如何设置我们的活动地区或部门。数字的原因是法国使用自己的 INSEE 数字。
国家统计和经济研究所 ( INSEE )是法国的国家统计和经济研究所,为其部门和地区创建了编号系统。由于这是识别法国部分的通用系统,pygal库开发人员使用相同的数字为地图图表分配高光。现在我们知道如何使用这个图表了,这次让我们修改它,不是用数据,而是用pygal库中包含的参数、方法和主题。
参数
我们已经在整本书中看到了与 pygal 相关的章节中使用的参数。在这里,我们将从基于地区的法国图表开始,但首先让我们继续并填写图表上的其余地区。将以下代码复制到您选择的编辑器中,并呈现图表:
# -*- coding: utf-8 -*-
import pygal
france_chart = pygal.FrenchMap_Regions()
france_chart.title = 'Sample Regions'
france_chart.add('Alsace', ['42'])
france_chart.add('Aquitaine', ['72'])
france_chart.add('Auvergne', ['83'])
france_chart.add('Brittany', ['53'])
france_chart.add('Burgundy', ['26'])
france_chart.add('Centre', ['24'])
france_chart.add('Champagne-Ardenne', ['21'])
france_chart.add(unicode('Franche-Comté', 'utf-8'), ['43'])
france_chart.add(unicode('Île-de-France', 'utf-8'), ['11'])
france_chart.add('Languedoc-Roussillon', ['91'])
france_chart.add('Limousin', ['74'])
france_chart.add('Lorraine', ['41'])
france_chart.add('Lower Normandy', ['25'])
france_chart.add(unicode('Midi-Pyrénées', 'utf-8'), ['73'])
france_chart.add('Nord-Pas-de-Calais', ['31'])
france_chart.add('Pays de la Loire', ['52'])
france_chart.add('Picardy', ['22'])
france_chart.add('Poitou-Charentes', ['54'])
france_chart.add(unicode('Provence-Alpes-Côte d\'Azur', 'utf-8'), ['93'])
france_chart.add(unicode('Rhône-Alpes', 'utf-8'), ['83'])
france_chart.add('Upper Normandy', ['23'])
france_chart.add('Corsica', ['94'])
france_chart.add('French Guiana', ['03'])
france_chart.add('Guadeloupe', ['01'])
france_chart.add('Mayotte', ['05'])
france_chart.add('Reunion', ['04'])
france_chart.render_to_file('france_map.svg')
当完成后,你应该会看到如下截图所示:

在之前的截图中,我们可以看到我们传说中相当多的区域。请注意它们在我们的图表中占据了多少空间。让我们修改一些格式化参数来清理它。
底部的传说
我们可以使用legend_at_bottom参数重新定位图表的图例,并输入一个值作为True或False。这里有一个例子和输出屏幕。注意FrenchMap_Regions()方法中的legend_at_bottom参数:
# -*- coding: utf-8 -*-
import pygal
france_chart = pygal.FrenchMap_Regions(legend_at_bottom=True)
france_chart.title = 'Sample Regions'
france_chart.add('Alsace', ['42'])
france_chart.add('Aquitaine', ['72'])
france_chart.add('Auvergne', ['83'])
france_chart.add('Brittany', ['53'])
france_chart.add('Burgundy', ['26'])
france_chart.add('Centre', ['24'])
france_chart.add('Champagne-Ardenne', ['21'])
france_chart.add(unicode('Franche-Comté', 'utf-8'), ['43'])
france_chart.add(unicode('Île-de-France', 'utf-8'), ['11'])
france_chart.add('Languedoc-Roussillon', ['91'])
france_chart.add('Limousin', ['74'])
france_chart.add('Lorraine', ['41'])
france_chart.add('Lower Normandy', ['25'])
france_chart.add(unicode('Midi-Pyrénées', 'utf-8'), ['73'])
france_chart.add('Nord-Pas-de-Calais', ['31'])
france_chart.add('Pays de la Loire', ['52'])
france_chart.add('Picardy', ['22'])
france_chart.add('Poitou-Charentes', ['54'])
france_chart.add(unicode('Provence-Alpes-Côte d\'Azur', 'utf-8'), ['93'])
france_chart.add(unicode('Rhône-Alpes', 'utf-8'), ['83'])
france_chart.add('Upper Normandy', ['23'])
france_chart.add('Corsica', ['94'])
france_chart.add('French Guiana', ['03'])
france_chart.add('Guadeloupe', ['01'])
france_chart.add('Mayotte', ['05'])
france_chart.add('Reunion', ['04'])
france_chart.render_to_file('france_map.svg')
下面的截图显示了我们脚本的结果:

图例设置
我们还可以使用允许整数值的legend_box_size参数来和格式化图例框;这将改变每个图例项的彩色框尺寸。这里有一个例子:
# -*- coding: utf-8 -*-
import pygal
france_chart = pygal.FrenchMap_Regions(legend_at_bottom=True, legend_box_size=3)
france_chart.title = 'Sample Regions'
france_chart.add('Alsace', ['42'])
france_chart.add('Aquitaine', ['72'])
france_chart.add('Auvergne', ['83'])
france_chart.add('Brittany', ['53'])
france_chart.add('Burgundy', ['26'])
france_chart.add('Centre', ['24'])
france_chart.add('Champagne-Ardenne', ['21'])
france_chart.add(unicode('Franche-Comté', 'utf-8'), ['43'])
france_chart.add(unicode('Île-de-France', 'utf-8'), ['11'])
france_chart.add('Languedoc-Roussillon', ['91'])
france_chart.add('Limousin', ['74'])
france_chart.add('Lorraine', ['41'])
france_chart.add('Lower Normandy', ['25'])
france_chart.add(unicode('Midi-Pyrénées', 'utf-8'), ['73'])
france_chart.add('Nord-Pas-de-Calais', ['31'])
france_chart.add('Pays de la Loire', ['52'])
france_chart.add('Picardy', ['22'])
france_chart.add('Poitou-Charentes', ['54'])
france_chart.add(unicode('Provence-Alpes-Côte d\'Azur', 'utf-8'), ['93'])
france_chart.add(unicode('Rhône-Alpes', 'utf-8'), ['83'])
france_chart.add('Upper Normandy', ['23'])
france_chart.add('Corsica', ['94'])
france_chart.add('French Guiana', ['03'])
france_chart.add('Guadeloupe', ['01'])
france_chart.add('Mayotte', ['05'])
france_chart.add('Reunion', ['04'])
france_chart.render_to_file('france_map.svg')
下面的截图显示了我们脚本的结果:

查看了解我们如何使用legend_at_bottom和legend_box_size参数。我们可以在参数之间使用逗号来堆叠它们,并允许多种组合。我们仍然有过多的标签渗进下一列的图例中;让我们继续用这些其他属性来调整它们。
在接下来的几个代码示例中,我将删除数据集,以便我们能够专注于理解参数。如果您需要重新注册,请参考我们在参数部分的初始图表。现在,让我们缩小标签的字体大小,并使用legend_font_size参数调整框的大小:
# -*- coding: utf-8 -*-
import pygal
france_chart = pygal.FrenchMap_Regions(legend_at_bottom=True, legend_box_size=8, legend_font_size=8)
# ///Data-sets (continued)
以下屏幕截图显示了脚本的结果:

很好!现在有一个传说项目还是有点小;让我们使用truncate_legend参数将和一起修剪以保持一致性,如下代码所示:
# -*- coding: utf-8 -*-
import pygal
france_chart = pygal.FrenchMap_Regions(legend_at_bottom=True, legend_box_size=8, legend_font_size=8, truncate_legend=6)
下面的截图显示了我们脚本的结果:

干得好!这是现在看起来更和更有条理。我们甚至可以通过移除或将其设置为False来禁用legend_at_bottom参数。它看起来像下面截图中显示的内容:

关于传说还有一点要注意的是,如果你不需要它们,你可以禁用它们。请记住,图表图例有助于图表用户使用数据。由于我们没有 x 和 y 轴标签,我们应该可以禁用图例。为此,您只需将show_legend参数设置为False,如下代码所示:
# -*- coding: utf-8 -*-
import pygal
france_chart = pygal.FrenchMap_Regions(show_legend=False, legend_box_size=8, legend_font_size=8, truncate_legend=6)
下面的截图显示了我们的脚本的结果,没有图例:

太棒了!现在,我们已经修改了图例,以允许为我们的图表提供更大的灵活性。
标签设置
让我们看看一个传统的折线图,我们可以在其中使用标签。这是一个简单的图表,包含一些虚拟数据集。将代码复制到您选择的编辑器中并运行脚本。您的输出应该是下一个截图中显示的内容。
您也可以使用与图例类似的参数来设置标签设置。让我们建立一个简单的折线图,我们可以看到我们的变化。将以下代码添加到您选择的编辑器中并运行脚本。务必将输出保存为lineparam.svg:
# -*- coding: utf-8 -*-
import pygal
param_line_chart = pygal.Line()
param_line_chart.title = 'Parameter Line Chart'
param_line_chart.x_labels = map(str, ["Data Object 1", "Data Object 2", "Data Object 3", "Data Object 4", "Data Object 5", "Data Object 6"])
param_line_chart.add('Data-Set 1', [8, 16, 24, 32, 48, 56])
param_line_chart.add('Data-Set 2', [2, 4, 6, 8, 10, 12])
param_line_chart.add('Data-Set 3', [1, 3, 5, 7, 9, 12])
param_line_chart.render_to_file('lineparam.svg')
下面的截图显示了我们脚本的结果:

折线图有一些具体的参数,比如第三章、pygal入门中提到的fill=true。您也可以使用label_font_size参数调整指定标签的大小,如以下代码所示:
# -*- coding: utf-8 -*-
import pygal
param_line_chart = pygal.Line(fill=True, label_font_size=20)
param_line_chart.title = 'Parameter Line Chart'
param_line_chart.x_labels = map(str, ["Data Object 1", "Data Object 2", "Data Object 3", "Data Object 4", "Data Object 5", "Data Object 6"])
param_line_chart.add('Data-Set 1', [8, 16, 24, 32, 48, 56])
param_line_chart.add('Data-Set 2', [2, 4, 6, 8, 10, 12])
param_line_chart.add('Data-Set 3', [1, 3, 5, 7, 9, 12])
param_line_chart.render_to_file('lineparam.svg')
下面的截图显示了我们脚本的结果:

我们可以使用x_label_rotation参数旋转标签,并使用stroke=false参数一起移除线条,如下代码所示:
# -*- coding: utf-8 -*-
import pygal
param_line_chart = pygal.Line(fill=False, stroke=False, label_font_size=20, x_label_rotation=50)
param_line_chart.title = 'Parameter Line Chart'
param_line_chart.x_labels = map(str, ["Data Object 1", "Data Object 2", "Data Object 3", "Data Object 4", "Data Object 5", "Data Object 6"])
param_line_chart.add('Data-Set 1', [8, 16, 24, 32, 48, 56])
param_line_chart.add('Data-Set 2', [2, 4, 6, 8, 10, 12])
param_line_chart.add('Data-Set 3', [1, 3, 5, 7, 9, 12])
param_line_chart.render_to_file('lineparam.svg')
下面的截图显示了我们脚本的结果:

折线图有一个特定的参数interpolate='cubic',它允许线条在数据中弯曲。这里有一个例子:
# -*- coding: utf-8 -*-
import pygal
param_line_chart = pygal.Line(interpolate='cubic', label_font_size=20, x_label_rotation=50)
param_line_chart.title = 'Parameter Line Chart'
param_line_chart.x_labels = map(str, ["Data Object 1", "Data Object 2", "Data Object 3", "Data Object 4", "Data Object 5", "Data Object 6"])
param_line_chart.add('Data-Set 1', [8, 16, 24, 32, 48, 56])
param_line_chart.add('Data-Set 2', [2, 4, 6, 8, 10, 12])
param_line_chart.add('Data-Set 3', [1, 3, 5, 7, 9, 12])
param_line_chart.render_to_file('lineparam.svg')
下面的截图显示了我们脚本的结果:

图表标题设置
我们对过去两章的图表标题相当熟悉,但是很高兴知道我们也可以给 x 和 y 轴命名。看看下面的代码:
# -*- coding: utf-8 -*-
import pygal
param_line_chart = pygal.Line(interpolate='cubic', label_font_size=20, x_label_rotation=50)
param_line_chart.title = 'Parameter Line Chart'
param_line_chart.x_title='Data-Sets (X Axis)'
param_line_chart.y_title='Values (Y Axis)'
param_line_chart.x_labels = map(str, ["Data Object 1", "Data Object 2", "Data Object 3", "Data Object 4", "Data Object 5", "Data Object 6"])
param_line_chart.add('Data-Set 1', [8, 16, 24, 32, 48, 56])
param_line_chart.add('Data-Set 2', [2, 4, 6, 8, 10, 12])
param_line_chart.add('Data-Set 3', [1, 3, 5, 7, 9, 12])
param_line_chart.render_to_file('lineparam.svg')
下面的截图显示了我们脚本的结果:

我们可以使用title_font_size、x_title_font_size和y_title_font_size参数来调整 x、y 和图表标题的字体大小,如下代码所示:
# -*- coding: utf-8 -*-
import pygal
param_line_chart = pygal.Line(interpolate='cubic', label_font_size=20, x_label_rotation=50, title_font_size=24, x_title_font_size=24, y_title_font_size=24)
param_line_chart.title = 'Parameter Line Chart'
param_line_chart.x_title='Data-Sets (X Axis)'
param_line_chart.y_title='Values (Y Axis)'
param_line_chart.x_labels = map(str, ["Data Object 1", "Data Object 2", "Data Object 3", "Data Object 4", "Data Object 5", "Data Object 6"])
param_line_chart.add('Data-Set 1', [8, 16, 24, 32, 48, 56])
param_line_chart.add('Data-Set 2', [2, 4, 6, 8, 10, 12])
param_line_chart.add('Data-Set 3', [1, 3, 5, 7, 9, 12])
param_line_chart.render_to_file('lineparam.svg')
下面的截图显示了我们脚本的结果:

不显示数据
一个值得关注的参数是no_data_text参数,它允许我们为图表设置一个文本覆盖,以防我们在没有加载数据的情况下创建图表。如果我们正在构建一个动态图表,通过文件或命令行参数从数据源(在线或本地)提取数据,这将非常有用。下面是no_data_text参数的一个例子:
# -*- coding: utf-8 -*-
import pygal
param_line_chart = pygal.Line(no_data_text='Unable to load data')
param_line_chart.title = 'Parameter Line Chart'
param_line_chart.x_title='Data-Sets (X Axis)'
param_line_chart.y_title='Values (Y Axis)'
param_line_chart.x_labels = map(str, ["Data Object 1", "Data Object 2", "Data Object 3", "Data Object 4", "Data Object 5", "Data Object 6"])
#param_line_chart.add('Data-Set 1', [8, 16, 24, 32, 48, 56])
#param_line_chart.add('Data-Set 2', [2, 4, 6, 8, 10, 12])
#param_line_chart.add('Data-Set 3', [1, 3, 5, 7, 9, 12])
param_line_chart.render_to_file('lineparam.svg')
下面的截图显示了我们脚本的结果:

皮格尔主题
到目前为止,我们已经为 pygal 图表使用了默认主题,简称为默认。然而,pygal 提供了 14 个预先构建的主题。让我们用另一个名为霓虹的主题来更新我们的折线图代码:
# -*- coding: utf-8 -*-
import pygal
from pygal.style import NeonStyle
param_line_chart = pygal.Line(interpolate='cubic', fill=True, style=NeonStyle)
param_line_chart.title = 'Parameter Line Chart'
param_line_chart.x_title='Data-Sets (X Axis)'
param_line_chart.y_title='Values (Y Axis)'
param_line_chart.x_labels = map(str, ["Data Object 1", "Data Object 2", "Data Object 3", "Data Object 4", "Data Object 5", "Data Object 6"])
param_line_chart.add('Data-Set 1', [8, 16, 24, 32, 48, 56])
param_line_chart.add('Data-Set 2', [2, 4, 6, 8, 10, 12])
param_line_chart.add('Data-Set 3', [1, 3, 5, 7, 9, 12])
param_line_chart.render_to_file('lineparam.svg')
下面的截图显示了我们脚本的结果:

如我们所见,霓虹风格类似于默认风格;但是,与我们早期使用默认样式的fill参数相比,Neon 样式的fill参数应用了轻微的透明度。同样,如果我们检查我们的代码,我们会看到样式只是一个应用于图表实例的参数,在所选样式的名称后面有一个后缀样式;有关 pygal 主题的完整列表,请查看位于http://pygal.org/builtin_styles/的主题框架文档。
另外,看一下前面代码中import pygal下面的行,它是从pygal.style import NeonStyle行读取的。默认情况下,内置样式不包含在我们的import pygal语句中,所以我们需要添加它们,在这种情况下,我们指定要导入NeonStyle主题。让我们使用红蓝主题尝试一种更轻的风格,并覆盖我们的NeonStyle,在我们的import和style参数中,如下代码所示:
# -*- coding: utf-8 -*-
import pygal
from pygal.style import RedBlueStyle
param_line_chart = pygal.Line(interpolate='cubic', fill=True, style=RedBlueStyle)
param_line_chart.title = 'Parameter Line Chart'
param_line_chart.x_title='Data-Sets (X Axis)'
param_line_chart.y_title='Values (Y Axis)'
param_line_chart.x_labels = map(str, ["Data Object 1", "Data Object 2", "Data Object 3", "Data Object 4", "Data Object 5", "Data Object 6"])
param_line_chart.add('Data-Set 1', [8, 16, 24, 32, 48, 56])
param_line_chart.add('Data-Set 2', [2, 4, 6, 8, 10, 12])
param_line_chart.add('Data-Set 3', [1, 3, 5, 7, 9, 12])
param_line_chart.render_to_file('lineparam.svg')
以下截图显示了我们脚本的结果:

看起来不错!如果你想找到一个特定的主题,请参考 pygal 的网站,获取关于 http://pygal.org/styles/ T2 主题的完整文档。您也可以在http://cabaret.pygal.org/使用 pygal 的风格工具在线测试主题。
总结
在这一章中,我们已经介绍了很多关于 pygal 的内容,我们应该能够很好地处理 pygal 构建图表,并根据我们的需要对它们进行修改。在下一章中,我们将从图表中休息一下,开始处理构建动态图表的数据方面。
六、导入动态数据
既然我们现在已经了解了如何使用pygal库以及构建图表和图形,现在是开始考虑使用 Python 构建应用的时候了。
在本章中,我们将了解从网络中提取数据、解析数据、将其添加到我们的代码库中并将数据格式化为可用格式的基础知识,我们还将了解如何将这些基础知识应用到我们的 Python 代码中。我们还将讨论解析 XML 和 JSON 数据。
从网络上获取数据
对于许多非开发人员来说,开发人员能够神奇地从在线资源中提取数据,并将其与 iPhone 应用或 Windows Store 应用集成,或者将数据提取到能够根据请求生成各种版本数据的云资源中,这似乎是一种巫术。
平心而论,他们确实有一个大致的了解;数据从网络中提取,并格式化到他们选择的应用中。他们可能无法完全了解流程工作流是如何发生的。一些开发人员的情况也是如此——许多开发人员主要从事一种技术,这种技术只能在锁定的环境中工作,或者一般情况下,他们的应用不使用互联网。再次,他们理解背后的逻辑;不知何故,一个 RSS 源被拉进了一个应用。
在许多语言中,相同的任务以不同的方式完成,通常取决于使用哪种语言。让我们看几个例子,使用 Packt 自己的新闻 RSS 提要,使用 iOS 应用通过 Objective-C 拉入数据。
现在,如果您正在阅读这篇文章,并且不熟悉 Objective-C,没关系,重要的是我们在 iPhone 应用中显示了一个 XML 文件的内部 XML 内容:
#import "ViewController.h"
@interfaceViewController ()
@property (weak, nonatomic) IBOutletUITextView *output;
@end
@implementationViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSURL *packtURL = [NSURLURLWithString:@"http://www.packtpub.com/rss.xml"];
NSURLRequest *request = [NSURLRequestrequestWithURL:packtURL];
NSURLConnection *connection = [[NSURLConnectionalloc] initWithRequest:requestdelegate:selfstartImmediately:YES];
[connection start];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSString *downloadstring = [[NSStringalloc] initWithData:dataencoding:NSUTF8StringEncoding];
[self.outputsetText:downloadstring];
}
- (void)didReceiveMemoryWarning
{
[superdidReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
在这里,我们可以在 iPhone 模拟器中看到,我们的 XML 输出是通过 HTTP 从 Web 动态拉到我们的 iPhone 模拟器的。这就是我们想用 Python 开始做的事情:

XML 复习
可扩展标记语言 ( XML )是一种数据标记语言,它为数据组设置一系列规则和层次结构,数据组存储为静态文件。通常,服务器会定期更新网络上的这些 XML 文件,以便重新用作数据源。XML 非常容易理解,因为它类似于 HTML。在这种情况下,您可以从文档声明开始:
<?xml version="1.0" encoding="utf-8"?>
接下来,设置一个根节点。节点就像一个 HTML 标记(也称为节点)。通过节点名称周围的括号,可以看出它是一个节点。例如,这里有一个名为root的节点:
<root></root>
请注意,我们通过用反斜杠创建同名节点来关闭节点。我们还可以给节点添加参数并赋值,如下图root节点所示:
<root parameter="value"></root>
XML 中的数据是通过层次结构设置的。为了声明这个层次结构,我们创建了另一个节点,并将它放在父节点中,如下面的代码所示:
<root parameter="value">
<subnode>Subnode's value</subnode>
</root>
在前面的父节点中,我们创建了一个subnode。在subnode里面,我们有一个内在的价值叫做Subnode's value。现在,用编程术语来说,从 XML 数据文件中获取数据是一个叫做解析的过程。通过解析,我们可以指定在 XML 结构中的什么位置获取特定的值;例如,我们可以抓取 XML 结构并获取内部内容,如下所示:
/root/subnode
这通常被称为 XPath 语法,一种跨语言的浏览 XML 文件的方式。有关 XML 和 XPath 的更多信息,请分别查看:http://www.w3.org/TR/REC-xml/和此处http://www.w3.org/TR/xpath/的完整规范。
RSS 和 ATOM
真正简单的联合 ( RSS )只是 XML 的一个变种。RSS 是一个规范,它定义了发送数据常用的特定节点。通常,许多博客订阅源都包含一个 RSS 选项,供用户从这些网站上获取最新信息。RSS 中使用的一些节点包括rss、channel、item、title、description、pubDate、link和GUID。
从从网络获取数据部分查看本章中我们的 iPhone 示例,我们可以看到典型的 RSS 结构需要什么。RSS 提要通常很容易被发现,因为规范要求根节点被命名为rss才能成为真正的 RSS 文件。
在某些情况下,一些网站和服务会使用.rss而不是.xml;这通常很好,因为大多数 RSS 内容的读者会像 XML 文件一样拉入 RSS 数据,就像在 iPhone 示例中一样。
另一种形式的 XML 叫做原子。ATOM 是另一个类似于 RSS 的规范,但是比 RSS 发展得晚得多。正因为如此,ATOM 比 RSS 有更多的特性:XML 命名空间、指定的内容格式(视频或音频特定的 URL)、对国际化的支持以及多语言支持,仅举几个例子。
与 RSS 相比,ATOM 确实有一些不同的节点;例如,RSS 源的根节点将是<rss>。ATOM 的词根以<feed>开头,所以很容易看出区别。另一个区别是 ATOM 也可以以.atom或.xml结尾。
有关 RSS 和 ATOM 规范的更多信息,请查看以下网站:
理解 HTTP
所有这些来自 Packt Publishing 网站的 RSS 提要的样本都显示了一个使用的共性,不管编码在其中的技术如何,那就是用于下拉这些静态文件的方法是通过超文本传输协议 ( HTTP )。HTTP 是互联网交流的基础。这是一个包含两种不同类型请求的协议:数据请求或GET和数据推送,称为POST。
通常,当我们使用 HTTP 下载数据时,我们使用 HTTP 的GET方法来下拉数据。如果我们提到一个特定的类型,GET请求将返回一个字符串或另一个数据类型。我们可以直接使用这个值,也可以保存到一个变量中。
通过POST请求,我们将值发送给处理任何传入值的服务;假设我们创建了一个新的博客文章的标题,并需要添加到当前标题的列表中,一种常见的方法是使用 URL 参数。网址参数是一个现有的网址,但带有后缀键值对。
以下是带有网址参数的POST请求的模拟示例:
http://www.yourwebsite.com/blogtitles/?addtitle=Your%20New%20Title
如果我们的服务设置正确,将扫描POST对addtitle键的请求并读取值,在本例中为:Your New Title。对于我们的请求,我们可能会在标题中注意到%20。这是一个转义字符,允许我们发送一个带有空格的值;在这种情况下,%20在我们的价值观中是一个空间的定位。
对于本书的其余部分,我们将坚持GET请求,因为我们将只从网络上读取数据;但是,这将为您提供开始处理请求的概述。
在 Python 中使用 HTTP
来自 packkt Publishing 网站的 RSS 样本显示了我们在 HTTP 中工作时在编程中使用的一些共性;一个是我们总是考虑连接可能出现问题的可能性,并且我们总是在完成后关闭我们的请求。下面是一个例子,说明如何使用名为urllib2的内置库在 Python 中完成相同的 RSS 提要请求:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib2
try:
#Open the file via HTTP.
response = urllib2.urlopen('http://www.packtpub.com/rss.xml')
#Read the file to a variable we named 'xml'
xml = response.read()
#print to the console.
print(xml)
#Finally, close our open network.
response.close()
except:
#If we have an issue show a message and alert the user.
print('Unable to connect to RSS...')
如果我们查看下面的控制台输出,我们可以看到 XML 内容,就像我们在 iOS 代码示例中看到的一样:

在这个例子中,注意到我们把我们的 HTTP 请求包装在一个try except块周围。对于来自另一种语言的人来说,except可以被认为是与catch相同的语句。这使我们能够检测是否发生了错误,这可能是不正确的网址或缺乏连接,例如,以编程方式为我们的 Python 脚本设置一个替代结果。
用 HTTP 解析 Python 中的 XML
有了这些例子包括我们的 Python 版本的脚本,它仍然返回某种类型的字符串,这本身对于从完整字符串中获取值没有多大用处。为了从通过 HTTP 提取的 XML 中获取特定的字符串和值,我们需要解析它。幸运的是,Python 主库中为此有一个内置对象,称为ElementTree,它是 Python 中 XML 库的一部分。
让我们将ElementTree结合到我们的示例中,看看它是如何工作的:
# -*- coding: utf-8 -*-
import urllib2
from xml.etree import ElementTree
try:
#Open the file via HTTP.
response = urllib2.urlopen('http://www.packtpub.com/rss.xml')
tree = ElementTree.parse(response)
root = tree.getroot()
#Create an 'Element' group from our XPATH using findall.
news_post_title = root.findall("channel//title")
#Iterate in all our searched elements and print the inner text for each.
for title in news_post_title:
print title.text
#Finally, close our open network.
response.close()
except Exception as e:
#If we have an issue show a message and alert the user.
print(e)
下面的截图显示了我们脚本的结果:

正如我们看到的,我们的输出显示了每个博客文章的每个标题。注意我们如何使用channel//item进行我们的findall()方法。这个使用的是 XPath,它允许我们以速记的方式书写如何迭代一个 XML 结构。它是这样工作的;让我们以http://www.packtpub.com饲料为例。我们有一个根,然后是通道,然后是一系列标题元素。
findall()方法找到每个元素,并将它们保存为 Python 中使用的特定于 XML 库ElementTree的Element类型,并将每个元素保存到一个数组中。然后我们可以使用for in循环迭代每一个,并使用文本property将内部文本打印到Element类型。
注
你可能注意到在最近的例子中,我用一点额外的代码改变了except并增加了Exception as e。这允许我们帮助调试问题,并将它们打印到控制台,或者向用户显示更深入的反馈。一个Exception允许 Python 库内置的警告和错误通过控制台打印出来或者用代码处理。它们也有我们可以使用的更具体的异常,例如IOException,它是专门用于文件读写的。
关于 JSON
现在,另一种在处理 web 数据时变得越来越常见的数据类型是 JSON 。JSON 是 JavaScript 对象符号的缩写,顾名思义,确实是真正的 JavaScript。近年来,随着移动应用的兴起和 丰富的互联网应用 ( RIA )的出现,它变得流行起来。
JSON 对于 JavaScript 开发人员来说非常棒;与 XML 相比,在 HTML 内容中工作更容易。正因为如此,JSON 正在成为网络和移动应用开发中更常见的数据类型。
用 HTTP 解析 Python 中的 JSON
用 Python 解析 JSON 数据是一个非常相似的过程;然而,在这个的例子中,我们的ElementTree库是不需要的,因为只对 XML 起作用。我们需要一个库,用来使用 Python 解析 JSON。幸运的是,Python 库的创建者已经为我们准备了一个库,简称json。
让我们使用json导入构建一个类似于我们的 XML 代码的例子;当然,我们需要使用不同的数据源,因为我们不会使用 XML。我们可能注意到的一件事是,可以使用的公共 JSON 提要并不多,其中许多提要需要使用代码,该代码允许开发人员通过开发人员应用编程接口(如推特的 JSON 应用编程接口)生成 JSON 提要。为了避免这种情况,我们将使用谷歌图书应用编程接口的样本网址,该网址将显示《傲慢与偏见》的演示数据《T2》、《简·奥斯汀》的演示数据《T4》。我们可以通过输入以下网址来查看 JSON (或下载文件):
https://www.googleapis.com/books/v1/volumes/s1gVAAAAYAAJ
注
请注意,该应用编程接口使用 HTTPS,许多 JSON 应用编程接口正在转向传输数据的安全方法,所以一定要将安全包含在 HTTP 中,与 HTTPS 一起。
让我们来看看 JSON 的输出:
{
"kind": "books#volume",
"id": "s1gVAAAAYAAJ",
"etag": "yMBMZ85ENrc",
"selfLink": "https://www.googleapis.com/books/v1/volumes/s1gVAAAAYAAJ",
"volumeInfo": {
"title": "Pride and Prejudice",
"authors": [
"Jane Austen"
],
"publisher": "C. Scribner's Sons",
"publishedDate": "1918",
"description": "Austen's most celebrated novel tells the story of Elizabeth Bennet, a bright, lively young woman with four sisters, and a mother determined to marry them to wealthy men. At a party near the Bennets' home in the English countryside, Elizabeth meets the wealthy, proud Fitzwilliam Darcy. Elizabeth initially finds Darcy haughty and intolerable, but circumstances continue to unite the pair. Mr. Darcy finds himself captivated by Elizabeth's wit and candor, while her reservations about his character slowly vanish. The story is as much a social critique as it is a love story, and the prose crackles with Austen's wry wit.",
"readingModes": {
"text": true,
"image": true
},
"pageCount": 401,
"printedPageCount": 448,
"dimensions": {
"height": "18.00 cm"
},
"printType": "BOOK",
"averageRating": 4.0,
"ratingsCount": 433,
"contentVersion": "1.1.5.0.full.3",
"imageLinks": {
"smallThumbnail": "http://bks8.books.google.com/books?id=s1gVAAAAYAAJ&printsec=frontcover&img=1&zoom=5&edge=curl&imgtk=AFLRE73F8btNqKpVjGX6q7V3XS77QA2PftQUxcEbU3T3njKNxezDql_KgVkofGxCPD3zG1yq39u0XI8s4wjrqFahrWQ-5Epbwfzfkoahl12bMQih5szbaOw&source=gbs_api",
"thumbnail": "http://bks8.books.google.com/books?id=s1gVAAAAYAAJ&printsec=frontcover&img=1&zoom=1&edge=curl&imgtk=AFLRE70tVS8zpcFltWh_7K_5Nh8BYugm2RgBSLg4vr9tKRaZAYoAs64RK9aqfLRECSJq7ATs_j38JRI3D4P48-2g_k4-EY8CRNVReZguZFMk1zaXlzhMNCw&source=gbs_api",
"small": "http://bks8.books.google.com/books?id=s1gVAAAAYAAJ&printsec=frontcover&img=1&zoom=2&edge=curl&imgtk=AFLRE71qcidjIs37x0jN2dGPstn6u2pgeXGWZpS1ajrGgkGCbed356114HPD5DNxcR5XfJtvU5DKy5odwGgkrwYl9gC9fo3y-GM74ZIR2Dc-BqxoDuUANHg&source=gbs_api",
"medium": "http://bks8.books.google.com/books?id=s1gVAAAAYAAJ&printsec=frontcover&img=1&zoom=3&edge=curl&imgtk=AFLRE73hIRCiGRbfTb0uNIIXKW4vjrqAnDBSks_ne7_wHx3STluyMa0fsPVptBRW4yNxNKOJWjA4Od5GIbEKytZAR3Nmw_XTmaqjA9CazeaRofqFskVjZP0&source=gbs_api",
"large": "http://bks8.books.google.com/books?id=s1gVAAAAYAAJ&printsec=frontcover&img=1&zoom=4&edge=curl&imgtk=AFLRE73mlnrDv-rFsL-n2AEKcOODZmtHDHH0QN56oG5wZsy9XdUgXNnJ_SmZ0sHGOxUv4sWK6GnMRjQm2eEwnxIV4dcF9eBhghMcsx-S2DdZoqgopJHk6Ts&source=gbs_api",
"extraLarge": "http://bks8.books.google.com/books?id=s1gVAAAAYAAJ&printsec=frontcover&img=1&zoom=6&edge=curl&imgtk=AFLRE73KIXHChsznTbrXnXDGVs3SHtYpl8tGncDPX_7GH0gd7sq7SA03aoBR0mDC4-euzb4UCIDiDNLYZUBJwMJxVX_cKG5OAraACPLa2QLDcfVkc1pcbC0&source=gbs_api"
},
"language": "en",
"previewLink": "http://books.google.com/books?id=s1gVAAAAYAAJ&hl=&source=gbs_api",
"infoLink": "http://books.google.com/books?id=s1gVAAAAYAAJ&hl=&source=gbs_api",
"canonicalVolumeLink": "http://books.google.com/books/about/Pride_and_Prejudice.html?hl=&id=s1gVAAAAYAAJ"
},
"layerInfo": {
"layers": [
{
"layerId": "geo",
"volumeAnnotationsVersion": "6"
}
]
},
"saleInfo": {
"country": "US",
"saleability": "FREE",
"isEbook": true,
"buyLink": "http://books.google.com/books?id=s1gVAAAAYAAJ&hl=&buy=&source=gbs_api"
},
"accessInfo": {
"country": "US",
"viewability": "ALL_PAGES",
"embeddable": true,
"publicDomain": true,
"textToSpeechPermission": "ALLOWED",
"epub": {
"isAvailable": true,
"downloadLink": "http://books.google.com/books/download/Pride_and_Prejudice.epub?id=s1gVAAAAYAAJ&hl=&output=epub&source=gbs_api"
},
"pdf": {
"isAvailable": true,
"downloadLink": "http://books.google.com/books/download/Pride_and_Prejudice.pdf?id=s1gVAAAAYAAJ&hl=&output=pdf&sig=ACfU3U3dQw5JDWdbVgk2VRHyDjVMT4oIaA&source=gbs_api"
},
"webReaderLink": "http://books.google.com/books/reader?id=s1gVAAAAYAAJ&hl=&printsec=frontcover&output=reader&source=gbs_api",
"accessViewStatus": "FULL_PUBLIC_DOMAIN",
"quoteSharingAllowed": false
}
}
JSON 的一个缺点是很难读取复杂的数据。因此,如果我们运行一个复杂的 JSON 提要,我们可以使用 JSON 可视化工具可视化它。Visual Studio 在其所有版本中包含一个,一个网络搜索还将显示多个在线网站,您可以在其中粘贴 JSON,并且将显示一个易于理解的数据结构。这里有一个使用http://jsonviewer.stack.hu/加载我们的示例 JSON 网址的例子:

接下来,让我们使用我们的urllib2库来重用我们现有的一些 Python 代码,以请求我们的 JSON 提要,然后我们将使用 Python 的 JSON 库来解析它。让我们从 JSON(根)节点开始解析本书的volumeInfo节点,接下来是volumeInfo作为子节点。下面是我们在 XML 部分的例子,使用 JSON 解析所有子元素:
# -*- coding: utf-8 -*-
import urllib2
import json
try:
#Set a URL variable.
url = 'https://www.googleapis.com/books/v1/volumes/s1gVAAAAYAAJ'
#Open the file via HTTP.
response = urllib2.urlopen(url)
#Read the request as one string.
bookdata = response.read()
#Convert the string to a JSON object in Python.
data = json.loads(bookdata)
for r in data ['volumeInfo']:
print r
#Close our response.
response.close()
except:
#If we have an issue show a message and alert the user.
print('Unable to connect to JSON API...')
这是我们的输出。应该匹配volumeInfo的子节点,在输出画面中匹配,如下图所示:

干得好!现在,让我们抓住title的值。看看下面的例子,注意我们有两个括号:一个代表volumeInfo,另一个代表title。这类似于导航我们的 XML 层次结构:
# -*- coding: utf-8 -*-
import urllib2
import json
try:
#Set a URL variable.
url = 'https://www.googleapis.com/books/v1/volumes/s1gVAAAAYAAJ'
#Open the file via HTTP.
response = urllib2.urlopen(url)
#Read the request as one string.
bookdata = response.read()
#Convert the string to a JSON object in Python.
data = json.loads(bookdata)
print data['volumeInfo']['title']
#Close our response.
response.close()
except Exception as e:
#If we have an issue show a message and alert the user.
#'Unable to connect to JSON API...'
print(e)
以下截图展示了我们脚本的结果:

正如你在前面的截图中看到的,我们返回一行,其中Pride and Prejudice是从我们的 JSON 数据中解析出来的。
关于 JSONP
JSONP 、或者带 Padding 的 JSON,其实是 JSON,但是设置和传统的 JSON 文件不同。JSONP 是 web 跨浏览器脚本编写的一种变通方法。一些网络服务可以提供 JSONP,而不是纯 JSON JavaScript 文件。问题是 JSONP 与许多基于 JSON Python 的解析器不兼容,包括这里介绍的解析器,所以您会希望尽可能避免 JSONP 风格的 JSON。
那么如何才能发现 JSONP 文件呢;他们有不同的分机吗?不,它只是 JSON 数据的包装器;这里有一个没有 JSONP 的例子:
/*
*Regular JSON
*/
{ authorname: 'Chad Adams' }
The same example with JSONP:
/*
* JSONP
*/
callback({ authorname: 'Chad Adams' });
请注意,我们用函数包装器或回调包装了我们的 JSON 数据。通常情况下,这是我们的解析器中的漏洞,并表明这是一个 JSONP 格式的 JSON 文件。在 JavaScript 中,我们甚至可以用这样的代码来调用它:
/*
* Using JSONP in JavaScript
*/
callback = function (data) {
alert(data.authorname);
};
用 Python 实现 JSONP
如果需要,我们可以绕过 JSONP 数据源;这只是需要一点工作。在通过我们的 JSON 解析器运行字符串之前,我们可以使用 Python 中的str.replace()方法剥离回调。如果我们在我们的 JSON 解析器示例中解析我们的示例 JSONP 文件,字符串将如下所示:
#Convert the string to a JSON object in Python.
data = json.loads(bookdata.replace('callback(', '').) .replace(')', ''))
总结
在这一章中,我们介绍了从网络中提取字符串和数据的 HTTP 概念和方法。我们学习了如何使用 Python 使用urllib2库来做到这一点,并解析了 XML 数据和 JSON 数据。我们讨论了 JSON 和 JSONP 之间的区别,以及如果需要,如何围绕 JSONP 工作。
在下一章中,我们将使用动态 web 数据用pygal库构建一个工作简单的应用。
七、把这一切放在一起
我们一起经历了学习 Python 数据可视化开发、处理数据和使用pygal库创建图表的漫长过程。现在是时候将这些技能付诸实践了。本章的目标是利用我们的知识创建一个包含网络动态数据的图表。在本章中,我们将涵盖以下主题:
- 从网上导入 2 个月的 RSS 博客文章
- 为新的条形图数据集格式化我们的 RSS 数据
- 建立一个简单的条形图来显示过去两个月的博客文章,传递文章的数量
- 创建一个主应用脚本来处理执行,并将我们的代码分成模块,我们将把这些模块导入到主脚本中
博客的图表使用情况
我们将从 Packt Publishing 的 RSS 提要开始,并使用 RSS 提要中的数据创建一个图表。这个图表将具体包括一个月有多少文章发表;至此,我们已经熟悉了使用 HTTP 从 Web 上找到的位置解析 XML。然后,我们将从这个提要创建我们自己的数据集。
为此,我们需要执行以下任务:
- 找出一个月内有多少帖子
- 过滤每个月的帖子数
- 最后,根据这两个月的数据生成图表
像任何编程任务一样,无论是编写的容易性还是代码的可维护性,成功的关键都是分解完成工作所需的任务。为此,我们将把这个任务分解成 Python 模块。
整理我们的数据
我们的第一个任务是将来自 Packt Publishing 网站的 RSS 源拉进我们的 Python 代码中。为此,我们可以从第 6 章、导入动态数据中重用我们的 Python HTTP 和 XML 解析器示例,但是我们将从pubDate中抓取日期,而不是抓取每个title节点的标题。pubDate对象是一个 RSS 标准约定,用于在基于 XML 的 RSS 源中指示日期和时间。
我们从第六章、导入动态数据修改代码,抓取pubDate对象。我们将创建一个新的 Python 脚本文件,并将其称为LoadRssFeed.py。然后,我们将在我们选择的编辑器中使用这些代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib2
from xml.etree import ElementTree
try:
#Open the file via HTTP.
response = urllib2.urlopen('http://www.packtpub.com/rss.xml')
tree = ElementTree.parse(response)
root = tree.getroot()
#List of post dates.
news_post_date = root.findall("channel//pubDate")
'''Iterate in all our searched elements and print the inner text for each.'''
for date in news_post_date:
print(date.text)
#Finally, close our open network.
response.close()
except Exception as e:
#If we have an issue show a message and alert the user.
print(e)
请注意,我们现在不是找到所有标题,而是使用 XPath 路径channel//pubDate找到pubDate。我们还将约会对象的名单更新为news_post_date。这将有助于澄清我们的代码。让我们运行代码并查看结果:

望好;我们可以知道我们有一个结构化的日期和时间格式,现在我们可以根据字符串中的内容进行过滤,但是让我们对此进行更多的挖掘。像大多数语言一样,Python 也有一个time库,允许字符串转换成datetime Python 对象。我们如何判断这些值是否已经不是date的对象?让我们将print (date.text)方法包装在type函数中,以渲染对象的类型,而不是对象的输出,如下所示:print type(date.text)。让我们重新运行代码,看看结果:

将日期字符串转换为日期
在继续之前,我们需要将中的任何字符串转换成 Python 中可用的类型,例如,我们从 RSS 提要中获取发布日期。拥有一些已经制作好的功能来格式化或搜索我们的日期不是很好吗?嗯,Python 对此有一个内置类型叫做time。将字符串转换为time对象非常容易,但是首先,我们需要在代码的顶部添加time的import语句,例如import time。接下来,由于我们的pubDate节点不是我们可以轻松解析的多个字符串,所以让我们使用split()方法将它们拆分成一个数组。
我们将使用replace()方法删除字符串中的任何逗号。我们甚至可以运行它,我们的输出窗口将在每个pubDate周围显示括号,每个数组项之间用逗号隔开,表示我们成功地将单个字符串分割成字符串数组。
这里我们使用一个循环,循环中有一个从 RSS 源中提取的不同时间元素的列表:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib2
from xml.etree import ElementTree
try:
#Open the file via HTTP.
response = urllib2.urlopen('http://www.packtpub.com/rss.xml')
tree = ElementTree.parse(response)
root = tree.getroot()
#List of post dates.
news_post_date = root.findall("channel//pubDate")
print(news_post_date)
'''Iterate in all our searched elements and print the inner text for each.'''
for date in news_post_date:
'''Create a variable striping out commas, and generating a new array item for every space.'''
datestr_array = date.text.replace(',', '').split(' ')
'''Show each array in the Command Prompt(Terminal).'''
print(datestr_array)
#Finally, close our open network.
response.close()
except Exception as e:
'''If we have an issue show a message and alert the user.'''print(e)
在这里,当我们循环代码时,我们可以看到每个time对象的列表,月、日、年、时间等等;这将有助于获取与我们解析的 RSS 提要相关的特定时间值:

使用 strptime
strptime()方法或条带时间是在time模块中找到的方法,它允许我们使用我们的字符串数组创建一个date变量。我们只需要在strptime()方法中指定年、月、日、时。让我们为在for循环中创建的字符串数组创建一个变量。然后,用我们的strptime()方法创建一个date类型变量,并用我们的字符串数组格式化它。
看看下面的代码,注意我们如何使用news_post_date列表来构造for循环,以匹配我们的字符串数组来显示从 RSS 提要接收到的日期列表,我们使用strptime()方法将其解析为 Python time对象。让我们继续添加以下代码,看看我们的结果:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib2, time
from xml.etree import ElementTree
try:
#Open the file via HTTP.
response = urllib2.urlopen('http://www.packtpub.com/rss.xml')
tree = ElementTree.parse(response)
root = tree.getroot()
#Array of post dates
news_post_date = root.findall("channel//pubDate")
#Iterate in all our searched elements and print the inner text for each.
for date in news_post_date:
'''Create a variable striping out commas, and generating a new array item for every space.'''
datestr_array = date.text.replace(',', '').split(' ')
'''Create a formatted string to match up with our strptime method.'''
formatted_date = "{0} {1}, {2}, {3}".format(datestr_array[2], datestr_array[1], datestr_array[3], datestr_array[4])
#Parse a time object from our string.
blog_datetime = time.strptime(formatted_date, "%b %d, %Y, %H:%M:%S")
print blog_datetime
#Finally, close our open network.
response.close()
except Exception as e:
#If we have an issue show a message and alert the user.
print(e)
正如我们所看到的,通过 RSS 提要的每个循环显示一个time.struct_time对象。struct_time对象允许我们指定要使用time对象的哪个部分;让我们仅将月份打印到控制台:

我们现在可以通过打印blog_datetime.tm_mon来轻松完成,其中引用了我们的struct_time方法中的tm_mon命名参数。例如,这里我们得到每个帖子的月数,如下所示:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib2
from xml.etree import ElementTree
import time
def get_all_rss_dates():
'''Create a global array to our function to save our month counts.'''
month_count = []
try:
#Open the file via HTTP.
response = urllib2.urlopen('http://www.packtpub.com/rss.xml')
tree = ElementTree.parse(response)
root = tree.getroot()
#Array of post dates.
news_post_date = root.findall("channel//pubDate")
'''Iterate in all our searched elements and print the inner text for each.'''
for date in news_post_date:
'''Create a variable striping out commas, and generating a new array item for every space.'''
datestr_array = date.text.replace(',', '').split(' ')
'''Create a formatted string to match up with our strptime method.'''
formatted_date = "{0} {1}, {2}, {3}".format(datestr_array[2], datestr_array[1], datestr_array[3], datestr_array[4])
'''Parse a time object from our string.'''
blog_datetime = time.strptime(formatted_date, "%b %d, %Y, %H:%M:%S")
'''Add this date's month to our count array'''
month_count.append(blog_datetime.tm_mon)
#Finally, close our open network.
response.close()
except Exception as e:
'''If we have an issue show a message and alert the user.'''
print(e)
for mth in month_count:
print(mth)
#Call our function.
get_all_rss_dates()
下面的截图显示了我们的脚本的结果:

有了这个输出,我们可以看到数字6,表示六月,数字5,表示五月。太好了。我们现在已经修改了代码,以使用来自 Web 的数据,并显示我们在输出中指定的相同类型的数据。如果你对blog_datetime上的字符串格式感到好奇,你可以参考字符串格式索引,我已经把它包含在下表中了。在https://docs . python . org/2/library/datetime . html # strftime-strptime-behavior上也有详细的列表。
占位符
|
描述
|
| --- | --- |
| %a | 这个是缩写的工作日名称 |
| %A | 这个是没有缩写的工作日名称 |
| %b | 这个是缩写的月份名称 |
| %B | 这个是没有缩写的月份名称 |
| %c | 这是优选的日期和时间表示 |
| %C | 这是日期的世纪(2000 年将返回 20,1900 年将返回 19) |
| %d | 这个显示了一个月中的某一天 |
| %D | 这个和%m / %d / %y一样 |
| %g | 这是就像%G一样,但是没有世纪 |
| %G | 这个给出了四位数的年份,比如 2014 年 |
| %H | 此以 24 小时格式(00 至 23)显示小时;大多数博客使用 24 小时制 |
| %I | 此给出 12 小时格式的小时(01 至 12) |
| %j | 一年中的第天(001 到 366) |
| %m | 月(01 至 12) |
| %M | 分钟 |
| %p | 使用上午或下午 |
| %S | 这会显示日期的秒数 |
| %T | 这个给出当前时间,等于%H:%M:%S |
| %W | 这个是当年的周数 |
| %w | 星期几为小数,周日=0 |
| %x | 这给出了没有时间的优选日期表示 |
| %X | 这个给出了没有日期的优选时间表示 |
| %y | 这只是返回年份的最后两位数字(例如,2014 将返回 14) |
| %Y | 这个给出了包括世纪在内的年份(如果年份是 2014 年,产量就是 2014 年) |
| %Z或%z | 这个给出了时区的名称或时区的缩写(例如,东部标准时间,或 EST) |
将输出保存为计数数组
有了我们的数据类型,我们想要计算一个月内有多少帖子。为此,我们需要将每个帖子放入一个分组列表中,以便在我们的for循环之外使用。我们可以通过在for循环外创建一个空数组并将每个blog_datetime.tm_mon对象添加到我们的数组中来做到这一点。
让我们在下面的代码中这样做,但是首先,我们将把它包装在一个函数中,因为我们的代码文件开始变得有点大了。如果你还记得在第 2 章、Python reviewer中,我们将我们的大代码块包装在函数中,这样我们就可以重用或清理我们的代码。我们将把代码包装在get_all_rss_dates名称函数中,并在最后一行调用它。此外,我们将在我们的try catch准备追加值之前添加month_count数组变量,这是我们在for循环中所做的,然后打印month_count数组变量。让我们看看这会呈现什么:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib2
from xml.etree import ElementTree
import time
def get_all_rss_dates():
#create a global array to our function to save our month counts.
month_count = []
try:
#Open the file via HTTP.
response = urllib2.urlopen('http://www.packtpub.com/rss.xml')
tree = ElementTree.parse(response)
root = tree.getroot()
#Array of post dates.
news_post_date = root.findall("channel//pubDate")
'''Iterate in all our searched elements and print the inner text for each.'''
for date in news_post_date:
'''Create a variable striping out commas, and generating a new array item for every space.'''
datestr_array = date.text.replace(',', '').split(' ')
'''Create a formatted string to match up with our strptime method.'''
formatted_date = "{0} {1}, {2}, {3}".format(datestr_array[2], datestr_array[1], datestr_array[3], datestr_array[4])
'''Parse a time object from our string.'''
blog_datetime = time.strptime(formatted_date, "%b %d, %Y, %H:%M:%S")
'''Add this dates month to our count array'''
month_count.append(blog_datetime.tm_mon)
'''Finally, close our open network.'''
response.close()
except Exception as e:
'''If we have an issue show a message and alert the user.'''
print(e)
print month_count
#Call our function
get_all_rss_dates()
下面是截图,显示了我们的月份列表和与月份对应的数字。在这种情况下,5代表 5 月份,6代表 6 月份(您的数字可能会因月份而异):

计数数组
现在,我们的阵列已经准备就绪,让我们统计一下 6 月和 5 月的帖子。在写这本书的时候,我们在 6 月份有 7 个帖子,在 5 月份有很多帖子。
让我们打印出五月在帕克特出版网站新闻提要上的博客文章数量。为此,我们将使用count()方法,该方法允许我们在数组中搜索特定的值。在这种情况下,5就是我们要寻找的值:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib2
from xml.etree import ElementTree
import time
def get_all_rss_dates():
'''create a global array to our function to save our month counts.'''
month_count = []
try:
#Open the file via HTTP.
response = urllib2.urlopen('http://www.packtpub.com/rss.xml')
tree = ElementTree.parse(response)
root = tree.getroot()
#Array of post dates.
news_post_date = root.findall("channel//pubDate")
'''Iterate in all our searched elements and print the inner text for each. '''
for date in news_post_date:
'''Create a variable striping out commas, and generating a new array item for every space.'''
datestr_array = date.text.replace(',', '').split(' ')
'''Create a formatted string to match up with our strptime method.'''
formatted_date = "{0} {1}, {2}, {3}".format(datestr_array[2], datestr_array[1], datestr_array[3], datestr_array[4])
'''Parse a time object from our string.'''
blog_datetime = time.strptime(formatted_date, "%b %d, %Y, %H:%M:%S")
'''Add this date's month to our count array'''
month_count.append(blog_datetime.tm_mon)
'''Finally, close our open network. '''
response.close()
except Exception as e:
'''If we have an issue show a message and alert the user. '''
print(e)
print month_count.count(5)
#Call our function
get_all_rss_dates()
正如我们在下面的控制台中看到的,我们得到了给定月份写的帖子数量(在屏幕和代码中,这是 5 月份):

在我们的输出窗口中,我们可以看到我们的结果是 2014 年 5 月的43帖子。如果我们将计数搜索更改为六月,或者更确切地说,代码中的6会怎么样?让我们更新代码并重新运行:

我们的输出显示7为 6 月份的总博客帖子。在这一点上,我们已经测试了我们的代码,现在我们有了一个可以显示 5 月和 6 月的工作数据集。
Python 模块
好了,我们已经设置了图表的数据端,从网络中提取数据,并用 Python 将这些数据解析成可用的对象。我们可能认为,现在很容易将这些值插入 pygal 图表并结束一天的工作,在某种程度上,这是正确的;然而,我们希望代码更加智能。
还记得我们关于如何将大块 Python 代码包装成函数的讨论吗;好吧,对于一个更大的项目,我们希望模块更高;所以,我们首先想到的是:什么是模块?我们在这本书的课程中使用过模块吗?
是的,每当我们在代码中使用import语句时,我们都会使用time、pygal或urllib2。这是一个模块,(有时称为库),更有趣的是,我们可以制作自己的模块,从而允许我们模块化代码。
构建主方法
在许多编程语言中,存在一个main函数的概念,它是程序执行中调用的第一个函数。让我们在这里创建一个名为main_chartbuild的文件,只需在我们的 IDE 中创建main_chartbuild.py文件。
接下来,我们要移除在LoadRssFeed中的初始测试期间发现的get_all_rss_dates()方法调用,然后使用我们的main_chartbuild.py文件中的import调用我们的get_all_rss_dates()函数的dates方法。然后,我们将引用我们的方法,但在它前面加上我们的import 名称,如下所示:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import LoadRssFeed
#Call our 'LoadRssFeed' function.
LoadRssFeed.get_all_rss_dates()
如果我们重新运行脚本,我们应该会看到与我们 6 月的帖子计数相同的结果,即7:

修改我们的 RSS 以返回值
由于我们现在使用我们的LoadRssFeed作为库模块,我们将希望修改最终结果以返回一个我们可以在我们即将构建的chart模块中使用的数组。我们将在一个数组中返回两个计数,一个用于 5 月,一个用于 6 月。
此外,我们将删除print语句,因为我们知道它工作正常。所以,去掉LoadRssFeed、get_all_rss_dates功能末尾的print线,换成return [month_count.count(5), month_count.count(6)]。这将允许我们返回一个对象,但为我们的图表保留两个值。文件的实现如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib2
from xml.etree import ElementTree
import time
def get_all_rss_dates():
'''create a global array to our function to save our month counts.'''
month_count = []
try:
'''Open the file via HTTP.'''
response = urllib2.urlopen('http://www.packtpub.com/rss.xml')
tree = ElementTree.parse(response)
root = tree.getroot()
'''Array of post dates.'''
news_post_date = root.findall("channel//pubDate")
'''Iterate in all our searched elements and print the inner text for each.'''
for date in news_post_date:
'''Create a variable striping out commas, and generating a new array item for every space.'''
datestr_array = date.text.replace(',', '').split(' ')
'''Create a formatted string to match up with our strptime method.'''
formatted_date = "{0} {1}, {2}, {3}".format(datestr_array[2], datestr_array[1], datestr_array[3], datestr_array[4])
'''Parse a time object from our string.'''
blog_datetime = time.strptime(formatted_date, "%b %d, %Y, %H:%M:%S")
'''Add this date's month to our count array'''
month_count.append(blog_datetime.tm_mon)
'''Finally, close our open network.'''
response.close()
except Exception as e:
'''If we have an issue show a message and alert the user.'''
print(e)
'''Return two counts for both months.'''
return [month_count.count(5), month_count.count(6)]
型式
Packt Publishing 网站的 RSS feed 只占过去两个月的帖子,因此,例如,如果您在 10 月份阅读这篇文章,您需要将每个month_count项目的计数设置为 10 月份的10和 9 月份的9。
构建我们的图表模块
接下来,让我们创建一个新的 Python 文件,作为我们使用 pygal 的图表构建库。我们将此文件命名为chart_build.py,并将其与我们的LoadRssFeed.py和main_chartbuild.py文件一起添加到我们的项目根目录中。
接下来,打开chart_build.py文件,让我们构建一个简单的条形图,显示 5 月和 6 月的帖子数量。就像我们构建的LoadRssFeed模块一样,我们将图表代码包装在一个函数中,该函数带有一个名为dataarr的参数,表示一个数据数组。在我们将数据添加到图表之前,让我们设置图表配置。
为我们的图表构建便携式配置
在 pygal 中,我们通常创建一个图表,在其中,我们指定参数来设置图表的设置,并将我们的参数值添加到我们调用的图表对象中。pygal库提供了一种模块化配置选项的方法。
这很有帮助,因为在这个例子中,我们只使用一个图表,但是如果我们有八个或十二个图表要构建呢?拥有一个可移植的配置来设置图表布局和主题可能非常有用,而不是每次都重写配置。
看看下面的代码。这里,我正在创建一个名为ConfigChart的 Python 类,它有一个名为Config的参数,基本上覆盖了我们分配给它的图表中的Config对象。在类内部,我有一个可以干净地更新和修改的参数列表。注意我也导入pygal,使用from pygal,我也导入Config作为单独的对象工作:
import pygal
from pygal import Config
'''Creating our own class with values to pass in as parameters to our chart.'''
class ConfigChart(Config):
show_legend = True
human_readable = True
fill = True
'''explicit_size sets a fixed size to our width height properties.'''
explicit_size = True
width = 860
height = 640
title= 'Posts per month on Packtpub.com'
y_label = 'Posts'
x_label = 'Month'
y_title = 'Posts'
x_title = 'Month'
show_y_labels = True
show_x_labels = True
stroke = False
tooltip_font_size = 42
title_font_size = 24
'''Always include an error message for any dynamic data.'''
no_data_text = 'Unable to load data'
正如所见,我已经添加了我们在第 5 章、调整 pygal 中讨论的许多值,并且还确保了我们的no_data_text参数在任何连接问题的情况下都有值。此外,当我们想要在浏览器中渲染时,我已经设置了width和height以及将名为explicit_size的参数设置为True,以强制 SVG 输出为固定大小。
设置我们的数据图表
现在,让我们完成设置我们的图表以接收我们的chart_build.py文件中的数据。在这里,我已经将我的 pygal 图表创建代码包装在一个名为generate_chart的函数中,并添加了一个参数来处理从 RSS 提要中提取的图表数据。
这是图表的最终代码,包括我们的ConfigChart类,应用于我们的图表对象。请注意,对于图表的add()方法,我只是添加了带有数组索引的dataarr,以分别指定May和June的值:
import pygal
from pygal import Config
'''Creating our own class with values to pass in as parameters to our chart.'''
class ConfigChart(Config):
show_legend = True
human_readable = True
fill = True
'''explicit_size sets a fixed size to our width height properties.'''
explicit_size = True
width = 860
height = 640
title= 'Posts per month on Packtpub.com'
y_label = 'Posts'
x_label = 'Month'
y_title = 'Posts'
x_title = 'Month'
show_y_labels = True
show_x_labels = True
stroke = False
tooltip_font_size = 42
title_font_size = 24
'''Always include an error message for any dynamic data.'''
no_data_text = 'Unable to load data'
'''Generate chart based on imported array, (with 2 values)'''
def generate_chart(dataarr):
'''Initialize the chart with our ConfigChart class.'''
mychart = pygal.Bar(ConfigChart())
'''Add data to the chart for May and June.'''
mychart.add('May', dataarr[0])
mychart.add('June', dataarr[1])
'''Launch our web browser with our SVG, (we can also render to file as well)'''
mychart.render_in_browser()
在运行main_chartbuild.py之前,在LoadRssFeed.py文件中检查我们的代码。我们这里做了一个改动;我们将print异常更改为throw,如果在我们的try / catch块中遇到数据连接问题,将会出现异常。如果我们公开部署这个应用,我们会为消费者添加一个 UI 错误,而不是一个异常。
在这段代码中,我们进行了更新,返回了两个将传递到图表中的值:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import urllib2
from xml.etree import ElementTree
import time
def get_all_rss_dates():
'''create a global array to our function to save our month counts.'''
month_count = []
try:
'''Open the file via HTTP. '''
response = urllib2.urlopen('http://www.packtpub.com/rss.xml')
tree = ElementTree.parse(response)
root = tree.getroot()
'''Array of post dates.'''
news_post_date = root.findall("channel//pubDate")
'''Iterate in all our searched elements and print the inner text for each. '''
for date in news_post_date:
'''Create a variable striping out commas, and generating a new array item for every space.'''
datestr_array = date.text.replace(',', '').split(' ')
'''Create a formatted string to match up with our strptime method.'''
formatted_date = "{0} {1}, {2}, {3}".format(datestr_array[2], datestr_array[1], datestr_array[3], datestr_array[4])
'''Parse a time object from our string.'''
blog_datetime = time.strptime(formatted_date, "%b %d, %Y, %H:%M:%S")
'''Add this date's month to our count array'''
month_count.append(blog_datetime.tm_mon)
'''Finally, close our open network. '''
response.close()
except Exception as e:
'''If we have an issue show a message and alert the user.'''
throw
#Return two counts for both months.
return [month_count.count(5), month_count.count(6)]
配置我们的主要功能来传递数据
还有一个要做的任务是设置我们的main函数,就是我们的main_chartbuild.py,在我们的脚本执行的时候调用。当我们的LoadRssFeed模块返回一个我们传递到图表中的数组时,我们将从我们构建的chart模块中调用generate_chart()方法并传递结果,如以下代码所示:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import LoadRssFeed, chart_build
#Call our 'LoadRssFeed' function.
chart_build.generate_chart(LoadRssFeed.get_all_rss_dates())
让我们运行我们的main_chartbuild.py文件,看看结果:

项目改进
成功!我们通过从 web 源拉取数据构建了一个动态图表,并生成了一个具有特定配置的图表,可以轻松移动、更新和复制!现在,对于我们在一起的时间,这是一个动态图表上非常简单的例子。根据实际情况,该脚本可能每十分钟自动更新一次,例如,在图表构建服务器上,或者从具有 shell 访问权限的另一种语言调用,从而触发 Python 脚本。
此外,考虑数据以及它如何创建不同类型的图表。我们可以使用我们一起构建的LoadRssFeed模块创建数组,并提取特定作者的帖子数量,或者特定单词在博客文章标题中出现的次数。我们已经体验过使用 Python 处理复杂的应用,以及将网络上的实时数据拉进图表进行显示。
以下是独立于月份的修改代码:
import pygal
from pygal import Config
'''Creating our own class with values to pass in as parameters to our chart. '''
class ConfigChart(Config):
show_legend = True
human_readable = True
fill = True
#explicit_size sets a fixed size to our width height properties.
explicit_size = True
width = 860
height = 640
title= 'Posts per month on Packtpub.com'
y_label = 'Posts'
x_label = 'Month'
y_title = 'Posts'
x_title = 'Month'
show_y_labels = True
show_x_labels = True
stroke = False
tooltip_font_size = 42
title_font_size = 24
#Always include an error message for any dynamic data.
no_data_text = 'Unable to load data'
'''Generate chart based on imported array, (with 2 values) '''
def generate_chart(dataarr):
'''Initialize the chart with our ConfigChart class. '''
mychart = pygal.Bar(ConfigChart())
'''Add data to the chart for May and June. '''
for data in dataarr:
mychart.add(data[0], data[1])
#mychart.add('June', dataarr[1])
'''Launch our web browser with our SVG,
(we can also render to file as well) '''
mychart.render_in_browser()
if __name__ == '__main__':
generate_chart([('May', 10), ('June', 20), ('July', 30)])
LoadRssFeed.py
import urllib2
from xml.etree import ElementTree
from collections import defaultdict
def get_all_rss_dates():
month_count = defaultdict(int)
try:
#Open the file via HTTP.
response = urllib2.urlopen('http://www.packtpub.com/rss.xml')
tree = ElementTree.parse(response)
root = tree.getroot()
news_post_date = root.findall("channel//pubDate")
#Iterate in all our searched elements and print the inner text for each.
for date in news_post_date:
month_count[ date.text[8:11]] += 1
response.close()
except Exception as e:
#If we have an issue show a message and alert the user.
throw
return list(month_count.items())
if __name__ == '__main__':
print(get_all_rss_dates())
总结
在这一章中,我们使用通过 HTTP 从网络中动态提取的 RSS 数据,构建了一个基本的,但实际工作的动态图表应用。我们使用pygal库创建了一个图表,然后使用 pygal 的Config类修改了我们的图表,并自动启动网络浏览器来显示图表。
八、更多资源
所以我们可能会问,现在怎么办?还有什么要知道的?嗯,实际上相当多,正如在第 1 章、设置您的开发环境以及其他章节开头提到的,这是使用 Python 的数据可视化的介绍,但不是全部。
用 Python 构建动态图表是目前非常需要的技能。存在的原因是双重的;首先,Python 是免费的,跨平台的,允许构建服务器存在于一个 Linux 服务器上,一个微软 IIS 服务器;或者一台 Mac 服务器,你就明白了。
另一个原因是 Python 处理数据计算的速度非常快,特别是在医疗、大气甚至金融数据等方面。根据需要不同图表风格和不同交互性的数据类型,我们将需要使用不同的图表库,这就引出了一个问题,除了 pygal,还有什么?
matplotlib 库
在图形和数据图表的 T2 Python 世界中,最受欢迎的库之一是 T4 的 T3 matplotlib。虽然这个名字听起来很傻,但简单来说,matplotlib是一个 2D 和三维绘图库,可以生成高质量的硬拷贝图形和图表。现在,matplotlib库在早期很容易使用,但很快就会变得非常复杂。
还记得在第 2 章Python reviewer中,我们讨论过从头开始创建自己的图形和图表吗?matplotlib 不仅允许我们构建图表和图形,还允许我们在框架内创建的 2D 静态图像和 3D 对象中绘制图形、小部件和运行动画。查看 matplotlib 网站上的示例:http://matplotlib.org/examples/index.html。
安装 matplotlib 库
还记得第三章开头的吗,pygal 入门,pygal 需要安装lxml?matplotlib 也是,但是在 Linux 系统上有一组不同的库;matplotlib 可以使用以下命令通过大多数 Linux 通用 Python 包管理器及其所有必需的依赖项轻松安装,因此您不需要 Debian 安装程序:
sudo apt-get install Python-matplotlib
型式
如果您使用的是基于 Windows 的系统,请考虑使用基于 Mac 或 Linux 的系统,因为它们需要一点额外的工作来正确安装matplotlib。如果您只能访问 Windows,请考虑安装一个带有诸如 Ubuntu 之类的操作系统的虚拟机。
以下截图显示了 matplotlib 下载页面:

matplotlib 的库下载页面
现在,如果你在基于 Windows 或 Mac 的操作系统中工作,matplotlib 建议下载 matplotlib 的二进制安装程序,为此开发人员为 Windows 机器创建了一个 exe 安装程序,或者为 Mac 系统创建了一个 dmg 安装程序。这允许将 C 映像代码正确安装到机器上,就像第 1 章、为 Window 的系统设置开发环境中的lxml一样。可以在这里抓取 Windows 和 Mac 的安装程序:http://matplotlib.org/downloads.html。
型式
对于 matplotlib 的 Mac 用户来说,撰写本文时的链接显示 10.6 是唯一的安装程序;这些将适用于任何英特尔 Mac,包括 10.9 小牛。
如果您在 Linux 系统上遇到 matplotlib 的问题,也可以在该页面上找到该平台的 tarball 安装程序;请确保安装 Python 运行时附带的版本。
一个依赖性是 matplotlib 使用 Numeric Python 或 NumPy 来处理复杂的数学运算,通常用于基本图表创建和/或在数据中创建曲线。通过 pip 下载这个依赖关系稍微容易一点,使用以下命令中显示的 pip 命令。如果您遇到问题,请查看在http://www.scipy.org/安装 SciPy 堆栈,它还包括maplotlib以及额外的插件。
sudo pip install numpy
型式
对于 Windows 用户,NumPy 不包括 64 位版本。您将需要 32 位版本的 Python 2.7 来运行它。
创建简单的 matplotlib 图表
一旦安装了库和依赖项,matplotlib 中的大多数基本图表都很容易构建。在下面的代码中,我们有一个带有标准 Python 和 Unicode 声明的示例代码,我们只需使用范围0 – 541创建一个数字列表,然后使用一个简单的for循环向图表中添加值,并保存文件,同时在 matplotlib 查看器中显示它:
#!/usr/bin/env Python
# -*- coding: utf-8 -*-
from matplotlib import pyplot
#Create a range from 0 - 541
X = range(0, 541)
#Set the values for Y by iterating thru X's range.
Y = [i*i for i in X]
'''Assign a range of values to the graph, the dash goes between each value.'''
pyplot.plot(X, Y, '-')
#Set chart's labels and title
pyplot.title('Plotting x*x')
pyplot.xlabel('X Axis')
pyplot.ylabel('Y Axis')
#Save the chart as a PNG to our project directory.
pyplot.savefig('Simple.png')
#Show the chart in the Python runtime viewer.
pyplot.show()
下面的截图显示了我们脚本的结果:

注意这里的我们图表的Simple.png文件是用savefig()函数创建的,实际上是透明的。这是由于 matplotlib 如何渲染 PNG 图表。现在,如果我们看最后一行,pyplot.show(),这将告诉matplotlib显示下面的窗口,显示我们保存到我们的Simple.png文件的相同图表。
下面是我们使用 matplotlib 提供的 Python 图表查看器的图表。您可以使用左下角的控件来操作图表:

在保存按钮旁边的中可以找到的控件之一是子图配置工具。单击打开控件子集以调整图表,如下图所示:

当对你的图表外观满意时,可以通过保存按钮进行保存。如果你犯了错误,你可以点击重置或者点击主窗口工具栏上的主页按钮。
matplotlib库包括许多标准图表类型以及支持 3D 的图表。让我们构建一个简单的三维图表。看看下面的代码:
#!/usr/bin/env Python
# -*- coding: utf-8 -*-
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
#Create a curve on the z axis.
x = np.linspace(0, 1, 100)
y = np.sin(x * -1 * np.pi) / 2 + 0.5
ax.plot(x, y, zs=0, zdir='z', label='Our random sin curve, only for z-axis')
'''Specify colors, and loop thru each and randomize the scatter dots, and add them to the chart data.'''
colors = ('r', 'g', 'k', 'b')
for c in colors:
x = np.random.sample(42)
y = np.random.sample(42)
ax.scatter(x, y, 1, zdir='x', c=c)
#Apply the legend.
ax.legend()
#Add the X, Y, and Z axis.
ax.set_xlim3d(0, 1)
ax.set_ylim3d(0, 1)
ax.set_zlim3d(0, 1)
#Show the chart.
plt.show()
对于这张图表,请注意我们使用的是matplotlib的模块mpl_toolkits;这包括Axes3D模块。这允许我们绘制基于 3D 的图表。这里我们要在 z 轴上画一条曲线,在 x 轴上随机散布多色点。让我们运行代码并查看结果:

带matplotlib的 3D 图表的一个大特点是我们可以用鼠标旋转 3D 图表,通过将鼠标拖动到图表图像上;下面是一个更新图表的例子,以获得更好的点和曲线角度:

这只是对 matplotlib 为图表可视化开发人员提供的的一个尝试,我们在 pygal 中介绍的许多相同类型的图表被翻译成 matplotlib,但它包括各种插件和更多的 3D 图表和绘图工具。
有关该库的更多信息,请访问 matplotlib 网站。考虑到它的受欢迎程度,有很多关于这个主题的书可以让你从进入图书馆的各种入口开始。
绘图
Plotly(https://plot.ly/)是 Python 数据可视化空间中一个新的图表库。Plotly 与其他制图 Python 库的区别在于它非常面向业务和组织。Plotly 是一家公司,不像网络上的其他一些图书馆。Python 开发人员需要在网站上注册一个开发人员帐户,以接收要在代码中使用的 API 密钥,如下图所示:

Plotly 的概念很简单。图表要么在 Plotly 网站上托管的在线工具中创建,要么通过代码创建;在我们的例子中,是一个与 Plotly 的服务集成的 Python 库,因为 Plotly 托管每个保存的图表,并且它允许共享托管的图表。这有助于与非技术用户共享数据。例如,以下是在 Plotly 编辑器中制作的一个图表的截图,用户可以在网上分享:

Plotly 给出的优势是很容易分享交互式图表。Plotly 还有一个在线工具,如前一张截图所示,无需任何代码即可创建图表,并上传数据以显示到在线可共享图表中。开发人员也可以通过使用 Chrome 应用在 Chrome OS 上使用 Plotly。
另一方面,Plotly 作为一个基于云的库运行良好。重要的是要知道 Plotly 的库需要 Python 3 或更高版本;所以在安装之前,我们需要从 Python 的网站(http://www.python.org/)下载 Python 3。在安装 Plotly 之前,如果需要,也可以在 2 旁边运行 Python 3。用 Python 3 安装 Plotly 也非常容易。使用pip可以轻松安装plotly库,如下代码所示:
pip install plotly
请记住,Plotly 需要 Python 3 或更高版本才能工作。否则,如果 pip 在 Python 2.7 中运行,一些依赖项将不会被安装。现在,一旦我们安装了我们的库,我们需要为 API 指定我们的登录信息,这特定于在https://plot.ly/网站上使用的登录或 OpenID。请确保包含请求的登录信息;否则,任何图表都将导致类似于以下屏幕截图所示的对话框:

幸运的是,Plotly 有一个页面,如果您登录了,它将生成一个包含用户特定信息的登录对象示例。我们可以在 https://plot.ly/Python/getting-started/找到这个。
加上这些,让我们在 Plotly 中构建一个简单的图表。在这里,我们将把 Plotly 导入到我们的脚本中,并再次作为一个模块,导入所有图表类型供我们使用,最后导入我们的信息供 API 使用。
这里我们将用一些样本数据构建一个简单的折线图。复制以下代码并运行脚本,记住运行脚本需要注册用户名和 API 密钥。在下面的代码示例中,我在括号中添加了占位符来填充:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''import Plotly, and the main plotly object as a variable.'''
import plotly;
import plotly.plotly as py
'''import all chart types.'''
from plotly.graph_objs import *
'''Set the username, APIKey, and streaming IDs as given on https://plot.ly/python/getting-started/'''
plotly.tools.set_credentials_file(username='[username]', api_key='[apikey]', stream_ids=['[streamingkey1]', '[streamingkey2]'])
'''Create a data-set for a Plotly scatter plot.'''
trace0 = Scatter(
x=[5, 10, 15, 20],
y=[20, 40, 35, 16]
)
'''Assign the chart's data to an array typed variable (in this case trace0) to hold data.'''
data = Data([trace0])
'''Create a URL with the data loaded via the API, and open a web browser to the chart on success.'''
unique_url = py.plot(data, filename = 'basic-line')
如果成功,我们应该看到我们的默认网络浏览器打开图表,我们可以通过网络上 Plotly 查看器左侧的社交网络图标轻松共享该图表。请注意,如果我们将鼠标悬停在图表上,我们可以获得类似 pygal 的动画和数据,但我们也可以放大图表或选择图表的特定区域来获取更复杂的数据。

干得好!让我们再建立一个图表。这一次将是两个数据集的散点图。我们将做一个模拟图,比较纳斯卡粉丝和从欧洲和美国取样的 f1 粉丝(记住,这只是样本数据)。
在这个图表中,我们还将结合一些标签来更好地设置图表的样式,并用我们自己的样式来设置点的格式。同样,我们将需要我们的 API 登录和密钥;此代码示例中设置了占位符:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''import Plotly, and the main plotly object as a variable.'''
import plotly;
import plotly.plotly as py
'''import all chart types.'''
from plotly.graph_objs import *
'''Set the username, APIKey, and streaming IDs as given on https://plot.ly/python/getting-started/'''
plotly.tools.set_credentials_file(username='[username]', api_key='[apikey]', stream_ids=['[streamingkey1]', '[streamingkey2]'])
'''Create a data-set for a scatter plot.'''
trace0 = Scatter(
#Create an array for each value.
x=[27984, 9789],
y=[34, 27],
text=['Formula 1 Fans', 'Nascar Fans'],
name='European automotive fans',
mode='markers',
marker=Marker(
line=Line(
color='rgb(124, 78, 42)',
width=0
),
size=48,
color='rgb(124, 78, 42)'
)
)
trace1 = Scatter(
#Create an array for each value.
x=[10117, 340159],
y=[38, 31],
text=['Formula 1 Fans', 'Nascar Fans'],
name='North America automotive fans',
mode='markers',
marker=Marker(
line=Line(
color='rgb(114, 124, 195)',
width=0
),
size=48,
color='rgb(114, 124, 195)'
)
)
'''Set chart's titles, labels, and values.'''
layout = Layout(
title='Fan comparisons of automotive sports in the United States and Europe',
xaxis=XAxis(
title='Amount of fans',
showgrid=True,
zeroline=False
),
yaxis=YAxis(
title='Average age of fans sampled',
showline=True
)
)
'''Assign the data to an array typed variable to hold data.'''
data = Data([trace0, trace1])
'''Add full chart labels to the chart.'''
fig = Figure(data=data, layout=layout)
'''Create a URL with the data loaded via the API, pass the data to the figure which is passed here, and then open a web browser to the chart on success.'''
unique_url = py.plot(fig, filename = 'line-style')
下面的截图显示了我们脚本的结果:

因此,正如我们所看到的,Plotly 非常容易使用,并且有了我们的 pygal 背景,这个将在未来的任何项目中很好地工作。有关 Python 的 Plotly API 的信息,请查看位于https://plot.ly/Python/的开发人员网站。
Pyvot
Pyvot(http://pytools.codeplex.com/wikipage?title=Pyvot)是一个 Python 数据到微软 Excel 的转换器,是一个非常好用的工具,可以将图表数据或者通用 Python 值导出到 Excel。可以这样使用pip安装:
pip install Pyvot
也可以用easy_install安装:
easy_install Pyvot
注
有一点需要注意的是,在写完这本书的时候,Pyvot 已经不再由作者维护了,大部分都是被微软工作人员或者微软 MVPs 在 Visual Studio 中用于 Python 的技术演示,所以我们就不在这本书里贴示例代码了。如果您需要 Pyvot CodePlex 网站上的文档,http://pytools.codeplex.com/wikipage?title=Pyvot 会很有帮助。还有一点需要注意的是,Pyvot 在一些 Python 图表项目中是可以常见到的,这主要是因为与 Visual Studio 和 Excel 的紧密集成。
库本身在 Python 2 和 3 项目中仍然运行良好,但是如果需要一个维护的库,请查看:pyxl(https://www.pyxll.com/)或数据硝基(https://datanitro.com/)。
以下屏幕截图显示了 Pyvot 的 CodePlex 站点,其中包含下载链接和视频文档演练:

总结
在本章中,我们用matplotlib和 Plotly 的概述和基本用法来总结。我们通过使用诸如 Pyvot、PyXLL 和 DataNitro 这样的库来导出数据。
这本书的一个收获是,在 Python 语言中,数据可视化的选择非常多。我对新的和当前的 Python 开发人员的建议是,找到一个能很好地满足您的需求和项目目标的库。对于这本书,我们涵盖了pygal库,因为它简单且易于使用的文档,如第 3 章、pygal入门中所述。现在尝试本章中提到的其他一些库,看看什么数据可视化库最适合您。
九、附录 a:参考资料和资源
在使用数据可视化库时,Python 社区提供了相当多的资源和工具,以及社区帮助。这是一个供进一步阅读的网站列表,包括本书涵盖的图书馆。
寻求帮助和支持的链接
以下是帮助和支持链接:
- pygal 的创作者 kozea 和一个通用的开源讨论板可以在上找到
- 普通 Python 问题的堆栈溢出可以在http://stackoverflow.com/questions/tagged/Python找到
- 使用 Python 进行数据可视化的堆栈溢出问题可以在上找到
- Python 代码的 Snipplr(非常适合 Python 代码片段)可以在http://snipplr.com/all/language/Python找到
图表库
以下是不同图表库的链接:
- matplotlib 可以在http://matplotlib.org找到
- pygal 可以在http://pygal.org找到
- 可在 https://plot.ly 找到 plot
- PyChart】可以在http://home.gna.org/pychart/找到
- iGraph:可以在http://igraph.org/redirect.html网络找到吗
- Graphviz】可以在http://www.graphviz.org/Gallery.php找到
- pygooglechart(谷歌图表的 Python 包装)可以在 https://github.com/gak/pygooglechart(T2)找到
Python 的编辑器和 ide
下面的是 Python 不同编辑器和 ide 的链接:
- Visual Studio 的 Python 工具(主要用于本书,与 pygal 配合良好)可以在http://pytools.codeplex.com找到
- 日食的皮德夫可以在 http://pydev.org 的 T2 找到
- Mac 的 CodeRunner (一个运行快速 Python 脚本的不错的编辑器,可以很好地处理 matplotlib 项目)可以在http://krillapps.com/coderunner/找到
- 崇高的文本(一个伟大的,轻量级的跨平台编辑编辑器)可以在http://www.sublimetext.com找到
- py charm(PyDev 和 Visual Studio 的完整 IDE 替代品)可以在http://www.jetbrains.com/pycharm/找到
其他库和 Python 替代外壳
以下是其他库和 Python 替代外壳的链接:
- 蟒蛇可以在https://store.continuum.io/cshop/anaconda/找到
- 在https://www.enthought.com/products/canopy/可以找到天篷
- Python 影像库(PIL),Python 中常见的影像库,可以在http://www.Pythonware.com/products/pil/找到
- IPython(一个功能丰富的 shell,常用于 matplotlib 项目)可以在http://iPython.org找到
- IronPython (Python plus 访问。NET 框架和 WPF 可视化工具)可以在http://ironPython.net找到
- Jython(支持 Java 访问的 Python)可以在http://www.jython.org找到
- 皮沃特可以在 http://pytools.codeplex.com/wikipage?title=Pyvot 找到
- PyXLL】可以在https://www.pyxll.com/找到
- 数据硝基可以在https://datanitro.com/找到



浙公网安备 33010602011771号