书名:Python数据可视化编程实战(第2版)
ISBN:978-7-115-48842-8
本书由人民邮电出版社发行数字版。版权所有,侵权必究。
您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。
我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。
如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。
著 [爱尔兰] 伊戈尔•米洛瓦诺维奇(Igor Milovanović)
[法] 迪米特里•富雷斯(Dimitry Foures)
[意大利] 朱塞佩•韦蒂格利(Giuseppe Vettigli)
译 颛清山
责任编辑 武晓燕
人民邮电出版社出版发行 北京市丰台区成寿寺路11号
邮编 100164 电子邮件 315@ptpress.com.cn
读者服务热线:(010)81055410
反盗版热线:(010)81055315
Copyright ©2015 Packt Publishing. First published in the English language under the title Python Data Visualization Cookbook, Second Edition.
All rights reserved.
本书由英国Packt Publishing公司授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。
版权所有,侵权必究。
本书是一本使用Python实现数据可视化编程的实战指南,介绍了如何使用Python最流行的库、通过70余种方法创建美观的数据可视化效果。
全书共9章,分别介绍了准备工作环境、了解数据、绘制并定制化图表、学习更多图表和定制化、创建3D可视化图表、用图像和地图绘制图表、使用正确的图表理解数据、matplotlib相关知识以及使用Plot.ly进行云端可视化。
本书适合那些对Python编程有一定基础的开发人员阅读,它可以帮助读者从头开始了解数据、数据格式、数据可视化,并学会使用Python可视化数据。
Igor Milovanović是一名在Linux系统和软件工程领域有深厚背景的、经验丰富的开发人员,熟悉创建可扩展数据驱动分布式富软件系统的技术。
身为一个高性能系统设计的布道者,他对软件架构和软件开发方法论有着浓厚的兴趣。他一直坚持倡导促进高质量软件的方法论,如测试驱动开发、一键部署和持续集成。
他也拥有丰富的产品开发知识。他拥有领域经验知识,并参加过官方培训,他能够在业务和开发人员之间很好地传递业务知识和业务流程。
Igor非常感谢他的女友允许他把大量的时间花费在本书的写作上而没有陪伴她,并在他无休止地谈论本书时甘愿做一个热心的听众。他感谢他的哥哥一直以来做他最坚强的后盾。他还要感谢他的父母,给予他自由的发展空间,让他成为今天的自己。
Dimitry Foures是一名拥有应用数学和理论物理背景的数据科学家。在里昂高等师范学校(法国)完成他的物理专业的本科学业后,他继续在巴黎综合理工学院学习流体力学,并获得了一等硕士学位。他拥有剑桥大学应用数学专业博士学位。他目前在剑桥的一家智能能源初创公司担任数据科学家一职,与剑桥大学有着非常密切的合作。
Giuseppe Vettigli是一名数据科学家,他在产业界和学术界从事过多年的研究工作。他的工作关注从结构化及非结构化数据中提取信息进行机器学习模型的开发和应用。他也经常发表利用Python进行科学计算和数据可视化的文章。
Kostiantyn Kucher出生在乌克兰敖德萨。他于2012年在敖德萨国立理工大学获得计算机科学专业的硕士学位。他使用Python、Matplotlib和PIL从事机器学习和图像识别的工作。
Kostiantyn从2013年开始成为一名计算机科学专业信息可视化方向的博士研究生。 在Andreas Kerren博导的指导下,他在瑞典林奈大学计算机科学系的ISOVIS小组进行研究。
Kostiantyn也是本书第1版的技术评阅者。
图形可视化是展示数据的一个非常好的手段,好的图表自己会说话。毋庸多言,在Python的世界里,matplotlib是最著名的绘图库,它支持几乎所有的2D绘图和部分3D绘图,被广泛地应用在科学计算和数据可视化领域。但是介绍matplotlib的中文书籍很少,大部分书籍只是在部分章节中提到了matplotlib的基本用法,因此在内容和深度上都力有不逮。本书则是一本专门介绍matplotlib的译著。
matplotlib是一个开源项目,由John Hunter发起。关于matplotlib的由来,有一个小故事。John Hunter和他研究癫痫症的同事借助一个专有软件做脑皮层电图分析,但是他所在的实验室只有一份该电图分析软件的许可。他和许多一起工作的同事不得不轮流使用该软件的硬件加密狗。于是,John Hunter便有了开发一个工具来替代当前所使用的软件的想法。当时MATLAB被广泛应用在生物医学界中,John Hunter最初是想开发一个基于MATLAB的版本,但是由于MATLAB的一些限制和不足,加上他本身对Python非常熟悉,于是就有了matplotlib的诞生。
所以,无论从名字上,还是从所提供的函数名称、参数及使用方法,matplotlib都与MATLAB非常相似。对于一个MATLAB开发人员,使用matplotlib会相当得心应手。即使对不熟悉MATLAB的开发人员(譬如我),对其函数的使用也能够一目了然,而且matplotlib有着非常丰富的文档和实例,再加上本书的介绍,学习起来将会非常轻松。
matplotlib命令提供了交互绘图的方式,在Python的交互式shell中,我们可以执行matplotlib命令来实时地绘制图形并对其进行修改。生成的图像可以保存成许多格式,这取决于其所使用的后端,但绝大多数后端都支持如png、pdf、ps、eps和svg等格式。
在本书中,作者对内容进行了整理,剔除了一些与章节联系不是很大的延伸内容,对排版也进行了修改,使得本书在内容编排上更加简洁紧凑。在本书中,作者还引入了pandas和Plot.ly。其中pandas是一个功能强大且高性能的数据分析工具。Plot.ly是一款非常优秀的在线图表工具,它非常注重图表的可操作性及分享。在内容上,第2版更加完整地涵盖了Python数据可视化领域用到的主流工具。限于篇幅,本书不可能对所有工具做完整详细的介绍,但本书所讲的工具能满足读者大部分的数据可视化需要,读者可以根据自己的需要有选择地深入学习。
在这里,我要特别感谢我的妻子董秋影,在精神和专业知识上,她都给予了我莫大的帮助,没有她就没有这本译稿的完成。她从事医疗图像算法工作,对各种图形和算法以及MATLAB都有很深的了解,本书的每一章都经过了她认真的审阅校对。最后,感谢人民邮电出版社武晓燕老师专业细心地审核,和武老师合作很轻松、很开心。
由于译者水平有限,错误和失误在所难免,如有任何意见和建议,请不吝指正,我将感激不尽。
颛清山
2018年1月于 墨尔本
最好的数据是我们能看到并理解的数据。作为开发人员和数据科学家,我们希望可以创造并构建出最全面且容易理解的可视化图形。然而这并非易事,我们需要找到数据,对它读取、清理、过滤,然后使用恰当的工具将其可视化。本书通过直接和简单(有时不那么简单)的方法解释了进行数据读取、清理以及可视化的流程。
本书涉及如何读取本地数据、远程数据、CSV、JSON以及关系型数据库中的数据。
通过matplotlib,我们能用一行简单的Python代码绘制出一些简单的图表,但是进行更高级的绘图还需要Python之外的其他知识。我们需要理解信息理论和人类的审美学来生成最吸引人的可视化效果。
本书将介绍在Python中使用matplotlib绘图的一些实战练习,以及不同图表特性的使用情况及其用法示例。
第1章,准备工作环境,包括一些安装方法,以及如何在不同平台上安装所需的Python包和库的一些建议。
第2章,了解数据,介绍通用的数据格式,以及如何进行读写,读写的格式如CSV、JSON、XSL或者关系型数据库。
第3章,绘制并定制化图表,着手绘制简单的图表并涉及一些图表定制化的内容。
第4章,学习更多图表和定制化,继续上一章内容,介绍更多的高级表格和网格定制化。
第5章,创建3D可视化图表,介绍三维数据的可视化,如3D柱状图、3D直方图,以及matplotlib动画。
第6章,用图像和地图绘制图表,涵盖图像处理、在地图上投射数据以及创建CAPTCHA测试图像。
第7章,使用正确的图表理解数据,涵盖一些更高级的绘图技术的讲解和实战练习,如频谱图和相关性图形。
第8章,更多的matplotlib知识,介绍一些图表如甘特图、箱线图,并且介绍如何在matplotlib中使用LaTeX渲染文本。
第9章,使用Plot.ly进行云端可视化,介绍如何使用Plot.ly在云端环境中创建和分享可视化图形。
学习本书时,你需要在自己的操作系统上安装Python 2.7.3或更高版本。
本书用到的另一个软件包是IPython,它是一个交互式的Python环境,功能非常强大、灵活。你可以通过基于Linux平台的包管理工具或者用于Windows和Mac OS系统的预安装文件安装它。
一般来说,如果你对Python的安装和相关软件的安装不熟悉,那么强烈推荐你使用预打包的Python科学发行包,如Anaconda、Enthought Python发行包或者Python(x,y)进行安装。
其他所需的软件主要是一些Python安装包,读者可全部通过Python安装管理器pip进行安装。pip本身通过Python的easy_install安装工具安装。
本书是为那些已经了解Python编程,并想学习如何使用实用的方法对手头的数据进行可视化的开发人员和数据科学家编写的。如果你对数据可视化有所耳闻,但却不知道从何着手,本书将会从头开始指导你如何了解数据、数据格式、数据可视化,以及如何使用Python可视化数据。
你需要知道一些一般的编程概念,如果你有编程经验会非常有帮助。本书中的代码几乎是逐行讲解的。阅读本书不需要任何数学知识,书中介绍的每一个概念都有详细的讲解。此外,本书还提供了一些参考资料,以供有兴趣的人员进一步阅读。
在本书中,你会发现几个频繁出现的标题(准备工作、操作步骤、工作原理、补充说明、另请参阅)。
为了清楚地说明如何完成一个实战练习,我们包括以下几个小节。
本节告诉你练习要达到的目的,以及完成练习所需的软件或其他准备工作。
本节包括练习中需要遵循的步骤。
本节通常是对上节提到的内容进行详细的讲解。
本节包括了一些练习相关的附加内容,以帮助读者对该练习有更深入的了解。
本节提供了练习相关的更多有用的信息。
在本书中,不同的信息由一些不同体例的文本来区分。这里有一些文本体例的例子以及它们的含义解释。
书中的代码文字、数据库表名、文件夹名称、文件名、文件扩展名、路径名、模拟URL、用户输入和Twitter用户名的显示格式如下:“我们把小演示程序封装在DemoPIL类中,这样可以共享示例函数run_fixed_filters_demo
的代码,并能很容易地对其进行扩展。”
代码块设置如下:
def _load_image(self, imfile):
self.im = mplimage.imread(imfile)
当我们想要让你关注代码块中的某一特定部分时,相关的行或元素将设置为粗体:
for a in range(10):
print a
所有的命令行输入或者输出的写法如下:
$ sudo python setup.py install
警告或者重要的说明出现在这样的一个文本框中。
表示提示和技巧。
我们欢迎读者的反馈意见。如果对本书有任何的想法,喜欢或者不喜欢哪些内容,都可以告诉我们。这些反馈意见对于帮助我们创作出对大家真正有所帮助的作品至关重要。
你可以将一般的反馈以电子邮件的形式发送到feedback@packtpub.com,并在邮件主题中包含书名。
如果你在某一方面很有造诣,并且愿意著书或参与合著,可以参考我们的作者指南。
本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。
本书提供如下资源:
本书源代码;
书中彩图文件。
要获得以上配套资源,请在异步社区本书页面中点击 ,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。
作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。
当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,点击“提交勘误”,输入勘误信息,单击“提交”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。
我们的联系邮箱是contact@epubit.com.cn。
如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。
如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线提交投稿(直接访问www.epubit.com/selfpublish/submission即可)。
如果您是学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。
如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。
“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。
“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。
异步社区
微信服务号
本章包含以下内容。
本章介绍一些必备的工具类库,以及如何进行安装与配置。本章是后面章节的基础,掌握这部分内容十分必要。建议那些没有使用过Python进行数据处理、图像处理以及数据可视化经验的读者不要跳过本章。如果跳过,可以在需要安装配套工具软件或需要确定工程所支持的软件版本时,再返回本章阅读相关内容。
本章介绍了matplotlib及其依赖的软件在Linux平台上的几种安装方法。
这里假设你已经安装了Linux系统并且已安装好Python(推荐使用Debian/Ubuntu或RedHat/SciLinux)。在上面这些提到的Linux系统发行版中,Python通常是已经默认安装了的。如果没有,也可以使用标准的软件安装方式非常方便地进行安装。本书假设你安装的Python版本为2.7或以上。
几乎所有的代码均可在Python 3.3及以上版本的环境下工作,但是因为大部分操作系统提供的Python版本仍然是2.7(甚至是2.6),本书代码基于Python 2.7版本。这种Python版本间的区别并不大,主要是在软件包版本和部分代码上存在差别(在Python3.3以上版本,请用range方法替换xrange方法)。
本书也假设读者知道如何使用操作系统的软件包管理工具进行软件包的安装,并且知道如何使用命令行终端。
构建matplotlib运行环境,需要满足相关软件依赖。
Matplotlib的构建过程依赖NumPy、libpng和freetype软件包。要从源代码构建matplotlib,必须先要安装好NumPy库。读者可以访问其官网了解安装NumPy库的方法(请安装1.4或以上版本,Python 3需要NumPy 1.5或以上版本)。
NumPy库提供处理大数据集的数据结构和数学方法。诸如元组、列表或字典等Python的默认数据结构同样可以很好地支持数据的插入、删除和连接。NumPy 的数据结构支持 “矢量”操作,使用简便,同时具有很高的执行效率。矢量操作在实现时充分考虑了大数据的需要,基于C语言的实现方式也保证了执行效率。
基于NumPy构建的SciPy库,是Python的标准科学计算和数学计算工具包,包含了大量的专用函数和算法。而大部分函数和算法源自著名的Netlib软件仓库(参见其官网),但实际上是使用C语言和Fortran语言实现的。
安装NumPy库的步骤如下。
(1)安装Python-NumPy软件包。
sudo apt-get install python-numpy
(2)检查软件包版本。
$ python -c 'import numpy; print numpy.__version__'
(3)安装所需的库。
$ sudo apt-get build-dep python-matplotlib
如果你使用的操作系统是RedHat或基于RedHat的Linux发行版(Fedora、SciLinux或Centos),可以使用yum工具进行安装,方法与apt-get工具类似。
$ su -c 'yum-builddep python-matplotlib'
安装matplotlib及其依赖软件的方法有很多:从源代码安装、使用预编译完成的二进制文件安装、通过操作系统软件包管理工具安装,或安装内置了matplotlib的python预打包发布版本。
使用包管理工具大概是最简单的安装方式。例如在Ubuntu系统中,在命令行终端中输入以下命令:
# in your terminal, type:
$ sudo apt-get install python-numpy python-matplotlib python-scipy
如果读者期望使用matplotlib的最新特性,最佳选择是通过源代码进行安装。安装方式包含以下步骤:获取源代码、构建依赖库和参数配置、编译以及安装。
从代码托管站点SourceForge下载最新代码进行安装,操作步骤如下。
$ cd ~/Downloads/
$ wget https://downloads.sourceforge.net/project/matplotlib/matplotlib/
matplotlib-1.4.3/matplotlib-1.4.3.tar.gz[1]
$ tar xzf matplotlib-1.4.3.tar.gz
$ cd matplotlib-1.4.3
$ python setup.py build
$ sudo python setup.py install
下载示例代码
如果你是在Packt官网上购买图书,你可以在网站上下载你所购所有图书的示例代码文件。如果你是在其他地方购得本书,可以访问官网进行注册,代码文件会通过电子邮件直接发送给你,你也可以在异步社区www.epubit.com上下载。
这里我们使用标准的Python发布工具Distutils以从源代码安装matplotlib。安装过程需要提前安装依赖的软件包。关于使用标准Linux包管理工具安装依赖软件的具体方法,可参考1.2.1节。
针对不同的数据可视化项目,你可能需要安装一些额外的可选软件包。
无论在什么项目上工作,我们都推荐你安装IPython。IPython是一款交互式Python命令行工具,它已经提供了matplotlib库及其相关软件包(例如NumPy和SciPy),你可以直接使用。IPython工具的安装与使用方法十分简单明了,读者可通过 IPython的官方网站查看相关细节。
如果同时在多个项目上工作,或是需要在不同项目间频繁切换,你会发现将所有的软件都安装在操作系统层级上不是一个最佳选择。因为当需要在不同系统(产品环境)上运行软件时,这种方式会带来问题。如果此时才发现缺少特定的软件包,或是产品环境中已经安装的软件包存在版本冲突,将是非常痛苦的。为避免这种情况发生,可以选择使用virtualenv。
virtualenv是由Ian Bicking创建的开源项目。通过这个项目,开发人员可以把不同项目的工作环境隔离开,从而能够更容易地维护多种不同的软件包版本。
举例来说,有一个遗留Django网站系统是基于Django 1.1和 Python 2.3版本开发的,但与此同时,一个新项目要求必须基于Python2.6来开发。在我工作过的项目中,根据项目的需要同时使用多个版本的Python(以及相关软件包)的情况非常普遍。
virtualenv能够让我们很容易地在不同的运行环境之间切换。如果需要切换到另外的机器或者需要在产品服务器(或客户的工作站主机)上部署软件, 用virtualenv能够很容易地重新构建出相同的软件包环境。
安装virtualenv需要用到Python和pip。pip是Python软件包的安装和管理工具,它是easy_install的替代工具。本书中大部分的软件包都是用pip工具进行管理的。只需在终端中以root身份执行如下命令,就可以很容易地完成pip的安装:
# easy_install pip
virtualenv本身已经相当不错了,然而如果配合virtualenvwrapper,一切会变得更加简单,并且组织多个虚拟环境的工作也会更加容易。virtualenvwrapper的功能特性请参考其官网。
安装virtualenv和virtualenvwrapper工具的步骤如下。
(1)安装virtualenv和virtualenvwrapper。
$ sudo pip install virtualenv
$ sudo pip install virtualenvwrapper
# 创建保存虚拟环境的目录,并使用export导出为环境变量。
$ export VIRTENV=~/.virtualenvs
$ mkdir -p $VIRTENV
# 使用source命令调用(执行)shell脚本来激活它。
$ source /usr/local/bin/virtualenvwrapper.sh
# 创建一个虚拟环境
$ mkvirtualenv virt1
(2)在创建的virt1环境中安装matplotlib。
(virt1)user1:~$ pip install matplotlib
(3)最好把以下代码添加到~/.bashrc
中。
source /usr/local/bin/virtualenvwrapper.sh
下面是一些有用的频繁使用的命令。
mkvirtualenv ENV
:创建名为ENV的虚拟环境并激活。workon ENV
:激活先前创建的ENV虚拟环境。deactivate
:退出当前虚拟环境。pip不但提供了安装软件包的方法,而且可以记录操作系统上安装的Python软件包及版本。命令pip freeze能打印出当前环境所安装的软件包和版本号。
$ pip freeze
matplotlib==1.4.3
mock==1.0.1
nose==1.3.6
numpy==1.9.2
pyparsing==2.0.3
python-dateutil==2.4.2
pytz==2015.2
six==1.9.0
wsgiref==0.1.2
我们看到,即使我们只安装了matplotlib,仍然有许多其他软件包被安装到系统中。除去pip本身依赖的wsgiref,其他的都是matplotlib依赖的软件包,它们随着matplotlib被自动安装进来。
当把项目从一个环境(很有可能是一个虚拟环境)迁移到另一个环境中时,目标环境同样需要安装所有必须的软件包(和源环境相同版本),来保证代码能正常运行。如果两个不同的环境包含不同的软件包、甚至是相同软件包的不同版本,是非常容易出问题的。这会导致程序执行过程中的冲突或者无法预期的行为。
为了避免这个问题,pip freeze可以用来保存一份当前环境配置的复制文件。以下命令会把命令的结果保存到requirements.txt文件中:
$ pip freeze > requirements.txt
这个文件可以用来在新环境上安装所有必需的库:
$ pip install –r requirements.txt
运行以上命令,所有必需的软件包的指定版本会被自动安装到系统中。这样我们就确保了代码运行的环境始终是一致的。为每一个开发项目创建一个虚拟环境和一个requirements.txt文件是一个非常好的实践。因此建议读者在安装软件包前先创建一个新的虚拟环境,以避免和其他项目发生冲突。
下面是从一个机器迁移到另一个机器的全部流程。
$ mkvirtualenv env1
(env1)$ pip install matplotlib
(env1)$ pip freeze > requirements.txt
$ mkvirtualenv env2
(env2)$ pip install -r requirements.txt
在Mac OS X上获取matplotlib最简便的方式是使用预打包的python发布版本,例如Enthought Python Distribution(EPD)。读者可以直接访问EPD网站,下载并安装操作系统对应的最新稳定版。
倘若EPD软件不满足要求,或者因为其他一些原因(如版本问题)而无法使用,也可以用手动(麻烦点)的方式安装Python、matplotlib和其依赖软件。
对于Apple没有在操作系统中预装的软件,包括Python和matplotlib,Homebrew(或者MacPorts)项目可以使其安装过程变得很容易。实际上,Homebrew是基于Ruby和Git的,可以被自动下载和安装。软件安装顺序为:首先安装Homebrew,之后安装Python,随后安装诸如virtualenv的工具软件,接下来安装matplotlib的依赖(NumPy和SciPy),最后安装matplotlib。接下来就开始吧。
(1)在终端中输入并执行下面的命令。
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/
install/master/install)"
命令执行完成后,试着运行brew update 或brew doctor命令来检查brew是否能够正常工作。
(2)然后,将Homebrew
目录添加到系统path环境变量中。这样,使用Homebrew安装的软件包能够获得比其他版本更高的优先级。打开~/.bash_profile
文件(或者/Users/[your-user-name]/.bash_profile
)并在文件末尾添加以下代码。
export PATH=/usr/local/bin:$PATH
(3)重新启动命令行终端使其加载新的path环境变量。之后,以下这行简单的代码就可以完成Python的安装。
brew install python --framework –universal
本命令同时也将安装Python所需的其他软件。
(4)更新path环境变量(添加到同一行)。
export PATH=/usr/local/share/python:/usr/local/bin:$PATH
(5)在命令行输入python –version
,检查python是否安装成功。
正常的话,会看到Python版本信息为2.7.3。
(6)pip应该也已经安装完毕。如果还没有,可使用easy_install
安装pip。
$ easy_install pip
(7)这时,任何所需软件包的安装过程就变得非常简单了。例如,安装virtualenv和virtualenvwrapper:
pip install virtualenv
pip install virtualenvwrapper
(8)是时候向一直以来的目标迈进了——安装matplotlib。
pip install numpy
brew install gfortran
pip install scipy
(9)检查安装是否成功,启动Python并执行以下命令。
import numpy
print numpy.__version__
import scipy
print scipy.__version__
quit()
(10)安装matplotlib。
pip install matplotlib
在本节中,我们将演示如何安装Python和matplotlib。这里假设系统中没有预先安装Python。
在Windows上安装matplotlib有两种方式。最简单的方式是安装预打包的Python环境,如EPD、Anaconda、SageMath和Python(x,y)。这也是本书推荐的安装方式,尤其对于初学者来说。第二种方式是使用预编译的二进制文件来安装matplotlib和依赖软件包。这里需要注意安装的NumPy和SciPy的版本,因为并非所有的版本都与最新版matplotlib二进制文件相互兼容,这势必会给整个安装过程带来一些困难。这种安装方法也有自身的优势,如果想要获取最新功能,即使功能还未正式发布,仍然能够通过编译matplotlib或某软件库的某个特定版本来使用它。
要安装免费或商业Python科学发布版,按照其项目网站上提供的步骤可以很容易安装成功,这也是推荐使用的安装方式。
如果单纯使用matplotlib,不期望被Python和依赖软件包版本所困扰,可以考虑使用Enthought Python Distribution(EPD)发布版。使用matplotlib所需的预打包库和所有必须的依赖软件(SciPy、NumPy、IPython以及其他)均已包含在EPD发布版中。
通常,先下载安装Windows Installer安装文件(*.exe),然后就可以使用matplotlib来做本书中的练习了。
Python(x,y)是针对Windows 32位系统的免费科学计算项目,其中包含了matplotlib需要使用的依赖文件,它是在Windows系统上安装matplotlib的一种非常简单(而且是免费的)的方式。因为Python(x,y)和Python模块安装器相互兼容,所以可以很容易地在Python (x,y)基础上扩展安装其他Python库。在安装Python(x,y)之前请确保系统没有安装Python。
下面简短地说明一下如何使用预编译的Python、NumPy、SciPy和matplotlib二进制文件进行matplotlib的安装。
(1)首先,下载官方的.msi安装文件安装对应平台(x86或x86-64)的标准Python程序。
(2)然后,下载并安装NumPy和SciPy的官方二进制文件。
(3)在正确安装NumPy和SciPy之后,下载最新稳定版matplotlib二进制安装文件,并按照官方说明进行安装。
请注意,在Windows安装文件中matplotlib的示例相当有限。如果想尝试使用示例程序,可以下载并参考matplotlib源文件包中的examples子目录。
Python图像库(PIL)为Python提供了图像处理能力。PIL支持的文件格式相当广泛,在图像处理领域提供了相当强大的功能。
快速数据访问、点运算(point operations)、滤波(filtering)、图像缩放、旋转、任意仿射转换(arbitrary affine transforms)是PIL中一些应用非常广泛的特性。例如,图像的统计数据即可通过histogram方法获得。
PIL 同样可以应用在其他方面,如批量处理、图像压缩、缩略图生成、图像格式转换以及图像打印。
PIL 可以读取多种图像格式,而图像写入支持的格式范围限定在图像交换和展示方面最通用的格式(有意为之)。
最容易也是最值得推荐的方式,是通过操作系统平台的包管理工具进行安装。
在Debian/Ubuntu系统中安装的命令如下。
$ sudo apt-get build-dep python-imaging
$ sudo pip install http://effbot.org/downloads/Imaging-1.1.7.tar.gz
我们通过apt-get
系统工具安装PIL所需的所有依赖软件,并通过pip安装PIL的最新稳定版本。一些老版本的Ubuntu系统通常没有提供PIL的最新发布版本。
在RedHat/SciLinux系统中,安装命令如下。
# yum install python-imaging
# yum install freetype-devel
# pip install PIL
有一个专门针对PIL编写的在线手册。读者可以访问其官网进行阅读,或是下载PDF版本。
Pillow是PIL的一个分支,其主要目的是解决安装过程中的一些问题。Pillow很容易安装(在写作本书期间,Pillow
是OS X系统上的唯一选择)。
在Windows平台上,可使用二进制安装文件安装PIL。下载.exe
安装文件,执行该文件将安装PIL到Python的site-packages目录。
如果需要在虚拟环境下使用PIL,可手动将PIL.pth
文件和位于C:\Python27\Lib\site-packages
下的PIL目录复制到virtualenv的site-packages目录下。
我们需要的大部分数据都可以通过HTTP或类似协议获得,因此我们需要一些工具来实现数据访问。Python的requests库能让这部分工作变得轻松起来。
虽然Python提供的urllib2
模块提供了访问远程资源的能力以及对HTTP协议的支持,但使用该模块完成一些基本任务的工作量还是很大的。
requests模块提供新的API,减轻了使用web服务的痛苦,使其变得更直接。requests封装了很多HTTP 1.1的内容,仅在需要实现非默认行为时才暴露相关内容。
安装requests
模块最好的方式是使用pip,安装命令如下。
$ pip install requests
如果并不是所有项目都需要requests,或是不同的项目需要使用不同版本的requests,也可以在virtualenv虚拟环境中执行该安装命令。
为了让读者很快地熟悉requests的功能,下面给出一个使用requests的小例子。
import requests
r = requests.get('http://github.com/timeline.json')
print r.content
在本例中,我们向GitHub站点的URI发送HTTP GET
请求,以JSON格式返回了GitHub网站的活动时间表(也可以通过访问官网得到HTML版本的活动时间表)。在成功读取HTTP响应后,对象r包含了HTTP响应内容以及其他属性信息(HTTP状态码、cookies、HTTP头元数据,甚至包括当前响应所对应的请求信息)。
matplotlib库提供了强大的绘图功能,是本书使用最多的Python库。在其配置文件即.rc
文件中,已经为大部分属性设定了默认值。本节会介绍如何通过应用程序代码修改matplotlib的相关属性值。
如前所述,matplotlib配置信息是从配置文件读取的。在配置文件中可以为matplotlib的几乎所有的属性指定永久有效的默认值。
在代码执行过程中,有两种方式可以更改运行参数:使用参数字典(rcParams
)或调用matplotlib.rc()
命令。在第一种方式中,我们可以通过rcParams
字典访问并修改所有已经加载的配置项;在第二种方式中,我们可以通过向matlotlib.rc()
传入属性的关键字元组来修改配置项。
如果需要重置动态修改后的配置参数,可以调用matplotlib.rcdefaults()
将配置重置为标准设置。
下面两段代码演示了上面介绍的方式。
matplotlib.rcParams
的例子。import matplotlib as mp1
mpl.rcParams['lines.linewidth'] = 2
mpl.rcParams['lines.color'] = 'r'
matplotlib.rc()
函数调用的例子。import matplotlib as mpl
mpl.rc('lines', linewidth=2, color='r')
上面两个例子具有相同的语义。第二个例子中,我们设定后续的所有图形使用的线条宽度为2个点。第一个例子中的最后一条语句表明,除非用本地设置覆盖它,否则该语句之后的所有线条的颜色均为红色。请看下面的例子。
import matplotlib.pyplot as plt
import numpy as np
t = np.arange(0.0, 1.0, 0.01)
s = np.sin(2 * np.pi * t)
# 设置线条颜色为红色
plt.rcParams['lines.color'] = 'r'
plt.plot(t,s)
c = np.cos(2 * np.pi * t)
# 设置线宽
plt.rcParams['lines.linewidth'] = '3'
plt.plot(t,c)
plt.show()
首先,为了绘制正弦和余弦曲线,我们导入了matplotlib.pyplot
和NumPy
模块。在绘制第一个图像之前,通过plt.rcParams['lines.color']= 'r'
语句显式地设置线条颜色为红色。接下来,对于第二个图像(余弦曲线),通过语句plt.rcParams ['lines.linewidth'] = '3'
显式地设定线宽为3个点。
调用matplotlib.rcdefaults()
方法可以重置该设置。
在本节中,我们看到如何通过动态改变配置参数来改变matplotlib图表的风格。matplotlib.rcParams对象是我们修改参数的接口,对于matplotlib它是全局的对象,任何对它的改变都会影响接下来绘制的所有图表。
本节介绍matplotlib使用的各种配置文件的位置,以及这些配置文件的意义,同时还将介绍配置文件中的具体配置项。
如果不想在每次使用matplotlib时都在代码开始部分进行配置(像1.8节我们做的那样),就需要为不同的项目设定不同的默认配置项。本节将介绍如何做到这一点。这种配置方式使得配置项与代码分离,从而使代码更加整洁。此外,你可以很容易在同事间甚至项目间分享配置模板。
如果一个项目中对于matplotlib的特性参数总会设置相同的值,就没有必要在每次编写新的绘图代码时都进行相同的配置。取而代之的,应该是在代码之外,使用一个永久的文件设定matplotlib参数默认值。
通过matplotlibrc 配置文件,matplotlib提供了对这种配置方式的支持。在matplotlibrc文件中包含了绝大部分可以变更的属性。
配置文件可能存在于3个不同的位置,而它们的位置决定了它们的应用范围。这3个位置说明分别如下。
matplotlibrc
。$HOME
目录下(在Windows
系统中,也就是Documents and Settings
目录)。可以用matplotlib.get_configdir()
命令来找到当前用户的配置文件目录。请参考随后的命令示例。在shell中运行下面的命令,即可打印出配置文件目录的位置:
$ python -c 'import matplotlib as mpl; print mpl.get_configdir()'
配置文件包括以下配置项。
TkAgg
和GTKAgg
。如果你想了解前面提到的(和我们没有提到的)每个设置的详细信息,最好的方式是访问matplotlib项目的网站,那里提供了最新的API文档。如果需要获得进一步帮助,可以在用户和开发邮件组留言。本书最后也提供了一些有用的在线资源。
[1] 作者给出的是1.3.1版本下载地址,应为作者笔误。此外,版本1.4.3是作者成书时的最新版本。 ——译者注
本章包含以下内容。
在本章中,我们将更多注意力放在展现的数据所表达的含义上,以及如何通过图表把它有效地表达出来。我们将展示一些新的技术和图表,当知道想要传达给用户什么信息后,我们对这些图表的理解会更深刻。有这样的一个问题:“为什么要以这种方式展示数据?”这在数据探索阶段是最重要的一个问题。如果没能很好地理解数据就把它以某种形式展示出来,那么毫无疑问,读者也将难以正确地理解这些数据。
很多情况下,在读日报及类似的文章时,人们常常发现媒体机构用图表歪曲了事实。一个常见的例子是用线性标度来创建所谓的恐慌图。图表中有一个在很长一段时间(若干年)内持续增长的值,其起始值要比最新的值小好几个量级。然而在正确的可视化时,这些值可以(并且通常应该)用线形图或者近似线性的图表表示,把它们要强调的一些恐慌因素忽略。
使用对数标度时,连续值的比例是常量。这在读对数图表时是非常重要的。使用线性(算术)标度时,连续值之间的距离是常量。换句话说,对数图表按数量级顺序有一个常量的距离。这在接下来的图表中可以看到,生成图表的代码在后面也会解释。
根据一般经验,遇到以下情况应该使用对数标度。
不要盲目地遵循这些规则,它们更像是指导,而不是规则,要始终依靠你自己对于手头的数据和项目,或者客户对你提出的需求作判断。
根据数据范围的不同,我们应该使用不同的对数底。对数的标准底是10,但是如果数据范围比较小,以2为底数会好一些,因为其会在一个较小的数据范围下有更多的分辨率。
如果有适合在对数标度上显示的数据范围,我们会注意到,以前非常靠近而难以判断差异的值现在很好地区分开了。这让我们很容易读懂原来在线性标度下难以理解的数据。
对于长时间范围的数据的增长率图表,我们想看的不是在时间点所测量的绝对值,而是其在时间上的增长。虽然我们仍可以得到绝对值信息,但是这些信息的优先级较低。
再者,如果数据分布存在一个正偏态,例如工资,取值(工资)的对数能让数据更合乎模型,因为对数变换能提供一个更加正常的数据分布。
我们将用一段代码来证明上面所述的内容。这段代码用不同的标度(线性和对数)在两个不同的图表中显示了两个相同的数据集合(一个线性的,一个对数的)。
我们将借助后面的代码实现下面的步骤。
(1)生成两个简单的数据集合:指数/对数y和线性z。
(2)创建一个包含4个子区的图形。
(3)创建两个包含数据集合y的子区:一个为对数标度,一个为线性标度。
(4)创建两个包含数据集合z的子区:一个为对数标度,一个为线性标度。
代码如下:
from matplotlib import pyplot as plt
import numpy as np
x = np.linspace(1, 10)
y = [10 ** el for el in x]
z = [2 * el for el in x]
fig = plt.figure(figsize=(10, 8))
ax1 = fig.add_subplot(2, 2, 1)
ax1.plot(x, y, color='blue')
ax1.set_yscale('log')
ax1.set_title(r'Logarithmic plot of $ {10}^{x} $ ')
ax1.set_ylabel(r'$ {y} = {10}^{x} $')
plt.grid(b=True, which='both', axis='both')
ax2 = fig.add_subplot(2, 2, 2)
ax2.plot(x, y, color='red')
ax2.set_yscale('linear')
ax2.set_title(r'Linear plot of $ {10}^{x} $ ')
ax2.set_ylabel(r'$ {y} = {10}^{x} $')
plt.grid(b=True, which='both', axis='both')
ax3 = fig.add_subplot(2, 2, 3)
ax3.plot(x, z, color='green')
ax3.set_yscale('log')
ax3.set_title(r'Logarithmic plot of $ {2}*{x} $ ')
ax3.set_ylabel(r'$ {y} = {2}*{x} $')
plt.grid(b=True, which='both', axis='both')
ax4 = fig.add_subplot(2, 2, 4)
ax4.plot(x, z, color='magenta')
ax4.set_yscale('linear')
ax4.set_title(r'Linear plot of $ {2}*{x} $ ')
ax4.set_ylabel(r'$ {y} = {2}*{x} $')
plt.grid(b=True, which='both', axis='both')
plt.show()
代码将生成图7-1所示的图表。
图7-1
我们生成一些样本数据和两个相关的变量:y和z。变量y被表示为数据x的指数函数,变量z是x的简单线性函数。这展示了线性图表和指数图表的区别。
然后创建4个子区,上面一行子区是关于数据(x,y)的,下面一行子区是关于数据(x,z)的。
从左手边看,y轴列为对数标度;从右手边看,y轴列为线性标度。通过set_yscale('log')
分别对每一个坐标轴进行设置。
我们为每一个子区设置标题和标签,标签描述了所绘制的函数。
通过plt.grid(b=True, which='both', axis='both')
,我们为每个图的两个坐标轴和主次刻度打开网格显示。
我们观察到,在线性图表中线性函数是直线,在对数图表中对数函数也是直线。
频谱图是随时间变化的频谱表现,它显示了信号的频谱强度随时间的变化。
频谱图是把声音或者其他信号的频谱以可视化的方式呈现出来。它被用在很多科学领域中,从声音指纹如声音识别,到雷达工程学和地震学。
通常,频谱图的布局如下:x轴表示时间,y轴表示频率,第三个维度是频率—时间对的幅值,通过颜色表示。因为这是三维的数据,因此我们也可以创建3D图表来表示,其中强度表示为z轴上的高度。3D图表的问题是人们不太容易理解以及进行比较,而且它比2D图表占用更多的空间。
对于严谨的信号处理,我们将会研究更低级别的细节,进而能从中发现模式并自动识别一定的特征。但是对于本节数据可视化的内容,我们将借助一些著名的Python库来读取音频文件,对它进行采样,然后绘制出频谱图。
为了能读取WAV文件并把声音可视化出来,需要做一些准备工作。我们需要安装libsndfile1
系统库来读/写音频文件。这可以通过你喜欢的包管理工具完成。对于Ubuntu,使用以下命令:
$ sudo apt-get install libsndfilel-dev.
安装dev
包非常重要,它包含了头文件,从而通过pip
可以创建scikits.audiolab
模块。
我们也可以安装libasound
和ALSA(Advanced Linux Sound Architecture,高级Linux声音体系)头来避免编译时警告。这是可选的,因为我们不打算使用ALSA库提供的特性。对于Ubuntu Linux,执行以下命令:
$ sudo apt-get install libasound2-dev
我们用pip
安装用来读取WAV文件的scikits.audiolab
:
$ pip install scikits.audiolab
永远记住要进入当前工程的虚拟环境,因为这样才不会弄脏你的系统库。
本节将使用预录制的声音文件test.wav
,该文件可以在本书的文件代码库中找到,但你也可以自己生成一个样本文件。
在这个例子中,我们顺序地执行下面的步骤。
(1)读取包含一个已经录制的声音样本的WAV文件。
(2)通过NFFT
设置用于傅里叶变换的窗口长度。
(3)在采样时,使用noverlap
设置重叠的数据点。
import os
from math import floor, log
from scikits.audiolab import Sndfile
import numpy as np
from matplotlib import pyplot as plt
# Load the sound file in Sndfile instance
soundfile = Sndfile("test.wav")
# define start/stop seconds and compute start/stop frames
start_sec = 0
stop_sec = 5
start_frame = start_sec * soundfile.samplerate
stop_frame = stop_sec * soundfile.samplerate
# go to the start frame of the sound object
soundfile.seek(start_frame)
# read number of frames from start to stop
delta_frames = stop_frame - start_frame
sample = soundfile.read_frames(delta_frames)
map = 'CMRmap'
fig = plt.figure(figsize=(10, 6), )
ax = fig.add_subplot(111)
# define number of data points for FT
NFFT = 128
# define number of data points to overlap for each block
noverlap = 65
pxx, freq, t, cax = ax.specgram(sample, Fs=soundfile.samplerate,
NFFT=NFFT, noverlap=noverlap,
cmap=plt.get_cmap(map))
plt.colorbar(cax)
plt.xlabel("Times [sec]")
plt.ylabel("Frequency [Hz]")
plt.show()
代码生成的频谱图如图7-2所示。
图7-2
NFFT定义了每一个块中用于计算离散傅里叶变换的数据点的数量。当NFFT的值为2的幂次方时计算效率最高。窗口可以重叠,重叠(也就是重复)的数据点数量通过参数
noverlap
指定。
首先需要加载一个声音文件,这通过调用scikits.audiolab.SndFile
方法并传入一个文件名来完成。该方法将实例化一个声音对象,通过该对象我们可以查询数据并调用其中的方法。
为了读取频谱图所需要的数据,需要从声音对象中读取数据帧。这通过read_frames()
完成,该方法接收开始帧和结束帧的参数。把采样率和想要可视化的时间点(start, end
)相乘便可以计算出帧数量。
如果找不到音频文件(wave),可以生成一个。生成方法很简单,具体如下。
import numpy
def _get_mask(t, t1, t2, lvl_pos, lvl_neg):
if t1 >= t2:
raise ValueError("t1 must be less than t2")
return numpy.where(numpy.logical_and(t > t1, t < t2), lvl_pos,
lvl_neg)
def generate_signal(t):
sin1 = numpy.sin(2 * numpy.pi * 100 * t)
sin2 = 2 * numpy.sin(2 * numpy.pi * 200 * t)
# add interval of high pitched signal
sin2 = sin2 * _get_mask(t,2,5,1.0,0.0)
noise = 0.02 * numpy.random.randn(len(t))
final_signal = sin1 + sin2 + noise
return final_signal
if __name__ == '__main__':
step = 0.001
sampling_freq=1000
t = numpy.arange(0.0, 20.0, step)
y = generate_signal(t)
# we can visualize this now
# in time
ax1 = plt.subplot(211)
plt.plot(t, y)
# and in frequency
plt.subplot(212)
plt.specgram(y, NFFT=1024, noverlap=900,
Fs=sampling_freq, cmap=plt.cm.gist_heat)
plt.show()
这将生成图7-3所示的信号,其中顶部的图形是生成的信号。这里,x轴表示时间,y轴表示信号的幅值。底部的图形是相同的信号在频率域中的呈现。这里,x轴如顶部图一样表示时间(通过选择采样率来匹配时间),y轴表示信号的频率。
图7-3
一个二维的火柴杆图(stem plot)把数据显示为沿x轴的基线延伸的线条。圆圈(默认值)或者其他标记表示每个杆的结束,其y轴表示了数据值。
本节将讨论如何创建火柴杆图。
不要把火柴杆图和茎叶图(stem and leaf plot)混淆,茎叶图是把最不重要的数值表示为叶,把较高位的值表示为茎的一种数据表现方法,如图7-4所示。
图7-4
我们将使用一个离散值序列来绘制火柴杆图,这种离散的数据用普通的线性图表是无法展示的。
绘制离散序列的火柴杆图,数据值表示为每个杆末端的标记。从基线(通常在y = 0处)延伸到数据值点的线称为杆。
我们将使用matplotlib的stem()
函数绘制火柴杆图。这个函数可以只使用一系列的y值,x值为生成的一个从0到len(y)-1的简单序列。如果把x和y序列都提供给stem()
函数,该函数会把它们用于两个坐标轴。
我们要为火柴杆图配置下面的一些格式器。
linefmt
:杆线的线条格式器。markerfmt
:火柴杆线条末端的标记用该参数格式化。basefmt
:规定基线的外观。label
:设置火柴杆图图例的标签。hold
:把所有当前图形放在当前坐标轴上。bottom
:在y轴方向设置基线位置,默认值为0。参数hold
是图表的一个常见的特性。如果它是打开状态(True
),接下来的所有图表都会被添加到当前坐标轴上。否则,每一个图形会创建新的图表和坐标轴。
执行下面的步骤来创建一个火柴杆图。
(1)生成随机噪声数据。
(2)设置火柴杆参数。
(3)绘制火柴杆。
下面是相应的代码。
import matplotlib.pyplot as plt
import numpy as np
# time domain in which we sample
x = np.linspace(0, 20, 50)
# random function to simulate sampled signal
y = np.sin(x + 1) + np.cos(x ** 2)
# here we can setup baseline position
bottom = -0.1
# True -- hold current axes for further plotting
# False -- opposite. clear and use new figure/plot
hold = False
# set label for legend.
label = "delta"
markerline, stemlines, baseline = plt.stem(x, y, bottom=bottom,
label=label, hold=hold)
# we use setp() here to setup
# multiple properties of lines generated by stem()
plt.setp(markerline, color='red', marker='o')
plt.setp(stemlines, color='blue', linestyle=':')
plt.setp(baseline, color='grey', linewidth=2, linestyle='-')
# draw a legend
plt.legend()
plt.show()
以上代码生成的图形如图7-5所示。
图7-5
首先我们需要准备一些数据。对于本节来说,生成的伪采样信号已经够用了。在真实世界里,任何离散序列数据都适合用火柴杆图来呈现。我们用Numpy的numpy.linspace
、numpy.cos
和numpy.sin
函数生成该信号。
然后,设置火柴杆图的标签和基线的位置,基线位置的默认值为0.0。
如果想要绘制多个火柴杆图,可以将hold
的值设置为True
,这样所有火柴杆图将会被渲染在相同的坐标轴中。
调用matplotlib.stem
会返回3个对象。第一个是markerline
,它是一个Line2D
的实例,保存了表示火柴杆本身的线条的引用。它仅仅渲染了标记,不包括连接标记的线条。可以通过编辑该Line2D
实例的属性让线条可见,操作步骤会在后面讲解。最后一个对象baseline
也是一个Line2D
实例,保存了表示stemlines
原点的水平线条的引用。返回的第二个对象是stemlines
,它表示茎线的Line2D
实例的集合(目前是Python列表)。
通过setp
函数把属性应用到这些对象或这些对象集合的所有的线条(Line2D
实例)上,我们用返回的对象来处理火柴杆图的可视化需求。
你可以尝试一些设置,来理解setp
是如何改变图形风格的。
流线图可以被用来可视化矢量场的流态。如科学和自然学科中的磁场、万有引力和流体运动等均可以用流线图表示。
矢量场通过为每个点指定一个线条和一个或多个箭头的方式进行可视化。强度可以用线条长度表示,方向由指向特定方向的箭头表示。
通常,力的强度用特定流线的长度显示,但是有时也可以用流线的密度来表示。
我们可以用matplotlib的matplotlib.pyplot.streamplot
函数来可视化矢量场。该函数通过在流场中均匀地填充流线来创建图形。最初该函数是用来可视化风模型或者液体流动的,因此不需要严格的矢量线条,而是需要矢量场的统一表现形式。
该函数最重要的参数是(X
,Y
),它是一维Numpy数组的等距网格。(U
,V
)参数匹配的是(X
,Y
)速率的二维Numpy数组。U
和V
矩阵在维度上的行数必须等于Y
的长度,列的数量必须匹配X
的长度。
流线图的线条宽度可以单独控制,如果linewidth
参数是一个二维数组,将匹配u
和v
速率的形状,或者可以是所有线条都可以接受的一个简单的整数。
对于所有流线,颜色不仅是一个值,而且可以是如linewidth
参数一样的矩阵。
箭头(FancyArrowPatch
类)用来表示矢量方向,可以通过两个参数控制它们。arrowsize
改变箭头的大小,arrowstyle
改变箭头的格式(例如“simple
”“->
”)。
我们通过一个简单的例子来了解一下流线图。执行下面的步骤。
(1)创建矢量数据。
(2)打印中间值。
(3)绘制流线图。
(4)显示用来可视化矢量的流线的图形。
示例代码如下。
import matplotlib.pyplot as plt
import numpy as np
Y, X = np.mgrid[0:5:100j, 0:5:100j]
U = X
V = Y
from pprint import pprint
print "X"
pprint(X)
print "Y"
pprint(Y)
plt.streamplot(X, Y, U, V)
plt.show()
上述代码会输出以下文本信息。
X
array([[ 0. , 0.05050505, 0.1010101 , ..., 4.8989899 ,
4.94949495, 5. ],
[ 0. , 0.05050505, 0.1010101 , ..., 4.8989899 ,
4.94949495, 5. ],
[ 0. , 0.05050505, 0.1010101 , ..., 4.8989899 ,
4.94949495, 5. ],
...,
[ 0. , 0.05050505, 0.1010101 , ..., 4.8989899 ,
4.94949495, 5. ],
[ 0. , 0.05050505, 0.1010101 , ..., 4.8989899 ,
4.94949495, 5. ],
[ 0. , 0.05050505, 0.1010101 , ..., 4.8989899 ,
4.94949495, 5. ]])
Y
array([[ 0. , 0. , 0. , ..., 0. ,
0. , 0. ],
[ 0.05050505, 0.05050505, 0.05050505, ..., 0.05050505,
0.05050505, 0.05050505],
[ 0.1010101 , 0.1010101 , 0.1010101 , ..., 0.1010101 ,
0.1010101 , 0.1010101 ],
...,
[ 4.8989899 , 4.8989899 , 4.8989899 , ..., 4.8989899 ,
4.8989899 , 4.8989899 ],
[ 4.94949495, 4.94949495, 4.94949495, ..., 4.94949495,
4.94949495, 4.94949495],
[ 5. , 5. , 5. , ..., 5. ,
5. , 5. ]])
上面代码生成的流线图图表如图7-6所示。
图7-6
使用Numpy的mgrid
实例,通过检索二维的网状栅格,我们创建了X
和Y
的矢量场。指定网格的范围作为起点和终点(相应的为-2和2)。第三个索引表示步长。步长表示的是起点和终点之间包含的点的数量。如果想要包含终点值,可以使用一个复数作为步长,其中幅值表示起点和终点之间需要的点数量,包含终点。
然后,填充的网状栅格被用于计算矢量的速率。这里,为了演示,我们就简单地使用相同的meshgrid
属性作为矢量速率。这将生成一个图形,该图形清晰地显示了矢量场的线性依赖和流。
改变一下U
和V
的值,体会一下U
和V
是如何影响流线图的。例如,让U = np.sin(X)
或者V = sin(Y)
。然后,尝试改变起点和终点的值。图7-7是U = np.sin(X)
的图形。
图7-7
要清楚该图表是生成的线条和箭头补片的集合,因此没有办法(至少现在)更新现有的图形,因为线条和箭头对于矢量和场一无所知。将来的版本可能会实现它,但是目前它是matplotlib的一个公认的局限。
当然,这个例子只是让我们初步了解一下matplotlib流线图的特性和能力。
当你有真正的数据要可视化时,流线图就会体现出其真正的威力。理解了本节内容后,你就能知道流线图能应用在什么场合。这样当你拿到数据并知道其所属的领域后,你就能够选用最适合的工具来完成工作。
用颜色来编码数据会极大地影响观察者对可视化图形的理解,因为观察者们对不同颜色及其要表达的信息总会有一个直觉的假设。
其实,用颜色为数据添加额外的信息是件好事情。如果知道什么时候应该选用什么颜色就再好不过了。
如果你的数据不是自然地用颜色标示的(如地形/地势海拔或者物体的温度),那么最好不要人为地把它映射到自然色上。我们希望读者能恰当地理解数据,就需要选择一种能让读者容易理解的颜色。如果要展示与温度无关的财务数据,那么我们当然不希望读者把数据映射到表示温度的颜色上去。
如果数据与红色或绿色没有很强的关联时,要尽可能地避免使用这两种颜色。
为了帮助读者选择合适的颜色映射,我们将解释matplotlib
包中已有的一些颜色表。如果你了解了这些颜色表的用途,并且知道从哪里找到它们,会对你很有帮助,并且会节省很多时间。
颜色表一般可以归为以下几类。
matplotlib自带许多预定义的颜色表,我们可以把它们划分为几类。我们会为何时使用何种颜色表给出一些建议。最基本且常用的颜色表有autumn
、bone
、cool
、copper
、flag
、gray
、hot
、hsv
、jet
、pink
、prism
、sprint
、summer
、winter
和spectral
。
在Yorick科学可视化包中还有其他一些颜色表。这是从GIST包演变而来的,因此该集合中的所有颜色表名字中都有一个gist_
前缀。
Yorick科学可视化包是一个由C编写的解释型语言,最近不是非常活跃。我们可以在其官网中得到更多的信息。
这些颜色表集合包括以下表:gist_earth
、gist_heat
、gist_ncar
、gist_rainbow
和gist_stern
。
下面介绍基于ColorBrewer的颜色表,它们可以分为以下几类。
另外还有一些可用的颜色表,如表7-1所示。
表7-1
颜 色 表 |
描 述 |
---|---|
|
表示一个发散型的蓝—红—绿颜色表 |
|
表示一个发散型的蓝—白—红颜色表 |
|
对于3D阴影,色盲和颜色排序非常有用 |
|
表示一个有发散亮度的紫—蓝—绿—黄—橙—红光谱颜色表 |
|
表示一个发散型的蓝—白—红颜色表 |
|
表示地图标记的颜色(蓝、绿、黄、棕和白),最初来自IGOR Pro软件 |
这里展示的大多数颜色表都可以通过在颜色表名字后面加上_r
后缀进行反转,例如hot_r
是反向循环的hot颜色表。
在matplotlib中我们可以为许多项目设置颜色表。例如,颜色表可以设置在image, pcolor
和scatter
上。我们可以通过cmap
函数调用时传入的参数来设置颜色表。该参数是colors.Colormap
的预期实例。
我们也可以使用matplotlib.pyplot.set_cmap
为绘制在坐标轴上的最新的对象设置cmap
。
通过matplotlib.pyplot.colormaps
我们可以很容易地得到所有可用的颜色表。打开IPython,输入以下代码。
In [1]: import matplotlib.pyplot as plt
In [2]: plt.colormaps()
Out[2]:
['Accent',
'Accent_r',
'Blues',
'Blues_r',
...
'winter',
'winter_r']
注意,我们缩短了上面的输出列表,因为它包含了大约140个元素,会占用好几页。
上述代码将导入pyplot
函数接口,并允许调用colormaps
函数。colormaps
函数会返回一个所有已注册颜色表的列表。
最后,我们想向你展示如何创建一个美观的颜色表。在下面的例子中,我们需要进行以下操作。
(1)打开ColorBrewer网站,得到十六进制格式的diverging颜色表颜色值。
(2)生成随机样本x和y,其中y为所有值的累积和(模拟股票价格变动)。
(3)在matplotlib的散点图函数上做一些定制化。
(4)改变散点标记线条的颜色和宽度,使读者更容易理解。
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
# Red Yellow Green divergent colormap
red_yellow_green = ['#d73027', '#f46d43', '#fdae61',
'#fee08b', '#ffffbf', '#d9ef8b',
'#a6d96a', '#66bd63', '#1a9850']
sample_size = 1000
fig, ax = plt.subplots(1)
for i in range(9):
y = np.random.normal(size=sample_size).cumsum()
x = np.arange(sample_size)
ax.scatter(x, y, label=str(i), linewidth=0.1,
edgecolors='grey',
facecolor=red_yellow_green[i])
ax.legend()
plt.show()
上述代码将渲染出一个漂亮的图表,如图7-8所示。
图7-8
从ColorBrewer网站找到红—黄—绿diverging颜色表的颜色。然后,在代码中列出这些颜色,并把它们应用到散点图中。
ColorBrewer是一个由Cynthia Brewer、Mark Harrower编写的Web工具,宾夕法尼亚州立大学开发了其中的颜色表。它是一个非常好用的工具,可以选择不同范围的颜色表并把它们应用在地图上。这样,你可以快速地了解它们显示在一个图表上的样子。
有时候,我们不得不在matplotlib.rcParams
上做一些定制化,这是在创建一个图表或者坐标轴之前要做的第一件事情。
例如,为了为大多数matplotlib函数设置默认的颜色表,需要改变配置参数matplotlib. rcParams['axes.cycle_color']
。
通过matplotlib.pyplot.register_cmap我们
可以将一个新的颜色表注册到matplotlib,这样就可以通过get_cmap
函数找到它。我们可以通过两种不同的方式使用它,这两种签名形式如下。
register_cmap(name='swirly', cmap=swirly_cmap)
register_cmap(name='choppy', data=choppydata, lut=128)
第一种签名指定一个颜色表作为colors.Colormap
的实例,并通过name
参数注册。参数name
可以忽略,在这种情况下,它将继承cmap
实例提供的name
属性。
对于第二种签名,我们向线性分隔的颜色表构造函数传入3个参数,随后把该颜色表注册到matplotlib。
我们可以通过把name
参数传入matplotlib.pyplot.get_cmap
函数来得到相应的colors.Colormap
实例。
下面的代码展示了如何使用matplotlib.colors.LinearSegmented Colormap
创建你自己的颜色表:
from pylab import *
cdict = {'red': ((0.0, 0.0, 0.0),
(0.5, 1.0, 0.7),
(1.0, 1.0, 1.0)),
'green': ((0.0, 0.0, 0.0),
(0.5, 1.0, 0.0),
(1.0, 1.0, 1.0)),
'blue': ((0.0, 0.0, 0.0),
(0.5, 1.0, 0.0),
(1.0, 0.5, 1.0))}
my_cmap = matplotlib.colors.LinearSegmentedColormap('my_
colormap',cdict,256)
pcolor(rand(10,10),cmap=my_cmap)
colorbar()
执行该方法很简单,实际上比较难的部分是如何给出信息丰富的颜色组合。这种颜色组合不会丢掉任何可视化的数据信息,同时读者看起来赏心悦目。
对于基本的颜色列表(在之前的表中列出的颜色表),可以用pylab
快捷方式来设置颜色表。例如:
imshow(X)
hot()
这将设置图像X的颜色表为cmap = 'hot'。
我们经常会遇到散点图,因为它们是呈现两个变量之间关系时最常用的图表。如果想快速地查看两个变量的数据,并看看它们之间是否有关系(也就是相关性),我们可以快速地绘制一个散点图。对于一个散点图,必须有一个变量可以被改变,比如说,实验者系统地改变这个变量,这样就可以观察到它对另一个变量可能产生的影响。
通过本节的学习,你将学会如何理解散点图。
举个例子,比如我们想看两个事件是怎么相互影响的,或者它们是否真的相互影响。这种可视化在大数据集合上尤其有用,因为当只有数据时,我们没有办法通过查看原生格式的数据得到任何结论。
如果数值之间存在相关性,这种相关性可以是正相关也可以是负相关。正相关指在增大X的值时,Y的值也会增加。负相关是增加X的值,Y的值会减小。在理想情况下,正相关是一条从坐标轴的左下角到右上角的线段。理想的负相关是一条从坐标轴的左上角到右下角的线段。
两个数据点之间理想的正相关是值为1,理想的负相关是值为-1。所有在此区间内的值表示两个值之间存在较弱的相关性。通常,从两个变量的真正关联的角度看,-0.5与0.5之间的值被认为是没有价值的。
一个正相关的例子是,放到慈善罐中的钱的总数与看到罐子的人数呈正相关性。一个负相关的例子是,从地点B到地点A所需要的时间,取决于地点A与地点B之间的距离。距离越大,完成这段旅行所花费的时间也越长。
我们这里展示的正相关的例子并不完美,因为每次访问时,不同的人放的钱的数量可能不同。但是一般来讲,我们可以假定看到罐子的人数量越多,罐子里的钱就越多。
但是要记住,即使散点图显示了两个变量间存在相关性,但是它可能不是一个直接相关。可能有第三个变量影响所绘制的两个变量,因此相关性就仅仅是绘制的变量与第三个变量相关。最后,也许仅是看上去存在明显的相关性,但是在其背后并不存在真正的关系。
通过下面的示例代码,我们将展示散点图如何解释变量间的关联。
我们使用的数据是从Google Trends门户网站获得的,在那里可以下载到包含给定参数的相关搜索量的归一化值的CSV文件。
将数据存储在ch07_search_data.py Python
模块中,这样就可以在接下来的代码中导入它。内容如下。
# ch07_search_data
# daily search trend for keyword 'flowers' for a year
DATA = [
1.04, 1.04, 1.16, 1.22, 1.46, 2.34, 1.16, 1.12, 1.24, 1.30, 1.44,
1.22, 1.26,
1.34, 1.26, 1.40, 1.52, 2.56, 1.36, 1.30, 1.20, 1.12, 1.12, 1.12,
1.06, 1.06,
1.00, 1.02, 1.04, 1.02, 1.06, 1.02, 1.04, 0.98, 0.98, 0.98, 1.00,
1.02, 1.02,
1.00, 1.02, 0.96, 0.94, 0.94, 0.94, 0.96, 0.86, 0.92, 0.98, 1.08,
1.04, 0.74,
0.98, 1.02, 1.02, 1.12, 1.34, 2.02, 1.68, 1.12, 1.38, 1.14, 1.16,
1.22, 1.10,
1.14, 1.16, 1.28, 1.44, 2.58, 1.30, 1.20, 1.16, 1.06, 1.06, 1.08,
1.00, 1.00,
0.92, 1.00, 1.02, 1.00, 1.06, 1.10, 1.14, 1.08, 1.00, 1.04, 1.10,
1.06, 1.06,
1.06, 1.02, 1.04, 0.96, 0.96, 0.96, 0.92, 0.84, 0.88, 0.90, 1.00,
1.08, 0.80,
0.90, 0.98, 1.00, 1.10, 1.24, 1.66, 1.94, 1.02, 1.06, 1.08, 1.10,
1.30, 1.10,
1.12, 1.20, 1.16, 1.26, 1.42, 2.18, 1.26, 1.06, 1.00, 1.04, 1.00,
0.98, 0.94,
0.88, 0.98, 0.96, 0.92, 0.94, 0.96, 0.96, 0.94, 0.90, 0.92, 0.96,
0.96, 0.96,
0.98, 0.90, 0.90, 0.88, 0.88, 0.88, 0.90, 0.78, 0.84, 0.86, 0.92,
1.00, 0.68,
0.82, 0.90, 0.88, 0.98, 1.08, 1.36, 2.04, 0.98, 0.96, 1.02, 1.20,
0.98, 1.00,
1.08, 0.98, 1.02, 1.14, 1.28, 2.04, 1.16, 1.04, 0.96, 0.98, 0.92,
0.86, 0.88,
0.82, 0.92, 0.90, 0.86, 0.84, 0.86, 0.90, 0.84, 0.82, 0.82, 0.86,
0.86, 0.84,
0.84, 0.82, 0.80, 0.78, 0.78, 0.76, 0.74, 0.68, 0.74, 0.80, 0.80,
0.90, 0.60,
0.72, 0.80, 0.82, 0.86, 0.94, 1.24, 1.92, 0.92, 1.12, 0.90, 0.90,
0.94, 0.90,
0.90, 0.94, 0.98, 1.08, 1.24, 2.04, 1.04, 0.94, 0.86, 0.86, 0.86,
0.82, 0.84,
0.76, 0.80, 0.80, 0.80, 0.78, 0.80, 0.82, 0.76, 0.76, 0.76, 0.76,
0.78, 0.78,
0.76, 0.76, 0.72, 0.74, 0.70, 0.68, 0.72, 0.70, 0.64, 0.70, 0.72,
0.74, 0.64,
0.62, 0.74, 0.80, 0.82, 0.88, 1.02, 1.66, 0.94, 0.94, 0.96, 1.00,
1.16, 1.02,
1.04, 1.06, 1.02, 1.10, 1.22, 1.94, 1.18, 1.12, 1.06, 1.06, 1.04,
1.02, 0.94,
0.94, 0.98, 0.96, 0.96, 0.98, 1.00, 0.96, 0.92, 0.90, 0.86, 0.82,
0.90, 0.84,
0.84, 0.82, 0.80, 0.80, 0.76, 0.80, 0.82, 0.80, 0.72, 0.72, 0.76,
0.80, 0.76,
0.70, 0.74, 0.82, 0.84, 0.88, 0.98, 1.44, 0.96, 0.88, 0.92, 1.08,
0.90, 0.92,
0.96, 0.94, 1.04, 1.08, 1.14, 1.66, 1.08, 0.96, 0.90, 0.86, 0.84,
0.86, 0.82,
0.84, 0.82, 0.84, 0.84, 0.84, 0.84, 0.82, 0.86, 0.82, 0.82, 0.86,
0.90, 0.84,
0.82, 0.78, 0.80, 0.78, 0.74, 0.78, 0.76, 0.76, 0.70, 0.72, 0.76,
0.72, 0.70,
0.64]
我们需要执行下面的步骤。
(1)使用一个干净的数据集合,该集合是在Google Trend上flowers
关键字一年的搜索量,把该数据集合导入到变量d
中。
(2)使用一个相同长度(365个数据点)的随机正态分布作为Google Trend数据集合,这个集合为d1
。
(3)创建包含4个子区的图表。
(4)在第一个子区中,绘制d
和d1
的散点图。
(5)在第二个子区中,绘制d1
和d1
的散点图。
(6)在第三个子区中,绘制d1
和反序d1
的散点图。
(7)在第四个子区中,绘制d1
和由d1
与d
组成的数据集合的散点图。
下面的代码演示了本节之前解释的关系:
import matplotlib.pyplot as plt
import numpy as np
# import the data
from ch07_search_data import DATA
d = DATA
# Now let's generate random data for the same period
d1 = np.random.random(365)
assert len(d) == len(d1)
fig = plt.figure()
ax1 = fig.add_subplot(221)
ax1.scatter(d, d1, alpha=0.5)
ax1.set_title('No correlation')
ax1.grid(True)
ax2 = fig.add_subplot(222)
ax2.scatter(d1, d1, alpha=0.5)
ax2.set_title('Ideal positive correlation')
ax2.grid(True)
ax3 = fig.add_subplot(223)
ax3.scatter(d1, d1*-1, alpha=0.5)
ax3.set_title('Ideal negative correlation')
ax3.grid(True)
ax4 = fig.add_subplot(224)
ax4.scatter(d1, d1+d, alpha=0.5)
ax4.set_title('Non ideal positive correlation')
ax4.grid(True)
plt.tight_layout()
plt.show()
当执行上面代码时,得到图7-9所示的输出。
图7-9
在上面的输出中,我们清楚地看到在不同的数据集合之间是否存在相关性。其中,第二幅(右上)图显示了数据集合d1
和d1
自身(显然地)之间理想的正相关。第四幅(右下)图表明数据集合间存在一个正相关,但不是理想正相关。我们用d1
和d
(随机的)构建的这个数据集合来模拟两个相似的信号(事件)。第二幅图使用d
和d1
绘制的子区图形中有一定的随机性(或者噪声),但还是可以和原始(d
)信号进行比较。
我们也可以为散点图添加直方图,通过这种方式我们能了解更多关于所绘制的数据的信息。我们可以添加水平直方图和垂直直方图来显示在x轴和y轴上数据点的频率。通过这种方法,我们可以同时看到整个数据集合的汇总信息(直方图)和每一个数据点(散点图)。
下面是一个生成散点—直方图组合的代码示例,该代码使用了本节中提到的两个相同的数据集合。代码的重点是scatterhist()
函数,我们可以给它传入不同的数据集合,它使用我们提供的数据集合对一些变量(直方图中bin的数量、坐标轴的范围等)进行设置。
我们从通常的导入开始,代码如下。
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import make_axes_locatable
下面代码定义了生成散点直方图的函数,给函数一个(x,y)数据集合和一个可选的figsize
参数。
def scatterhist(x, y, figsize=(8,8)):
"""
Create simple scatter & histograms of data x, y inside given plot
@param figsize: Figure size to create figure
@type figsize: Tuple of two floats representing size in inches
@param x: X axis data set
@type x: np.array
@param y: Y axis data set
@type y: np.array
"""
_, scatter_axes = plt.subplots(figsize=figsize)
# the scatter plot:
scatter_axes.scatter(x, y, alpha=0.5)
scatter_axes.set_aspect(1.)
divider = make_axes_locatable(scatter_axes)
axes_hist_x = divider.append_axes(position="top", sharex=scatter_
axes,
size=1, pad=0.1)
axes_hist_y = divider.append_axes(position="right",
sharey=scatter_axes,
size=1, pad=0.1)
# compute bins accordingly
binwidth = 0.25
# global max value in both data sets
xymax = np.max([np.max(np.fabs(x)), np.max(np.fabs(y))])
# number of bins
bincap = int(xymax / binwidth) * binwidth
bins = np.arange(-bincap, bincap, binwidth)
nx, binsx, _ = axes_hist_x.hist(x, bins=bins, histtype='stepfilled',
orientation='vertical')
ny, binsy, _ = axes_hist_y.hist(y, bins=bins, histtype='stepfilled',
orientation='horizontal')
tickstep = 50
ticksmax = np.max([np.max(nx), np.max(ny)])
xyticks = np.arange(0, ticksmax + tickstep, tickstep)
# hide x and y ticklabels on histograms
for tl in axes_hist_x.get_xticklabels():
tl.set_visible(False)
axes_hist_x.set_yticks(xyticks)
for tl in axes_hist_y.get_yticklabels():
tl.set_visible(False)
axes_hist_y.set_xticks(xyticks)
plt.show()
现在,加载数据并调用函数来生成并显示图表。
if __name__ == '__main__': # import the data
from ch07_search_data import DATA as d
# Now let's generate random data for the same period
d1 = np.random.random(365)
assert len(d) == len(d1)
# try with the random data
# d = np.random.randn(1000)
# d1 = np.random.randn(1000)
scatterhist(d, d1)
上述代码将生成图7-10所示的图表。
图7-10
互相关图形可以应用在以下场景:如果有从两个不同的观察结果得到的两个不同数据集合,我们想知道这两个数据集合是否是相关的。我们想把它们交叉关联来看其是否以某种方式匹配。或者,我们在一个较大的数据样本中寻找一个较小数据样本的模式,这个模式有可能不是一个简单或者明显的模式。
我们将使用pyplot lab中的matplotlib.pyplot.xcorr
函数。这个函数可以绘制两个数据集合之间的相互关系。通过这种方式我们可以看出绘制的值之间是否存在某个显著的模式。这里假设传入的x
和y
参数的长度相同。
如果传入的normed
参数为True
,可以通过零延迟(即没有时间延迟或者时差)的互关联对数据进行归一化。
在内部,由Numpy的numpy.correlate
函数来完成相关性计算。
通过参数usevlines
(置为True
),我们告诉matplotlib用vlines()
而不是plot()
绘制相关图形的线条。二者的主要区别是,如果使用plot()
,可以使用标准的Line2D
属性设置线条风格,该属性通过**kwargs
参数传入matplotlib.pyplot.xcorr
函数。
在下面的例子中,我们需要执行以下步骤。
(1)导入matplotlib.pyplot
模块。
(2)导入numpy
包。
(3)使用一个干净的数据集合,该集合是Google中对关键字flowers
一年的搜索量趋势。
(4)绘制数据集合(真实的和仿造的)和互相关图表。
(5)为了标签和刻度有一个较好的显示效果使用紧凑布局。
(6)为了能更容易地理解图表添加恰当的标签和网格。
下面代码将会执行以上步骤。
import matplotlib.pyplot as plt
import numpy as np
# import the data
from ch07_search_data import DATA as d
total = sum(d)
av = total / len(d)
z = [i - av for i in d]
# Now let's generate random data for the same period
d1 = np.random.random(365)
assert len(d) == len(d1)
total1 = sum(d1)
av1 = total1 / len(d1)
z1 = [i - av1 for i in d1]
fig = plt.figure()
# Search trend volume
ax1 = fig.add_subplot(311)
ax1.plot(d)
ax1.set_xlabel('Google Trends data for "flowers"')
# Random: "search trend volume"
ax2 = fig.add_subplot(312)
ax2.plot(d1)
ax2.set_xlabel('Random data')
# Is there a pattern in search trend for this keyword?
ax3 = fig.add_subplot(313)
ax3.set_xlabel('Cross correlation of random data')
ax3.xcorr(z, z1, usevlines=True, maxlags=None, normed=True, lw=2)
ax3.grid(True)
plt.ylim(-1,1)
plt.tight_layout()
plt.show()
以上代码将生成图7-11所示的图表。
图7-11
我们使用了一个具有可识别模式(请参考图7-11,在数据集合上两个峰值以相似的方式重复)的真实数据集合。另一个数据集合仅是一些随机正态分布的数据,该数据和从公共服务Google Trends上拿到的真实数据有着相同的长度。
我们把这两个数据集合绘制在输出图表的上半部来对其进行可视化。
使用matplotlib的xcorr
函数,然后调用NumPy的correlate()
函数,计算互相关性并把其绘制在图表的下半部。
NumPy中的互相关性计算返回一个相关系数数组,该数组表示了两个数据集合(如果应用在信号处理领域,通常指信号)的相似度。
互相关图表,或者叫相关图是指通过相关值的高度(出现在某个时间延迟的竖线)表现,告诉我们这两个信号是否相关。我们可以看到有不止一条竖线(在时间延迟n
上的相关系数)在0.5之上。
举个例子,如果两个数据集合在100s的时间延迟(也就是通过两种不同的传感器观察到的相同对象在相隔100s的两个时间点间的变化)上有相关性,则将在上图输出中的x=100的位置上看到一个竖线(表示相关系数)。
自相关表示一个给定的时间序列在一个连续的时间间隔上与自身的延迟(也就是时间上的延迟)之间的相似度。它发生在一个时间序列研究中,指在一个给定的时间周期内的错误在未来的时间周期上会继续存在。例如,如果我们在预测股票红利的走势,某一年红利过高估计往往会导致对接下来年份红利的过高估计。
时间序列分析数据引出了许多不同的科学应用和财务流程,例如生成的财务绩效报表、一段时间的价格、波动性计算等。
在分析未知数据时,自相关可以帮助我们检测数据是否是随机的。对此我们可以使用相关图。它可以提供如下问题的答案:数据是随机的吗?这个时间序列数据是一个白噪声信号吗?它是正弦曲线形的吗?它是自回归的吗?这个时间序列数据的模型是什么?
我们将使用matplotlib来比较两组数据。一组是某个关键字一年(365天)的Google每日搜索量的趋势。另一组是符合正态分布的365个随机测量值(生成的随机数据)。
接下来我们将分析两个数据集合的自相关性,并比较相关图是如何可视化数据中的模式的。
本小节的步骤如下。
(1)导入matplotlib.pyplot
模块。
(2)导入numpy
包。
(3)使用一个干净的Google一年搜索量的数据集合。
(4)绘制数据和其自相关图表。
(5)用NumPy生成一个相同长度的随机数据集合。
(6)在相同图表上绘制随机数据集合和其自相关图表。
(7)添加合适的标签和网格以更好地理解图表。
下面是代码部分。
import matplotlib.pyplot as plt
import numpy as np
# import the data
from ch07_search_data import DATA as d
total = sum(d)
av = total / len(d)
z = [i - av for i in d]
fig = plt.figure()
# plt.title('Comparing autocorrelations')
# Search trend volume
ax1 = fig.add_subplot(221)
ax1.plot(d)
ax1.set_xlabel('Google Trends data for "flowers"')
# Is there a pattern in search trend for this keyword?
ax2 = fig.add_subplot(222)
ax2.acorr(z, usevlines=True, maxlags=None, normed=True, lw=2)
ax2.grid(True)
ax2.set_xlabel('Autocorrelation')
# Now let's generate random data for the same period
d1 = np.random.random(365)
assert len(d) == len(d1)
total = sum(d1)
av = total / len(d1)
z = [i - av for i in d1]
# Random: "search trend volume"
ax3 = fig.add_subplot(223)
ax3.plot(d1)
ax3.set_xlabel('Random data')
# Is there a pattern in search trend for this keyword?
ax4 = fig.add_subplot(224)
ax4.set_xlabel('Autocorrelation of random data')
ax4.acorr( z, usevlines=True, maxlags=None, normed=True, lw=2)
ax4.grid(True)
plt.show()
上述代码将生成图7-12所示的图表。
图7-12
通过观察左手边的图表,我们能很容易地识别出搜索量数据的模式;左下方的图表是正态分布的随机数据,其模式不是很明显,但仍然有可能。
在随机数据上计算自相关性并绘制自相关图表,可以看到在0处的相关性很高,这是我们所期望的,数据在没有任何时间延迟的时候和自身是相关的。但在无时间延迟之前和之后,信号几乎为0。因此我们可以安全地推断初始时间的信号和任何时间延迟上的信号没有相关性。
再看一下真实的数据——Google搜索量趋势,我们可以看到在0s时间延迟上有相同的表现,我们也可以预料对于任何自相关信号都会有相同的表现。但是我们看到在0s时间延迟之后的大约20、60和110天存在很强的信号。这表明在Google搜索引擎上这个特殊的搜索关键字以及人们搜索它的方式之间存在一个模式。
我们把为什么这里会存在一个很大差异的解释工作留给读者。请记住相关和因果关系是两个非常不同的概念。
自相关通常应用在当我们想要识别未知数据的模式和试图把数据匹配到一个模型的时候。识别给定数据集合模型的第一步,就是查看数据与自身的相关性。这需要Python以外的知识,它需要数学建模和各种统计测试(Ljung-Box测试、Box-Pierce测试等)的知识,这些知识能帮助我们解答可能遇到的问题。