精通-Python-数据可视化-全-

精通 Python 数据可视化(全)

原文:Mastering Python Data Visualization

协议:CC BY-NC-SA 4.0

零、前言

数据可视化旨在清晰地提供信息,并帮助查看者定性地理解它们。一幅画胜过千言万语这一广为人知的说法,可以改写为“一幅画讲述一个故事,也是一大堆文字的集合”。因此,可视化是一个非常宝贵的工具,可以帮助观众快速理解一个概念。然而,数据可视化与其说是一种技能,不如说是一门艺术,因为如果你试图过度使用它,它可能会产生相反的效果。

我们目前面临着大量的数据,这些数据包含了许多洞见,这些洞见是现代成功的关键。重要的是找到数据,清理它,并使用正确的工具来可视化它。这本书解释了使用 Python 包可视化数据的几种不同方法,以及在许多不同领域非常有用的例子,如数值计算、金融模型、统计和机器学习以及遗传学和网络。

本书展示了在 Mac OS X 10.10.5 上使用 Python 2.7、IPython 0.13.2、matplotlib 1.4.3、NumPy 1.9.2、SciPy 0.16.0 和 conda build 1 . 14 . 1 版本开发的示例代码。

这本书涵盖了什么

第一章,一个数据可视化概念框架,阐述了数据可视化实际上应该被称为“知识推理的信息可视化”。本章涵盖了框架,解释了从数据/信息到知识的过渡,以及有意义的表示(通过对数、颜色图、散点图、相关性等)如何使知识更容易掌握。

第二章数据分析与可视化,说明了可视化的重要性,展示了可视化过程中的几个步骤,包括几个可供选择的工具选项。可视化方法已经存在很久了,我们很早就接触到了;例如,即使是小孩子也能解读条形图。交互式可视化有许多优点,本章用例子来解释它们。

第 3 章Python IDE 入门解释了如何使用 Continuum Analytics 中的 Anaconda,而无需担心单独安装每个 Python 库。Anaconda 简化了打包和部署方法,使 IPython 笔记本与其他库一起运行变得更加容易。

第 4 章数值计算和交互式绘图,涵盖了交互式绘图方法,并附有计算物理和应用数学中的工作示例。一些值得注意的例子是插值方法、逼近、聚类、采样、相关和使用 SciPy 的凸优化。

第 5 章金融和统计模型探讨了金融工程,其中有许多数值和图形方法,成为探索 Python 的有趣用例。本章包括股票报价,回归分析,蒙特卡罗算法,模拟方法和例子。

第 6 章统计和机器学习,涵盖了统计方法,如线性和非线性回归以及使用 numpy、scipy、matplotlib 和 scikit-learn 的聚类和分类方法。

第 7 章生物信息学、遗传学和网络模型,涵盖了一些有趣的例子,比如现实生活中的社交网络和有向图实例,适合这些问题的数据结构,以及网络分析。本章使用特定的库,如图形工具、网络、matplotlib、scipy 和 numpy。

第八章高级可视化,涵盖仿真方法和信号处理实例,展示几种可视化方法。这里,我们还对其他高级工具进行了比较,例如 Julia 和 D3.js。

附录前进探索可视化,给出了 conda 的概述,并列出了各种 Python 库。

这本书你需要什么

对于这本书,您需要在操作系统上安装 Python 2.7.6 或更高版本。对于本书中的示例,使用了 Mac OS X 10.10.5 的 Python 默认版本(2.7.6)。本书使用的其他软件包是 IPython,这是一个交互式 Python 环境。IPython 的新版本叫做 Jupyter,它现在有 50 种不同语言的内核。

如果可能的话,安装预先打包的科学 Python 发行版,例如来自 Continuum 的 Anaconda 或 entorn Python 发行版。Anaconda 通常附带 300 多个 Python 包。对于预打包列表中没有包含的 Python 包,您可以使用 pip 或 conda 来安装它们。在附录前进探索可视化中提供了一些示例。

这本书是给谁的

有很多关于 Python 和数据可视化的书籍。然而,很少有人可以推荐给那些想在现有的 Python 知识基础上进行构建的人,更少有人讨论利基技术来使您的代码更容易使用和重用。如果你知道一些关于 Python 编程的知识,但是有一种永不满足的动力去学习更多,这本书将向你展示获得分析结果和产生惊人的视觉显示的方法。

这本书涵盖了使用现实问题产生分析结果的方法。不是写给初学者的,但是如果需要澄清,可以按照书中建议的阅读提示来做。如果这本书是你第一次接触 Python 或数据可视化,你最好学习一些介绍性的文本。我最喜欢的是约翰·古塔格教授的《计算机科学与编程导论》和加州大学洛杉矶分校的内森·尤的《T2 可视化》。

惯例

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

文本中的码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、伪 URL、用户输入和 Twitter 句柄如下所示:“首先我们使用 SciPy 中的norm()来创建正态分布样本,稍后,使用 NumPy 中的hstack()来水平堆叠它们并应用 SciPy 中的gaussian_kde()

代码块设置如下:

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
students = pd.read_csv("/Users/Macbook/python/data/ucdavis.csv")
g = sns.FacetGrid(students, palette="Set1", size=7)
g.map(plt.scatter, "momheight", "height", s=140, linewidth=.7, edgecolor="#ffad40", color="#ff8000")
g.set_axis_labels("Mothers Height", "Students Height")

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

import blockspring 
import json  

print blockspring.runParsed("stock-price-comparison", 
   { "tickers": "FB, LNKD, TWTR", 
   "start_date": "2014-01-01", "end_date": "2015-01-01" }).params

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

conda install jsonschema

Fetching package metadata: ....
Solving package specifications: .
Package plan for installation in environment /Users/MacBook/anaconda:

The following packages will be downloaded:

 package                    |            build
 ---------------------------|-----------------
 jsonschema-2.4.0           |           py27_0          51 KB

The following NEW packages will be INSTALLED:

 jsonschema: 2.4.0-py27_0

Proceed ([y]/n)?

新名词重要词语以粗体显示。您在屏幕上看到的单词,例如菜单或对话框中的单词,会出现在文本中,如下所示:“此外,您可以选择复制代码选项,将代码块的内容复制到 Canopy 的复制粘贴缓冲区中,以便在编辑器中使用。”

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

型式

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

读者反馈

我们随时欢迎读者的反馈。让我们知道你对这本书的看法——你喜欢或不喜欢什么。读者反馈对我们来说很重要,因为它有助于我们开发出你真正能从中获益的标题。

要给我们发送一般反馈,只需发送电子邮件<[feedback@packtpub.com](mailto:feedback@packtpub.com)>,并在您的邮件主题中提及书名。

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

客户支持

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

下载示例代码

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

下载本书的彩色图片

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

勘误表

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

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

盗版

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

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

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

问题

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

一、数据可视化的概念框架

互联网和社交媒体在现代的存在导致了大量的数据,数据量的增长超乎想象。这是如何以及何时开始的?

十年前,一种新的经营方式出现了:公司从整个企业的来源收集、组合和处理大量数据。他们的目标是使用大量数据来改进决策过程。大约在同一时间,亚马逊、雅虎和谷歌等处理大量数据的公司取得了重大进展。这些里程碑导致了支持大数据的多项技术的诞生。我们不会详细讨论大数据,但会尝试探索为什么许多组织改变了他们的方式,使用类似的想法来进行更好的决策。

这些大量的数据究竟是如何用来做出更好的决策的?我们最终会做到这一点,但首先让我们试着理解数据、信息和知识之间的区别,以及它们如何与数据可视化相关联。人们可能会想,为什么我们在谈论数据、信息和知识。有一个故事绘图连接了我们如何开始,我们从什么开始,所有这些事情如何有益于业务,以及可视化的作用。我们将通过简要回顾相关步骤来确定数据可视化所需的概念框架。

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

  • 数据、信息、知识和洞察力之间的区别
  • 信息转化为知识,进而转化为洞察力
  • 收集、处理和组织数据
  • 数据可视化的历史
  • 可视化数据如何帮助决策?
  • 可视化图

数据、信息、知识和洞察力

术语数据信息知识在计算机科学的上下文中被广泛使用。这些术语有许多定义,往往相互冲突,不一致。在我们深入这些定义之前,我们将了解这些术语如何与可视化相关。数据可视化的主要目标是获得对数据或信息的洞察(隐藏的真相)。本书中关于数据、知识和洞察力的整个讨论都是在计算机科学的背景下进行的,而不是心理学或认知科学。对于认知语境,可以参考https://www . UCSF . edu/news/2014/05/114321/converting-data-knowledge-insight-and-action

数据

术语数据 意味着一个可以从中得出结论的前提。虽然数据和信息在某种背景下看起来是相互关联的,但数据实际上是指数字形式的离散客观事实。数据是基本的构建模块,当以不同的方式组织和排列时,会产生有助于回答一些业务问题的信息。

数据可以是非常简单的东西,但是数量庞大且没有组织。这种离散的数据不能单独用于决策,因为它没有意义,更重要的是,因为它们之间没有结构或关系。数据收集、传输和存储的过程因数据类型和存储方法而异。数据有多种形式;一些值得注意的形式如下:

  • CSV 文件
  • 数据库表
  • 文档格式(Excel、PDF、Word 等)
  • HTML 文件
  • JSON 文件
  • 文本文件
  • XML 文件

信息

信息是经过处理的数据,作为一个商业问题的答案。当我们添加关系或关联时,数据就变成了信息。关联是通过为数据提供上下文或背景来实现的。背景是有帮助的,因为它允许我们回答关于数据的问题。

例如,让我们假设给定的篮球运动员的数据包括身高、体重、位置、大学、出生日期、选秀、选秀轮次、NBA 首秀和招募排名。问题的答案“身高超过六尺,打控卫位置的选秀状元是谁?”也是信息。

同样,每个玩家的分数都是一条数据。“今年谁的场均得分最高,他的得分是多少”这个问题的答案是“勒布朗·詹姆斯,27.47”,这也是信息。

知识

当人类解释和组织信息并利用这些信息来推动决策时,知识就出现了。知识是通过经验获得的数据、信息和技能。知识包括做出适当决策的能力以及执行决策的技能。

基本要素——连接数据——让我们了解每条信息的相对重要性。通过比较过去的结果和识别模式,我们不必从头开始构建问题的解决方案。下图总结了数据、信息和知识的概念:

Knowledge

知识以递增的方式变化,特别是当信息被重新排列或重组时,或者当一些计算算法改变时。知识就像一个指向算法结果的箭头,依赖于来自数据的过去信息。在许多实例中,知识也是通过与结果的视觉交互获得的。另一方面,洞察力打开了通往未来的道路。

数据分析与洞察

在我们深入洞察的定义及其与业务的关系之前,让我们看看捕捉洞察的想法是如何开始的。十多年来,组织一直在努力理解他们拥有的所有数据和信息,尤其是在数据量爆炸式增长的情况下。他们都意识到了数据 分析(也称为数据分析分析)的重要性,以便根据现有数据和信息做出最佳或现实的业务决策。

分析依赖于数学算法来确定数据之间的关系,从而产生洞察力。理解洞察力的一个简单方法是考虑一个类比:当数据没有结构和与业务的适当一致时,它通过将数据转换为更结构化的形式并使其更接近业务目标来给出更清晰和更深刻的理解。顿悟就是有突破性结果出来的“灵光乍现”时刻。人们不应该混淆术语分析和商业智能。分析具有预测能力,而商业智能基于对历史数据的分析提供结果。

分析通常适用于更广泛的数据,因此,数据协作通常发生在内部和/或外部。在一些商业范例中,协作只发生在广泛的数据集集合的内部,但在大多数其他情况下,外部连接有助于连接点或完成难题。外部数据连接的两个最常见来源是社交媒体和消费者群。

在本章的后面,我们将参考现实生活中的商业故事,这些故事通过应用分析来获得洞察力和推动商业价值,改进决策,并更好地了解他们的客户,从而取得了一些显著的成果。

数据的转换

到现在我们已经知道什么是数据,但是现在的问题是:收集数据的目的是什么?数据对于描述物理或社会现象以及进一步回答关于该现象的问题是有用的。因此,确保数据没有错误、不准确或不完整非常重要;否则,基于该数据的响应也将是不准确或不完整的。

有不同类别的数据,有些是过去的性能数据实验数据基准数据。过去的性能数据和实验数据是非常不言自明的。另一方面,基准数据是将两个不同项目或产品的特性与标准测量进行比较的数据。数据被转换成信息,被进一步处理,然后用于回答问题。因此,很明显,我们的下一步是实现这一转变。

将数据转化为信息

数据被收集并根据内容及其重要性以几种不同的形式存储。例如,如果数据是关于季后赛篮球比赛的,那么它将是文本和视频格式的。另一个例子是一个国家所有城市的温度记录,通过不同的格式收集和提供。从数据到信息的转换涉及数据的收集、处理和组织,如下图所示:

Transforming data into information

收集到的数据需要一些处理和组织,这些数据以后可能有也可能没有结构、模型或模式。然而,这个过程至少给了我们一个有组织的方式来寻找关于数据的问题的答案。这个过程可以是基于篮球运动员总得分的简单排序,也可以是基于城市和州名的排序。

从数据到信息的转换也可能不仅仅是排序,比如统计建模或计算算法。正是这种从数据到信息的转换非常重要,它使得数据能够被查询、访问和操作。在某些情况下,当有大量不同的数据时,转换可能涉及处理方法,如过滤、聚合、应用相关性、缩放和归一化以及分类。

数据收集

数据采集是一个耗时的过程。因此,企业正在寻找更好的方法来自动化数据捕获。然而,对于许多过程来说,手动数据收集仍然很普遍。现代通过自动过程进行的数据收集使用传感器等输入设备。例如,水下珊瑚礁通过传感器进行监测;农业是传感器用于监测土壤特性、控制灌溉和施肥方法的另一个领域。

另一种自动收集数据的方法是扫描文档和日志文件,这是服务器端数据收集的一种形式。手动过程包括通过基于网络的方法收集数据,这些数据存储在数据库中,然后可以转换成信息。如今,基于网络的协作环境正受益于改进的通信和数据共享。

传统的可视化和可视化分析工具通常是为在单台机器上与可视化应用交互的单个用户设计的。扩展这些工具以包括对协作的支持,显然已经在增加可视化在现实世界中的范围和适用性方面取得了很大进展。

数据预处理

今天,由于数据的大小和可能来自多个不同的来源和类型,数据非常容易受到噪声和不一致性的影响。有数据清理数据整合数据约简数据转换等多种数据预处理技术。数据清理可用于消除噪声和纠正数据中的不一致。数据集成将来自多个来源的数据合并成一种连贯的格式,通常称为数据仓库。例如,数据缩减可以通过合并、聚合和消除冗余特征来减少数据大小。数据转换可以应用于数据被缩放到较小范围内的情况,从而提高处理和可视化数据的准确性和效率。数据的转换周期如下图所示:

Data preprocessing

异常检测是对收集的数据中可能不符合预期行为或模式的异常数据的识别。异常也被称为异常值或噪声;例如,在信号数据中,不寻常的特定信号被认为是噪声,而在交易数据中,异常值是欺诈性的交易。准确的数据收集对于保持数据的完整性至关重要。除了异常的负面影响,另一方面,异常值也非常重要——例如,特别是在人们想要发现欺诈性保险索赔的情况下。

数据处理

数据处理是转化过程中的重要一步。当务之急是将重点放在数据质量上。有助于准备数据以便更好地分析和理解数据的一些处理步骤是相关性建模聚类。还有其他处理技术,但我们将在这里只讨论两种最流行的处理方法。

依赖建模是对数据进行建模以确定表示的性质和结构的基本原则。该过程搜索数据元素之间的关系;例如,百货公司可能会收集顾客购买习惯的数据。这个过程有助于百货商店推断关于频繁购买的信息。

聚类的任务是发现数据中以某种方式具有“相似模式”的组,而不使用数据中已知的结构。

组织数据

数据库管理系统允许用户以结构化格式存储数据。但是,数据库太大,无法放入内存。结构化数据有两种方式:

  • 以表格、树或图形等结构化格式将大数据存储在磁盘中
  • 使用数据结构格式将数据存储在内存中,以加快访问速度

数据结构包括一组不同的格式,用于构造能够存储和访问数据的数据。一般的数据结构类型有数组、文件、表、树、列表、映射等。任何数据结构都是为了组织数据以适应特定的目的而设计的,这样就可以在运行时存储、访问和操作数据。可以选择或设计数据结构来存储数据,以便使用各种算法对其进行处理,从而实现更快的访问。

被收集、处理和组织起来以便有效存储的数据更容易理解,这导致可以更好地理解信息。

获取数据集

对于无法访问组织数据的读者来说,互联网上有大量的资源,这些资源包含来自几个不同来源的丰富数据集,例如:

将信息转化为知识

信息是可量化、可度量的,它有形状,可以访问、生成、存储、分发、搜索、压缩、复制。它可以用信息量来量化。

信息通过离散算法的应用转化为知识,知识被期望比信息更具定性。在一些问题领域,知识继续经历一个不断发展的循环。这种演变尤其发生在数据实时变化的时候。

知识就像食谱,让你用信息做面包,在这种情况下,面粉和酵母的成分。另一种看待知识的方式是将数据和信息结合起来,在此基础上增加经验和专家意见来帮助决策。知识不仅仅是过滤或算法的结果。

这一转变涉及哪些步骤,变化是如何发生的?自然,它不能自己发生。尽管信息这个词根据定义会有不同的解释,但我们将在计算的背景下进一步探讨它。

一个简单的类比来说明信息和知识的区别:特定课程的课程材料为你提供了关于概念的必要信息,老师后来通过讨论帮助学生理解概念。这有助于学生获得课程知识。通过类似的过程,需要做一些事情来将信息转化为知识。下图展示了从信息到知识的转化:

Transforming information into knowledge

如图所示,当信息通过一些离散算法聚集和运行时,就会转化为知识。需要对信息进行汇总,以获得更广泛的知识。通过这种转变获得的知识有助于回答关于数据或信息的问题,例如公司哪个季度的销售收入最大?广告在多大程度上推动了销售?或者说,今年发布了多少新品?

将知识转化为洞察力

在传统系统中,信息被处理,然后被分析以生成报告。自从互联网出现以来,经过处理的信息已经并且总是可用的,社交媒体已经成为一种新的商业方式。

组织一直在使用外部数据,通过数据分析获得洞察力。例如,消费者通过推特发推来衡量用户情绪,用来跟踪对产品品牌的意见。在某些情况下,有更高比例的用户在社交媒体上对新产品给出积极的信息,比如 iPhone 或平板电脑。分析工具可以提供这种情绪的数字证据,而这正是数据可视化发挥重要作用的地方。

另一个说明这种转变的例子是,网飞在 2009 年宣布了一项最佳协同过滤算法竞赛,根据以前的评分来预测电影的用户评分。该竞赛的获胜者使用了实用主义理论,在预测用户评级方面取得了 10.05%的改进,这增加了网飞的商业价值。

Transforming knowledge into insight

使用协作和分析将知识转化为洞察力,如前面的图所示。洞察意味着看到解决方案并意识到需要做什么。获取数据和信息很容易,组织有已知的方法来实现这一点,但获得洞察力非常困难。获得洞察力需要新的创造性思维和连接点的能力。除了应用创造性思维,数据分析和数据可视化在获得洞察力方面也发挥着重要作用。数据可视化被认为是一门艺术,也是一门科学。

数据可视化历史

可视化的根源在于一个悠久的历史传统,即使用原始绘画和墙上的地图、数字表和粘土上的绘画来表示信息。然而,它们并不被称为可视化或数据可视化。数据可视化是一个新名词;它表达了这样的想法,即它不仅仅涉及以图形形式表示数据。数据背后的信息应该通过良好的展示以直观的方式展现出来;图形应该有助于观众看到数据的结构。

计算机前的可视化

在巴比伦时代早期,图画是在粘土上画的,在后期则是在纸莎草纸上画的。这些绘画和地图的目的是为观众提供对信息的定性理解。我们也知道理解图片是我们的自然本能,因为信息的视觉呈现更容易被感知。本节仅包括可视化历史的部分细节。对于详细的细节和例子,我们推荐两个有趣的资源:

米纳德的俄国战役(1812)

查尔斯·米纳德是一名在巴黎工作的土木工程师。他用形象的地图概括了 1812 年的战争——拿破仑向莫斯科进军。这张地图是一张简单的图片,它既是一张视觉时间线,也是一张地理地图,描绘了军队的规模和方向、温度以及地标和位置。爱德华·塔夫特教授将这张图片描述为有史以来最好的统计图表,这是出了名的。

Minard's Russian campaign (1812)

楔形从左边的厚开始,我们看到军队在波兰边境开始了 422,000 人的战役。随着越来越深入俄罗斯,温度越来越低,楔形变得越来越窄。这种可视化设法将许多不同的数字和地理事实浓缩成一幅图像:当军队减少时,减少的原因,以及随后他们的撤退。

伦敦的霍乱流行(1831-1855)

1831 年 10 月,英国发生了第一例亚洲霍乱,超过 52000 人死于这场流行病。随后,在 1848-1849 年和 1853-1854 年,更多的霍乱流行导致大量死亡。

1855 年,约翰·斯诺博士绘制了一张地图,显示了聚集在伦敦布罗德街水泵周围的霍乱死亡人数。约翰·斯诺博士的这张地图是一个里程碑式的图形发现,但不幸的是,它是在那个时期结束时设计出来的。他的地图显示了每个死者的位置,这为他的结论提供了一个视角,即疫情的源头可能是布罗德街一个水泵的污染水。大约在那个时候,图表的使用在经济和国家规划中变得很重要。

统计图形(1850-1915)

到了 18 世纪中期,视觉化在整个欧洲迅速发展。1863 年,高尔顿的欧洲多元天气图中有一页显示了 1861 年 12 月的气压、风向、雨量和温度(资料来源:剑桥大学出版社弗朗西斯·高尔顿的生平、书信和著作)。

在这一时期,统计图形成为主流,有许多教科书都是这样写的。这些教科书包含了图解方法的详细描述,讨论了频率,以及尺度和基线的选择对差异和比率的视觉估计的影响。它们还包含历史图表,其中两个或多个时间序列可以显示在一个图表上,以便对它们的历史进行比较。

数据可视化的后期发展

1962 年,约翰·图基呼吁承认数据分析是统计学的一个合法的分支;不久之后,他开始在标题为探索性数据分析 ( EDA )下发明各种新的、简单而有效的图形显示,随后是探索性空间数据分析 ( ESDA )。图基后来在 1977 年写了一本名为探索性数据分析的书。有许多工具可用于图形技术的电子设计自动化,如下所示:

  • 方块-触须图(方块图)
  • 柱状图
  • 多元 ari 图表(来自烛台图表)
  • 运行序列图
  • 帕累托图(以维尔弗雷多·帕累托命名)
  • 散点图
  • 多维标度
  • 目标投射追踪

科学计算中的可视化正在成为一个重要的基于计算机的领域,其目标是提高对数据的理解并做出快速的实时决策。今天,医生诊断疾病的能力取决于视力。例如,在髋关节置换手术中,定制的髋关节现在可以在手术前制造。可以在手术前使用无创 3D 成像进行精确测量,从而将术后身体排斥反应的数量从 30%减少到仅 5%(来源:http://bone smart . org/hip/hip-imports-specialized-and-custom-fited-options/)。

三维人脑结构和功能的可视化是一个具有深远意义的研究前沿。几乎没有什么进步改变了神经科学和脑成像技术的领域,比如看内部和阅读活人大脑的能力。为了大脑研究的持续进展,有必要在许多抽象层次上整合结构和功能信息。

硬件性能功率一直在上升的速度告诉我们,我们已经能够分析脱氧核糖核酸序列并直观地表示它们。计算机的未来发展预示着医学和其他科学领域将会有更光明的进步。

可视化如何帮助决策?

有多种方式可以直观地表示数据。然而,只有很少的几种方法可以描述数据,使人们能够直观地看到一些东西并观察新的模式。数据可视化并不像看起来那么容易;这是一门艺术,需要大量的实践和经验。(就像画画一样——一个人不可能从第一天就成为大师级的画家,这需要大量的练习。)

人类感知在数据可视化领域发挥着重要作用。一双健康的人眼具有大约 200 度的水平总视场(其中大约 120 度由双眼共享)。大约四分之一的人脑参与视觉处理,这比任何其他感觉都多。在听觉、视觉和嗅觉这三种感官中,人类视觉的感知能力最强,达到了 60%。

有效的可视化帮助我们分析和理解数据。作者斯蒂芬·利特尔(Stephen Light)描述了以下八种定量信息(通过可视化),这些信息可能有助于我们理解或交流一组数据(来源:https://www . Perceptual edge . com/articles/ie/the _ right _ graph . pdf):

  • 时间序列
  • 等级
  • 部分对整体
  • 偏差
  • 频数分布
  • 相互关系
  • 名义比较
  • 地理或地理空间

科学家绘制了人类基因组图谱,这也是为什么我们面临着将知识转化为可视化表示以便更好地理解的挑战的原因之一。换句话说,我们可能必须找到新的方法来直观地呈现人类基因组,这样普通人就不难理解了。

可视化适合哪里?

需要注意的是数据可视化不是科学可视化。科学可视化处理具有固有物理结构的数据,例如流过飞机机翼的空气分子。另一方面,信息可视化处理抽象数据,并帮助解决涉及大型数据集的问题。挑战之一是确保数据是干净的,然后减少维度,从而丢弃不必要的信息。

无论我们在哪里看到知识或数据价值的增加,可视化都可以使用。这可以通过做更多的数据分析和运行算法来确定。数据分析可能从最简单的形式变化到更复杂的形式。

有时候,观察平均值、中位数或总数之外的数据是有价值的,因为这些测量只测量看起来很明显的东西。有时,区域周围的聚合或值隐藏了需要特别关注的有趣细节。一个经典的例子是“安斯科姆四重奏”,它由四个数据集组成,这些数据集具有几乎相同的简单统计属性,但在绘制图表时却显得非常不同。有关这方面的更多信息,可以参考链接:https://en.wikipedia.org/wiki/Anscombe%27s_quartet

Where does visualization fit in?

大多数情况下,非常适合可视化的数据集可以采取不同的形式,但是有些数据集比其他数据集更容易理解。在某些情况下,有必要对它们进行多次分析,以便更好地理解可视化,如上图所示。

一个好的可视化不仅仅是一个人可以看的静态图片,就像博物馆里的展览一样。这是允许我们深入并找到更多关于数据变化的东西。例如,先查看,缩放和过滤,更改某些显示比例的值,并以增量方式查看结果,如本·施内德曼的所述。有时候,在一个显示器上和一个标尺上显示所有东西要困难得多,只有通过经验,人们才能更好地理解这些可视化方法。进一步总结,可视化在组织和理解数据方面都很有用,尤其是当数据丰富的时候。

交互式可视化作为一种新的交流形式正在出现,它允许用户分析信息,以便构建他们自己对数据的新理解。

今天的数据可视化

虽然计算的许多领域旨在用自动化取代人类的判断,但可视化系统是独一无二的,并且被明确设计成不取代人类。事实上,它们旨在让人类积极参与整个过程;为什么会这样?

数据可视化是一门艺术,由数据驱动,但由人类在各种计算工具的帮助下创造。一个艺术家用画笔和颜色等工具和材料画一幅画。同样,另一位艺术家试图借助计算工具创建数据可视化。视觉化可以在美学上令人愉悦,并有助于使事情变得清晰;有时,根据创建它的用户,它可能缺少这些品质中的一个或两个。

如今,有超过 30 种不同的数据可视化表示,每一种都有理由以特定的方式表示数据。随着可视化方法的进步,我们拥有的不仅仅是条形图和饼图。尽管数据可视化有许多好处,但由于缺乏理解,在某些情况下,由于仪表板上的东西杂乱无章,变得过于繁琐,这些好处被削弱了。

呈现数据的方式有很多种,但在大多数情况下,其中只有少数有意义;这将在本章后面的章节中详细解释。在那次讨论之前,让我们来看看一些重要的东西的列表,这些东西可以很好地可视化。

什么是好的可视化?

良好的可视化帮助用户探索和理解数据,提供价值和深刻的见解。它是有效的,视觉上吸引人的,可扩展的,并且容易理解(好的可视化不必太复杂)。可视化是一个中心工具,通过进行研究和分析,使用任何可以回答数据问题的工具来发现数据中的模式和趋势。

有效可视化背后的主要原则是确定您想要表达的主要观点,识别受众的水平和背景,准确地表示数据,然后创建一个清晰的演示文稿,向该受众传达信息。

示例:使用一个小样本数据源创建了以下表示,该数据源显示了 1970-2012 年间获得十个不同学科学位的男女比例(womens-undergrad-degrees.csv和来自【http://www.knapdata.com/python/】mens-undergrad-degrees.csv):

What is a good visualization?

http://nces.ed.gov/programs/digest/d11/tables/dt11_290.asp 的完整数据源维护着整套数据。

一个简单的方法是在一个尺度上表示它们,尽管不同学科之间的数字没有关系。让我们分析一下,看看这种表示是否有意义,如果没有意义,那么我们还需要什么?还有其他的表述吗?

首先,关于不同学科的所有数据都显示在一个屏幕上,这是一个很好的比较。然而,如果我们需要获得 2000 年的信息,没有简单的方法。除非有类似于金融股票图表的交互式显示模式,否则没有简单的方法来确定 2000 年多学科授予学位的信息。这些图中另一个令人困惑的部分是百分比加起来并不是 100%。另一方面,一个学科内授予学位的男女比例加起来是 100%;例如,男性和女性在卫生专业学科中获得学位的比例分别为 15.2%和 84.8%。

我们可以通过其他可视化方法来表示这些吗?人们可以为每一年创建气泡图,有一个带有年份选择的交互式可视化,也有一个播放按钮来转换每一年的气泡。

这种可视化更适合我们正在查看的数据。我们还可以使用与原始图相同的滑块,并通过突出显示所选年份的数据使其具有交互性。一个好习惯是用几种不同的方式来可视化数据,看看某个显示是否比另一个更有意义。如果数值范围非常大(例如,从 20 到 200,000),我们可能必须在对数标度上对数值进行标度。

人们可以用 Python 编写一个程序来完成这个气泡图。其他替代语言是使用 D3.js 的 JavaScript 和使用 R-Studio 的 R。留给读者去探索其他的可视化选项。

Google Motion Chart 可以在developers . Google . com/Chart/interactive/docs/gallery/Motion Chart 进行可视化来表示这个交互图表?csw = 1 #示例显示了一个类似于这个气泡图的工作示例。这里显示的气泡图只有三年,但你可以为所有年份创建另一个气泡图。

What is a good visualization?

数据可视化是在数据分析之后必须使用的过程。我们之前也注意到,数据转换、数据分析、数据可视化都做了几次;为什么会这样?我们都知道那句名言,知识就是有正确的答案,智慧就是问正确的问题。数据分析有助于我们更好地理解数据,从而能够回答关于数据的问题。然而,当数据以几种不同的方式被可视化表示时,一些新的问题出现了,这也是为什么会有重复的分析和可视化过程的原因之一。

数据可视化是数据探索的主要工具之一,几乎总是先于或启发数据分析。可视化显示数据的工具很多,但做分析的工具就少了。像 Julia、R 和 Python 这样的编程语言在执行数据分析方面排名较高,但是在可视化方面,基于 JavaScript 的 D3.js 在生成交互式数据可视化方面具有更大的潜力。

在 R 和 Python 之间,R 是一门比较难学的语言。另一方面,Python 要容易得多。这一点在 Quora 上也有争论;人们可以在互联网上查看这一点的有效性。如今,Python 中有许多用于统计建模和数据分析的工具,因此,它是数据科学的一个有吸引力的选择。

可视化图

我们进行可视化的原因之一是为了确认我们对数据的了解。然而,如果数据不被很好地理解,你可能不会对数据提出正确的问题。

创建可视化时,第一步是明确要回答的问题。换句话说,可视化将如何帮助?接下来还有另一个挑战——知道正确的绘图方法。一些可视化方法如下:

  • 条形图和饼图
  • 箱线图
  • 泡泡图
  • 柱状图
  • 核密度估计 ( KDE )图
  • 线和曲面图
  • 网络图
  • 散点图
  • 树形图
  • 小提琴绘图

在确定可视化应该传达的信息的过程中,查看以下问题是有意义的:

  • 我们在处理多少变量,我们试图描绘什么?
  • x 轴和 y 轴是指什么?(对于 3D, z 轴也是如此。)
  • 数据大小标准化了吗?数据点的大小有什么意义吗?
  • 我们是否使用了正确的颜色选择?
  • 对于时间序列数据,我们是试图识别趋势还是相关性?

如果变量太多,在不同的数据子集上绘制同一个图的多个实例是有意义的。这种技术被称为格子格子绘图。它允许查看者快速提取大量关于复杂数据的信息。

考虑一个学生数据子集,其中包含关于(gender, sleep, tv, exercise, computer, gpa)(height, momheight, dadheight)的不同寻常的混合信息。computertvsleepexercise的单位是小时,height单位是英寸,gpa单位是 4.0。

Visualization plots

前面的数据是一个变量比通常更多的例子,因此,做一个网格图来可视化和查看这些变量之间的关系是有意义的。

我们执行可视化的原因之一是确认我们对数据的了解。然而,如果数据没有被很好地理解,人们可能不会对它提出正确的问题。

由于数据中只有两种性别,因此有 10 种可能的变量组合(sleeptv)、(sleepexercise)、(sleepcomputer)、(sleepgpa)、(tvexercise)、(tvcomputer)、(tvgpa)、(exercisecomputer)、(exercisegpa)和(computer另外两个,(heightmomheight)和(heightdadheight)为第二套。以下是除(sleeptv)、(tvexercise以外的所有组合。

Visualization plots

我们的目标是找到什么样的变量组合可以用来使这些数据变得有意义,或者看看这些变量是否有任何有意义的影响。因为数据是关于学生的,所以gpa可能是驱动其他变量相关性的关键变量。上图描绘的散点图显示,更多的女学生比男学生拥有更高的gpa,更多的男学生花更多的时间在电脑上,并获得类似的gpa值范围。虽然这里显示了所有散点图,但目的是找出哪些数据起着更重要的作用,以及我们能从这些数据中得出什么意义。

Visualization plots

更高的蓝点数量(对于 y 轴上的gpa来说)表明有更多的女生拥有更高的gpa(该数据是从 UCSD 收集的)。

数据可从http://www.knapdata.com/python/ucdavis.csv下载。

可以使用 seaborn包,用很少的代码行显示散点图,下面的例子显示了与学生花在电脑上的时间相比,gpa沿 x 轴的散点图:

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

students = pd.read_csv("/Users/kvenkatr/Downloads/ucdavis.csv")

g = sns.FacetGrid(students, hue="gender", palette="Set1", size=6)
g.map(plt.scatter, "gpa", "computer", s=250, linewidth=0.65,
  edgecolor="white")

g.add_legend()

型式

下载示例代码

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

这些图是使用matplotlibpandasseaborn库包生成的。Seaborn是基于matplotlib的统计数据可视化库,由斯坦福大学的 Michael Waskom 创建。关于这些库的更多细节将在后面的章节中讨论。

Seaborn库中有很多有用的类。特别是当我们需要在数据子集内分别可视化一个变量的分布或多个变量之间的关系时,FacetGrid类就派上了用场。FacetGrid可以绘制三维,即行、列和色调。这些库包及其功能将在后面的章节中描述。

创建可视化时,第一步是明确要回答的问题。换句话说,可视化将如何帮助?另一个挑战是选择正确的绘图方法。

条形图和饼图

我们什么时候选择条形图和饼图?它们是最古老的可视化方法,饼图最适合用来比较整体的各个部分。然而,条形图可以比较不同组之间的事物来显示模式。

条形图、直方图和饼图帮助我们比较不同的数据样本,对它们进行分类,并确定数据值在该样本中的分布。条形图有几种不同的样式,从单一的、多重的和堆叠的。

条形图

当你有很好地分成不同类别的数字数据时,条形图特别有效,因此你可以快速看到数据中的趋势。

条形图在跨类别比较数据时非常有用。一些值得注意的例子包括:

  • 不同尺寸牛仔裤的体积
  • 过去二十年世界人口变化
  • 按部门划分的支出百分比

除了这一点,还要考虑以下几点:

  • 为条形图添加颜色以获得更大的影响力:用条形图展示收入表现具有信息性,但添加颜色以揭示利润会增加视觉洞察力。然而,如果有太多的条,颜色可能会使图表看起来笨拙。
  • 在一个仪表盘上包含多个条形图:这有助于查看者快速比较相关信息,而不是翻阅一堆电子表格或幻灯片来回答问题。
  • 在一个轴的两侧放置条形:沿着一个连续的轴绘制正负数据点是发现趋势的有效方法。
  • 使用堆叠条形图或并排条形图:将相关数据显示在彼此的顶部或旁边,可以让您的分析更深入,同时解决多个问题。

这些图可以用不到 12 行 Python 代码来实现,更多的例子将在后面的章节中讨论。

对于条形图,每列代表一个由特定类别定义的组;对于直方图,每一列代表一个由定量变量定义的组。对于条形图, x 轴没有低端或高端值,因为 x 轴上的标签是分类的,而不是定量的。另一方面,在柱状图中,会有一系列的值。下图显示了 2000 年至 2009 年美国奥斯卡获奖者和提名者的统计数据:

Bar graphs

下面的 Python 代码使用matplotlib显示电影中一个小数据样本的条形图(这可能不一定是一个真实的例子,但给出了一个绘图和比较的想法):

[5]: import numpy as np
     import matplotlib.pyplot as plt

     N = 7
     winnersplot = (142.6, 125.3, 62.0, 81.0, 145.6, 319.4, 178.1)

     ind = np.arange(N)  # the x locations for the groups
     width = 0.35        # the width of the bars

     fig, ax = plt.subplots()
     winners = ax.bar(ind, winnersplot, width, color='#ffad00')

     nomineesplot = (109.4, 94.8, 60.7, 44.6, 116.9, 262.5, 102.0)
     nominees = ax.bar(ind+width, nomineesplot, width,
       color='#9b3c38')

     # add some text for labels, title and axes ticks
     ax.set_xticks(ind+width)
     ax.set_xticklabels( ('Best Picture', 'Director', 'Best Actor',
       'Best Actress','Editing', 'Visual Effects', 'Cinematography'))

     ax.legend( (winners[0], nominees[0]), ('Academy Award Winners',  
       'Academy Award Nominees') )

     def autolabel(rects):
       # attach some text labels
       for rect in rects:
         height = rect.get_height()
         hcap = "$"+str(height)+"M"
         ax.text(rect.get_x()+rect.get_width()/2., height, hcap,
           ha='center', va='bottom', rotation="vertical")

     autolabel(winners)
     autolabel(nominees)

     plt.show()

饼图

说到饼状图,真的要考虑回答“各部分组成有意义的整体了吗?”以及“你有足够的房产来用圆形视图表示它们吗?”。有一些批评者在饼状图上崩溃了,其中一个主要原因是,当有许多类别时,很难得到比例并比较这些类别来获得任何洞察力。(来源:https://www . quora . com/How-and-why-is-pie-charts-被数据可视化专家认为是邪恶的)。

饼图对于显示单个空间或整个地图的比例非常有用。一些值得注意的例子包括:

  • 调查的响应类别
  • 特定技术的前五大公司市场份额(在这种情况下,人们可以很快知道哪些公司在市场中占有主要份额)

除此之外,请考虑以下几点:

  • 将饼图楔形限制为八个:如果要表示的比例超过八个,考虑一个条形图。由于房地产有限,很难有意义地表现和解释这些作品。
  • 在地图上叠加饼图:饼图可以更容易地在地图上展开并突出地理趋势。(这里的楔形也应该是有限的。)

考虑以下简单饼状图的代码,比较几个学科的录取人数是如何分布的:

[6]: import matplotlib.pyplot as plt

     labels = 'Computer Science', 'Foreign Languages', 
       'Analytical Chemistry', 'Education', 'Humanities', 
       'Physics', 'Biology', 'Math and Statistics', 'Engineering'

     sizes = [21, 4, 7, 7, 8, 9, 10, 15, 19]
     colors = ['yellowgreen', 'gold', 'lightskyblue', 'lightcoral',
       'red', 'purple', '#f280de', 'orange', 'green']
     explode = (0,0,0,0,0,0,0,0,0.1)
     plt.pie(sizes, explode=explode, labels=labels, 
       autopct='%1.1f%%', colors=colors)
     plt.axis('equal')
     plt.show()

下面的饼图示例显示了一些选定的顶级研究领域的大学录取人数:

Pie charts

方框图

箱线图也被称为“盒须图”。这是一种基于五个数字汇总显示数据分布的标准化方式:最小值、第一个四分位数、中值、第三个四分位数和最大值。下图显示了如何阅读箱线图:

Box plots

箱线图是以图形方式检查一组或多组数据的快速方法,它们一次只占用较少的空间来定义五个摘要。对于这种用法,我们可以想到的一个例子是:如果两个或两个以上的班级进行同样的考试,那么一个方框图可以告诉我们一个班级的大多数学生什么时候比另一个班级的大多数学生做得更好。另一个例子是,如果有更多的人吃汉堡,中位数会更高,或者顶部的胡须会比底部的长。在这种情况下,它可以很好地概括数据分布。

在我们试图理解何时使用方框图之前,这里有一个需要理解的定义。数据值集合中的异常值是与其他值相距异常距离的观察值。

箱线图在显示一组数据的分布时最有用。一些值得注意的例子如下:

  • 识别数据中的异常值
  • 确定数据是如何向两端倾斜的

除此之外,请考虑以下几点:

  • 隐藏框内的点:关注异常值
  • 跨分布比较:箱线图有利于快速比较数据集之间的分布

散点图和气泡图

散点图是一种显示两个变量的可视化方法。它们相交点的模式可以图形化地显示关系模式。散点图是在同一组个体上测量的两个变量之间关系的可视化。另一方面,气泡图显示三维数据。每个实体及其相关数据的三元组 (a,b,c) 被绘制成一个圆盘,通过 xy 位置表示这三个变量中的两个,第三个显示测量的显著性量。

散点图

数据通常显示为点的集合,并经常用于绘制各种相关性。例如,当一组数据值的增加也会增加另一组数据值时,就会发现正相关。前面显示的学生记录数据有各种散点图,显示了它们之间的相关性。

在下面的例子中,我们将学生的身高与他们母亲的身高进行比较,以确定是否存在正相关。数据可从http://www.knapdata.com/python/ucdavis.csv下载。

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
students = pd.read_csv("/Users/Macbook/python/data/ucdavis.csv")
g = sns.FacetGrid(students, palette="Set1", size=7)
g.map(plt.scatter, "momheight", "height", s=140, linewidth=.7, edgecolor="#ffad40", color="#ff8000")
g.set_axis_labels("Mothers Height", "Students Height")

我们使用seaborn包演示了这个例子,但是也可以只使用matplotlib来完成,这将在下一节中显示。前面代码的散点图如下所示:

Scatter plots

散点图对于研究两个不同变量之间的关系非常有用。一些值得注意的例子如下:

  • 男性与女性不同年龄患皮肤癌的可能性
  • 智商测试分数与平均成绩的相关性

除此之外,请考虑以下几点:

  • 添加趋势线或最佳拟合线(如果关系是线性的):添加趋势线可以显示数据值之间的相关性
  • 使用信息性标记类型:如果要透露的故事是关于可以通过相关形状和颜色进行视觉增强的数据,则应使用信息性标记类型

气泡图

以下示例显示了如何使用彩色地图作为第三个维度,该维度可以指示销售量或任何适当的推动利润的指标:

 [7]: import numpy as np
     import pandas as pd
     import seaborn as sns
     import matplotlib.pyplot as plt

     sns.set(style="whitegrid")
     mov = pd.read_csv("/Users/MacBook/python/data/2014_gross.csv")

     x=mov.ProductionCost
     y=mov.WorldGross
     z=mov.WorldGross

     cm = plt.cm.get_cmap('RdYlBu')
     fig, ax = plt.subplots(figsize=(12,10))

     sc = ax.scatter(x,y,s=z*3, c=z,cmap=cm, linewidth=0.2, alpha=0.5)
     ax.grid()
     fig.colorbar(sc)

     ax.set_xlabel('Production Cost', fontsize=14)
     ax.set_ylabel('Gross Profits', fontsize=14)

     plt.show()
..-.

以下散点图是使用颜色映射的示例的结果:

Bubble charts

气泡图对于比较三个数值数据维度中的数据之间的关系非常有用: x 轴数据、 y 轴数据以及由气泡大小表示的数据。气泡图类似于 XY 散点图,不同的是散点图上的每个点都有一个与之相关的附加数据值,该值由以 XY 点为中心的圆或“气泡”的大小来表示。此处显示了气泡图的另一个示例(没有 python 代码,以展示不同的风格):

Bubble charts

在前面的显示中,气泡图显示了不同大洲的预期寿命人均国内生产总值

气泡图最有用的是显示沿两个轴的数据浓度,第三个数据元素是测量的显著性值。一些值得注意的例子如下:

  • 电影的制作成本和总利润,以及如示例中所示的按加热比例衡量的重要性

除此之外,请考虑以下几点:

  • 添加颜色和形状意义:通过改变大小和颜色,可以将数据点转化为清晰回答某些问题的可视化
  • 使其具有交互性:如果数据点太多,气泡图可能会变得混乱,所以将它们按时间轴或类别分组,并以交互方式可视化

KDE 图

核密度 估计 ( KDE )是一种非参数方法,用于估计概率密度函数及其在观测数据点上的平均值,以创建平滑近似。它们与直方图密切相关,但有时可以被称为核的概念赋予平滑性或连续性。

概率密度函数 ( PDF )的核心是 PDF 的形式,其中省略了不是域中任何变量的函数的任何因素。我们将只关注它的可视化方面;关于更多的理论,可以参考统计学书籍。

有几个不同的 Python 库可以用来完成不同深度和级别的 KDE 绘图,包括matplotlibScipyscikit-learnseaborn。以下是 KDE 绘图的两个例子。在后面的章节中,将会有更多的例子,只要有必要,展示 KDE 绘图的各种其他方式。

在下面的示例中,我们使用大小为 250 的随机数据集和seaborn包,用几行简单的线显示分布图:

KDE plots

可以使用seaborn显示数据图的简单分布,这里使用使用numpy.random生成的随机样本进行演示:

from numpy.random import randn
import matplotlib as mpl
import seaborn as sns
import matplotlib.pyplot as plt

sns.set_palette("hls")
mpl.rc("figure", figsize=(10,6))
data = randn(250)
plt.title("KDE Demonstration using Seaborn and Matplotlib", fontsize=20)
sns.distplot(data, color='#ff8000')

在第二个例子中,我们使用 SciPy 和 NumPy 演示了概率密度函数。首先我们使用 SciPy 中的norm()来创建正态分布样本,然后使用 NumPy 中的hstack()来水平堆叠它们,并应用 SciPy 中的gaussian_kde()

KDE plots

前面的图是使用 SciPy 和 NumPy 的 KDE 图的结果,如下所示:

from scipy.stats.kde import gaussian_kde
from scipy.stats import norm
from numpy import linspace, hstack
from pylab import plot, show, hist

sample1 = norm.rvs(loc=-1.0, scale=1, size=320)
sample2 = norm.rvs(loc=2.0, scale=0.6, size=320)
sample = hstack([sample1, sample2])
probDensityFun = gaussian_kde(sample)
plt.title("KDE Demonstration using Scipy and Numpy", fontsize=20)
x = linspace(-5,5,200)
plot(x, probDensityFun(x), 'r')
hist(sample, normed=1, alpha=0.45, color='purple')
show()

其他可视化方法如线面图、网络图、树状图、热图、雷达图或蜘蛛图、小提琴图将在接下来的几章中讨论。

总结

到目前为止,所展示的例子只是为了让你知道在做演示之前应该如何思考和计划。最重要的阶段是数据熟悉和可视化的准备过程。一个人能否首先获得数据或塑造理想的故事,主要取决于尝试的结果。这就像是“先有鸡还是先有蛋”的情况——数据是第一位还是焦点?最初,可能不清楚人们可能需要什么数据,但在大多数情况下,经过几次迭代,只要数据中没有错误,事情就会变得清楚。

通过进行一些清理或缩小维度(如果需要)来转换数据质量,并填补空白(如果有)。除非数据是好的,否则人们在视觉上展示数据的努力将会白费。在实现了对数据的合理理解之后,确定什么样的可视化可能是合适的是有意义的。在某些情况下,最好以几种不同的方式显示它,以便清楚地看到故事。

二、数据分析和可视化

大多数可视化故事都是从某个问题开始的,这个问题是针对正在探索或收集数据的主题。这个问题包含了故事的前提,并把我们引向数据对故事绘图进行考察的点。这样的数据考察以一个问题开始,例如,【2014 年报告了多少埃博拉死亡病例?由一群人通过相互协作来实现。数据传播者的角色应该是创造一种信息体验,改变受众对其故事的看法。

故事的关键部分涉及将可视化置于有意义的上下文中的过程。上下文提供了回答以下问题的知识:

  • 有足够的数据吗?
  • 这种数据存在的时间范围内吗?
  • 全球哪些相关事件会影响这些数据?

重申一下,理解数据并确定试图回答的问题的背景是很重要的。有时,人们甚至在问题最终确定之前就开始挖掘数据,在这种情况下,一旦对数据有了更好的理解,你可能会对问题有一个更好或更清晰的版本。

这个过程从输入数据开始,假设人们有办法从某个来源获取、解析和收集所需的信息。在某些情况下,最好将收集的信息可视化以消除噪声,而在其他一些情况下,可以在应用可视化方法之前过滤和分析数据。在本章中,我们将学习数据探索的不同方式,以进一步将它们用于可视化方法。我们还将按照以下顺序讲述一些有趣的数据故事和相关概念:

  • 获取、解析和过滤数据以检测异常值和异常值、数据挖掘和提炼、可视化表示和交互
  • 用数据创造有趣的故事
  • 可视化的感知、呈现方法和最佳实践
  • 交互式可视化—探索事件侦听器和布局

为什么可视化需要规划?

可视化的整个过程涉及不同技能集和领域专长的人。数据辩论者勤奋地收集数据并进行分析。数学家和统计学家理解视觉设计原则,并使用这些原则交流他们的数据。设计师或艺术家(在某些情况下,是前端开发人员)拥有可视化所需的技能,而业务分析师则关注客户行为模式、异常值或突然出现的异常趋势。但是,它总是从获取或收集数据开始,并遵循以下步骤:

  • 从外部来源、网站或磁盘上的文件获取或收集数据
  • 解析和过滤数据使用编程方法解析、清理和减少数据
  • 分析细化去除噪声和不必要的维度,找到模式
  • 表示和交互以更易访问和理解的方式呈现数据

这个过程遵循的程度因不同的问题而异,在某些情况下,所做的分析比数据过滤更多。如前一章所述,在某些情况下,分析和可视化是迭代完成的。换句话说,这些步骤的分布并不总是可预测和一致的。

埃博拉的例子

为了说明上一节提到的步骤,以及它们如何导致一个可以理解的可视化,让我们考虑一下我们之前的问题,即【2014 年报告了多少埃博拉死亡病例?这个特殊的问题引出了非常具体的数据,通常由世界卫生组织(http://www.who.int/en/)或人道主义数据交换(https://hdx.rwlabs.org)维护。这一数据的原始来源是世界卫生组织 ( 世卫组织),但人道主义数据交换 ( HDX )是贡献者。然而,请注意,我们将在一个地方获得所有数据,以及本书的 Python 源代码示例。

这些数据包含有关埃博拉病毒在几内亚、利比里亚、马里、尼日利亚、塞内加尔、塞拉利昂、西班牙、联合王国和美利坚合众国传播的信息。

该信息的投稿人网址为https://data.hdx.rwlabs.org/dataset/ebola-cases-2014/

逗号分隔值 ( CSV )格式的数据文件内容包括指标、国家名称、日期以及死亡人数或感染人数,具体取决于指标所述。共有 36 个不同的指标,前 10 名如下(其他可在附录前进探索可视化查看):

  • 过去 7 天可能出现的埃博拉病例数
  • 过去 21 天可能的埃博拉死亡人数
  • 过去 21 天疑似埃博拉病例数
  • 过去 7 天疑似埃博拉病例数
  • 过去 21 天疑似埃博拉死亡人数
  • 过去 21 天确诊的埃博拉病例比例
  • 过去 7 天确诊的埃博拉病例比例
  • 过去 21 天确诊的埃博拉死亡比例
  • 过去 7 天疑似埃博拉病例的比例
  • 过去 21 天疑似埃博拉死亡的比例

此时,看完指标清单,我们最初的单一问题,即【2014 年报告了多少埃博拉死亡病例?可以改为多组问题。为了简单起见,我们将重点放在这个问题上,看看如何进一步分析数据,并提出可视化方法。首先,让我们看一下读取数据文件的方法。

在任何编程语言中,读取文件的方式都不止一种,其中一种选择就是使用 Python 的 pandas 库,该库性能高,使用数据结构和数据分析工具。另一种选择是使用 csv 库读取 csv 格式的数据文件。它们之间有什么区别?他们两个都能做这项工作。在熊猫的旧版本中,大数据的内存映射存在问题(也就是说,如果 CSV 格式的数据文件非常大),但现在已经进行了优化。让我们从下面的代码开始:

[1]:  with open('("/Users/kvenkatr/python/ebola.csv ', 'rt') as f: 
        filtereddata = [row for row in csv.reader(f) if row[3] != "0.0" and 
        row[3] != "0" and "deaths" in row[0]]

[2]:    len(filtereddata)
Out[2]: 1194

前面的过滤也可以使用熊猫来执行,如下所示:

import pandas as pd
eboladata = pd.read_csv("/Users/kvenkatr/python/ebola.csv")
filtered = eboladata[eboladata["value"]>0]
filtered = filtered[filtered["Indicator"].str.contains("deaths")]
len(filtered)

数据可从http://www.knapdata.com/python/ebola.csv下载。下一步是以读取文本 ( rt )格式打开数据文件。它读取每一行,并进一步过滤死亡人数为零的行,因为指示器字符串中有单词deaths。这是一个非常简单的过滤器,用于忽略没有报告病例或死亡的数据。仅打印过滤数据的前五行会显示以下内容:

[3]:  filtereddata[:5]
Out[3]: 
[['Cumulative number of confirmed Ebola deaths', 'Guinea','2014-08-29', '287.0'],
 ['Cumulative number of probable Ebola deaths','Guinea','2014-08-29',
  '141.0'],
 ['Cumulative number of suspected Ebola deaths','Guinea','2014-08-29',
  '2.0'],
 ['Cumulative number of confirmed, probable and suspected Ebola deaths',
  'Guinea','2014-08-29','430.0'],
 ['Cumulative number of confirmed Ebola deaths',
  'Liberia','2014-08-29','225.0']]

如果每个国家报告的埃博拉病例的所有数据都要分开,我们如何进一步过滤这些数据?我们可以在国家栏上对它们进行分类。该数据文件共有四列:indicatorcountrydatenumber value,如下图所示:

[4]:  import operator
      sorteddata = sort(filtereddata, key=operator.itemgetter(1))
[5]:  sorteddata[:5]
Out[5]: 
[['Cumulative number of confirmed Ebola deaths', 'Guinea','2014-08-29', '287.0'],
 ['Cumulative number of probable Ebola deaths','Guinea','2014-08-29',
  '141.0'],
 ['Cumulative number of suspected Ebola deaths','Guinea','2014-08-29',
  '2.0'],
 ['Cumulative number of confirmed, probable and suspected Ebola deaths',
  'Guinea','2014-08-29','430.0'],
 ['Number of confirmed Ebola deaths in the last 21 days', 'Guinea',
  '2014-08-29','8.0']]

在查看了目前为止的数据后,有两个指标似乎与我们开始这一数据考察的背景有关:

  • 累计确诊埃博拉死亡人数
  • 累计确诊、可能和疑似埃博拉死亡人数

通过多次应用可视化,我们还注意到,在几个国家中,几内亚、利比里亚和塞拉利昂的确诊死亡人数多于其他国家。我们现在来看看这三个国家的死亡报告是如何绘制的:

import matplotlib.pyplot as plt  
import csv 
import operator 
import datetime as dt  

with open('/Users/kvenkatr/python/ebola.csv', 'rt') as f: 
  filtereddata = [row for row in csv.reader(f) if row[3] != "0.0" and 
  row[3] != "0" and "deaths" in row[0]] 

sorteddata = sorted(filtereddata, key=operator.itemgetter(1))  
guineadata  = [row for row in sorteddata if row[1] == "Guinea" and 
  row[0] == "Cumulative number of confirmed Ebola deaths"] 
sierradata  = [row for row in sorteddata if row[1] == "Sierra Leone" and 
  row[0] == "Cumulative number of confirmed Ebola deaths"] 
liberiadata = [row for row in sorteddata if row[1] == "Liberia" and 
  row[0] == "Cumulative number of confirmed Ebola deaths"] 

g_x = [dt.datetime.strptime(row[2], '%Y-%m-%d').date() for 
  row in guineadata] 
g_y = [row[3] for row in guineadata] 

s_x = [dt.datetime.strptime(row[2], '%Y-%m-%d').date() for 
  row in sierradata] 
s_y = [row[3] for row in sierradata] 

l_x = [dt.datetime.strptime(row[2], '%Y-%m-%d').date() for
  row in liberiadata] 
l_y = [row[3] for row in liberiadata] 

plt.figure(figsize=(10,10))
plt.plot(g_x,g_y, color='red', linewidth=2, label="Guinea") 
plt.plot(s_x,s_y, color='orange', linewidth=2, label="Sierra Leone")
plt.plot(l_x,l_y, color='blue', linewidth=2, label="Liberia")
plt.xlabel('Date', fontsize=18)
plt.ylabel('Number of Ebola Deaths', fontsize=18)
plt.title("Confirmed Ebola Deaths", fontsize=20)
plt.legend(loc=2)
plt.show()

结果将会是如下图所示:

The Ebola example

我们可以为另一个指标构建一个类似的图,即累计确诊、疑似和疑似埃博拉死亡人数。(这可能不是最好的方法,但我们可以纳入更多国家的数据,并绘制出类似的结果。)

import matplotlib.pyplot as plt  
import csv 
import operator 
import datetime as dt  

with open('/Users/kvenkatr/python/ebola.csv', 'rt') as f: 
  filtereddata = [row for row in csv.reader(f) if row[3] != "0.0" and 
  row[3] != "0" and "deaths" in row[0]] 

sorteddata = sorted(filtereddata, key=operator.itemgetter(1))  

guineadata  = [row for row in sorteddata if row[1] == "Guinea" and 
  row[0] == "Cumulative number of confirmed, probable and suspected Ebola deaths"] 
sierradata  = [row for row in sorteddata if row[1] == "Sierra Leone" and 
  row[0] == " Cumulative number of confirmed, probable and suspected Ebola deaths "] 
liberiadata = [row for row in sorteddata if row[1] == "Liberia" and 
  row[0] == " Cumulative number of confirmed, probable and suspected Ebola deaths "] 

g_x = [dt.datetime.strptime(row[2], '%Y-%m-%d').date() for 
  row in guineadata] 
g_y = [row[3] for row in guineadata] 

s_x = [dt.datetime.strptime(row[2], '%Y-%m-%d').date() for 
  row in sierradata] 
s_y = [row[3] for row in sierradata] 

l_x = [dt.datetime.strptime(row[2], '%Y-%m-%d').date() for
  row in liberiadata] 
l_y = [row[3] for row in liberiadata] 

plt.figure(figsize=(10,10))
plt.plot(g_x,g_y, color='red', linewidth=2, label="Guinea") 
plt.plot(s_x,s_y, color='orange', linewidth=2, label="Sierra Leone")
plt.plot(l_x,l_y, color='blue', linewidth=2, label="Liberia")
plt.xlabel('Date', fontsize=18)
plt.ylabel('Number of Ebola Deaths', fontsize=18)
plt.title("Probable and Suspected Ebola Deaths", fontsize=20)
plt.legend(loc=2)
plt.show()

绘图应该是这样的:

The Ebola example

一个体育的例子

为了说明另一个例子,以及一个特定的可视化方法如何比另一个更好地工作,让我们考虑一个不同的问题:截至 2015 年 2 月,美国足坛四分卫的前五大触球记录是什么?这一数据的原始来源是美国橄榄球联盟的莱恩·道森和美国劳联统计。(数据来源:http://www . pro-football-reference . com/players/D/dawsle00 . htm。)

该数据包含了前 22 名四分卫的信息:培顿·曼宁、布雷特·法弗、丹·马里诺、德鲁·布里斯、汤姆·布拉迪、弗兰克·塔肯顿、约翰·埃尔韦、沃伦·穆恩、约翰·尤尼塔斯、文尼泰斯塔维尔达、乔·蒙塔纳、戴夫·Krieg、埃利·曼宁、桑尼·尤根森、丹·福茨、菲利普·里弗斯、本·罗瑟里斯伯格、德鲁·布莱索、布默·埃西森、约翰·哈德尔、蒂特尔和托尼·罗莫:

A sports example

在我们想到可视化方法之前,需要做一点分析。这些四分卫在不同的时期打过球。例如,布雷特·法弗从 1991 年到 2010 年,丹·马里诺从 1983 年到 1999 年。挑战在于,如果我们使用条形图或气泡图,它们只会在一个维度上显示结果。

第一步是解析 CSV 文件,这里有几个选项。我们可以使用熊猫read_csv功能,也可以使用csv模块,该模块有一些方便的功能,例如DictReader:

import csv 
import matplotlib.pyplot as plt

# csv has Name, Year, Age, Cmp, Att, Yds, TD, Teams
with open('/Users/MacBook/java/qb_data.csv') as csvfile: 
   reader = csv.DictReader(csvfile) 
   for row in reader:
     name  = row['Name']
     tds = row['TD']

四分卫数据是从本节前面列出的来源下载的;过滤后的数据也可在http://www.knapdata.com/python/qb_data.csv获得。csv模块包括将行作为字典使用的类,以便可以命名字段。DictReaderDictWriter类将这些行翻译成字典,而不是列表。字典的关键字可以是传入的或从输入的第一行推断的(该行包含标题)。通过DictReader读取 CSV 文件的内容,其中列输入值被视为字符串:

#ways to call DictReader

# if fieldnames are Name, Year, Age, Cmp, Att, Yds, TD, Teams
fieldnames = ['Name', 'Year', 'Age', 'Cmp', 'Att', 'Yds', 'TD', 'Teams'] 

reader = csv.DictReader(csvfile, fieldNames=fieldnames)
# If csv file has first row as Name, Year, Cmp, Att, Yds, TD, Teams
#   we don't need to define fieldnames, the reader automatically recognizes
#   them. 

为了将一些值转换成数字,我们可能需要一个转换并返回数值的函数。我们还在prepare.py中增加了getcolors()``num()等功能,可以在以后的例子中使用:

# num(s) and getcolors() functions 
def num(s):
  try:
    return int(s)
  except ValueError:
    return 0  

def getcolors():
  colors = [(31, 119, 180), (255,0,0), (0,255,0), (148, 103, 189), (140, 86, 75), (218, 73, 174), (127, 127, 127), (140,140,26), (23, 190, 207), (65,200,100), (200, 65,100), (125,255,32), (32,32,198), (255,191,201), (172,191,201), (0,128,0), (244,130,150), (255, 127, 14), (128,128,0), (10,10,10), (44, 160, 44), (214, 39, 40), (206,206,216)]

  for i in range(len(colors)):
    r, g, b = colors[i]
    colors[i] = (r / 255\. , g / 255\. , b / 255.)
  return colors

直观地表示结果

根据输入数据中的字段名称,对于每个四分卫,他们的触地统计数据或传球码统计数据可以绘制在时间线上。现在我们知道要画什么了,下一步就是弄清楚怎么画。

简单的 X-Y 带场(年,触地)或(触地,年)的绘图应该是一个好的开始。然而,到目前为止,在这个输入数据文件中有 252 个四分卫,其中大多数是不相关的。因此,用不同的颜色展示它们是没有意义的。(为什么?我们有 252 种不同的颜色吗?)我们可以尝试绘制前 7 名或前 10 名的结果,如下图所示:

Visually representing the results

下面的 Python 程序演示了如何使用matplotlib根据触地次数显示前 10 名四分卫,该程序产生的图是上图所示的:

import csv
import matplotlib.pyplot as plt

# The following functions can be in separate file 
#  (If it does, you need to import) 
def num(s):
  try:
    return int(s)
  except ValueError:
    return 0  

def getcolors():
  colors = [(31, 119, 180), (255,0,0), (0,255,0), (148, 103, 189), (140, 86, 75), (218, 73, 174), (127, 127, 127), (140,140,26), (23, 190, 207), (65,200,100), (200, 65,100), (125,255,32), (32,32,198), (255,191,201), (172,191,201), (0,128,0), (244,130,150), (255, 127, 14), (128,128,0), (10,10,10), (44, 160, 44), (214, 39, 40), (206,206,216)]

  for i in range(len(colors)):
    r, g, b = colors[i]
    colors[i] = (r / 255\. , g / 255\. , b / 255.)
  return colors

def getQbNames():
  qbnames = ['Peyton Manning']
  name=''
  i=0
  with open('/Users/MacBook/java/qb_data.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
      if ( row['Name'] != name and qbnames[i] != row['Name']):
        qbnames.append(row['Name'])
        i = i+1
  return qbnames

def readQbdata():
  resultdata = []
  with open('/Users/MacBook/java/qb_data.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    resultdata = [row for row in reader]
  return resultdata

fdata=[]
prevysum=0

#    -- functions End -- 

qbnames = getQbNames()
fdata = readQbdata()

i=0
rank=0
prevysum=0
lastyr=0
highrank=244
colorsdata = getcolors() 

fig = plt.figure(figsize=(15,13))
ax=fig.add_subplot(111,axisbg='white')

# limits for TD
plt.ylim(10, 744) 
plt.xlim(1940, 2021)

colindex=0
lastage=20

for qbn in qbnames:
  x=[]
  y=[]
  prevysum=0
  for row in fdata: 
    if ( row['Name'] == qbn and row['Year'] != 'Career'):
      yrval = num(row['Year'])
      lastage = num(row['Age'])
      prevysum += num(row['TD'])
      lastyr = yrval
      x += [yrval]
      y += [prevysum]

  if ( rank > highrank):
    plt.plot(x,y, color=colorsdata[colindex], label=qbn, linewidth=2.5)
    plt.legend(loc=0, prop={'size':10}) 
    colindex = (colindex+1)%22
    plt.text(lastyr-1, prevysum+2, qbn+"("+str(prevysum)+"):" +str(lastage), fontsize=9)

  else:
    plt.plot(x,y, color=colorsdata[22], linewidth=1.5) 
    rank = rank +1 
plt.xlabel('Year', fontsize=18)
plt.ylabel('Cumulative Touch Downs', fontsize=18)
plt.title("Cumulative Touch Downs by Quarter Backs", fontsize=20)
plt.show() 

当绘图( X,Y )切换到( Y,X )时,有足够的空间显示四分卫的名字。在前面的代码片段中,我们可能需要进行以下更改:

Visually representing the results

如果我们翻转 xy 轴,那么就有更多的空间来显示四分卫的名字和总触地得分,如前面的图所示。要做到这一点,可能需要切换 xy ,让标签文字按照新的 xy 轴正确定位。

plt.xlim(10, 744)  
plt.ylim(1940, 2021)

# remaining code all un-changed except

y += [num(row['Year'])]
x += [prevysum]

# Don't forget to switch the x,y co-ordinates of text display

plt.text(prevysum+2, lastyr-1, qbn+"("+str(prevysum)+"):" str(lastage), fontsize=9)

乍一看,我们只能分辨出职业生涯达阵得分数领先的四分卫(截至 2014-2015 足球赛季)。基于这种可视化,您可以进一步尝试分析和理解我们还可以从这些数据中推断出什么。这个问题的答案基于以下问题的答案:

  • 哪个四分卫在他们的职业生涯中打得最久?
  • 今天有谁能超越培顿·曼宁的触地得分记录吗?

在我们从输入文件中读取的字段中,Age恰好是我们拥有的字段值之一。有许多方法可以试验Age的起始值,用于绘制AgeTouchdown的统计数据。要回答第一个问题,我们必须跟踪Age而不是Year。以下代码片段可以在单独的函数中使用(如果必须经常使用的话),也可以包含在主脚本中:

maxage = 30

with open('/Users/MacBook/java/qb_data.csv') as csvfile:
  reader = csv.DictReader(csvfile)
    for row in reader:
      if ( num(row['Age']) > maxage ):
        maxage = num(row['Age']) 

print maxage 

运行前面的代码块显示44是四分卫的最大年龄(在联盟中活跃时,有三个这样的四分卫:沃伦·穆恩、维尼·泰斯特维德和史蒂夫·德伯格。技术上,乔治·布兰达一直打到了 48 岁(这是作为球员的最大年龄),但他从四分卫开始,也是一名踢球的人(有些年了)。

为了回答另一个问题,我们根据四分卫年龄绘制了触地得分统计数据,如下所示:

import csv
import matplotlib.pyplot as plt

# The following functions can be in a separate file
#    -- functions Begin -- 
def num(s):
  try:
    return int(s)
  except ValueError:
    return 0  

def getcolors():
  colors = [(31, 119, 180), (255,0,0), (0,255,0), (148, 103, 189), (140, 86, 75), (218, 73, 174), (127, 127, 127), (140,140,26), (23, 190, 207), (65,200,100), (200, 65,100), (125,255,32), (32,32,198), (255,191,201), (172,191,201), (0,128,0), (244,130,150), (255, 127, 14), (128,128,0), (10,10,10), (44, 160, 44), (214, 39, 40), (206,206,216)]

  for i in range(len(colors)):
    r, g, b = colors[i]
    colors[i] = (r / 255\. , g / 255\. , b / 255.)
  return colors

def getQbNames():
  qbnames = ['Peyton Manning']
  name=''
  i=0
  with open('/Users/MacBook/java/qb_data.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
      if ( row['Name'] != name and qbnames[i] != row['Name']):
        qbnames.append(row['Name'])
        i = i+1
  return qbnames

def readQbdata():
  resultdata = []
  with open('/Users/MacBook/java/qb_data.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    resultdata = [row for row in reader]
  return resultdata

fdata=[]
prevysum=0

#    -- functions End -- 

qbnames = getQbNames()
fdata = readQbdata()

i=0
rank=0
prevysum=0
lastyr=0
highrank=244
colorsdata = getcolors() 

fig = plt.figure(figsize=(15,13))
ax=fig.add_subplot(111,axisbg='white')

# limits for TD
plt.ylim(10, 744)
#change xlimit to have age ranges 
plt.xlim(20, 50)

colindex=0
lastage=20

for qbn in qbnames:
  x=[]
  y=[]
  prevysum=0
  for row in fdata: 
    if ( row['Name'] == qbn and row['Year'] != 'Career'):
      yrval = num(row['Year'])
      lastage = num(row['Age'])
      prevysum += num(row['TD'])
      lastyr = yrval
      x += [lastage]
      y += [prevysum]

  if ( rank > highrank):
    if ( lastage == 44):
      plt.plot(x,y, color='red', label=qbn, linewidth=3.5)
    else:
      plt.plot(x,y, color=colorsdata[colindex], label=qbn, linewidth=2.5)
      plt.legend(loc=0, prop={'size':10}) 

    colindex = (colindex+1)%22
    plt.text(lastage-1, prevysum+2, qbn+"("+str(prevysum)+"):" +str(lastage), fontsize=9)

  else:
    if ( lastage == 44):
      plt.plot(x,y, color='red', label=qbn, linewidth=3.5)
      plt.text(lastage-1, prevysum+2, qbn+"("+str(prevysum)+"):" +str(lastage), fontsize=9)
    else:         
      plt.plot(x,y, color=colorsdata[22], linewidth=1.5) 
    rank = rank +1 

plt.xlabel('Age', fontsize=18)
plt.ylabel('Number of Touch Downs', fontsize=18)
plt.title("Touch Downs by Quarter Backs by Age", fontsize=20)
plt.show() 

Visually representing the results

当你看一下标绘结果,只有两个四分卫的成绩能和 35 岁的培顿·曼宁相提并论,那就是德鲁·布里斯和汤姆·布拉迪。然而,考虑到汤姆·布拉迪目前的年龄和他迄今为止的成就,似乎只有德鲁·布里斯更有可能超越培顿·曼宁的触地得分记录。

下图显示了这一结论,并给出了基于 35 岁的简单数据图。对比前四名四分卫的成绩——培顿·曼宁、汤姆·布拉迪、德鲁·布里斯和布雷特·法弗——我们可以看到,德鲁·布里斯在 35 岁时的成就堪比佩顿在同样年龄时的成就。虽然《纽约时报》以标题为题的文章《为什么培顿·曼宁的记录很难被打破》得出了不同的结论,但以下绘图,至少是倾向于德鲁·布里斯可能打破记录的可能性:

Visually representing the results

用数据创造有趣的故事

数据可视化定期提升其用数据揭示故事的能力,在某些情况下,可视化地揭示不那么琐碎的故事。在最近的过去,记者们一直在将可视化更多地融入他们的叙述中,这通常有助于我们更好地理解他们的故事。在商业世界中,很少有人能掌握数据如何与一个既能在情感上又能在智力上吸引观众的有意义的故事联系起来。正如拉迪亚德·吉卜林所写的那样,如果历史是以故事的形式教授的,它将永远不会被忘记;类似的想法也适用于数据。因此,我们应该明白,如果以正确的方式呈现,数据会被更好地理解和记住。

为什么故事如此重要?

我们今天有许多可视化的工具和方法:条形图和饼图、表格、折线图、气泡图、散点图等等,不胜枚举。然而,使用这些工具,重点是数据探索,而不是辅助叙述。虽然有可视化的例子确实有助于讲故事,但它们很少。这主要是因为找到故事比计算数字要困难得多。还有 读者驱动的叙事作者驱动的叙事

作者驱动的叙事具有由作者选择并呈现给公众读者的数据和可视化。另一方面,读者驱动的叙事为读者提供了玩数据的工具和方法,这给了读者更多的灵活性和选择来分析和理解可视化。

读者驱动的叙事

2010 年,斯坦福大学的研究人员研究并回顾了讲故事的新兴重要性,并为叙事可视化提出了一些设计策略。根据他们的研究,纯作者驱动的方法在可视化中有严格的线性路径,依赖于消息传递,并且没有交互性,而读者驱动的方法没有规定的图像排序,没有消息传递,并且具有高度的交互性。作者驱动方法的一个例子是幻灯片演示。该研究列出的七种可视化叙事包括杂志风格、标注图表、分区海报、流程图、连环画、幻灯片和一部电影/视频/动画。

Gapminder

读者驱动的叙事与数据驱动的叙事相结合的一个经典例子是《Gapminder World》(http://gapminder.org/world)。它收集了 600 多个国际经济、环境、卫生、技术等数据指标。它提供了学生可以用来研究现实世界问题和发现模式、趋势和相关性的工具。这是由 Trendalyzer 软件开发的,该软件最初由瑞典的汉斯·罗斯林基金会开发,后来在 2007 年 3 月被谷歌收购。

Gapminder

Gapminder 使用的信息可视化技术是一个交互式气泡图,默认设置为五个变量: XY 、气泡大小、颜色和一个由滑块控制的时间变量。这种滑动控制和对沿着 XY 轴的选择使其具有交互性。然而,创造一个故事,即使有这样的工具,也不一定容易。讲故事是一门手艺,可以成为一种有效的知识分享技巧,因为它比大多数其他交流方式更有效地传达了背景和情感内容。

最有魅力的说书人都明白理解观众的重要性。他们可能会给一个孩子和一个成年人讲同样的故事,但是发音会有所不同。同样,数据驱动或读者驱动的故事应该根据谁在听或研究它来调整。例如,对于高管来说,统计数据可能是关键,但商业智能经理最有可能对方法和技术感兴趣。

今天有很多 JavaScript 框架可以用来创建交互式可视化,最流行的是 D3.js,使用 Python,今天只有几种方法可以创建交互式可视化(不使用 Flash)。一种方法是生成 D3.js 可以用来绘图的 JSON 格式的数据,第二种选择是使用 Plotly(http://www . Plotly . ly)。我们将在这一章的最后一节更详细地讨论 Plotly。

国情咨文

推特根据奥巴马总统演讲期间的推文创建了一个可视化工具,可以根据位置和主题绘制推文。这种可视化很有趣,因为它在一个地方捕捉了很多细节。滚动演讲,看看推特的反应;发布在http://twitter.github.io/interactive/sotu2015/#p1

The State of the Union address

美国的死亡率

从 1968 年到 2010 年,美国的死亡率下降了大约 17%,这些年我们有详细的数据(来自 http://www.who.int/healthinfo/mortality_data/en/和 T2)。几乎所有这些进步都可以归因于男性生存前景的改善。看起来进步在 20 世纪 90 年代中期就停止了,但其中一个原因可能是从那以后人口老龄化了很多。人们可能会读到彭博对此的完整描述,但这里我们试图展示两个可视化:

  • 1968-2010 年期间男性、女性和合计死亡率
  • 死亡率为七个年龄组展示了一些有趣的结果

Mortality rate in the USA

该示例的代码如下:

import csv
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(15,13))
plt.ylim(740,1128)
plt.xlim(1965,2011)
# Data from http://www.who.int/healthinfo/mortality_data/en/
with open('/Users/MacBook/Downloads/mortality1.csv') as csvfile:
  mortdata = [row for row in csv.DictReader(csvfile)]

x=[]
males_y=[]
females_y=[]
every_y=[]
yrval=1968
for row in mortdata:
  x += [yrval]
  males_y += [row['Males']]
  females_y += [row['Females']]
  every_y += [row['Everyone']]
  yrval = yrval + 1

plt.plot(x, males_y, color='#1a61c3', label='Males', linewidth=1.8)
plt.plot(x, females_y, color='#bc108d', label='Females', linewidth=1.8)
plt.plot(x, every_y, color='#747e8a', label='Everyone', linewidth=1.8)
plt.legend(loc=0, prop={'size':10})
plt.show()

死亡率是按每 10 万人计算的。通过将人口分成不同的年龄组,预期寿命的提高是持续的,尤其是 25 岁以下年龄组的进步最大。25-44 岁年龄组以下的人口到底发生了什么(红色显示)?关于彭博的叙述将另一个事实联系起来,很好地阐明了原因,即在此期间,艾滋病造成的死亡人数对该年龄组产生了影响。

Mortality rate in the USA

艾滋病每年夺去 4 万多名美国人的生命,其中 75%的人年龄在 25-44 岁之间。因此,不寻常的结果是在那个时间窗口看到的。

import csv
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(15,13))
plt.ylim(35,102)
plt.xlim(1965,2015)

colorsdata = ['#168cf8', '#ff0000', '#009f00', '#1d437c', '#eb912b', '#8663ec', '#38762b']
labeldata = ['Below 25', '25-44', '45-54', '55-64', '65-74', '75-84', 'Over 85']

# using reader() instead of DictReader() so that we could loop to 
# build y-values in list
with open('/Users/MacBook/Downloads/mortality2.csv') as csvfile:
  mortdata = [row for row in csv.reader(csvfile)]

x=[]
for row in mortdata:
  yrval = int(row[0])
  if ( yrval == 1969 ):
    y = [[row[1]],[row[2]],[row[3]],[row[4]],[row[5]],[row[6]],[row[7]]]
  else:
   for col in range(0,7):
     y[col] += [row[col+1]]
  x += [yrval]

for col in range(0,7):
  if ( col == 1 ):
    plt.plot(x, y[col], color=colorsdata[col], label=labeldata[col], linewidth=3.8)
  else:
    plt.plot(x, y[col], color=colorsdata[col], label=labeldata[col], linewidth=2)

plt.legend(loc=0, prop={'size':10})
plt.show()

csv.reader()csv.DictReader()的区别在于,当输入的 CSV 文件有字段名(或列名)时,DictReader()使用字段名作为关键字,使用该列中的实际值作为数据值。在前面的例子中,我们使用了reader(),因为当涉及到循环时( y[col] = [row[col+1]] )会很方便。此外,对于reader(),如果 CSV 文件中存在列名,则应忽略第一行。

我们还将这两个示例的过滤数据作为mortality1.csvmortality2.csvhttp://www.knapdata.com/python提供。

对于mortdata[:4],每种阅读方法的结果都是不同的。换句话说,当我们使用reader()mortdata[:4]的结果如下:

[['1969', '100', '99.92', '97.51', '97.47', '97.54', '97.65', '96.04'],  ['1970', '98.63', '97.78', '97.16', '97.32', '96.2', '96.51', '83.4'],  ['1971', '93.53', '95.26', '94.52', '94.89', '93.53', '93.73', '89.63'],  ['1972', '88.86', '92.45', '94.58', '95.14', '94.55', '94.1', '89.51']] 

使用DictReader(),假设 CSV 文件有字段名,四行显示如下:

[{'25-44': '99.92', '45-54': '97.51', '55-64': '97.47', '65-74': '97.54', '75-84': '97.65', 'Below 25': '100', 'Over 85': '96.04', 'Year': '1969'},
 {'25-44': '97.78', '45-54': '97.16', '55-64': '97.32', '65-74': '96.2', '75-84': '96.51', 'Below 25': '98.63', 'Over 85': '83.4', 'Year': '1970'},
 {'25-44': '95.26', '45-54': '94.52', '55-64': '94.89', '65-74': '93.53', '75-84': '93.73', 'Below 25': '93.53', 'Over 85': '89.63', 'Year': '1971'},
 {'25-44': '92.45', '45-54': '94.58', '55-64': '95.14', '65-74': '94.55', '75-84': '94.1', 'Below 25': '88.86', 'Over 85': '89.51', 'Year': '1972'}]

其他几个示例叙述

人们可以探索、想象、互动和玩耍的例子数不胜数。一些值得注意的问题如下:

  • 经济衰退如何重塑经济 255 图(纽约时报):这段叙述展示了在大衰退结束后的五年里,经济如何重新获得了失去的 900 万个工作岗位,突出了哪些行业比其他行业恢复得更快。(来源:http://tinyurl.com/nwdp3pp。)
  • 2013 年华盛顿奇才队的投篮明星(华盛顿邮报):这张互动图是几年前根据 2013 年华盛顿奇才队的表现而创作的,试图分析和看看签下保罗·皮尔思如何能够带来中距离投篮的大幅提升。(来源:http://www . Washington post . com/WP-SRV/special/sports/wizard-shooting-star/。)

作者驱动的叙事

纽约**时报制作了一些世界上最好的数据可视化、多媒体和互动故事。他们对这些项目的抱负一直是在非常有声望的水平上达到新闻标准,并为读者创造真正新的体验。其中的讲故事文化是这部作品背后的能量来源之一。

例如,有一个名为也门混乱地理*的数据和作者驱动叙事的组合。2015 年 3 月 26 日,沙特阿拉伯喷气式飞机袭击了也门的目标,以打击胡塞叛军组织。也门对沙特阿拉伯、伊朗和美国等关键角色扮演着重要角色。《纽约时报》的作者们直观地捕捉到,胡塞武装的影响力在过去几年有所增长。

Author-driven narratives

也门是基地组织在阿拉伯半岛最活跃的分支之一的所在地。自 2009 年以来,美国已经在也门进行了至少 100 次空袭。除了基地组织的占领之外,伊斯兰国在该地区也有活动,最近,他们声称对萨那两个什叶派清真寺的爆炸事件负责,这两起爆炸造成 135 多人死亡。以下可视化来自美国企业研究所关键威胁项目调查新闻局:

Author-driven narratives

另一个很好的例子是大卫·麦坎德斯对大西洋过去的可视化,它展示了过度捕捞之前的海洋是什么样子。很难想象过度捕捞对海洋造成的损害。效果是看不见的,隐藏在海洋中。下图显示了 1900 年和 2000 年北大西洋中被广泛食用的鱼类的生物量。人们普遍食用的鱼类包括金枪鱼、鳕鱼、黑线鳕、鳕鱼、大比目鱼、鲱鱼、鲭鱼、鳕鱼、鲑鱼、海鳟鱼、条纹鲈鱼、鲟鱼和大菱鲆,其中许多现在都很脆弱或濒临灭绝。

不列颠哥伦比亚大学的维利·克里斯滕森博士和他的同事使用生态系统模型、水下地形图、鱼获记录和统计分析来呈现本世纪不同时期大西洋鱼类的生物量。

Author-driven narratives

感知和呈现方法

在过去,数据的大小和种类并没有带来太大的挑战;因此,感知和分析数据很简单。今天,在无数的领域中有大量的数据,可视化可以为人类感知和交互数据的可视化提供有价值的帮助。人为因素对整个可视化过程有很大贡献,以便更好地理解数据并帮助决策任务。

可视化技术可以分为两个领域:

  • 科学 可视化:这涉及到具有内在物理实体的科学数据
  • 信息 可视化:这涉及到抽象数据(空间或非空间)

大多数可视化系统的设计都是为了让人类和计算机能够相互协作,各自执行以下任务:

  • 直观地表示数据以增强数据分析
  • 直观显示模型、数据解释、想法、假设和洞察力
  • 帮助用户通过为他们的假设找到支持或矛盾的证据来改进他们的模型
  • 帮助用户组织和分享他们的想法

对视觉感知的新见解来自于信息可视化之外的各个学科的工作,如人的因素和人机交互。数据可视化的最大优势之一是我们处理视觉信息的速度比语言信息快得多。20 世纪 20 年代,心理学家在德国研究知觉组织,第一批是格式塔理论家。

感知的格式塔原则

完形这个词的意思是“有组织的整体”,或者换句话说,当单独识别的部分对整体有不同的特征时。例如,为了描述一棵树,你可以说它有不同的部分,如树干、树叶、树枝、果实(在某些情况下)。然而,当我们看整棵树时,我们意识不到部分,而是意识到整个物体——在这种情况下,是树。

格式塔知觉的原理如下:

  • Proximity: Objects that are close together or connected to each other are perceived as a group, reducing the need to process smaller objects separately.

    The Gestalt principles of perception

  • Similarity: Objects that share similar attributes, color, or shape are perceived as a group.

    The Gestalt principles of perception

  • Common fate: When both the principles of proximity and similarity are in place, a movement takes place. Then they appear to change grouping.

    The Gestalt principles of perception

  • Good continuation: Some things are important as a whole, which means if there are interruptions, then it changes the perceptive reading. In the following image, we perceive the two crossed lines instead of four lines meeting at the center:

    The Gestalt principles of perception

  • Closure: Even if a part of the border of a shape is missing, we still tend to see the shape as completely enclosed by the border and ignore the gaps.

    The Gestalt principles of perception

了解这些原则对于创建任何可视化方法都非常有用。

让我们用一个例子来进一步阐述。邻近是指如果形状看起来彼此相似,则将形状分组在一起的视觉方法。这样的群体通常被视为一个单一的单位。例如,下图显示了如何区分邻近关系:

The Gestalt principles of perception

可视化的一些最佳实践

第一个重要的步骤是知道努力背后的目标是什么。如何知道可视化是否有目的?了解观众是谁以及这将如何帮助他们也非常重要。

一旦知道了这些问题的答案,并且很好地理解了可视化的目的,下一个挑战就是选择正确的方法来呈现它。最常用的可视化类型可以根据以下内容进一步分类:

  • 比较和排名
  • 相互关系
  • 分配
  • 特定位置或地理数据
  • 部分到整体的关系
  • 趋势随着时间的推移

对比排名

比较和排名可以有多种方式,但是传统的方式是使用条形图。条形图被认为是将数量值编码为同一基线上的长度。然而,这并不总是显示比较和排名的最佳方式。例如,要显示非洲国内生产总值排名前 12 的国家,以下演示是一种创造性的可视化方式(礼貌: Stats Legend,Andrew Gelman 和 Antony Unwin ):

Comparison and ranking

相关性

一个简单的相关性分析是一个很好的开始来识别测量之间的关系,虽然相关性不能保证关系。为了确认这种关系确实存在,通常需要一种统计方法。下面是一个构建简单散点图来检测两个因素之间的相关性的示例,比如某大学学生中的gpatvgpaexercise:

Correlation

但是,我们可以使用其他方式来显示相关矩阵。例如,可以使用散点图、热图或一些特定的例子来显示 S&P 100 指数股票之间的影响网络。(以下两幅图取自http://www.sthda.com高通量分析统计工具。)为了进一步强调,一个相关矩阵包含矩阵形式的数据。数据通过使用缩放的颜色映射进行关联,如下例所示。更多详情,建议您参考网站:http://www.sthda.com

Correlation

相关矩阵用于同时研究多个变量之间的相关性。结果是一个包含每个变量和其他变量之间的相关系数的表。热图起源于 2D 在数据矩阵中显示值。有许多不同的配色方案可以用来说明热图,每个方案都有感知上的优点和缺点。

Correlation

分布

分布分析显示了定量值在它们的范围内是如何分布的,因此在数据分析中非常有用。例如,比较一个班学生的期中、期末考试和课程总成绩的家庭作业的等级分布。在本例中,我们将讨论两种最常用的图表类型。一个是 直方图(如下图所示),另一个是方块 方块触须图。

Distribution

直方图的形状很大程度上取决于指定的面元大小和位置。盒须图非常适合显示多种分布。他们把所有的数据点——在这种情况下是每个学生的分数——打包到一个触须显示器中。现在,您可以轻松地识别所有类别中的低值、第 25 百分位值、中间值、第 75 百分位值和最大值,所有这些都是同时进行的。

Distribution

在 Python 中方便地绘制这些图的方法之一是使用 Plotly,这是一种在线分析和可视化工具。Plotly 为 Python、R、Julia 和 JavaScript 提供在线绘图、分析和统计工具以及科学绘图库。直方图和盒须图的例子,参考https://plot.ly/python/histograms-and-box-plots-tutorial

特定位置或地理数据

地图是显示特定位置数据的最佳方式。当地图与另一个详细说明地图显示内容的图表(如从大到小排序的条形图、显示趋势的折线图等)配对时,使用地图最为合适。例如,下图显示了各大洲地震的强度对比:

Location-specific or geodata

部分对整体的关系

饼图是显示部分到整体关系的常用工具,但也有其他方法。分组条形图有利于将类别中的每个元素与其他元素进行比较,也有利于跨类别比较元素。然而,分组使得区分每组总数之间的差异变得更加困难。这就是堆叠柱形图的由来。

Part-to-whole relationships

堆叠柱形图非常适合显示总数,因为它们直观地将所有类别聚集在一个组中。不利的一面是,比较各个类别的规模变得更加困难。堆叠也表示部分到整体的关系。

一段时间内的趋势

分析数据最常用的可视化方法之一是显示一段时间内的趋势。在下面的例子中,绘制了 2009-2015 年对可穿戴设备初创公司的投资。说明可穿戴设备的投资几年来一直在上升;2014 年的交易活动如火如荼,有 61 笔交易完成,总价值 4.27 亿美元,而 2013 年(仅一年前)只有 43 笔交易价值 1.66 亿美元。

Trends over time

有了这个观察,看看未来几年市场如何演变将会很有趣。

Python 中的可视化工具

分析和可视化数据需要几个软件工具:编写代码的文本编辑器(最好是语法高亮显示的)、运行和测试代码的 Python 和附加库,或许还有呈现结果的工具。软件工具分为两类:通用软件工具和特定软件组件。

开发工具

通用软件工具是一个集成开发环境 ( IDE ),它是一个应用,在一个包中拥有所有的生产力工具。从处理 Python 库的角度来看,这些 ide 通常非常方便。关于这些集成开发环境工具的更多细节将在下一章中讨论。在这一章中,我们将把我们的讨论限制在对来自 Enthought】的树冠和来自 Continuum Analytics蟒蛇的简单介绍。

具体软件组件有BokehIPythonmatplotlibNetworkXSciPyNumPyScikit-learnSeaborn等 Python 绘图库。这两个 ide 都有一个非常方便的方法来处理这些绘图库的添加、删除和更新。

来自恩思的树冠

entorn Canopy 有一个免费版本,是在 BSD 风格的许可下发布的,除了其他几个库之外,还附带了作为绘图工具的 GraphCanvasSciMathChaco 。它拥有高级文本编辑器、集成的 IPython 控制台、图形包管理器和在线文档链接。Canopy 分析环境简化了科学家、工程师和分析师的数据分析、可视化、算法设计和应用开发。

Canopy from Enthought

来自连续体分析的蟒蛇

Anaconda IDE 基于 conda 应用。Conda 是一个用于查找和安装软件包的应用。conda 包是包含系统级库、Python 模块、可执行程序或其他组件的二进制 tarball。Conda 跟踪包和平台细节之间的依赖关系,使得从不同的包集合创建工作环境变得简单。

Anaconda 有 sypder-app ,一个科学的 Python 开发环境,也有一个 IPython 查看器。除此之外,IPython 还可以作为图形用户界面或基于网络的笔记本推出。最方便的是可以在主目录中安装 Python,而不需要触碰系统安装的 Python。到目前为止,并不是所有的包都准备好使用 Python 3;因此,最好将 Python 2 与这些 ide 一起使用。

IPython()http://ipython.scipy.org/)提供了一个增强的、交互式的 Python 外壳,并且强烈推荐主要是因为数据分析和可视化本质上是交互式的。大多数平台都支持 IPython。IPython 附带的一些附加功能如下:

  • 选项卡完成:这包括变量、函数、方法、属性和文件名的完成。标签完成是通过 GNU Readline 库(http://tiswww.case.edu/php/chet/readline/rltop.html)实现的,非常上瘾。在接触到 GNU Readline 之后,很难回到常规的命令行界面。
  • 命令历史功能:这将发布之前使用的命令的完整记录的命令历史。

Anaconda from Continuum Analytics

交互可视化

要使可视化被认为是交互式的,它必须满足两个标准:

  • 人类输入:对信息的视觉表示的某些方面的控制必须对人类可用
  • 响应时间:人类做出的改变必须及时融入可视化

当必须处理大量数据来创建可视化时,这变得非常困难,有时是不可能的,即使以当前的技术;因此,“交互式可视化”通常应用于在输入几秒钟内向用户提供反馈的系统。许多交互式可视化系统支持导航隐喻,类似于通过物理世界的导航。

互动的好处是,人们可以在更短的时间内探索更大的信息空间,这可以通过一个平台来理解。然而,这种交互的一个缺点是,它需要大量时间来彻底检查每一种可能性,以测试可视化系统。此外,设计系统以保证对用户行为的即时响应可能需要大量的算法关注。

任何可视化方法都需要良好的布局规划。一些布局方法会自动生成对称的图形;或者,一些绘图方法从发现数据的对称性开始。交互式可视化是使用事件侦听器实现的,对一些人来说,这是众所周知的常识,但无论如何,下面的部分描述了它的全部内容。

事件监听器

事件监听器是当鼠标被移动或点击时使用的过程。技术上,事件有很多种,但纯粹为了交互可视化,你只需要知道当用户用鼠标在可视化中导航时会发生什么。交互的延迟,也就是系统响应鼠标动作的时间,非常重要。

最显而易见的原则是,用户确实应该有某种动作已经完成的确认,而不是被悬着不知道动作是否还在进行。因此,诸如突出显示选定项目的反馈是确认所需操作已成功完成的好方法。视觉反馈通常应该发生在大约一秒钟的即时反应等待时间内。以下是谷歌图表中的一个 JavaScript 事件监听器示例:

chart = new google.visualization.PieChart(document.getElementById( 'chart_div'));
google.visualization.events.addListener(chart, 'select', selectHandler);
chart.draw(data, options);

function selectHandler() {
  var selectedItem = chart.getSelection()[0];
  var value = data.getValue(selectedItem.row, 0);
  alert('The user selected ' + value);
}

另一个原则是,如果一个动作花费的时间比用户自然预期的要长得多,那么应该向用户显示某种进度指示器。用 JavaScript 编写事件侦听器要容易得多,但是为了使用用 Python 编写的绘图方法创建交互式可视化,应该使用 Plotly。

还有另一个模块,图形工具(https://graph-tool . skill . de),可以用来以直接的方式执行动画。它使用 GTK+在交互窗口和屏幕外显示动画到文件。这个想法是为了容易地生成可视化,可以用于演示和嵌入网站。

布局

为了直观高效地显示数据,了解布局方法非常重要。美学是衡量布局算法优劣的标准之一。为了使布局结果更易读,如果可能的话,结构需要有层次或对称;一个关键因素是空间的利用。

一个好的布局是解开和理解任何图形的关键。一般来说,为了更好地理解,每种布局都特别适合不同类型的数据可视化。一些值得注意的布局方法如下:

  • 圆形布局
  • 放射状布局
  • 气球布局

圆形布局

表格是数据的天然容器。每当信息被呈现时,通过表格呈现的机会非常高。然而,在许多情况下,当这些信息很复杂(因此表格很大)时,表格表示很难直观地解析,表格数据中的模式仍然不透明。

换句话说,有用的容器并不总是呈现数据的有用方式。该表很好地呈现了单个数据,但是它们之间的相互关系以及它们组成的模式几乎不可见。圆形布局可以使用几种不同的组合(定性和定量)在单个可视化中显示,如下图所示:

Circular layout

例如,如上图所示,在有限的空间内显示复杂的关系是很直观的。

Circular layout

上图显示了以圆形布局显示的复杂层次关系的示例。

放射状布局

Sunburst 可视化是一种用于显示树状结构的径向空间填充可视化技术(如上图所示)。还有其他填充空间的可视化方法,使用其他可视化编码来描述层次结构。例如,树形图是一种空间填充可视化,它使用“包含”来显示“父子”关系。有几个微妙的变化可以改善这种可视化传达信息的方式。

由于每个轨道的长度随着半径的增加而增加,因此节点的空间往往更大。随着层级的增加,径向树会将更多的节点分布在更大的区域。

气球布局

气球布局有不同的变化,人们甚至可以将它们视为气泡。但是,如果我们使用不同颜色和大小的气球(或圆圈/气泡),在这个可视化中可以显示更多,如下图所示:

Balloon layout

总结

可视化方法的原则对于创建一个有效的故事是有用的。本章解释的叙述给出了美学的概念和方法的巨大变化。

数据可视化的目标是通过所选择的可视化显示方法,向用户清晰有效地传达信息。有效的可视化有助于分析和推理数据和证据。它使复杂的数据更易于访问、理解和使用。用户可能有特定的分析任务,如进行比较或理解因果关系,图形的设计原则遵循任务。

表通常用于用户查找变量的特定度量,而各种类型的图表用于显示一个或多个变量的数据模式或关系。

数据可视化既是一门艺术,也是一门科学,就像解决一个数学问题。没有一个正确的方法来解决它。同样,没有一种正确的方法来创建可视化方法。可视化工具有很多,我们知道一些支持 Python 的工具。在下一章中,将讨论关于这些工具的更多细节。*

三、Python 集成开发环境入门

Python 是一种广泛使用的编程语言,已经存在了 20 多年。在许多其他东西中,这种语言因其简单性和动态类型而非常受欢迎。类型(基准)动态确定数据对象的类型。它有一个语法,允许程序员写很少的代码行。Python 支持多种编程范例,包括函数式、面向对象和过程式。

Python 解释器可以在几乎所有正在使用的操作系统上使用。它的内置数据结构与动态绑定相结合,使得它作为一种高性能语言来快速连接现有的操作组件非常有吸引力。即使在分布式应用中,Python 也作为粘合剂与 Hive (NoSQL)一起使用,以非常快速和高效地完成一些事情。Python 功能强大,在软件开发社区中很受欢迎,它需要一个交互式环境来创建、编辑、测试、调试和运行程序。

一个 集成开发环境 ( IDE )是一个软件应用,它提供了一套全面而强大的工具来为运行 Windows、Linux 或 Mac OS 操作系统的目标系统构建应用。这些工具提供单一且一致的集成环境,旨在最大限度地提高工作效率。Python 编程的 IDE 有很多选择。细节将在本章的下一节讨论。此外,我们将讨论以下主题:

  • Python 中的集成开发环境工具
  • 安装指南—下载和安装工具的说明
  • conda 命令行界面 ( CLI )和 Spyder
  • IDE 工具中的数据可视化工具是特定于对可视化有用的库的
  • 交互式可视化包
  • 使用集成开发环境工具的一些绘图示例

Python 中的 IDE 工具

分析和可视化数据需要几个软件工具:一个编写代码的文本编辑器(最好是语法高亮显示),运行和测试代码的附加工具和库,也许还有另一套呈现结果的工具。集成开发环境有许多优点。一些值得注意的问题如下:

  • 语法高亮显示(立即显示错误或警告)
  • 在调试模式下单步执行代码
  • 交互式控制台
  • 与交互式图形笔记本(如 IPython)的集成

Python 3.x 对比 Python 2.7

Python 3.x 与 2.x 版本不向后兼容。这就是为什么 Python 2.7 仍在使用的原因。在本书中,我们将使用 Python 2.7,尽量不要将重点放在 Python 3.x 上,这个问题超出了本书的范围,建议您搜索如何编写适用于不同版本的代码的信息。一些 IDE 工具有使用这两个版本的具体说明。在某些情况下,代码的编写可能会有所不同。

交互工具的类型

在进一步讨论 Python IDEs 的之前,考虑显示交互式数据可视化的不同方式是很重要的。创建交互式数据可视化有许多选项,但在这里,我们将只考虑两种流行的工具来实现这一点:

  • 伊普提洪伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁伊普提翁
  • 阴谋地

IPython

2001 年,Fernando Perez 开始致力于 IPython,这是一个增强的交互式 Python shell,具有一些改进,例如历史缓存、配置文件、对象信息和会话日志记录。最初专注于 Python 中的交互计算,后来包括 Julia、R、Ruby 等等。一些特性——比如自动添加括号和完成标签——非常省时,并且在可用性方面非常有效。在标准 Python 中,要完成选项卡,您必须导入几个模块,而 IPython 默认提供选项卡完成。

IPython 为 Python 脚本编写提供了以下丰富的工具集:

  • 方便的终端命令和基于 Qt 的工具
  • 纯基于网络的笔记本电脑的交互环境,具有与独立笔记本电脑相同的核心功能;它还支持代码、文本、数学表达式和内联图
  • 方便的交互式数据可视化;这种能力是许多 ide 集成支持 IPython 的原因
  • 易于使用的高性能多处理计算工具

对 IPython 最有帮助的四个命令及其简要描述:

|

命令

|

描述

|
| --- | --- |
| ? | 这指定了 IPython 特性的介绍和概述 |
| %quickref | 这表示快速参考 |
| --help-all | 这指定了 Python 的帮助 |
| %who/%whos | 这给出了关于标识符的信息 |

IPython 笔记本是一个基于网络的交互式计算环境。在这里,您可以将代码、数学和绘图合并到一个文档中。

IPython(http://ipython.scipy.org/)提供了一个增强的交互式 Python 外壳,强烈推荐,主要是因为数据分析和可视化本质上是交互式的。大多数平台都支持 IPython。IPython 附带的一些附加功能包括:

  • 选项卡完成:这包括变量、函数、方法、属性和文件名的完成。标签完成是使用 GNU Readline 库(http://tiswww.case.edu/php/chet/readline/rltop.html)实现的。在接触到 GNU Readline 之后,很难回到常规的命令行界面。
  • 命令历史功能:这将发布以前使用的命令的完整记录的命令历史。

在 IPython 上运行的一个示例如下图所示。要了解更多关于 IPython 和 IPython 笔记本的信息,请参考http://nbviewer.ipython.org

IPython

绘图

Plotly 是一个在线分析和数据可视化工具,提供在线绘图、分析和统计工具,以实现更好的协作。该工具使用 Python 构建,用户界面使用 JavaScript,可视化库使用 D3.js、HTML 和 CSS。Plotly 包括许多语言的科学图形库,如 Arduino、Julia、MATLAB、Perl、Python 和 r .有关 Plotly 的示例来源,请参考https://Plotly . ly/~ etpinard/84/fig-31a-Hans-roslings-bubble-chart-For-year-2007/

以下是显示全球人均国内生产总值的臭名昭著的泡沫图例子。

Plotly

Plotly 提供了一种方便的方式将绘图从matplotlib转换为 Plotly,如以下代码所示(假设您有一个 Plotly 帐户并使用您的凭据登录):

import plotly.plotly as py
import matplotlib.pyplot as plt
#auto sign-in with credentials or use py.sign_in()
mpl_fig_obj = plt.figure()
#code for creating matplotlib plot
py.plot_mpl(mpl_fig_obj)

Python IDE 的类型

以下是目前流行的一些 Python IDEs:

  • PyCharm :这指定了基于 Java Swing 的用户界面
  • PyDev :表示基于 SWT 的用户界面(在 Eclipse 上工作)
  • 互动编辑为 巨蟒 ( IEP )
  • 来自恩思的天篷:这个是基于 PyQt 的
  • 连续分析公司 Spyder 的分布图(T4):这也是基于 PyQt

皮卡姆

PyCharm 是为数不多的有很大特色的热门 ide 之一,社区版是免费的。PyCharm 4.0.6 社区版是当前版本,可在https://www.jetbrains.com/pycharm/download免费下载。他们有快捷方式参考卡可用于苹果电脑,Linux 和视窗系统。佩德罗·克罗格博士在http://pedrokroger.net/getting-started-pycharm-python-ide/写了一篇关于 PyCharm 的详细描述。您可以参考此链接了解更多详细信息。在许多有趣的特性中,代码向导和 NumPy 数组查看器显示在下面的屏幕截图中:

PyCharm

极坐标投影可以快速完成,如前面的截图所示,随机样本数组的创建如下面的截图所示:

PyCharm

类似的随机样本在不同的 IDE(如 Spyder)中创建;下面是一个的例子:

rand_4 = np.random.random_sample((2,2,2,2))-1  
array([[[[-0.6565232 , -0.2920045 ],
[-0.45976502, -0.70469325]],
[[-0.80218558, -0.77538009],
[-0.34687551, -0.42498698]]],
[[[-0.60869175, -0.9553122 ], 
[-0.05888953, -0.70585856]], 
[[-0.69856656, -0.21664848],
[-0.29017137, -0.61972867]]]])

PyDev

PyDev 是 Eclipse IDE 的一个插件。换句话说,Eclipse 的插件足以利用普通集成开发环境可能具有的其他默认功能,而不是创建一个新的集成开发环境。PyDev 支持代码重构、图形调试、交互控制台、代码分析和代码折叠。

PyDev

您可以安装 PyDev 作为 Eclipse 的插件,或者安装 LiClipse,一个高级的 Eclipse 发行版。LiClipse 不仅增加了对 Python 的支持,还增加了对 CoffeeScript、JavaScript、Django 模板等语言的支持。

PyDev 预装在 LiClipse 中,但需要先安装 Java 7。完整安装步骤可参考http://pydev.org/manual_101_install.html

Python 交互编辑器(IEP)

IEP 是另一个 Python IDE,它拥有其他 IDE 中可用的类似工具,但看起来与您可能在微软 Windows 上使用的任何工具相似。

IEP 是一个跨平台的 Python IDE,旨在交互性和自省性,这使得它非常适合科学计算。它的实用设计旨在简单高效。

IEP 由两个主要的组件组成,编辑器和外壳,并使用一组可插拔工具以各种方式帮助程序员。一些示例工具是源结构、项目管理器、交互式帮助和工作区。一些关键特性如下:

  • 像任何现代 IDE 一样进行代码自省
  • 要么从命令行运行 Python 脚本,要么通过文件或 IPython 界面交互运行
  • Shells 作为后台进程运行
  • 多个外壳可以使用不同的 Python 版本(从 v2.4 到 3.x)

下面的截图显示了如何在同一个 IDE 中使用两个不同版本的 Python:

Interactive Editor for Python (IEP)

有些人不认为 IEP 是一个集成开发环境工具,但它服务于开发 Python 程序、编辑它们和运行它们的目的。它同时支持多个 Python 外壳。因此,对于想要使用多个图形用户界面工具包进行编程的人来说,这是一个非常有效的工具,例如 PySide、PyQt4、GTK 和 TK 交互。

IEP 是用(纯)Python 3 编写的,并且使用了 Qt GUI 工具包,但是它可以用来在任何可用的 Python 版本上执行代码。你可以从http://www.iep-project.org/downloads.html下载 IEP。

来自恩思的树冠

entorn Canopy 有一个免费版本,是在 BSD 风格的许可下发布的,除了其他几个库之外,它还附带了 GraphCanvasSciMathChaco 作为绘图工具。像所有的 IDEs 一样,它有一个文本编辑器。它还有 IPython 控制台,非常有用,能够运行和可视化结果。此外,它还附带了一个图形包管理器。当天篷启动时,它会提供一个带有编辑器包管理器文档浏览器的选项供选择。也可以尝试使用他们的培训材料,如下图所示:

Canopy from Enthought

除了其他开发代码之外,Canopy 还拥有 IPython 笔记本集成的便捷功能,您可以使用这些功能来创建数据可视化。像大多数 IDEs 一样,它有一个编辑器、一个文件浏览器和 IPython 控制台。此外,还有一个显示当前编辑状态的状态显示。雨篷集成开发环境的这些组件主要执行以下任务:

  • 文件浏览器:有了这个,你就可以从硬盘读取或者写入 Python 程序了
  • Python 代码编辑器:这指定了一个语法突出的代码编辑器,具有专门针对 Python 代码的附加功能
  • Python 窗格:这是一个集成的 IPython(交互式 Python)提示,可用于交互式运行 Python 程序,而不是从文件中运行
  • 编辑器状态栏:可以显示行号、列号、文件类型、文件路径

下面的截图显示了高亮显示的数字。这代表了之前描述的 IDEs 的组件。文件浏览器和 Python 窗格可以拖放到代码编辑器窗口或边框之外的不同位置。当一个窗格被拖动时,它可以停靠的位置以蓝色突出显示,如下图所示的:

Canopy from Enthought

文档通过名为座舱盖文档浏览器的浏览器组织,可从帮助菜单访问。这包括一些常用 Python 包的文档链接。

文档浏览器的一个重要特性是,它提供了对文档中呈现的示例代码的简单访问。当用户右键单击示例代码框时,将显示上下文菜单。此外,您可以选择复制代码选项,将代码块的内容复制到 Canopy 的复制粘贴缓冲区,以便在编辑器中使用。

Canopy 为个人提供了几种不同的产品,免费版本被称为 Canopy Express ,大约有 100 个核心包。这个免费版本对于科学和分析计算的 Python 开发来说是一个有用的工具。选择目标操作系统为 Windows、Linux 或 Mac OS 之一后,可在https://store.enthought.com/downloads/下载。

Python 开发环境中的挑战之一是,管理许多不同库和工具的包可能是一项非常耗时且令人生畏的任务。这就是他们的文档浏览器的样子。

Canopy from Enthought

Canopy 有一个包管理器,可以用来发现 Canopy 可用的 Python 包,并决定安装哪些附加包和删除哪些包。有一个方便的搜索界面来查找和安装任何可用的软件包,并恢复到以前的软件包状态。

Canopy 使用 Python 功能来确定可用的 Python 包。当 Canopy 启动时,它首先在虚拟环境中查找包并显示它们,如下图所示:

Canopy from Enthought

集成开发环境的编号突出显示区域是:

  1. 导航面板:这个和任何 IDE 都差不多;导航有一个树列表类型的结构来选择包管理器的组件。
  2. 主查看区:一旦左侧的选择发生变化,右侧面板将显示选中的项目,以及相关的套餐列表(如前一张截图所示)、带有标题为更多信息按钮的具体套餐信息等等。
  3. 搜索栏:这类似于任何搜索功能,有助于快速搜索包的名称和描述。例如,打字机器将列表过滤为 11 个包(匹配的数量可能因操作系统而异)。
  4. 订阅状态和帮助:这里会显示订阅的链接和当前使用的账号名称。
  5. 状态栏:对于用户进行的每一次导航,状态栏都会根据导航的变化显示结果当前状态的详细信息。

来自连续体分析的蟒蛇

蟒蛇是社区使用的最受欢迎的 ide 之一。它附带了一个已经集成的编译好的长的软件包列表。该 IDE 基于名为 conda 的核心组件(稍后将详细解释),您可以使用condapip安装或更新 Python 包。

Anaconda 是 Python 强大包的免费集合,支持大规模数据管理、分析和可视化,用于商业智能、科学分析、工程、机器学习等。

Anaconda 有一个 Scientific PYthon 开发环境 ( Spyder ),它也有一个 IPython 查看器。此外,IPython 可以作为图形用户界面或基于网络的笔记本推出。最方便的方面是可以在一个主目录下安装 Python,不要碰系统安装的 Python。并非所有的包都已经准备好使用 Python 3;因此,最好将 Python 2 与这些 ide 一起使用。Anaconda IDE 有两个重要组件,基于conda包管理器。两个组件是condaspyder

当 Anaconda 启动时,会出现以下截图。这为用户提供了几个选项,包括 IPython 控制台、IPython 笔记本、Spyder IDE 和 glueviz:

Anaconda from Continuum Analytics

Spyder 概述

Spyder 是一个 Python 开发环境,包含以下组件:

  • Python 代码编辑器:这个自带单独的函数浏览器,类编辑器自带对 Pylint 代码分析的支持。今天,代码完成已经成为一种规范,在所有的 ide 上都很方便,所以它也支持这一点。
  • 交互控制台:Python 语言最适合交互工作;因此,控制台必须具备支持对编辑器中编写的代码进行即时评估的所有必要工具。
  • 探索变量:在任何交互执行过程中探索变量有助于提高整体生产力。编辑变量也是可能的,例如字典,有时还可以编辑数组。

代码编辑器和 IPython 控制台如下图所示:

An overview of Spyder

conda 概述

Conda 是一个命令行工具,用于管理环境和 Python 的包,而不是使用pip。有多种方法可以查询和搜索包,必要时创建新的环境,并在现有的 conda 环境中安装和更新 Python 包。这个命令行工具还跟踪包和平台细节之间的依赖关系,帮助您从不同的包组合中创建工作环境。要检查运行的是 conda 的哪个版本,您可以输入以下代码(在我的环境中,它显示的是 3.10.1 版本):

Conda –v
3.10.1

一个 conda 环境是一个文件系统目录,包含一组特定的 conda 包。作为一个具体的例子,您可能希望有一个提供 NumPy 1.7 的环境和另一个为遗留测试提供 NumPy 1.6 的环境;conda 使这种混合和匹配变得容易。要开始使用环境,只需将PATH变量设置为指向其 bin 目录。

让我们看一个如何用 conda 安装一个名为 SciPy 的包的例子。假设您已经正确安装了 Anaconda,并且 conda 在运行路径中可用,您可能需要输入以下代码来安装 SciPy:

$ conda install scipy

Fetching package metadata: ....
Solving package specifications: .
Package plan for installation in environment /Users/MacBook/anaconda:

The following packages will be downloaded:

 package                    |            build
 ---------------------------|-----------------
 flask-0.10.1               |           py27_1         129 KB
 itsdangerous-0.23          |           py27_0          16 KB
 jinja2-2.7.1               |           py27_0         307 KB
 markupsafe-0.18            |           py27_0          19 KB
 werkzeug-0.9.3             |           py27_0         385 KB

The following packages will be linked:

 package                    |            build
 ---------------------------|-----------------
 flask-0.10.1               |           py27_1
 itsdangerous-0.23          |           py27_0
 jinja2-2.7.1               |           py27_0
 markupsafe-0.18            |           py27_0
 python-2.7.5               |                2
 readline-6.2               |                1
 sqlite-3.7.13              |                1
 tk-8.5.13                  |                1
 werkzeug-0.9.3             |           py27_0
 zlib-1.2.7                 |                1

Proceed ([y]/n)? 

您应该注意到,正在尝试安装的软件包上的任何依赖项都将被自动识别、下载和链接。如果需要安装或更新任何 Python 包,您必须使用以下代码:

conda install <package name>  or conda update <package name> 

下面是一个使用 conda(更新 matplotlib)从命令行更新包的例子:

conda update matplotlib

Fetching package metadata: ....
Solving package specifications: .
Package plan for installation in environment /Users/MacBook/anaconda:

The following packages will be downloaded:

 package                    |            build
 ---------------------------|-----------------
 freetype-2.5.2             |                0         691 KB
 conda-env-2.1.4            |           py27_0          15 KB
 numpy-1.9.2                |           py27_0         2.9 MB
 pyparsing-2.0.3            |           py27_0          63 KB
 pytz-2015.2                |           py27_0         175 KB
 setuptools-15.0            |           py27_0         436 KB
 conda-3.10.1               |           py27_0         164 KB
 python-dateutil-2.4.2      |           py27_0         219 KB
 matplotlib-1.4.3           |       np19py27_1        40.9 MB
 ------------------------------------------------------------
 Total:        45.5 MB

The following NEW packages will be INSTALLED:

 python-dateutil: 2.4.2-py27_0 

The following packages will be UPDATED:

 conda:           3.10.0-py27_0    --> 3.10.1-py27_0 
 conda-env:       2.1.3-py27_0     --> 2.1.4-py27_0 
 freetype:        2.4.10-1         --> 2.5.2-0 
 matplotlib:      1.4.2-np19py27_0 --> 1.4.3-np19py27_1
 numpy:           1.9.1-py27_0     --> 1.9.2-py27_0 
 pyparsing:       2.0.1-py27_0     --> 2.0.3-py27_0 
 pytz:            2014.9-py27_0    --> 2015.2-py27_0 
 setuptools:      14.3-py27_0      --> 15.0-py27_0 

Proceed ([y]/n)?

要检查使用 Anaconda 安装的软件包,请导航到命令行并输入以下命令,以快速显示默认环境中安装的所有软件包的列表:

conda list 

此外,您始终可以通过常用方式安装软件包,例如pip install,或者使用setup.py文件从源位置安装。虽然 conda 是首选的打包工具,但是 Anaconda 并没有什么特别的地方阻止使用标准的 Python 打包工具(比如pip)。

IPython 不是必需的,但强烈建议使用。IPython 应该在安装 Python、GNU Readline 和 PyReadline 之后安装。默认情况下,Anaconda 和 Canopy 会执行这些操作。本书中的所有示例都使用了 Python 包,这是有充分理由的。在下面的部分,我们已经更新了这个列表。

水蟒可视化图

从获取数据、操纵和处理数据,到可视化和交流研究成果,Python 和 Anaconda 支持科学数据工作流中的多种流程。Python 可以用于各种各样的应用(甚至超越科学计算);用户可以快速采用这种语言,不需要学习新的软件或编程语言。Python 的开源可用性增强了研究结果,并使用户能够与世界各地的科学家和工程师的大型社区联系。

以下是一些可用于 Anaconda 的常见打印库:

  • matplotlib :这是最流行的 Python 绘图库之一。再加上 NumPy 和 SciPy,这是科学 Python 社区的主要驱动力之一。IPython 具有 pylab 模式,该模式专门设计用于使用 matplotlib 执行交互式绘图。
  • Plotly :这是一个在浏览器上工作的协同绘图和分析平台。它支持使用 IPython 笔记本的交互式图形。图形是交互式的,可以通过修改代码和交互式查看结果来进行风格化。使用 matplotlib 生成的任何绘图代码都可以轻松导出到 Plotly 版本。
  • Veusz :这是一个用 Python 和 PyQt 编写的 GPL-科学绘图包。Veusz 也可以嵌入到其他 Python 程序中。
  • Mayavi :这是一个三维绘图包,完全可以从 Python 中脚本化,类似于简单的 pylab 和类似 MATLAB 的接口,用于绘制数组。
  • NetworkX :这是一个 Python 语言软件包,用于创建、操作和研究复杂网络的结构、动力学和功能。
  • pygooglechart :这是一个功能强大的包,可以让你创建可视化的方法,还可以和 Google Chart API 接口。

表面-3D 图

三维图由定义为 Z 的数据生成,作为 (X,Y) 的函数。这在数学上表示为 Z=f(X,Y) 。在我们这里的例子中,我们将绘制 Z=sin(sqrt(X2+Y2)) ,这本质上类似于二维抛物线。我们的绘图需要遵循以下步骤:

  1. First, generate the X and Y grid with the following code:

    import numpy as np
    
    X = np.arange(-4, 4, 0.25) 
    Y = np.arange(-4, 4, 0.25) 
    X, Y = np.meshgrid(X, Y)
    Generate the Z data:
    R = np.sqrt(X**2 + Y**2)
    Z = np.sin(R)
    

    使用mpl_toolkits包绘制简单的三维表面 sin(sqrt(X2+Y2)) 如下图所示;使用颜色条表示打击和绘图图:

    The surface-3D plot

  2. 然后,绘制曲面,如下图所示:

    from mpl_toolkits.mplot3d import Axes3d
    from matplotlib import cm
    from matplotlib.ticker import LinearLocator, FormatStrFormatter
    import matplotlib.pyplot as plt
    import numpy as np
    
    fig = plt.figure(figsize=(12,9))
    ax = fig.gca(projection='3d')
    X = np.arange(-4, 4, 0.25)
    Y = np.arange(-4, 4, 0.25)
    X, Y = np.meshgrid(X, Y)
    R = np.sqrt(X**2 + Y**2)
    Z = np.sin(R)
    surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
    
    ax.set_zlim(-1.01, 1.01)
    ax.zaxis.set_major_locator(LinearLocator(10))
    ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
    
    fig.colorbar(surf, shrink=0.6, aspect=6)
    
    plt.show()
    

为了让这个三维绘图工作,你必须确保安装了 matplotlib 和 NumPy 。Anaconda 中的默认包附带了这些安装。

正方形地图图

通过比较和上一章讨论的排名示例,使用 squarify 算法(带有 matplotlib)显示非洲 GDP 排名前 12 的国家,您可以获得一个看起来类似于树状图的图,如下代码所示:

# Squarified Treemap Layout : source file (squarify.py)
# Implements algorithm from Bruls, Huizing, van Wijk, "Squarified Treemaps"
# squarify was created by Uri Laserson 
# primarily intended to support d3.js 

def normalize_sizes(sizes, dx, dy):
  total_size = sum(sizes)
  total_area = dx * dy
  sizes = map(float, sizes)
  sizes = map(lambda size: size * total_area / total_size, sizes)
  return sizes

def pad_rectangle(rect):
  if rect['dx'] > 2:
    rect['x'] += 1
    rect['dx'] -= 2
  if rect['dy'] > 2:
    rect ['y'] += 1
    rect['dy'] -= 2

def layoutrow(sizes, x, y, dx, dy):
  covered_area = sum(sizes)
  width = covered_area / dy
  rects = []
  for size in sizes:  
    rects.append({'x': x, 'y': y, 'dx': width, 'dy': size / width})
    y += size / width
  return rects

def layoutcol(sizes, x, y, dx, dy):
  covered_area = sum(sizes)
  height = covered_area / dx
  rects = []
  for size in sizes:
    rects.append({'x': x, 'y': y, 'dx': size / height, 'dy': height})
    x += size / height
  return rects

def layout(sizes, x, y, dx, dy):
  return layoutrow(sizes, x, y, dx, dy) if dx >= dy else layoutcol(sizes, x, y, dx, dy)

def leftoverrow(sizes, x, y, dx, dy):
  covered_area = sum(sizes)
  width = covered_area / dy
  leftover_x = x + width
  leftover_y = y
  leftover_dx = dx - width
  leftover_dy = dy
  return (leftover_x, leftover_y, leftover_dx, leftover_dy)

def leftovercol(sizes, x, y, dx, dy):
  covered_area = sum(sizes)
  height = covered_area / dx
  leftover_x = x
  leftover_y = y + height
  leftover_dx = dx
  leftover_dy = dy - height
  return (leftover_x, leftover_y, leftover_dx, leftover_dy)

def leftover(sizes, x, y, dx, dy):
  return leftoverrow(sizes, x, y, dx, dy) if dx >= dy else leftovercol(sizes, x, y, dx, dy)

def worst_ratio(sizes, x, y, dx, dy):
  return max([max(rect['dx'] / rect['dy'], rect['dy'] / rect['dx']) for rect in layout(sizes, x, y, dx, dy)])

def squarify(sizes, x, y, dx, dy):
  sizes = map(float, sizes)
  if len(sizes) == 0:
    return []
  if len(sizes) == 1:
    return layout(sizes, x, y, dx, dy)
  # figure out where 'split' should be
  i = 1
  while i < len(sizes) and worst_ratio(sizes[:i], x, y, dx, dy) >= worst_ratio(sizes[:(i+1)], x, y, dx, dy):
    i += 1
  current = sizes[:i]
  remaining = sizes[i:]
  (leftover_x, leftover_y, leftover_dx, leftover_dy) = leftover(current, x, y, dx, dy)
  return layout(current, x, y, dx, dy) + \
squarify(remaining, leftover_x, leftover_y, leftover_dx, leftover_dy)

def padded_squarify(sizes, x, y, dx, dy):
  rects = squarify(sizes, x, y, dx, dy)
  for rect in rects:
    pad_rectangle(rect)
  return rects

前一个代码显示的 squarify 函数可以用来显示非洲 GDP 排名前 12 的国家,如下代码所示:

import matplotlib.pyplot as plt
import matplotlib.cm
import random
import squarify

x = 0.
y = 0.
width = 950.
height = 733.
norm_x=1000
norm_y=1000

fig = plt.figure(figsize=(15,13))
ax=fig.add_subplot(111,axisbg='white')

initvalues = [285.4,188.4,173,140.6,91.4,75.5,62.3,39.6,29.4,28.5, 26.2, 22.2]
values = initvalues
labels = ["South Africa", "Egypt", "Nigeria", "Algeria", "Morocco",
"Angola", "Libya", "Tunisia", "Kenya", "Ethiopia", "Ghana", "Cameron"]

colors = [(214,27,31),(229,109,0),(109,178,2),(50,155,18), 
(41,127,214),(27,70,163),(72,17,121),(209,0,89), 
(148,0,26),(223,44,13), (195,215,0)] 
# Scale the RGB values to the [0, 1] range, which is the format matplotlib accepts. 
for i in range(len(colors)): 
  r, g, b = colors[i] 
  colors[i] = (r / 255., g / 255., b / 255.) 

# values must be sorted descending (and positive, obviously)
values.sort(reverse=True)

# the sum of the values must equal the total area to be laid out
# i.e., sum(values) == width * height
values = squarify.normalize_sizes(values, width, height)

# padded rectangles will probably visualize better for certain cases
rects = squarify.padded_squarify(values, x, y, width, height)

cmap = matplotlib.cm.get_cmap()

color = [cmap(random.random()) for i in range(len(values))]
x = [rect['x'] for rect in rects]
y = [rect['y'] for rect in rects]
dx = [rect['dx'] for rect in rects]
dy = [rect['dy'] for rect in rects]

ax.bar(x, dy, width=dx, bottom=y, color=colors, label=labels)

va = 'center'
idx=1

for l, r, v in zip(labels, rects, initvalues):
  x, y, dx, dy = r['x'], r['y'], r['dx'], r['dy']
  ax.text(x + dx / 2, y + dy / 2+10, str(idx)+"--> "+l, va=va,
     ha='center', color='white', fontsize=14)
  ax.text(x + dx / 2, y + dy / 2-12, "($"+str(v)+"b)", va=va,
     ha='center', color='white', fontsize=12)
  idx = idx+1
ax.set_xlim(0, norm_x)
ax.set_ylim(0, norm_y)
plt.show()

The square map plot

交互式可视化包

几年前除了 IPython 没有太多的交互工具。为了理解如何让任何可视化变得交互式,将它与现有工具(如 D3.js)进行比较是有意义的。D3.js 非常强大的原因之一是基于 JavaScript 的绘图框架可以使绘图呈现在网络上。此外,它还附带了所有可以轻松配置的事件驱动功能。

有两个可视化库被称为 BokehVisPy ,它们在今天可用的少数几个中很受欢迎。还有另一个工具叫做瓦卡里。这主要用于数据分析,在如何创建基于浏览器的可视化方面与 IPython 相似。 Ashiba 项目是由 Clayton Davis 在 Continuum 开发的另一个工具,但是由于 Continuum 的重点转移到了 Bokeh 和 Wakari,在过去的几年里,关于 Ashiba 的工作做得很少。

Bokeh

Bokeh 是一个用 Python 开发的交互式可视化库,旨在通过网络浏览器工作。Bokeh 这个名字来自哪里?这是一个日语单词,用来描述模糊或图像中不聚焦的部分。目标是开发一个非常类似 D3.js 美学的库;Bokeh 这个名字的选择似乎很匹配。Bokeh 写入 HTML5 Canvas 库,因此保证在支持 HTML5 的浏览器上工作。这很有用,因为您可能想要将基于 JavaScript 的图与 Python 进行比较。

我们不会详细说明这个工具。你可以在http://bokeh.pydata.org阅读和探索更多相关内容。然而,重要的是要知道 Bokeh 库的依赖关系。安装 Bokeh 库前,要求安装jsonschema,如下:

conda install jsonschema

Fetching package metadata: ....
Solving package specifications: .
Package plan for installation in environment /Users/MacBook/anaconda:

The following packages will be downloaded:

 package                    |            build
 ---------------------------|-----------------
 jsonschema-2.4.0           |           py27_0          51 KB

The following NEW packages will be INSTALLED:

 jsonschema: 2.4.0-py27_0

Proceed ([y]/n)?

使用 Bokeh、pandas、SciPy、matplotlib 和 ggplot 的交互式可视化示例可在上找到。

粘性

VisPy 是一个用于 2D 或三维绘图的可视化库,具有交互性和高性能。您可以利用 OpenGL 知识快速创建可视化。它也有不一定需要深入了解 OpenGL 的方法。有关更多信息,您可以阅读位于【vispy.org】的文档。

为了安装 VisPy 库,可以尝试conda install vispy命令,但它很可能会响应 binstar 搜索–t conda vispy的建议。以下代码是列表中的代码之一:

conda install --channel https://conda.binstar.org/asmeurer vispy

使用此命令,您将获得以下响应:

Fetching package metadata: ......
Solving package specifications: .
Package plan for installation in environment /Users/MacBook/anaconda:

The following packages will be downloaded:

 package                    |            build
 ---------------------------|-----------------
 numpy-1.8.2                |           py27_0         2.9 MB
 vispy-0.3.0                |       np18py27_0         679 KB
 ------------------------------------------------------------
 Total:         3.6 MB

The following NEW packages will be INSTALLED:

 vispy: 0.3.0-np18py27_0

The following packages will be DOWNGRADED:

 numpy: 1.9.2-py27_0 --> 1.8.2-py27_0 

Proceed ([y]/n)?

VisPy 的图库收藏中有很多例子。可以在【http://vispy.org/gloo.html?highlight=gloo#module-vispy.gloo】查看使用vispy.gloo命令和 GLSL 阴影代码的点显示的一个特定示例。

总结

现在有一套适合 Python 开发人员的工具和包。Python 有一个很大的标准库。这通常被认为是 Python 最大的优势之一。它有创建图形用户界面的模块,连接到关系数据库,伪随机数发生器,具有任意精度小数的算法,操作正则表达式。此外,还有绘制 2D 和三维图形的高性能软件包、机器学习和统计算法等。

我们已经看到,集成开发环境工具(如 Canopy 和 Anaconda)从计算和可视化的角度以及其他许多领域利用了高效的开发工作。有许多有效的方法来使用这些工具产生可视化方法。在接下来的几章中,将展示这些工具和包的有趣示例。

四、数值计算和交互式绘图

高性能数值计算领域位于许多学科和技能的十字路口。为了在今天成功地使用高性能计算,它需要编程、数据科学和应用数学的知识和技能。除此之外,计算问题的有效实现需要对处理和存储设备有所了解。

近年来,计算在科学中的作用已经发展到不同的水平。编程语言(如 R 和 MATLAB)在学术研究和科学计算中很常见。如今,Python 在科学计算中扮演着重要角色,这是有充分理由的。Python 社区汇集了许多高效的工具和包,不仅被研究社区使用,而且被成功的商业组织使用,如雅虎、谷歌、脸书和亚马逊。

科学计算中广泛使用的软件包有两种。分别是数值 Python 包 ( NumPy )和科学 Python 包 ( SciPy )。NumPy 因其高效的数组,尤其是易于索引而广受欢迎。在以下各节中,我们将讨论以下主题:

  • NumPy、SciPy 和 MKL 函数
  • 数字索引和逻辑索引
  • 数据结构—堆栈、队列、元组、集合、尝试和字典
  • 使用 matplotlib 可视化绘图,等等
  • 用 NumPy 和 SciPy 进行优化和插值,并举例说明
  • 将 Cython 与 NumPy 相结合以及 Cython 的优势

NumPy、SciPy 和 MKL 函数

几乎所有的科学和数值计算都需要用向量和矩阵的形式来表示数据,NumPy 用数组来处理所有这些。

NumPy 和 SciPy 是 Python 的计算模块,在预编译的快速函数中提供方便的数学和数值方法。NumPy 包提供了基本的例程来操作大型数组和矩阵的数字数据。SciPy 包扩展了 NumPy,用应用数学技术收集有用的算法。在 NumPy 中, ndarray 是一个数组对象,它表示具有已知大小的多维齐次项目数组。

NumPy

NumPy 不仅使用数组对象,还使用可以方便用于计算的线性代数函数。它提供了阵列和相关阵列功能的快速实现。使用数组对象,可以执行包括矩阵乘法、向量和矩阵转置、求解方程组、执行向量乘法和归一化等操作。

NumPy 通用函数

通用功能( ufunc )是通过每个元素、支持类型铸造和其他几个标准特征在ndarrays上运行的功能。换句话说,ufunc是接受标量输入并产生标量输出的函数的矢量化包装器。许多内置函数都是在编译好的 C 代码中实现的,这使得它更快。

NumPy 通用函数比 Python 函数更快,因为循环是在编译代码中执行的。此外,因为数组是类型化的,所以在任何类型的计算发生之前就知道它们的类型。

这里显示了一个对每个元素进行操作的简单示例ufunc:

import numpy as np
x = np.random.random(5)
print x
print x + 1   # add 1 to each element of x

[ 0.62229809  0.18010463  0.28126201  0.30701477  0.39013144] 
[ 1.62229809  1.18010463  1.28126201  1.30701477  1.39013144]

其他例子还有np.addnp.subtract

NumPy 的ndarray类似于 Python 中的列表,但它在只存储同类对象时相当严格。换句话说,使用 Python 列表,可以混合元素类型,例如第一个元素作为数字,第二个元素作为列表,下一个元素作为另一个列表(或字典)。对于大尺寸阵列来说,在操作ndarray的元件方面的性能明显更快,这将在这里进行演示。这里的例子表明它更快,因为我们将测量运行时间。然而,对于对 C 语言中的 NumPy 实现感兴趣的读者来说,在网站上可以找到关于这个实现的文档。

import numpy as np

arr = np.arange(10000000)
listarr = arr.tolist()

def scalar_multiple(alist, scalar):
    for i, val in enumerate(alist):
        alist[i] = val * scalar
    return alist

# Using IPython's magic timeit command
timeit arr * 2.4
10 loops, best of 3: 31.7 ms per loop
# above result shows 31.7 ms (not seconds)

timeit scalar_multiple(listarr, 2.4)
1 loops, best of 3: 1.39 s per loop
# above result shows 1.39 seconds (not ms)

在前面的代码中,每个数组元素占用 4 个字节。因此,一百万个整数数组占用大约 44 MB 的内存,列表使用 711 MB 的内存。但是,数组对于小的集合大小来说速度较慢,但是对于大的集合大小来说,它们使用的内存空间较少,并且比列表快得多。

NumPy 附带了许多有用的函数,大致分为三角函数、算术函数、指数和对数函数以及各种函数。在众多的杂函数中,线性卷积的convolve()和线性插值的interp()比较流行。此外,对于大多数涉及等间距数据的实验工作来说,linspace()random.rand()函数是被广泛使用的几个函数之一。

塑造和重塑手法

改变现有数组的形状比用新形状从旧数据创建新数组更有效。在第一个示例中,重塑发生在内存中(数组不存储在变量中),而在下面的代码中,数组首先存储在变量 a 中,然后 a 被重塑:

import numpy as np

np.dandom.rand(2,4)
array([[ 0.96432148,  0.63192759,  0.12976726,  0.56131001], 
    [    0.27086909,  0.92865208,  0.27762891,  0.40429701]])

np.random.rand(8).reshape(2,4)
array([[ 0.39698544,  0.88843637,  0.66260474,  0.61106802], 
       [ 0.97622822,  0.47652548,  0.56163488,  0.43602828]]) 

在上例中,创建8值后,它们被重新整形为有效的选择维度,如以下代码所示:

#another example
a = np.array([[11,12,13,14,15,16],[17,18,19,20,21,22]])

print a
[[11, 12, 13, 14, 15, 16], [17, 18, 19, 20, 21, 22]]

# the following shows shape is used to know the dimensions
a.shape
(2,6)

#Now change the shape of the array
a.shape=(3,4)
print a
[[11 12 13]  [14 15 16]  [17 18 19]  [20 21 22]]

xrange代替range,因为循环更快,避免了整数列表的存储;它只是一个接一个地产生它们。形状和重塑的对立面是ravel(),如下图所示:

#ravel example
a = np.array([[11,12,13,14,15,16],[17,18,19,20,21,22]])

a.ravel()
array([11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22])

插值的一个例子

以下是使用interp()进行插值的示例:

n=30 

# create n values of x from 0 to 2*pi 
x = np.linspace(0,2*np.pi,n) 

y = np.zeros(n) 

#for range of x values, evaluate y values
for i in xrange(n):    
   y[i] = np.sin(x[i])

下图显示的图像是简单正弦曲线插值的结果:

An example of interpolation

以下代码显示了使用和不使用插值绘制曲线:

import numpy as np
import matplotlib.pyplot as plt

# create n values of x from 0 to 2*pi 
x = np.linspace(0, 8*np.pi, 100)

y = np.sin(x/2)

#interpolate new y-values 
yinterp = np.interp(x, x, y)

#plot x,y values using circle marker (line style)
plt.plot(x, y, 'o')  

#plot interpolated curve using dash x marker
plt.plot(xvals, yinterp, '-x')  

plt.show()

向量化函数

通过 NumPy 和 SciPy 中的vectorize()向量化函数非常有效。矢量化能够通过逐元素应用相同的规则,将以标量为参数的函数转换为以数组为参数的函数。我们将在这里用两个例子来证明这一点。

第一个示例使用一个接受三个标量参数的函数来生成一个接受三个数组参数的矢量化函数,如以下代码所示:

import numpy as np

def addition(x, y, z):
    return x + y + z

def addpoly():
    i = np.random.randint(25)
    poly1 = np.arange(i, i+10)
    i = np.random.randint(25) 
    poly2 = np.arange(i, i+10)
    poly3 = np.arange(10, 18)
    print poly1
    print poly2
    print poly3
    print '-' * 32
    vecf = np.vectorize(addition)
    print vecf(poly1,poly2,poly3)

addpoly()

[ 4  5  6  7  8  9 10 11 12 13]
[13 14 15 16 17 18 19 20 21 22]
[10 11 12 13 14 15 16 17 18 19]
--------------------------------
[27 30 33 36 39 42 45 48 51 54]

注意arrange是内置 Python range函数的数组值版本。

第二个示例使用一个接受标量参数的函数来生成一个接受数组参数的矢量化函数,如以下代码所示:

import numpy as np

def posquare(x):
  if x >= 0: return x**2
  else: return -x

i = np.random.randint(25)
poly1 = np.arange(i,i+10)

print poly1
vecfunc = vectorize(posquare, otypes=[float]) 
vecfunc(poly1)

[14 15 16 17 18 19 20 21 22 23]
array([ 196., 225., 256., 289., 324., 361., 400., 441., 484., 529.])

还有另一个例子,借助于示例代码很有意思。此示例显示了用常数增加数组元素的三种方法,并测量运行时间以确定哪种方法更快:

import numpy as np
from time import time

def incrembyone(x):
    return x + 1

dataarray=np.linspace(1,5,1000000)

t1=time()
lendata = len(dataarray)
print "Len = "+str(lendata)
print dataarray[1:7]
for i in range(lendata):
    dataarray[i]+=1
print " time for loop (No vectorization)->" + str(time() - t1)

t2=time()

vecincr = np.vectorize(incrembyone) #1 
vecincr(dataarray) #2          
print " time for vectorized version-1:" + str(time() - t2)
t3 = time()

# This way to increment array elements with one line
# is pretty powerful, accomplishes same thing as #1 and #2
dataarray+=1  # how does this achieve the results
print dataarray[1:7]
print " time for vectorized version-2:" + str(time() - t3)

Len = 1000000
 [ 1.000004 1.000008 1.000012 1.000016 1.00002 1.000024]  
time for loop (No vectorization)->0.473765850067  
time for vectorized version-1:0.221153974533 # half the time

[ 3.000004 3.000008 3.000012 3.000016 3.00002 3.000024]  
time for vectorized version-2:0.00192213058472 # in fraction time

除了矢量化技术,还有另一种简单的编码实践可以让程序更高效。如果循环中使用了前缀符号,最好创建一个本地别名,并在循环中使用该别名。这里显示了一个这样的例子:

fastsin = math.sin

x = range(1000000)
for i in x:
    x[i] = fastsin(x[i])

NumPy 线性代数综述

以下是 NumPy 在线性代数中提供的一些著名函数的列表:

|

名字

|

描述

|
| --- | --- |
| dot(a,b) | 这是两个数组的点积 |
| linalg.norm(x) | 这是矩阵或向量范数 |
| linalg.cond(x) | 这指定了条件编号 |
| linalg.solve(A,b) | 这解决了线性系统Ax=b |
| linalg.inv(A) | 这表示A的倒数 |
| linalg.pinv(A) | 这指定了A的伪逆 |
| linalg.eig(A) | 这些是平方的特征值/向量A |
| linalg.eigvals(A) | 这些是一般A的特征值 |
| linalg.svd(A) | 这是一个奇异值分解 |

黑桃

NumPy 已经有很多方便的函数可以用于计算。那么,我们为什么需要 SciPy?SciPy 是用于数学、科学和工程的 NumPy 的扩展,它有许多软件包可用于线性代数、积分、插值、快速傅立叶变换、大型矩阵操作、统计计算等。下表显示了这些包的简要描述:

|

分装

|

功能简述

|
| --- | --- |
| scipy.cluster | 这指定了用于聚类的函数,包括矢量量化和 k 均值。 |
| scipy.fftpack | 这表示快速傅立叶变换的功能。 |
| scipy.integrate | 这指定了使用梯形、辛普森、龙贝格和其他方法进行数值积分的函数。它还规定了积分常微分方程的方法。人们可以用函数quaddblquadtplquad对函数对象执行单、双和三次积分。 |
| scipy.interpolate | 表示具有离散数值数据的插值对象以及线性和样条插值的函数和类。 |
| scipy.linalg | 这是 NumPy 中包装linalg的包装。NumPy 的所有功能都是scipy.linalg的一部分,还有其他几个功能。 |
| scipy.optimize | 这表示最大化和最小化函数,包括奈德-米德单纯形法、鲍威尔法、共轭梯度 BFGS 法、最小二乘法、约束优化器、模拟退火法、牛顿法、二等分法、布赖登-安德森法和线搜索法。 |
| scipy.sparse | 这指定了可以处理大型稀疏矩阵的函数。 |
| scipy.special | 这对于计算物理有特殊的作用,如椭圆、贝塞尔、伽马、β、超几何、抛物线、圆柱、马蒂厄和球面波。 |

除了前面列出的子包,SciPy 还有一个scipy.io包,该包具有加载名为spio.loadmat()的矩阵、保存名为spio.savemat()的矩阵以及通过scio.imread()读取图像的功能。当需要用 Python 开发计算程序时,最好检查 SciPy 文档,看看它是否包含已经完成预期任务的函数。

我们来看一个使用scipy.polyId()的例子:

import scipy as sp

# function that multiplies two polynomials
def multiplyPoly():  
    #cubic1 has coefficients 3, 4, 5 and 5 
    cubic1 = sp.poly1d([3, 4, 5, 5])  

    #cubic2 has coefficients 4, 1, -3 and 3
    cubic2 = sp.poly1d([4, 1, -3, 3]) 

    print cubic1   
    print cubic2 

    print '-' * 36

    #print results of polynomial multiplication
    print cubic1 * cubic2

multiplyPoly()  # produces the following result

   3     2
3 x + 4 x + 5 x + 5
   3     2
4 x + 1 x - 3 x + 3
------------------------------------
    6      5      4      3     2
12 x + 19 x + 15 x + 22 x + 2 x + 15  

结果与传统的逐项乘法相匹配,如下所示:

SciPy

因此,多项式表示可以用于积分、微分和其他计算物理。这些功能以及 NumPy、SciPy 和其他包扩展中的更多功能清楚地表明,Python 是 MATLAB 的另一个替代品,因此在一些学术环境中使用。

SciPy 提供了许多不同类型的插值。以下示例使用interpolate.splev,其使用 B 样条及其导数和interpolate.splprep用于二维曲线(一般为 N 维)的 B 样条表示:

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

t = np.arange(0, 2.5, .1)
x = np.sin(2*np.pi*t)
y = np.cos(2*np.pi*t)

tcktuples,uarray = sp.interpolate.splprep([x,y], s=0)
unew = np.arange(0, 1.01, 0.01)

splinevalues = sp.interpolate.splev(unew, tcktuples)

plt.figure(figsize=(10,10))
plt.plot(x, y, 'x', splinevalues[0], splinevalues[1], 
np.sin(2*np.pi*unew), np.cos(2*np.pi*unew), x, y, 'b')

plt.legend(['Linear', 'Cubic Spline', 'True'])
plt.axis([-1.25, 1.25, -1.25, 1.25])
plt.title('Parametric Spline Interpolation Curve')

plt.show()

下图是使用 SciPy 和 NumPy 进行样条插值的结果:

SciPy

让我们看一下数值积分中的一个例子,并使用一些 SciPy 函数(如 Simpson 和 Romberg)求解线性方程,并将这些函数与 NumPy 梯形函数进行比较。我们知道,当f(x)= 9–x2这样的函数从-3积分到3时,我们期望 36 个单位,如下图所示:

SciPy

上图显示了 9-x2 函数(沿 Y 轴对称)。数学上,从 -33 的积分是从 03 的积分的两倍。我们如何使用 SciPy 进行数值积分?下面的代码显示了一种使用 NumPy 中的梯形方法执行该操作的方法:

import numpy as np
from scipy.integrate import simps, romberg

a = -3.0; b = 3.0;
N = 10  

x = np.linspace(a, b, N)
y = 9-x*x
yromb = lambda x: (9-x*x)

t = np.trapz(y, x)
s = simps(y, x)
r = romberg(yromb, a, b)

#actual integral value
aiv = (9*b-(b*b*b)/3.0) - (9*a-(a*a*a)/3.0)

print 'trapezoidal = {0} ({1:%} error)'.format(t, (t - aiv)/aiv)
print 'simpsons = {0} ({1:%} error)'.format(s, (s - aiv)/aiv)
print 'romberg  = {0} ({1:%} error)'.format(r, (r - aiv)/aiv)
print 'actual value = {0}'.format(aiv)

trapezoidal = 35.5555555556 (-1.234568% error)
simpsons = 35.950617284 (-0.137174% error)
romberg  = 36.0 (0.000000% error)
actual value = 36.0

线性方程的一个例子

让我们尝试用三个变量 xyz 求解一组线性方程,如下所示:

  • x+2y–z = 2
  • 2x–3y+2z = 2
  • 3x+y–z = 2

NumPy 提供了一种求解线性方程的方便方法np.linalg.solve()。然而,输入应该是矢量形式的。下面的程序展示了如何求解线性方程。

import numpy as np

# Matrix A has coefficients of x,y and z
A = np.array([[1, 2, -1],
              [2, -3, 2],
              [3, 1, -1]])
#constant vector 
b = np.array([2, 2, 2])

#Solve these equations by calling linalg.solve
v = np.linalg.solve(A, b)

# v is the vector that has solutions
print "The solution vector is "
print v
# Reconstruct Av to see if it produces identical values 
print np.dot(A,v) == b

The solution vector is
[ 1\.  2\.  3.]
[ True  True  True]

注意np.dot(A,v)是矩阵乘法(不是A*v)。解向量v = [1,2,3]是正确的预期结果。

矢量化的数值导数

现在作为本节的最后一个例子,我们将看看 NumPy 提供的矢量化数字导数。应用微分的商法则,我们确实知道导数是The vectorized numerical derivative。但是,通过应用 Python 中的矢量化方法来计算没有循环的导数,我们将看到以下代码:

import numpy as np 
import matplotlib.pyplot as plt

x = np.linspace(-np.pi/2, np.pi/2, 44)
y = 1/(1+np.cos(x)*np.cos(x))
dy_actual = np.sin(2*x)/(1+np.cos(x)*np.cos(x))**2

fig = plt.figure(figsize=(10,10))
ax=fig.add_subplot(111,axisbg='white')

# we need to specify the size of dy ahead because diff returns 
dy = np.zeros(y.shape, np.float) #we know it will be this size
dy[0:-1] = np.diff(y) / np.diff(x)
dy[-1] = (y[-1] - y[-2]) / (x[-1] - x[-2])

plt.plot(x,y, linewidth=3, color='b', label='actual function')
plt.plot(x,dy_actual,label='actual derivative', linewidth=2, color='r')
plt.plot(x,dy,label='forward diff', linewidth=2, color='g')
plt.legend(loc='upper center')
plt.show()

在下面的例子中,我们可以看到你如何在同一个图中画出实际函数、它的导数和向前差。将实际导数插入dy_actual,使用从 NumPy 计算的diff()计算正向差。

以下图表是该程序的结果:

The vectorized numerical derivative

MKL 函数

英特尔的 MKL 函数提供向量和矩阵的高性能例程。此外,它们还包括快速傅立叶变换函数和矢量统计函数。这些功能已经过增强和优化,可在英特尔处理器上高效工作。对于 Anaconda 用户,Continuum 已经将这些 FFT 函数打包到 Python 库的二进制版本中,用于 MKL 优化。然而,作为 Anaconda Accelerate 包的一部分,MKL 优化作为一个附加组件提供。此处的图表显示了没有 MKL 时的慢度差异:

MKL functions

上图取自https://store.continuum.io/cshop/mkl-optimizations/

对于更大的阵列输入,MKL 在性能上有显著提升,如下图所示:

MKL functions

上图取自https://software . Intel . com/en-us/articles/numpsycpiy-with-Intel-mkl

Python 的性能

Python 程序员为了性能考虑,经常尝试用 C 语言重写自己最里面的循环,从 Python 中调用编译好的 C 函数。有许多项目旨在使这种优化变得更容易,例如 Cython。然而,最好是让他们现有的 Python 代码更快,而不依赖于另一种编程语言。

几乎没有其他选项可以提高 Python 中计算密集型程序的性能:

  • 使用 numapro:这是来自 Continuum Analytics 的 Python 编译器,可以编译 Python 代码,以便在支持 CUDA 的 GPU 或多核 CPU 上执行。该编译代码运行本机编译代码,比解释代码快几倍。Numbapro 通过在运行时启用编译来工作(这是及时准时编译)。有了 Numbapro,可以编写标准的 Python 函数,并在支持 CUDA 的 GPU 上运行它们。Numbapro 是为面向数组的计算任务而设计的,比如广泛使用的 NumPy 库。Numbapro 是 Numba 的增强版本,是 Anaconda Accelerate 的一部分,Anaconda Accelerate 是 Continuum Analytics 的商业许可产品。
  • 使用 Scipy.weave :这是一个模块,允许您插入 C 代码的片段,并将 NumPy 数组无缝传输到 C 层。它也有一些高效的宏。
  • 使用多核方法:Python 2.6 或更高版本的多处理包提供了一种相对简单的机制来创建子进程。现在连台式电脑都有多核处理器;让所有处理器工作是有意义的。这比使用线程要简单得多。
  • 使用称为池的进程池:这是多处理包中的另一个类。使用 pool,您可以定义要在池中创建的工作进程的数量,然后传递一个包含每个进程参数的 iterable 对象。
  • 在分布式计算包(如 Disco) 中使用 Python:这是一个轻量级、开源的分布式计算框架,基于 MapReduce 范式(http://discoproject.org)。其他类似的包有 Hadoop Streaming、mrjob、dumbo、Hadoop 和 pydoop。

标量选择

标量选择是从数组中选择元素的最简单的方法,对于一维数组使用[rowindex],对于二维数组使用[rowindex, columnindex]等等来实现。下面是显示数组元素引用的简单代码:

import numpy as np
x = np.array([[2.0,4,5,6], [1,3,5,9]])

x[1,2]
5.0

纯标量选择总是返回单个元素,而不是数组。所选元素的数据类型与所选内容中使用的数组的数据类型匹配。标量选择也可用于为数组元素赋值,如以下代码所示:

x[1,2] = 8

x
array([[2, 4, 5, 6],[1, 3, 8, 9]])

切片

数组可以像列表和元组一样被切片。数组切片与列表切片相同,只是语法更简单。使用[ : , :, ... :]语法对数组进行切片,其中数组的维数决定切片的大小,除了省略切片的这些维数,所有元素都被选中。比如b是三维阵列,b[0:2]b[0:2,:,:]一样。切片有速记符号。一些常见的有:

  • :和:0:n:1 ,其中 n 为阵长
  • m:和 m:n:m:n:1 相同,其中 n 为阵长
  • :n:0:n:1
  • :d:0:n:d ,其中 n 为阵长

所有这些切片方法都引用了数组的用法。这也适用于列表。对一维数组进行切片与对简单列表进行切片是一样的(因为可以看到一维数组相当于一个列表),并且所有切片操作的返回类型都与被切片的数组相匹配。以下是显示阵列切片的简单机制:

x = array([5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20])

# interpret like this – default start but end index is 2
y = x[:2]
array([5, 6])

# interpretation – default start and end, but steps of 2
y = x[::2]
array([5,7,9,11,13,15,17,19])

如果具有一种数据类型的元素被插入到具有不同数据类型的数组中,NumPy 会尝试自动转换数据类型。例如,如果数组具有整数数据类型,那么在数组中放置一个浮点数会导致该浮点数被截断,并将其存储为整数。这可危险了;因此,在这种情况下,数组应该被初始化为包含 floats,除非有充分的理由考虑决定使用不同的数据类型。此示例显示,即使一个元素是 float,而其余的是 integer,为了使其正常工作,也假定它是 float 类型:

a = [1.0, 2,3,6,7]
b = array(a)

b.dtype
dtype('float64')

用平板切片

矩阵中的数据以行为主的顺序存储,这意味着元素首先通过沿行计数,然后沿列计数进行索引。例如,在下面的矩阵中,有三行三列;元素的读取顺序为 4、5、6、7、8、9、1、2、3 (每行、每列):

Slice using flat

线性切片按照元素读取的顺序为数组的每个元素分配一个索引。在二维数组或列表中,线性切片的工作方式是首先在行中计数,然后在列中计数。为了使用线性切片,必须使用flat函数,如下代码所示:

a=array([[4,5,6],[7,8,9],[1,2,3]])
b = a.flat[:]

print b
[4, 5, 6, 7, 8, 9, 1, 2, 3]

数组索引

来自 NumPy 数组的元素可以使用四种方法进行选择:标量选择、切片、数值索引和逻辑(或布尔)索引。标量选择和切片是访问数组中元素的基本方法,这里已经讨论过了。数字索引和逻辑索引密切相关,允许更灵活的选择。数字索引使用位置列表或数组来选择元素,而逻辑索引使用包含布尔值的数组来选择元素。

数值索引

数值索引是切片记数法的替代方法。数值索引中的思想是使用坐标来选择元素。这类似于切片。使用数字索引创建的数组创建数据的副本,而切片只是数据的视图,而不是副本。为了性能,应该使用切片。切片类似于一维数组,但切片的形状由切片输入决定。

一维数组中的数值索引使用数值索引值作为数组中的位置(从 0 开始的索引),并返回与数值索引具有相同维度的数组。

请注意,数字索引可以是列表或 NumPy 数组,并且必须包含整数数据,如以下代码所示:

a = 10 * arange(4.0)
array([0.,10.,20.,30.])

a[[1]]  # arrays index is list with first element 
array([ 10.])

a[[0,3,2]] # arrays index are 0-th, 3-rd and 2-nd
array([  0.,  30.,  20.])

sel = array([3,1,4,2,3,3])  # array with repetition
a[sel]
array([ 30\.  10\.   0\.  20\.  30\.  30.])

sel = array([4,1],[3,2]])
a[sel]
array([[ 30.,10.], [ 0.,20.]])

这些示例表明,数值索引决定元素位置,数值索引数组的形状决定输出的形状。

类似于切片,数值索引可以使用flat函数进行组合,以使用数组的行主顺序从数组中选择元素。使用平面进行数值索引的行为与在底层数组的平面版本上使用数值索引的行为相同。这里显示了几个示例:

a = 10 * arange(8.0)
array([  0.,  10.,  20.,  30.,  40., 50., 60., 70.])

a.flat[[3,4,1]]
array([ 30., 40., 10.])

a.flat[[[3,4,7],[1,5,3]]]
array([[ 30., 40., 70.], [ 10., 50., 30.]])

逻辑索引

逻辑索引不同于切片和数值索引;它而是使用逻辑索引来选择元素、行或列。逻辑索引就像电灯开关,不是真就是假。纯逻辑索引使用与用于选择的数组大小相同的逻辑索引数组,并且总是返回一维数组,如以下代码所示:

x = arange(-4,5)

x < 0 
array([True, True, True, True, False, False, False, False, False], dtype=bool)

x[x>0]
array([1, 2, 3, 4])

x[abs(x) >= 2]  
array([-4, -3, -2,  2,  3,  4])

#Even for 2-dimension it still does the same
x = reshape(arange(-8, 8), (4,4))
x
array([[-8, -7, -6, -5], [-4, -3, -2, -1], [ 0,  1,  2,  3], [ 4,  5,  6,  7]])

x[x<0]
array([-8, -7, -6, -5, -4, -3, -2, -1])

下面是另一个演示逻辑索引的例子:

from math import isnan
a = [[3, 4, float('NaN')], [5, 9, 8], [3, 3, 2], [9, -1, float('NaN')]]

list2 = [3, 4, 5, 6]
list1_valid = [elem for elem in list1 if not any([isnan(element) for element in elem])]

list1_valid
[[3, 7, 8], [1, 1, 1]] 

list2_valid = [list2[index] for index, elem in enumerate(list1) if not any([isnan(element) for element in elem])]

list2_valid
 [4, 5]

其他数据结构

Python 具有诸如堆栈、列表、集合、序列、元组、列表、堆、数组、字典和 deque 等数据结构。我们已经在试图理解数组时讨论了列表。元组通常比列表更节省内存,因为它们是不可变的。

书库

list 方法作为栈使用非常方便,已知是一种抽象数据类型,其原理是操作后进先出。已知的操作包括使用append()在堆栈顶部添加项目,使用pop()从堆栈顶部提取项目,以及使用remove(item-value)移除项目,如以下代码所示:

stack = [5, 6, 8]
stack.append(6)
stack.append(8)

stack
[5, 6, 8, 6, 8]

stack.remove(8)
stack
[5, 6, 6, 8]

stack.pop()
8

stack.remove(8)
Traceback (most recent call last): 
File "<ipython-input-339-61d6322e3bb8>", line 1, in <module>     stack.remove(8)
  ValueError: list.remove(x): x not in list

pop()功能效率最高(恒定时间),因为所有其他元素都保留在它们的位置。但是,参数化版本pop(k)删除了列表中 k < n 索引处的元素,将所有后续元素转移到以填补删除后的空白。该操作的效率是线性的,因为移动量取决于索引 k 的选择,如下图所示:

Stacks

元组

元组是一系列看起来类似于列表的不可变对象。元组是异构的数据结构,这意味着它们的元素有不同的含义,而列表是一个同质的元素序列。元组有结构,列表有顺序。元组的一些示例是星期几、课程名称和评分标准,如以下代码所示:

#days of the week
weekdays = ("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")

#course names
courses = ("Chemistry", "Physics", "Mathematics", "Digital Logic", "Circuit Theory")

#grades
grades = ("A+", "A", "B+", "B", "C+", "C", "I")

元组有不可变的对象。这意味着您不能更改或从元组中移除它们。但是,元组可以完全删除,例如,“del grades”将删除此元组。此后,如果尝试使用该元组,将会出现错误。以下是内置的元组函数:

  • cmp(tup1, tup2):这个函数可以用来比较二元组的元素
  • len(tuple):这个函数可以用来获取元组的总长度
  • max(tuple):这个函数可以用来确定元组中的最大值
  • min(tuple):这个函数可以用来确定元组中的最小值
  • tuple(lista):该功能可用于将lista转换为tuple

Python 有一个max()函数,其行为与数值的预期一致。然而,如果我们传递一个字符串列表,max()返回最长的项目。

weekdays = ("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
print max(weekdays) 
Wednesday  

类似地min()对于字符串也有相同的行为。

print min(weekdays) 
Friday

当我们需要找到一个数组或列表中有多少元素时,len()是一个很方便的方法。

len(weekdays)
7

集合与列表相似,但在两个方面有所不同。首先,与列表(按位置或索引排序)相比,它们是一个无序的集合。其次,它们没有副本(如果你知道集合的数学定义)。用于集合的符号显示在以下命令中:

setoftrees = { 'Basswood', 'Red Pine', 'Chestnut', 'Gray Birch', 'Black Cherry'} 

newtree = 'Tulip Tree' 
if newtree not in setoftrees:  setoftrees.add(newtree)

现在有了这个命令,你可以看到setoftrees上是什么:

setoftrees  # typing this shows list of elements shown below
{'Basswood', 'Black Cherry', 'Chestnut', 'Gray Birch', 'Red Pine', 'Tulip Tree'}

然后,使用适当的拼写构建charsinmathcharsinchem,如以下代码所示

#example of set of operation on letters
charsinmath = set('mathematics')
charsinchem = set('chem')

现在,让我们试着看看这些集合中的值:

Charsinmath # typing this shows letters in charsinmath
{'a', 'c', 'e', 'h', 'i', 'm', 's', 't'}

charsinchem # typing this shows letters in charsinchem
{'c', 'e', 'h', 'm'}

为了找到设置的差异,我们需要显示charsinmath – charsinchem 如下:

# take away letters from charsinchem from charsinmath
charsinmath - charsinchem
{'a', 'i', 's', 't'}

队列

就像堆栈一样,使用列表作为队列是可能的。但是的区别是可以在列表的末尾或者列表的开头添加或者删除元素。虽然在列表的末尾添加和删除是有效的,但是从头开始做同样的事情是无效的,因为在这种情况下,元素必须被转移。

幸运的是,Python 的集合包中有deque,可以使用append()pop()appendleft()popleft()高效地实现从两端添加和移除元素,如以下代码所示:

from collections import deque

queue = deque(["Daniel", "Sid", "Mathew",  "Michael"]) 
queue.append("Dirk")      # Dirk arrives 
queue.append("Monte")   # Monte arrives queue

queue
deque(['Daniel', 'Sid', 'Mathew', 'Michael', 'Dirk', 'Monte'])

queue.popleft()  
'Daniel'  

queue.pop() 
'Monte'

queue.appendleft('William')
queue
deque(['William', 'Sid', 'Mathew', 'Michael', 'Dirk'])

queue.append('Lastone')
queue
deque(['William', 'Sid', 'Mathew', 'Michael', 'Dirk', 'Lastone'])

词典

字典是由键/值对组成的无序数据值的集合,它具有基于键作为索引访问值的独特优势。问题是,如果键是字符串,那么索引是如何工作的?密钥必须是可散列的:对密钥应用散列函数来提取存储值的位置。换句话说,散列函数接受一个键值并返回一个整数。字典然后使用这些整数(或哈希值)来存储和检索值。这里显示了一些示例:

#example 1: Top 10 GDP of Africa
gdp_dict = { 'South Africa': 285.4, 'Egypt': 188.4, 'Nigeria': 173, 'Algeria': 140.6, 'Morocco': 91.4, 'Angola': 75.5, 'Libya': 62.3, 'Tunisia': 39.6, 'Kenya': 29.4, 'Ethiopia': 28.5, 'Ghana': 26.2, 'Cameron': 22.2}

gdp_dict['Angola']
75.5

#example 2: English to Spanish for numbers one to ten
english2spanish = { 'one' : 'uno', 'two' : 'dos', 'three': 'tres', 
'four': 'cuatro', 'five': 'cinvo', 'six': 'seis', 'seven': 'seite',  'eight': 'ocho', 'nine': 'nueve', 'ten': 'diez'}

english2spanish['four'] 
'cuatro'

密钥应该是不可变的,以具有可预测的哈希值;否则,哈希值更改将导致不同的位置。此外,不可预测的事情可能会发生。默认字典不会按照插入的顺序保存值;因此,通过在插入后迭代,键/值对的顺序是任意的。

Python 的 collections 包有一个等价的OrderedDict()函数,它保持插入顺序中的对的顺序。默认字典和有序字典的另一个区别是,在前者中,如果它们有一组相同的键/值对(不一定是相同的顺序),等式总是返回true,而在后者中,只有当它们有一组相同的键/值对并且它们处于相同的顺序时,等式才返回true。下面的例子说明了这一点:

# using default dictionary
dict = {}

dict['cat-ds1'] = 'Introduction to Data Structures'
dict['cat-ds2'] = 'Advanced Data Structures'
dict['cat-la1'] = 'Python Programming'
dict['cat-la2'] = 'Advanced Python Programming'
dict['cat-pda'] = 'Python for Data Analysis'
dict['cat-ps1'] = 'Data Science in Python'
dict['cat-ps2'] = 'Doing Data Science'

for key, val in dict.items():  print key,val

cat-ps1 Data Science in Python 
cat-ps2 Doing Data Science 
cat-pda Python for Data Analysis 
cat-la2 Advanced Python Programming 
cat-la1 Python Programming 
cat-ds1 Introduction to Data Structures 
cat-ds2 Advanced Data Structures

#using OrderedDict (inserting data the same way as before)
odict = OrderedDict()

odict['cat-ds1'] = 'Introduction to Data Structures'
odict['cat-ds2'] = 'Advanced Data Structures'
odict['cat-la1'] = 'Python Programming'
odict['cat-la2'] = 'Advanced Python Programming'
odict['cat-pda'] = 'Python for Data Analysis'
odict['cat-ps1'] = 'Data Science in Python'
odict['cat-ps2'] = 'Doing Data Science'

for key, val in odict.items():  print key,val

cat-ds1 Introduction to Data Structures 
cat-ds2 Advanced Data Structures 
cat-la1 Python Programming 
cat-la2 Advanced Python Programming 
cat-pda Python for Data Analysis 
cat-ps1 Data Science in Python 
cat-ps2 Doing Data Science

如果您必须实现类似这样的东西,那么在计算上最好使用 ISBN 作为关键字,而不是像在库中那样使用目录号。但是,可能有旧书没有 ISBN 因此,必须使用等效的唯一键/值来保持与其他具有 ISBN 号的新书的一致性。哈希值通常是一个数字,使用数字键,哈希函数可能比字母数字键容易得多。

矩阵表示的字典

通常有很多的例子,可以在有键/值关联的时候应用字典。例如,说明缩写和名称;其中一个可以是键,另一个可以是值,但是将缩写作为键会更有效。其他的例子有单词和单词计数或者城市名称和人口。字典真正有效的一个有趣的计算领域是稀疏矩阵的表示。

稀疏矩阵

我们来考察一下矩阵的空间利用率;对于使用列表表示的100×100矩阵,每个元素占用 4 个字节;因此,矩阵需要 40,000 字节,大约是 40 KB 的空间。然而,在这 40,000 个字节中,如果只有 100 个有非零值,其余的都为零,那么空间就被浪费了。现在,为了讨论简单起见,让我们考虑一个较小的矩阵,如下图所示:

Sparse matrices

这个矩阵有大约 20%的非零值;因此,找到一种替代方法来表示矩阵的非零元素将是一个良好的开端。 1 有七个值, 23 各有五个值, 467 各有一个值。这可以表示如下:

A = {1: [(2,2),(6,6), (0,7),(1,8),(7,8),(3,9),(8,9)],
  2: [(5,2),(8,2),(6,3),(0,4),(0,9)],
  3: [(5,0),(8,0),(9,1),(1,3),(5,8)],
  4:[(1,1)], 6:[(2,0)], 7:[(2,5)]} 

然而,这种表示使得更难访问 A(i,j) th 值。有一种更好的方法可以用字典来表示这个稀疏矩阵,如下面的代码所示:

def getElement(row, col):
    if (row,col) in A.keys():
       r = A[row,col]
    else:
       r = 0
    return r

A={(0,4): 2, (0,7): 1, (1,1): 4, (1,3):3, (1,8): 1, (2,0): 6, (0,9): 2, (2,2):1, (2,5): 7, (3,9): 1, (5,0): 3, (5,2): 2, (5,8): 3, (6,3): 2, (6,6):1, (7,8): 1, (8,0): 3, (8,2): 2, (8,9): 1, (9,1): 3}

print getElement(1,3)
3

print getElement(1,2)
0

要访问矩阵 A(1,3) 处的元素,我们可以使用A【(1,3)】,但是如果该键不存在,它将抛出异常。为了使用键获取非零值,如果键不存在则返回0,我们可以使用名为getElement()的函数,如前面的代码所示。

可视化稀疏

借助方块图,我们可以直观地看到矩阵有多稀疏。下图显示了sparseDisplay()功能。这将为每个试图查看显示的矩阵条目使用方形框。黑色表示稀疏,而绿色表示非零元素:

Visualizing sparseness

下面的代码演示了如何显示稀疏性:

import numpy as np
import matplotlib.pyplot as plt

"""
  SquareBox diagrams are useful for visualizing values of a 2D array,
  Where black color representing sparse areas.  
"""
def sparseDisplay(nonzero, squaresize, ax=None):
    ax = ax if ax is not None else plt.gca()

    ax.patch.set_facecolor('black')
    ax.set_aspect('equal', 'box')
    for row in range(0,squaresize):
      for col in range(0,squaresize):
        if (row,col) in nonzero.keys():
           el = nonzero[(row,col)]
           if el == 0:  color='black' 
           else:  color = '#008000'
           rect = plt.Rectangle([col,row], 1, 1, 
                   facecolor=color, edgecolor=color)
           ax.add_patch(rect)

    ax.autoscale_view()
    ax.invert_yaxis()

if __name__ == '__main__': 
    nonzero={(0,4): 2, (0,7): 1, (1,1): 4, (1,3): 3, (1,8): 1, 
(2,0): 6, (0,9): 2, (2,2): 1, (2,5): 7, (3,9): 1, (5,0): 3, 
(5,2): 2, (5,8): 3, (6,3): 2, (6,6): 1, (7,8): 1, (8,0): 3, (8,2): 2, (8,9): 1, (9,1): 3}

    plt.figure(figsize=(4,4))
    sparseDisplay(nonzero, 10)
    plt.show()

这只是显示稀疏矩阵的一个快速示例。假设您有一个只有几个非零值的 30 x 30 矩阵,那么显示看起来会有点类似于下图。就空间利用率而言,这种情况下节省了 97%。换句话说,矩阵越大,占用的空间越小,如下图所示:

Visualizing sparseness

找到了用字典存储稀疏矩阵的方法后,你可能要记住不需要重新发明轮子。而且,考虑存储稀疏矩阵的可能性来理解字典的力量是有意义的。然而,真正推荐的是看看稀疏矩阵的 SciPy 和 pandas 包。在本书中,可能会有更多的机会在一些例子中使用这些方法。

记忆词典

记忆是计算科学中的一种优化技术,可以存储中间结果,否则会很昂贵。不是每个问题都需要记忆,但是当有一种通过调用函数来计算相同值的模式时,使用这种方法通常是有用的。可以使用这种方法的一个例子是在斐波那契函数的计算中,使用字典来存储已经计算的值,所以下次,您可以只搜索该值,而不是再次重新计算它,如以下代码所示:

fibvalues = {0: 0, 1: 1, 2:1, 3:2, 4:3, 5:5}

def fibonacci(n):
    if n not in fibvalues: 
        sumvalue = fibonacci(n-1) + fibonacci(n-2)
        fibvalues[n] = sumvalue  
    return fibvalues[n]

fibonacci(40)
102334155

print sorted(fibvalues.values())
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155]

#regular fibonacci without using dictionary
def fib(n):
   if n <= 1 : return 1
   sumval = fib(n-1)+fib(n-2)
   return sumval

fibvalues的字典对于防止重新计算斐波那契的值非常有用,但是fibcalled在这里仅用于说明通过使用字典,对于特定的 n 值,不能有一次以上的对fibonacci()的调用。通过比较fib()(不使用字典存储计算值)和fibonacci()的运行时间比率,我们可以看到绘制时,它看起来类似于下面的截图:

Dictionaries for memoization

from time import time

for nval in range(16,27):
  fibvalues = {0: 0, 1: 1, 2:1, 3:2, 4:3, 5:5}
  t3 = time()
  fibonacci(nval)
  diftime1 = time()-t3
  t2 = time()
  fib(nval)
  diftime2 = time()-t2
  print "The ratio of time-2/time-1 :"+str(diftime2/diftime1)

尝试

Trie (发音为 Trie 或 trai)是具有不同名称的数据结构(数字树基数树前缀树)。尝试对于搜索、插入和删除功能非常有效。这种数据结构非常适合存储。比如代数assocalltrie十个存储在 trie 中,看起来会和下图类似

Tries

在上图中,为了清楚起见,字符以大写形式显示,而在实际存储中,字符以单词形式存储。在 trie 的实现中,存储字数是有意义的。搜索功能非常有效,特别是当模式不匹配时,结果会更快。换句话说,如果搜索的,则在未找到字母 r 的级别确定失败。

其中一个流行的功能是最长前缀匹配。换句话说,如果我们要在字典中找到与特定搜索字符串匹配最长前缀的所有单词: base (例如)。结果可能是基础基础基线基础,如果在单词字典中找到的话,甚至更多单词。

Python 有很多不同的实现:suffix_treepytiretriedatrie等等。J. F .塞巴斯蒂安做了一个很好的比较研究,可以在https://github.com/zed/trie-benchmark查阅。

大多数搜索引擎都有一个名为倒排索引的 trie 实现。这是空间优化非常重要的核心部分。此外,搜索这种结构对于找到搜索字符串和文档之间的相关性非常有效。trie 的另一个有趣的应用是 IP 路由,其中包含大范围值的能力特别合适。也节省了空间。

Python 中一个简单的实现(不一定是最有效的)在下面的代码中显示为:

_end = '_end_'

# to search if a word is in trie
def in_trie(trie, word):
     current_dict = trie
     for letter in word:
         if letter in current_dict:
             current_dict = current_dict[letter]
         else:
             return False
     else:
         if _end in current_dict:
             return True
         else:
             return False

#create trie stored with words
def create_trie(*words):
    root = dict()
    for word in words:
        current_dict = root
        for letter in word:
            current_dict = current_dict.setdefault(letter, {})
        current_dict = current_dict.setdefault(_end, _end)
    return root

def insert_word(trie, word):
    if in_trie(trie, word): return

    current_dict = trie
    for letter in word:
            current_dict = current_dict.setdefault(letter, {})
    current_dict = current_dict.setdefault(_end, _end)

def remove_word(trie, word):
    current_dict = trie
    for letter in word:
        current_dict = current_dict.get(letter, None)
        if current_dict is None:
            # the trie doesn't contain this word.
            break
    else:
        del current_dict[_end]

dict = create_trie('foo', 'bar', 'baz', 'barz', 'bar')
print dict
print in_trie(dict, 'bar')
print in_trie(dict, 'bars')
insert_word(dict, 'bars')
print dict
print in_trie(dict, 'bars')

使用 matplotlib 的可视化

matplotlib是不是除了现在还有其他几个流行的绘图包之外的又一个流行的绘图包。matplotlib的能力现在正在被 Python 社区实现。这个包的创建者和项目负责人约翰·亨特将其总结为马特洛特利试图让简单的事情变得容易,让困难的事情变得可能。您只需很少的努力就可以生成非常高质量的出版物就绪图。在这一节中,我们将挑选几个有趣的例子来说明matplotlib的力量。

字云

单词云赋予在任何给定文本中出现频率更高的单词更大的重要性。它们也被称为标签云或加权词。您可以使用不同的字体、布局和配色方案来调整单词云。从出现的次数来看,一个词的力量的重要性在视觉上映射到其出现的大小。换句话说,在可视化中出现最多的单词是在文本中出现最多的单词。

除了它们出现的明显地图之外,单词云在社交媒体和营销方面还有几个有用的应用。一些应用如下:

  • 企业可以了解他们的客户以及他们如何看待他们的产品。一些组织使用了一种非常有创意的方法,让他们的粉丝或追随者发布关于他们对自己品牌的看法的词语,将所有这些词语放入一个词云,以更好地了解他们产品品牌的最常见印象。
  • 通过识别一个在网上很受欢迎的品牌来寻找了解竞争对手的方法。从他们的内容中创建一个词云,以更好地理解哪些词和主题吸引了产品目标市场。

为了创建单词云,您可以编写 Python 代码或使用已经存在的东西。来自 NYU 数据科学中心的安德烈亚斯·穆勒用 Python 创建了一个非常简单易用的单词云。它可以按照下一节给出的说明进行安装。

安装字云

为了更快的安装,你可以只使用pipsudo访问,如下代码所示:

sudo pip install git+git://github.com/amueller/word_cloud.git

或者,您可以通过 Linux 上的wget或 Mac OS 上的curl获得该包,代码如下:

wget https://github.com/amueller/word_cloud/archive/master.zip
unzip master.zip
rm master.zip 
cd word_cloud-master 
sudo pip install -r requirements.txt

对于 Anaconda IDE,您必须通过以下三个步骤使用conda安装它:

#step-1 command
conda install wordcloud

Fetching package metadata: ....
Error: No packages found in current osx-64 channels matching: wordcloud

You can search for this package on Binstar with
# This only means one has to search the source location
binstar search -t conda wordcloud

Run 'binstar show <USER/PACKAGE>' to get more details:
Packages:
 Name | Access       | Package Types   | 
 ------------------------- | ------------ | --------------- |
 derickl/wordcloud | public       | conda           |
Found 1 packages

# step-2 command
binstar show derickl/wordcloud

Using binstar api site https://api.binstar.org
Name:    wordcloud
Summary:
Access:  public
Package Types:  conda
Versions:
 + 1.0

To install this package with conda run:
conda install --channel https://conda.binstar.org/derickl wordcloud

# step-3 command
conda install --channel https://conda.binstar.org/derickl wordcloud

Fetching package metadata: ......
Solving package specifications: .
Package plan for installation in environment /Users/MacBook/anaconda:

The following packages will be downloaded:

 package                    |            build
 ---------------------------|-----------------
 cython-0.22                |           py27_0         2.2 MB
 django-1.8                 |           py27_0         3.2 MB
 pillow-2.8.1               |           py27_1         454 KB
 image-1.3.4                |           py27_0          24 KB
 setuptools-15.1            |           py27_1         435 KB
 wordcloud-1.0              |       np19py27_1          58 KB
 conda-3.11.0               |           py27_0         167 KB
 ------------------------------------------------------------
 Total:         6.5 MB

The following NEW packages will be INSTALLED:
 django:     1.8-py27_0
 image:      1.3.4-py27_0
 pillow:     2.8.1-py27_1
 wordcloud:  1.0-np19py27_1

The following packages will be UPDATED:
 conda:      3.10.1-py27_0 --> 3.11.0-py27_0
 cython:     0.21-py27_0   --> 0.22-py27_0
 setuptools: 15.0-py27_0   --> 15.1-py27_1

The following packages will be DOWNGRADED:

libtiff:    4.0.3-0       --> 4.0.2-1

Proceed ([y]/n)? y

输入单词云

在本节中,将有两个来源,您可以从中提取单词来构建单词云。第一个示例显示了如何从一些已知网站的 web 提要中提取文本,以及如何从其描述中提取单词。第二个例子展示了如何借助搜索关键词从推文中提取文本。这两个例子将需要feedparser包和tweepy包,通过遵循类似的步骤(如前面其他包所述),您可以轻松安装它们。

我们的方法是从这两个例子中收集单词,并将它们用作通用单词云程序的输入。

网络订阅源

如今,在大多数的“T5”新闻和科技服务网站中,都有分组有序的“T4”RSS 或 atom 订阅源。虽然我们的目标是将上下文限制在技术上,但是我们可以确定一些提要列表,如下面的代码所示。为了能够解析这些提要,feedparserparser()方法派上了用场。Word cloud 有自己的stopwords列表,但除此之外,我们还可以一边用一边收集数据,如下图所示(stopwords这里不全,但可以从网上任何已知资源收集更多):

import feedparser
from os import path
import re

d = path.dirname(__file__)
mystopwords = [  'test', 'quot', 'nbsp']

feedlist = ['http://www.techcrunch.com/rssfeeds/',
'http://www.computerweekly.com/rss',
'http://feeds.twit.tv/tnt.xml',
'https://www.apple.com/pr/feeds/pr.rss',
'https://news.google.com/?output=rss'
'http://www.forbes.com/technology/feed/'                  'http://rss.nytimes.com/services/xml/rss/nyt/Technology.xml',         'http://www.nytimes.com/roomfordebate/topics/technology.rss',
'http://feeds.webservice.techradar.com/us/rss/reviews'            'http://feeds.webservice.techradar.com/us/rss/news/software',
'http://feeds.webservice.techradar.com/us/rss',
'http://www.cnet.com/rss/',
'http://feeds.feedburner.com/ibm-big-data-hub?format=xml',
'http://feeds.feedburner.com/ResearchDiscussions-DataScienceCentral?format=xml',        'http://feeds.feedburner.com/BdnDailyPressReleasesDiscussions-BigDataNews?format=xml',
'http://http://feeds.feedburner.com/ibm-big-data-hub-galleries?format=xml',          'http://http://feeds.feedburner.com/PlanetBigData?format=xml',
'http://rss.cnn.com/rss/cnn_tech.rss',
'http://news.yahoo.com/rss/tech',
'http://slashdot.org/slashdot.rdf',
'http://bbc.com/news/technology/']          

def extractPlainText(ht):
    plaintxt=''
    s=0
    for char in ht:
        if char == '<': s = 1
        elif char == '>': 
            s = 0
            plaintxt += ' '
        elif s == 0: plaintxt += char
    return plaintxt

def separatewords(text):
    splitter = re.compile('\\W*')
    return [s.lower() for s in splitter.split(text) if len(s) > 3]

def combineWordsFromFeed(filename):
    with open(filename, 'w') as wfile:
      for feed in feedlist:
        print "Parsing " + feed
        fp = feedparser.parse(feed)
        for e in fp.entries:
          txt = e.title.encode('utf8') + 
               extractPlainText(e.description.encode('utf8'))
          words = separatewords(txt)

          for word in words:
            if word.isdigit() == False and word not in mystopwords:
               wfile.write(word)
               wfile.write(" ")
          wfile.write("\n")
    wfile.close()
    return

combineWordsFromFeed("wordcloudInput_FromFeeds.txt")

推特正文

为了访问推特应用编程接口,你需要访问令牌和消费者凭证,包括四个参数:access_tokenaccess_token_secretconsumer_keyconsumer_secret。为了获得这些密钥,您必须使用推特账户。获取这些密钥的步骤可以在推特网站上找到。涉及的步骤有:

  1. 登录推特账户。
  2. 导航到developer.twitter.com并使用管理我的应用来完成并获取前面提到的参数。

假设这些参数都准备好了,有了tweepy包,就可以通过 Python 访问推文了。下面的代码显示了一个简单的自定义流侦听器。在这里,当推文被流式传输时,有一个监听器监听状态并将状态写入文件。这可以在以后用来创建单词云。

该流使用过滤器来缩小关注于Python 程序数据可视化大数据机器学习统计的推特文本。tweepy流提供提取的推文。这可以永远运行,因为有无限的数据。我们如何设置它停止?访问速度可能比您预期的要慢,为了创建单词云,您可以想象提取一定数量的推文可能就足够了。因此,我们设置了一个限制,并将其称为MAX_TWEETS50,如以下代码所示:

import tweepy
import json
import sys
import codecs

counter = 0
MAX_TWEETS = 500

#Variables that contains the user credentials to access Twitter API 
access_token = "Access Token"
access_token_secret = "Access Secret"
consumer_key = "Consumer Key"
consumer_secret = "Consumer Secret"

fp = codecs.open("filtered_tweets.txt", "w", "utf-8")

class CustomStreamListener(tweepy.StreamListener):

     def on_status(self, status):
        global counter
        fp.write(status.text)
        print "Tweet-count:" +str(counter)
        counter += 1
        if counter >= MAX_TWEETS: sys.exit()

    def on_error(self, status):
        print status

if __name__ == '__main__':

    auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_token, access_token_secret)
    streaming_api = tweepy.streaming.Stream(auth,
               CustomStreamListener(), timeout=60)

    streaming_api.filter(track=['python program', 'statistics', 
             'data visualization', 'big data', 'machine learning'])

使用任何一包单词,你可以写少于 20 行的 Python 代码来生成单词云。一个字云生成一个图像,使用matplotlib.pyplot,可以使用imshow()显示字云图像。以下单词云可用于任何单词输入文件:

from wordcloud import WordCloud, STOPWORDS
import matplotlib.pyplot as plt
from os import path

d = path.dirname("__file__")
text = open(path.join(d, 'filtered_tweets.txt')).read()

wordcloud = WordCloud(
    font_path='/Users/MacBook/kirthi/RemachineScript.ttf',
    stopwords=STOPWORDS,
    background_color='#222222',
    width=1000,
    height=800).generate(text)

# Open a plot of the generated image.
plt.figure(figsize=(13,13))
plt.imshow(wordcloud)
plt.axis("off")
plt.show()

The Twitter text

所需字体文件可从多个网站下载(该字体的一个特定资源可在http://www.dafont.com/remachine-script.font获得)。无论字体文件位于何处,您都必须使用设置为font_path的精确路径。对于使用提要中的数据,只有一行发生了变化,如以下代码所示:

text = open(path.join(d, 'wordcloudInput_fromFeeds.txt')).read()

The Twitter text

使用类似的从推文中提取文本创建词云的思路,你可以在手机厂商的上下文中提取带有关键词的文本,比如 iPhone三星 Galaxy亚马逊 FireLG 擎天柱诺基亚 Lumia 等等,来决定消费者的情绪。在这种情况下,您可能需要一组额外的信息,即与单词相关联的积极和消极情绪值。

在有限的上下文中,有几种方法可以让你对推文进行情感分析。首先,一种非常天真的方法是将权重与对应于积极情绪的单词(如 w p )和消极情绪(如 w n 联系起来,应用以下符号 p(+) 作为积极情绪的概率,应用 p(-) 表示消极情绪:

The Twitter text

第二种方法是使用自然语言处理工具,并将训练好的分类器应用于以获得更好的结果。 TextBlob 是一个也有情感分析的文本处理包(http://textblob.readthedocs.org/en/dev)。

TextBlob 构建一个文本分类系统,并创建一个 JSON 格式的训练集。之后,使用这个训练和朴素贝叶斯分类器,它执行情感分析。我们将在后面的章节中尝试使用这个工具来演示我们的工作示例。

绘制股价图

美国最大的两家证券交易所是成立于 1792 年的纽约证券交易所和成立于 1971 年的纳斯达克。今天,大多数股票交易都是以电子方式进行的。甚至股票本身几乎都是以电子形式持有,而不是作为实物凭证。除了纳斯达克和纽交所,还有许多其他网站也提供实时股价数据。

获取数据

获取数据的网站之一是 Yahoo,它通过 API 提供数据,比如获取亚马逊的股价(低、高、开、关、量),网址是http://chart API . finance . Yahoo . com/instrument/1.0/amzn/chart data;type = quote 范围=3y/csv 。根据您选择的绘图方法,需要进行一些数据转换。例如,从该资源获得的数据包括没有任何格式的日期,如下面的代码所示:

uri:/instrument/1.0/amzn/chartdata;type=quote;range=3y/csv
ticker:amzn
Company-Name:Amazon.com, Inc.
Exchange-Name:NMS
unit:DAY
timestamp:
first-trade:19970516
last-trade:20150430
currency:USD
previous_close_price:231.9000
Date:20120501,20150430
labels:20120501,20120702,20121001,20130102,20130401,20130701,20131001,20140102,20140401,20140701,20141001,20150102,20150401
values:Date,close,high,low,open,volume
close:208.2200,445.1000
high:211.2300,452.6500
low:206.3700,439.0000
open:207.4000,443.8600
volume:984400,23856100
20120501,230.0400,232.9700,228.4000,229.4000,6754900
20120502,230.2500,231.4400,227.4000,227.8200,4593400
20120503,229.4500,232.5300,228.0300,229.7400,4055500
...
...
20150429,429.3700,434.2400,426.0300,426.7500,3613300
20150430,421.7800,431.7500,419.2400,427.1100,3609700

我们将讨论创建绘图的三种方法。每一种都有自己的优点和局限性。

在第一种方法中,使用matplotlib.cbook包和pylab包,您可以用以下代码行创建一个图:

from pylab import plotfile show, gca 
import matplotlib.cbook as cbook  
fname = cbook.get_sample_data('/Users/MacBook/stocks/amzn.csv', asfileobj=False) 
plotfile(fname, ('date', 'high', 'low', 'close'), subplots=False) 
show()

这将创建一个类似于下面截图中所示的图:

Obtaining data

在尝试使用这种方法绘图之前,还需要进行一次额外的编程工作。日期值必须格式化以将 20150430 表示为%d-%b-%Y。采用这种方法,图也可以一分为二,一个显示股价,另一个显示成交量,如下代码所示:

from pylab import plotfile show, gca 
import matplotlib.cbook as cbook  
fname = cbook.get_sample_data('/Users/MacBook/stocks/amzn.csv', asfileobj=False) 
plotfile(fname, (0,1,5), plotfuncs={f:'bar'}) 
show()

Obtaining data

第二种方法是使用matplotlib.mlabmatplotlib.finance的子包。这有方便的方法从http://ichart.finance.yahoo.com/table.csv?s=GOOG&a = 04&b = 12&c = 2014&d = 06&e = 20&f = 2015&g = d获取股票数据,为了展示一个样本,这里有一个代码片段:

ticker='GOOG'

import matplotlib.finance as finance
import matplotlib.mlab as mlab
import datetime

startdate = datetime.date(2014,4,12)
today = enddate = datetime.date.today()

fh = finance.fetch_historical_yahoo(ticker, startdate, enddate)   
r = mlab.csv2rec(fh); fh.close()
r.sort()
print r[:2]

[ (datetime.date(2014, 4, 14), 538.25, 544.09998, 529.56, 532.52002, 2568000, 532.52002)  (datetime.date(2014, 4, 15), 536.82001, 538.45001, 518.46002, 536.44, 3844500, 536.44)]

当您试图绘制股价比较图时,显示成交量信息是没有意义的,因为对于每个股票代码,成交量是不同的。此外,它变得太混乱,无法查看股票图表。

matplotlib已经有绘制股价图的工作示例,足够精细,包括相对强弱指标 ( RSI )和移动平均线收敛/发散 ( MACD ),可在http://matplotlib . org/examples/pylab _ examples/finance _ work 2 . html获得。关于 RSI 和 MACD 的详情,你可以在网上找到很多资源,但是在http://easyforextrading.co/how-to-trade/indicators/有一个有趣的解释。

为了尝试使用现有代码,对其进行修改,并使其适用于多个图表,创建了一个名为plotTicker()的函数。这有助于在同一坐标轴内绘制每个滚动条,如以下代码所示:

import datetime
import numpy as np

import matplotlib.finance as finance
import matplotlib.dates as mdates
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt

startdate = datetime.date(2014,4,12)
today = enddate = datetime.date.today()

plt.rc('axes', grid=True)
plt.rc('grid', color='0.75', linestyle='-', linewidth=0.5)
rect = [0.4, 0.5, 0.8, 0.5]

fig = plt.figure(facecolor='white', figsize=(12,11))

axescolor = '#f6f6f6' # the axes background color

ax = fig.add_axes(rect, axisbg=axescolor)
ax.set_ylim(10,800)

def plotTicker(ticker, startdate, enddate, fillcolor):
  """
     matplotlib.finance has fetch_historical_yahoo() which fetches 
     stock price data the url where it gets the data from is 
     http://ichart.yahoo.com/table.csv stores in a numpy record 
     array with fields: 
      date, open, high, low, close, volume, adj_close
  """

  fh = finance.fetch_historical_yahoo(ticker, startdate, enddate) 
  r = mlab.csv2rec(fh); 
  fh.close()
  r.sort()

  ### plot the relative strength indicator
  ### adjusted close removes the impacts of splits and dividends
  prices = r.adj_close

  ### plot the price and volume data

  ax.plot(r.date, prices, color=fillcolor, lw=2, label=ticker)
  ax.legend(loc='top right', shadow=True, fancybox=True)

  # set the labels rotation and alignment 
  for label in ax.get_xticklabels():
    # To display date label slanting at 30 degrees
    label.set_rotation(30)
    label.set_horizontalalignment('right')

  ax.fmt_xdata = mdates.DateFormatter('%Y-%m-%d')

#plot the tickers now
plotTicker('BIDU', startdate, enddate, 'red')
plotTicker('GOOG', startdate, enddate, '#1066ee')
plotTicker('AMZN', startdate, enddate, '#506612')

plt.show()

当你用这个来比较比都、谷歌、亚马逊的股价时,图会看起来类似于下面的截图:

Obtaining data

使用以下代码比较推特、脸书和领英的股价:

plotTicker('TWTR', startdate, enddate, '#c72020')
plotTicker('LNKD', startdate, enddate, '#103474')
plotTicker('FB', startdate, enddate, '#506612')

Obtaining data

现在,您也可以添加体积图。对于带音量的单个跑马灯图,请使用以下代码:

import datetime

import matplotlib.finance as finance
import matplotlib.dates as mdates
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt

startdate = datetime.date(2013,3,1)
today = enddate = datetime.date.today()

rect = [0.1, 0.3, 0.8, 0.4]   

fig = plt.figure(facecolor='white', figsize=(10,9))  
ax = fig.add_axes(rect, axisbg='#f6f6f6')

def plotSingleTickerWithVolume(ticker, startdate, enddate):

    global ax

    fh = finance.fetch_historical_yahoo(ticker, startdate, enddate)

    # a numpy record array with fields: 
    #     date, open, high, low, close, volume, adj_close
    r = mlab.csv2rec(fh); 
    fh.close()
    r.sort()

    plt.rc('axes', grid=True)
    plt.rc('grid', color='0.78', linestyle='-', linewidth=0.5)

    axt = ax.twinx()
    prices = r.adj_close

    fcolor = 'darkgoldenrod'

    ax.plot(r.date, prices, color=r'#1066ee', lw=2, label=ticker)
    ax.fill_between(r.date, prices, 0, prices, facecolor='#BBD7E5')
    ax.set_ylim(0.5*prices.max())

    ax.legend(loc='upper right', shadow=True, fancybox=True)

    volume = (r.close*r.volume)/1e6  # dollar volume in millions
    vmax = volume.max()

    axt.fill_between(r.date, volume, 0, label='Volume', 
                 facecolor=fcolor, edgecolor=fcolor)

    axt.set_ylim(0, 5*vmax)
    axt.set_yticks([])

    for axis in ax, axt:  
        for label in axis.get_xticklabels():
            label.set_rotation(30)
            label.set_horizontalalignment('right')

        axis.fmt_xdata = mdates.DateFormatter('%Y-%m-%d')

plotSingleTickerWithVolume ('MSFT', startdate, enddate)
plt.show()

随着单个滚动条的绘制以及音量和前面代码中的变化,该图将类似于下面的屏幕截图:

Obtaining data

您也可以选择使用第三种方法:使用blockspring套装。要安装blockspring,必须使用以下pip命令:

pip install blockspring

Blockspring 的方法是生成 HTML 代码。它以 JavaScript 格式自动生成绘图数据。当它与 D3.js 集成时,它提供了一个非常好的交互式绘图。令人惊讶的是,只有两行代码:

import blockspring 
import json  

print blockspring.runParsed("stock-price-comparison", 
   { "tickers": "FB, LNKD, TWTR", 
   "start_date": "2014-01-01", "end_date": "2015-01-01" }).params

根据操作系统,当运行该代码时,它会在默认区域生成 HTML 代码。

Obtaining data

体育运动中的可视化实例

让我们在这里考虑一个不同的例子来说明可视化数据的各种不同方法。我们不会选择计算问题,而是将自己局限于一组简单的数据,并展示可以进行多少不同的分析,最终导致可视化,以帮助澄清这些分析。

北美体育有好几个大联盟运动,我们就来比较一下其中的四个:全国足球联赛(NFL)棒球大联盟(MLB)全国篮球协会(NBA)全国曲棍球联赛。NFL 的球队总价值为 91.3 亿美元,总收入为 95.8 亿美元。我们将使用以下团队价值观和锦标赛数据来选择这项运动(此处仅显示部分数据):

The visualization example in sports

团队价值是比较不同团队的一个重要因素,但锦标赛也有价值。沿着 x 轴的年完成数、沿着 y 轴的冠军数以及代表每年平均冠军数的气泡大小的这个数据的简单图会给我们类似于下图的东西:

The visualization example in sports

但是,除非您可以通过显示标签或细节使其具有交互性,否则前面的图可能不会很有用。matplotlib 可以进行上述绘图,如以下代码所示:

import matplotlib.pyplot as plt
fig = plt.figure(figsize=(15,10), facecolor='w')

def plotCircle(x,y,radius,color, alphaval):
  circle = plt.Circle((x, y), radius=radius, fc=color,\
   alpha=alphaval)
  fig.gca().add_patch(circle)
  nofcircle = plt.Circle((x, y), radius=radius, ec=color, \
   fill=False)
  fig.gca().add_patch(nofcircle)

x = [55,83,90,13,55,82,96,55,69,19,55,95,62,96,82,30,22,39, \
  54,50,69,56,58,55,55,47,55,20,86,78,56]
y = [5,3,4,0,1,0,1,3,5,2,2,0,2,4,6,0,0,1,0,0,0,0,1,1,0,0,3,0, \
  0,1,0]
r = [23,17,15,13,13,12,12,11,11,10,10,10,10,10,9,9,9,8,8,8,8, \
    8,8,8,7,7,7,7,6,6,6]
for i in range(0,len(x)):
  plotCircle(x[i],y[i],r[i],'b', 0.1)

plt.axis('scaled')
plt.show()

你甚至可以用这个数值数据转换成 JavaScript 可以理解的格式(JSON 格式),这样在与 SVG 地图集成时,就有可能在地图上显示估值,如下图截图所示:

The visualization example in sports

如果显示相关的标签,前面带有气泡的地图会更好。然而,由于地图的某些区域空间不足,在地图上添加交互式实现并通过导航显示信息会更有意义。

可以参考http://tinyurl.com/oyxk72r的原始数据源。

http://www.knapdata.com/python/nfl_franch.html有一个备用电源。

除了普通的气泡图和地图上的气泡图,还有其他几种可视化方法可以应用。当显示 32 个团队的统计数据时,一种看起来混乱的视觉格式是饼状图或条形图。

它不仅看起来杂乱,标签也几乎不可读。显示这个饼图的全部意义在于说明在这种数据中,人们必须寻找可视化的替代方法,如下图所示:

The visualization example in sports

如果我们在团队价值的一定范围内组合一组团队,那么通过减少它们,我们可能能够以更有组织的方式展示它们,如下图所示:

The visualization example in sports

上图是通过将团队分成小组来显示团队价值的另一种方法,例如,用 2300,000,000 美元表示 23 亿美元,这意味着 23 亿美元。这样,数据标签是可读的。

总结

在过去的几十年里,计算已经成为许多领域中非常重要的一部分。事实上,许多学校的计算机科学课程,如斯坦福、加州大学伯克利分校、麻省理工学院、普林斯顿、哈佛、加州理工学院等,都因为这种变化而进行了修订,以适应跨学科课程。在大多数科学学科中,计算工作是实验和理论的重要补充。此外,绝大多数实验和理论论文都涉及一些数值计算、模拟或计算机建模。

Python 已经走了很长的路,今天 Python 社区已经发展到有资源和工具来帮助编写最少的代码来非常高效地完成计算中可能需要的几乎所有事情。我们只能在本章中挑选几个工作示例,但是在接下来的章节中,我们将查看更多示例。

五、金融和统计模型

金融和经济模型主要帮助数据的简化和抽象,并广泛使用概率和统计。看一看数据总是很重要的;数据分析的第一步应该是绘制数据。诸如坏数据、异常值和缺失数据等问题通常可以通过可视化数据来检测。只要有可能,就应该纠正错误数据,否则就应该丢弃。然而,在一些不寻常的情况下,例如在股票市场,异常值是很好的数据,应该保留。总而言之,重要的是要检测出不良数据和异常值,并了解它们,以便采取适当的措施。数据变量的选择在这些模型中起着重要的作用。

变量的选择很重要,因为模型的性质通常会决定被考察的事实。例如,为了衡量通货膨胀,需要一个行为模型,这样你就可以了解价格的真实变化,以及与通货膨胀直接相关的价格变化。

我们可以讨论许多有趣的模型及其应用,但是为了不超出本书的范围,我们将选择一些例子。在某些情况下,比如蒙特卡洛,我们也会选择一些在体育运动中的应用。在后面的章节中,我们将讨论以下主题:

  • 蒙特卡罗模拟—适用于许多领域的示例
  • 带有示例的价格模型
  • 通过示例了解波动性度量
  • 门槛模型——炮击隔离模型
  • 带有绘图选项的贝叶斯回归方法
  • 几何布朗、基于扩散的模拟和投资组合估值
  • 分析和创建实时交互图
  • 统计和机器学习概述

计算金融是计算机科学的一个领域,处理金融建模中出现的数据和算法。对于一些读者来说,这一章的内容可能很好理解,但对于其他人来说,看看这些概念将有助于学习一些新的见解,这些见解可能对他们的生活有用,或者适用于他们的兴趣领域。

在您了解蒙特卡罗模拟方法的应用之前,让我们看一个非常简单的投资和一段时间内总回报的例子。

确定性模型

投资的最终目的是盈利,投资或亏损的收益取决于价格的变化和持有的资产数量。投资者通常对与初始投资规模高度相关的收入感兴趣。回报衡量这一点,主要是因为回报是一种资产。例如,股票、债券或股票和债券的投资组合根据定义表示为变化,价格表示为初始价格的一部分。让我们看看总回报的例子。

总回报

我们假设为PT3tT6】为T8T10】时刻的投资金额。简单的总回报表示如下:

Gross returns

在这里, P t+1 为归还的投资价值,回报为 R t+1 。例如,如果Pt= 10Pt+1= 10.6,那么Rt+1= 0.06 = 6%。收益是无标度的,意味着它们不依赖于单位,但是收益依赖于 t 的单位(小时、天等)。换句话说,如果 t 是以年来衡量的,那么,更准确地说,这个净回报是每年 6%。

最近 k 年的总回报是 k 单年总回报的乘积(从 t-kt ,如下图所示:

Gross returns

这是一个确定性模型的例子,除了有一个我们没有提到的警告:你必须将通货膨胀率纳入每年的等式中。如果我们通过假设 F t 是对应于收益 R t 的通货膨胀,在前面的等式中包括这一点,我们将得到以下等式:

Gross returns

如果我们假设Ft= 0,那么前面的等式将适用。假设我们不包括通货膨胀,问这个问题:“2010 年初始投资 1 万美元,回报率 6%,几年后我的投资会翻倍吗?”

让我们试着用 Python 程序找到的答案。在这个程序中,我们还必须添加一条几乎类似于 y = 2x 的直线,并查看它与在 y 轴上绘制的返回值曲线的交点,年份在 x 轴上。首先,我们将绘制无线图,以确定投资价值是否在 12 年内几乎翻了一番。然后,我们将计算直线的斜率 m = 10,000/12 = 833.33 。因此,我们将833.33的斜率值包含在程序中,以显示返回值和直线。下面的代码将返回值与直线重叠进行比较:

import matplotlib.pyplot as plt

principle_value=10000  #invested amount
grossReturn = 1.06     # Rt

return_amt = []
x = []
y = [10000]
year=2010
return_amt.append(principle_value)
x.append(year)

for i in range(1,15):
  return_amt.append(return_amt[i-1] * grossReturn)
  print "Year-",i," Returned:",return_amt[i]

  year += 1
  x.append(year)
  y.append(833.33*(year-2010)+principle_value)

# set the grid to appear
plt.grid()

# plot the return values curve
plt.plot(x,return_amt, color='r')
plt.plot(x,y, color='b')

Year- 1 Returned: 10600.0
Year- 2 Returned: 11236.0
Year- 3 Returned: 11910.16
Year- 4 Returned: 12624.7696
Year- 5 Returned: 13382.255776
Year- 6 Returned: 14185.1911226
Year- 7 Returned: 15036.3025899
Year- 8 Returned: 15938.4807453
Year- 9 Returned: 16894.78959
Year- 10 Returned: 17908.4769654
Year- 11 Returned: 18982.9855834
Year- 12 Returned: 20121.9647184
Year- 13 Returned: 21329.2826015
Year- 14 Returned: 22609.0395575

看完图,你会想有没有办法弄清楚提供房贷的银行到底赚了多少钱。我们把这个留给你。

Gross returns

一个有趣的事实是,曲线在 2022 年之前与直线相交。此时,返回值正好是 20,000 美元。然而,在 2022 年,回报率将约为 20,121 美元。看了的总回报,股票也差不多吗?许多股票,尤其是成熟公司的股票,支付的股息必须计入等式。

如果在时间 t 之前支付股息(或利息) D t ,则时间 t 的总回报定义如下:

Gross returns

另一个例子是抵押贷款,从金融机构以一定的利率借入一定数量的贷款。在这里,为了了解业务的性质,我们将选择 35 万美元的贷款金额,利率为 5%,期限为 30 年。这是美国抵押贷款的一个典型例子(贷款金额和利率因贷款寻求者的信用历史和市场利率而异)。

一个简单的利息计算已知为 P (1 + rt) ,其中 P 为本金金额, r 为利率, t 为期限,因此 30 年期末累计金额为:

Gross returns

原来,到 30 年年底,你已经支付了两倍多的贷款金额(我们在这个计算中没有考虑房地产税):

from decimal import Decimal
import matplotlib.pyplot as plt

colors = [(31, 119, 180),(174, 199, 232),(255,128,0),(255, 15, 14),
      (44, 160, 44),(152, 223, 138),(214, 39, 40),(255,173, 61),
      (148, 103, 189),(197, 176, 213),(140, 86, 75),(196, 156, 148),
      (227, 119, 194),(247, 182, 210),(127, 127, 127),
      (199, 199, 199),(188, 189, 34), (219, 219, 141), 
      (23, 190, 207), (158, 218, 229)]

# Scale the RGB values to the [0, 1] range, which is the format matplotlib accepts.
for i in range(len(colors)):
    r, g, b = colors[i]
    colors[i] = (r / 255., g / 255., b / 255.)

def printHeaders(term, extra):
    # Print headers
    print "\nExtra-Payment: $"+str(extra)+" Term:"+str(term)+" years"
    print "---------------------------------------------------------"
    print 'Pmt no'.rjust(6), ' ', 'Beg. bal.'.ljust(13), ' ',
    print 'Payment'.ljust(9), ' ', 'Principal'.ljust(9), ' ',
    print 'Interest'.ljust(9), ' ', 'End. bal.'.ljust(13)
    print ''.rjust(6, '-'), ' ', ''.ljust(13, '-'), ' ',
    print ''.rjust(9, '-'), ' ', ''.ljust(9, '-'), ' ',
    print ''.rjust(9, '-'), ' ', ''.ljust(13, '-'), ' '

def amortization_table(principal, rate, term, extrapayment, printData=False):
    xarr=[]
    begarr = []

    original_loan = principal
    money_saved=0
    total_payment=0
    payment = pmt(principal, rate, term)
    begBal = principal

    # Print data
    num=1
    endBal=1
    if printData == True: printHeaders(term, extrapayment)
    while  (num < term + 1) and (endBal >0):

        interest = round(begBal * (rate / (12 * 100.0)), 2)
        applied = extrapayment+round(payment - interest, 2)
        endBal = round(begBal - applied, 2)
        if (num-1)%12 == 0 or (endBal < applied+extrapayment):
          begarr.append(begBal)
          xarr.append(num/12)
          if printData == True:
              print '{0:3d}'.format(num).center(6), ' ',
              print '{0:,.2f}'.format(begBal).rjust(13), ' ',
              print '{0:,.2f}'.format(payment).rjust(9), ' ',
              print '{0:,.2f}'.format(applied).rjust(9), ' ',
              print '{0:,.2f}'.format(interest).rjust(9), ' ',
              print '{0:,.2f}'.format(endBal).rjust(13)
        total_payment += applied+extrapayment
        num +=1
        begBal = endBal
    if extrapayment > 0 :
      money_saved = abs(original_loan - total_payment)
      print '\nTotal Payment:','{0:,.2f}'.format(total_payment).rjust(13)
      print '  Money Saved:','{0:,.2f}'.format(money_saved).rjust(13)
    return xarr, begarr, '{0:,.2f}'.format(money_saved)

def pmt(principal, rate, term):
    ratePerTwelve = rate / (12 * 100.0)

    result = principal * (ratePerTwelve / (1 - (1 + ratePerTwelve) ** (-term)))

    # Convert to decimal and round off to two decimal
    # places.
    result = Decimal(result)
    result = round(result, 2)
    return result

plt.figure(figsize=(18, 14))

#amortization_table(150000, 4, 180, 500)
i=0
markers = ['o','s','D','^','v','*','p','s','D','o','s','D','^','v','*','p','s','D']
markersize=[8,8,8,12,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8]

for extra in range(100,1700,100):
  xv, bv, saved = amortization_table(450000, 5, 360, extra, False)
  if extra == 0:
    plt.plot(xv, bv, color=colors[i], lw=2.2, label='Principal only', marker=markers[i], markersize=markersize[i])
  else:
    plt.plot(xv, bv, color=colors[i], lw=2.2, label="Principal plus\$"+str(extra)+str("/month, Saved:\$")+saved, marker=markers[i], markersize=markersize[i])
  i +=1

plt.grid(True)
plt.xlabel('Years', fontsize=18)
plt.ylabel('Mortgage Balance', fontsize=18)
plt.title("Mortgage Loan For $350,000 With Additional Payment Chart", fontsize=20)
plt.legend()
plt.show()

当这个程序运行时,你会得到所有额外付款情况下每 12 个月的摊销时间表,从 100 美元到 1600 美元。这只是其中一个例子:

Extra-Payment: $800 Term: 30 years 
-----------------------------------------------------------------Pmt no   Beg. bal.   Payment   Principal   Interest    End. bal.     ------   ---------   -------   ---------   ---------   ------
  1      350,000.00  1,878.88* 1,220.55  1,458.33   348,779.45   
 13      335,013.07  1,878.88  1,282.99  1,395.89   333,730.08   
 25      319,259.40  1,878.88  1,348.63  1,330.25   317,910.77   
 37      302,699.75  1,878.88  1,417.63  1,261.25   301,282.12   
 49      285,292.85  1,878.88  1,490.16  1,188.72   283,802.69   
 61      266,995.41  1,878.88  1,566.40  1,112.48   265,429.01   
 73      247,761.81  1,878.88  1,646.54  1,032.34   246,115.27   
 85      227,544.19  1,878.88  1,730.78    948.10   225,813.41   
 97      206,292.20  1,878.88  1,819.33    859.55   204,472.87  
109      183,952.92  1,878.88  1,912.41    766.47   182,040.51  
121      160,470.74  1,878.88  2,010.25    668.63   158,460.49  
133      135,787.15  1,878.88  2,113.10    565.78   133,674.05  
145      109,840.70  1,878.88  2,221.21    457.67   107,619.49  
157       82,566.78  1,878.88  2,334.85    344.03    80,231.93  
169       53,897.49  1,878.88  2,454.31    224.57    51,443.18  
181       23,761.41  1,878.88  2,579.87     99.01    21,181.54  
188        5,474.98  1,878.88  2,656.07     22.81     2,818.91  
189        2,818.91  1,878.88  2,667.13     11.75       151.78  

* $1878.88 includes $1078.88 plus $800 extra payment towards principal
Total Payment: $504,526.47 Money Saved: $154,526.47
Approximately after 15 years 10 months, one can pay off in half the time.

Python 代码生成了以下图表,该图表将抵押贷款支付的额外储蓄与本金储蓄进行了比较:

Gross returns

前面的图显示了抵押贷款余额下降早于 30 年,通过支付额外的数额对本金数额。

固定利率抵押贷款的月付款是借款人每月支付的金额,以确保贷款在期限结束时全额支付利息。月还款额取决于利率( r )作为分数,月还款额( N )称为贷款期限,借款金额( P )称为贷款本金;当你重新排列普通年金现值的公式时,我们得到了每月支付的公式。然而,每个月,如果额外的金额与固定的每月付款一起支付,那么贷款金额可以在更短的时间内还清。

在下面的图表中,我们试图使用从程序中节省下来的钱,并将这些钱与 500 美元到 1300 美元范围内的额外金额进行比较。如果我们仔细看,有了 800 美元的额外金额,你可以节省几乎一半的贷款金额,并在一半的期限内还清贷款。

Gross returns

上图显示了三种不同贷款金额的储蓄,其中额外缴款沿 x 轴显示,以千计的储蓄沿 y 轴显示。下面的代码使用了一个气泡图,该图也直观地显示了抵押贷款本金的额外节省:

import matplotlib.pyplot as plt

# set the savings value from previous example
yvals1 = [101000,111000,121000,131000,138000, 143000,148000,153000,158000]
yvals2 = [130000,142000,155000,160000,170000, 180000,190000,194000,200000]
yvals3 = [125000,139000,157000,171000,183000, 194000,205000,212000,220000]
xvals = ['500','600','700', '800', '900','1000','1100','1200','1300']

#initialize bubbles that will be scaled 
bubble1 = []
bubble2 = []
bubble3 = []

# scale it on something that can be displayed
# It should be scaled to 1000, but display will be too big 
# so we choose to scale by 5% (divide these by 20 again to relate 
# to real values)

for i in range(0,9): 
  bubble1.append(yvals1[i]/20) 
  bubble2.append(yvals2[i]/20) 
  bubble3.append(yvals3[i]/20) 

#plot yvalues with scaled by bubble sizes
#If bubbles are not scaled, they don't fit well
fig, ax = plt.subplots(figsize=(10,12))
plt1 = ax.scatter(xvals,yvals1, c='#d82730', s=bubble1, alpha=0.5)
plt2 = ax.scatter(xvals,yvals2, c='#2077b4', s=bubble2, alpha=0.5)
plt3 = ax.scatter(xvals,yvals3, c='#ff8010', s=bubble3, alpha=0.5)

#Set the labels and title 
ax.set_xlabel('Extra Dollar Amount', fontsize=16)
ax.set_ylabel('Savings', fontsize=16)
ax.set_title('Mortgage Savings (Paying Extra Every Month)', 
        fontsize=20)

#set x and y limits
ax.set_xlim(400,1450)
ax.set_ylim(90000,230000)

ax.grid(True)
ax.legend((plt1, plt2, plt3), ('$250,000 Loan', '$350,000 Loan',
     '$450,000 Loan'), scatterpoints=1, loc='upper left', 
      markerscale=0.17, fontsize=10, ncol=1)

fig.tight_layout()
plt.show()

通过创建散点图,可以更容易地查看哪种贷款类别比其他类别提供更多的储蓄,但为了简单起见,我们将只比较三种贷款金额:25 万美元、35 万美元和 45 万美元。

下图是散点图的结果,该散点图展示了每月支付额外费用所节省的费用:

Gross returns

随机模型

我们已经讨论了确定性模型,其中具有定量输入值的单个结果没有随机性。随机这个词来源于希腊单词 Stochastikos。意思是善于猜测或碰运气。这个的反义词是“确定的”、“确定的”或“确定的”。一个随机模型预测一组可能的结果,这些结果由它们的可能性或概率加权。例如,一枚硬币在空中翻转时,最终“肯定”会落在地球上,但它是正面还是反面是“随机”的。

蒙特卡罗模拟

蒙特卡洛模拟,也被视为概率模拟,是一种用于理解任何预测模型中风险和不确定性影响的技术。蒙特卡罗方法是 1940 年由斯坦尼斯瓦夫·乌拉姆发明的,当时他正在洛斯阿拉莫斯国家实验室从事几个核武器项目。如今,有了计算机,你可以很快生成随机数并运行模拟,但他令人惊讶地发现,这种方法是多年前计算非常困难的时候发现的。

在预测模型或任何提前计划未来的模型中,都有假设。这些可能是关于投资组合投资回报的假设,或者完成某项任务需要多长时间。由于这些是未来的预测,最好的办法是估计期望值。

蒙特卡洛模拟到底是什么?

蒙特卡罗模拟是一种迭代评估确定性模型的方法,该模型以随机数集作为输入。当模型复杂、非线性或涉及多个不确定参数时,通常使用这种方法。一个模拟通常会涉及超过 100,000 甚至一百万个模型评估。让我们看看确定性模型和随机模型之间的区别。确定性模型将具有确定性的实际输入,以产生一致的结果,如下图所示:

What exactly is Monte Carlo simulation?

让我们看看概率模型和确定性模型有什么不同。

随机模型的输入是概率性的,来自概率密度函数,产生的结果也是概率性的。随机模型是这样的:

What exactly is Monte Carlo simulation?

现在,我们如何用语言描述前面的图表?

首先,创建一个模型。假设你已经确定了三个随机输入: x1x2x3 ,确定了一个方法: f(x1,x2,x3) ,生成了一组 10,000 个输入的随机值(在某些情况下,可以少也可以多)。评估这些输入的模型,对这 10000 个随机输入重复一遍,记录为 yi ,其中 i 从 1 到 10000 运行。分析结果,选择一个最有可能的。

例如,如果我们找到问题的答案,那就是“洛杉矶快船队赢得第七场比赛的概率是多少?”在篮球背景下的一些随机输入,对这个问题有合理的意义,你可以通过运行蒙特卡罗模拟找到答案,并获得一个答案:他们有 45%的概率会赢。实际上他们输了。

蒙特卡洛模拟严重依赖随机数发生器;因此,弄清楚什么是执行蒙特卡罗模拟的最快和最有效的方法是有意义的?Hans Petter Langtangen 完成了一项出色的任务,该任务表明通过将代码移植到位于的 Cython 上,蒙特卡罗模拟可以变得更加高效,他还将其与纯 C 实现进行了比较。

让我们考虑几个例子来说明蒙特卡罗模拟。第一个例子显示了库存问题。稍后,我们将讨论体育中的一个示例(蒙特卡罗模拟适用于许多体育分析。

蒙特卡罗模拟中的一个库存问题

一名水果零售商销售人员每天销售一些水果并订购“T1”Y“T2”单位。每卖出一个单位就有 60 美分的利润,而在一天结束时没有卖出的单位则以每单位 40 美分的亏损被抛出。任意给定日期的需求 D 在【80,140】上均匀分布。零售商应该订购多少台才能使预期利润最大化?

让我们把利润表示为 P 。当您试图根据前面的问题描述创建方程时, s 表示售出的单位数量,而 d 表示需求,如下式所示:

An inventory problem in Monte Carlo simulation

使用这个利润的表示,下面的 Python 程序显示了最大利润:

import numpy as np
from math import log

import matplotlib.pyplot as plt

x=[]
y=[]

#Equation that defines Profit
def generateProfit(d):

   global s

   if d >= s: 
     return 0.6*s
   else:
     return 0.6*d - 0.4*(s-d)

# Although y comes from uniform distribution in [80,140]
# we are running simulation for d in [20,305]

maxprofit=0

for s in range (20, 305):

  # Run a simulation for n = 1000 
  # Even if we run for n = 10,000 the result would
  # be almost the same
  for i in range(1,1000):

     # generate a random value of d 
     d = np.random.randint(10,high=200)

     # for this random value of d, find profit and
     # update maxprofit
     profit = generateProfit(d)
     if profit > maxprofit:
        maxprofit = profit

  #store the value of s to be plotted along X axis 
  x.append(s)

  #store the value of maxprofit plotted along Y axis 
  y.append(log(maxprofit)) # plotted on log scale

plt.plot(x,y)
print "Max Profit:",maxprofit

# Will display this
Max Profit: 119.4

如下图所示,销量越多利润越大,但满足需求时,最大利润保持不变:

An inventory problem in Monte Carlo simulation

前面的图是对数刻度上显示的,这意味着 n=1000 的模拟运行的最大利润是 119.4。现在,让我们试着看一下解析解,看看模拟结果与解析方法的结果有多接近。

由于需求( D )在【80,140】中均匀分布,预期利润由以下积分得出:

An inventory problem in Monte Carlo simulation

使用分析方法的答案是 116,蒙特卡罗模拟也产生了大约这个数字:119。有时,它会产生 118 或 116。这取决于试验的次数。

让我们考虑另一个简单的例子,并寻求问题的答案:“在一个满是 30 名学生的教室里,一个以上的人过同一个生日的概率是多少?”我们将假设它不是闰年,而不是使用月和日,我们将只使用日历年中的天数,即 365 天。逻辑很简单。下面的代码显示了如何计算在 30 个学生的房间里,不止一个人过同一个生日的概率:

import numpy as np
numstudents = 30
numTrials = 10000
numWithSameBday = 0

for trial in range(numTrials):
    year = [0]*365

    for i in range(numstudents):
        newBDay = np.random.randint(365)
        year[newBDay] = year[newBDay] + 1

    haveSameBday = False
    for num in year:
        if num > 1:
           haveSameBday = True

    if haveSameBday == True:
        numWithSameBday = numWithSameBday + 1

prob = float(numWithSameBday) / float(numTrials)
print("The probability of a shared birthday in a class of ", numstudents, " is ", prob) 

('The probability of a shared birthday in a class of ', 30, ' is ', 0.7055)

换句话说,在一个 30 名学生的班级里,有 70%的概率两个人过同一个生日。下面的例子说明了如何应用蒙特卡罗模拟来知道在今天比过去更经常发生的情况下赢得比赛的可能性。

蒙特卡洛模拟在篮球中的应用

让我们考虑一个篮球比赛中的例子,它是在汗学院用 JavaScript 编写的。问题是,“当落后 3 分,只剩下 30 秒的时候,是尝试一个艰难的三分球还是一个轻松的两分球,然后再得到一次控球权?”(勒布朗·詹姆斯提问)。

大多数读者可能理解篮球比赛,但我们只会强调一些重要的规则。每个队都有 24 秒的控球时间。在这段时间内,如果他们得分(甚至在更短的时间内),对方球队将在接下来的 24 秒内获得控球权。然而,由于只剩下 30 秒,如果一名球员可以在大约不到 10 秒的时间内快速投进 3 分,那么对方球队还剩 20 秒左右。篮球和其他任何运动一样,竞争非常激烈,既然球员的目标是减少落后分,那么拿到 3 分对他们最有利。让我们试着写一个 Python 程序来回答这个问题。

在展示模拟程序之前,让我们看一下这个问题域中涉及的一些参数。为了确定在获胜概率方面,投三分球是否更好,球员和对方球队的以下统计非常重要。球员的threePtPercenttwoPtPercent不仅决定了他的实力,还决定了对方球队得标为oppTwoPtPercent的 2 分的百分比,以及对方球队在标为oppFtPercent的罚球百分比中的实力。

也有其他组合,但为了简单起见,我们就到此为止。对方的罚球命中率越高,我们的答案就越倾向于投三分球。您可以降低oppFtPercent的值,看看这里讨论的是什么。在这个例子中,我们将以不同的部分显示片段,您可以用任何您觉得合适的方式将它们放在一起,并使用它们来运行。首先,我们需要 NumPy 包,因为在这里,我们打算使用这个包中的随机数生成器,并且需要matplotlib来绘制,如下代码所示:

import numpy as np
import matplotlib.pyplot as plt

在许多示例中,我们将使用 tableau 中使用的标准颜色,您可能希望将其放在单独的文件中。以下颜色代码阵列可用于任何可视化绘图:

colors = [(31, 119, 180), (174, 199, 232), (255, 127,  14),
   (255, 187, 120), (44, 160, 44), (214,  39,  40), (148,103,189),
   (152, 223, 138), (255,152,150), (197, 176, 213), (140, 86, 75),
   (196, 156, 148), (227,119,194), (247, 182, 210), (127,127,127),
   (199, 199, 199),(188,189, 34),(219, 219, 141), (23, 190,207),
   (158, 218, 229),(217,217,217)]

# Scale RGB values to the [0, 1] range, format matplotlib accepts.
for i in range(len(colors)):
  r, g, b = colors[i]
  colors[i] = (r / 255., g / 255., b / 255.)

我们来看看的三分尝试。如果threePtPercent大于随机数,加班的概率更大,那么就保证赢。看看下面的代码:

def attemptThree():
  if np.random.randint(0, high=100) < threePtPercent:
    if np.random.randint(0, high=100) < overtimePercent:
      return True #We won!!
  return False #We either missed the 3 or lost in OT

两点尝试的逻辑有点复杂,因为这一切都是关于还剩多少时间和谁拥有球权。假设平均来说,尝试一次两分球只需要 5 秒钟,并且玩家标记为twoPtPercent的两分球得分百分比相当高,那么他们就可以获得一次两分球,这将从pointsDown变量中的值中扣除。以下功能用于两点评分尝试:

def attemptTwo():
  havePossession = True
  pointsDown = 3
  timeLeft = 30
  while (timeLeft > 0):
    #What to do if we have possession
    if (havePossession):
      #If we are down by 3 or more, we take the
      #2 quickly.  If we are down by 2 or less
      #We run down the clock first
      if (pointsDown >= 3):
        timeLeft -= timeToShoot2
      else:
        timeLeft = 0

      #Do we make the shot?
      if (np.random.randint(0, high=100) < twoPtPercent):
        pointsDown -= 2
        havePossession = False
    else:
      #Does the opponent team rebound?
      #If so, we lose possession.
      #This doesn't really matter when we run
      #the clock down
      if (np.random.randint(0, high=100) >= offenseReboundPercent):
        havePossession = False
      else:   #cases where we don't have possession
        if (pointsDown > 0):  #foul to get back possession

          #takes time to foul
          timeLeft -= timeToFoul

          #opponent takes 2 free throws
          if (np.random.randint(0, high=100) < oppFtPercent):
            pointsDown += 1

          if (np.random.randint(0, high=100) < oppFtPercent):
            pointsDown += 1
            havePossession = True
        else:
          if (np.random.randint(0, high=100) >= ftReboundPercent):
            #you were able to rebound the missed ft
            havePossession = True
          else:  
            #tied or up so don't want to foul; 
            #assume opponent to run out clock and take
            if (np.random.randint(0, high=100) < oppTwoPtPercent):
              pointsDown += 2 #They made the 2
            timeLeft = 0

  if (pointsDown > 0):
    return False
  else:
    if (pointsDown < 0):
      return True
    else:
      if (np.random.randint(0, high=100) < overtimePercent):
        return True
      else:
        return False

为了比较,我们将选择 5 名平均分不错或者平均分 2 分或者两者兼有的玩家,如下代码所示:

plt.figure(figsize=(14,14))
names=['Lebron James', 'Kyrie Irving', 'Steph Curry', 
       'Kyle Krover', 'Dirk Nowitzki']
threePercents = [35.4,46.8,44.3,49.2, 38.0]
twoPercents = [53.6,49.1,52.8, 47.0,48.6]
colind=0

for i in range(5):  # can be run individually as well
  x=[]
  y1=[]
  y2=[]
  trials = 400 #Number of trials to run for simulation
  threePtPercent = threePercents[i] # % chance of making 3-pt shot
  twoPtPercent = twoPercents[i] # % chance of making a 2-pt shot
  oppTwoPtPercent = 40 #Opponent % chance making 2-pter
  oppFtPercent = 70 #Opponent's FT %
  timeToShoot2 = 5 #How many seconds elapse to shoot a 2
  timeToFoul = 5 #How many seconds elapse to foul opponent
  offenseReboundPercent = 25 #% of regular offense rebound
  ftReboundPercent = 15 #% of offense rebound after missed FT
  overtimePercent = 50 #% chance of winning in overtime

  winsTakingThree = 0
  lossTakingThree = 0
  winsTakingTwo = 0
  lossTakingTwo = 0
  curTrial = 1

  while curTrial < trials:
    #run a trial take the 3
    if (attemptThree()):
      winsTakingThree += 1
    else:
      lossTakingThree += 1
      #run a trial taking a 2
      if attemptTwo() == True :
        winsTakingTwo += 1
      else:
        lossTakingTwo += 1

      x.append(curTrial)
      y1.append(winsTakingThree)
      y2.append(winsTakingTwo)
      curTrial += 1

  plt.plot(x,y1, color=colors[colind], label=names[i]+" Wins Taking Three Point", linewidth=2)
  plt.plot(x,y2, color=colors[20], label=names[i]+" Wins Taking Two Point", linewidth=1.2)
  colind += 2

legend = plt.legend(loc='upper left', shadow=True,)
for legobj in legend.legendHandles:
    legobj.set_linewidth(2.6)
plt.show()

这是通过设置范围1为单个玩家运行的,并且只包括该玩家的姓名和统计数据。在所有情况下,由于对手队的 2 分率很高(70%),对于所有球员,蒙特卡罗模拟通过 3 分得分产生了获胜的建议。让我们看看其中一个单独绘制和全部绘制在一起时的结果。

我们从http://www.basketball-reference.com/teams/的最新统计数据中挑选出了 3 分命中率相当不错的球员。统计数据截至 2015 年 5 月 12 日。在所有情况下,尝试得分 3 分更有可能获胜。如果对方球队的平均罚球数更低,那么结果就会不一样。

以下两个图显示了一名球员和五名从 NBA 联盟(2015)中选出的球员的结果:

Monte Carlo simulation in basketball

前面的截图显示了勒布朗的三分和两分尝试。以下绘图展示了其他四位玩家的尝试进行对比:

Monte Carlo simulation in basketball

波动图

到目前为止,我们已经看到了许多有用的 Python 包。一次又一次,我们看到了 matplotlib 的使用,但在这里,我们将用很少的代码行展示熊猫的使用,这样您就可以快速实现金融绘图。标准差是一个统计术语,用来衡量平均值周围的变化量或离差。这也是衡量波动性的一个指标。

根据定义,离差是实际值和平均值之间的差值。为了将波动性与封闭值一起绘制,这个示例说明了如何从给定的开始日期看到特定股票(如 IBM)的表现,并使用以下代码查看波动性:

import pandas.io.data as stockdata
import numpy as np
r,g,b=(31,  119, 180)
colornow=(r/255.,g/255.,b/255.)
ibmquotes = stockdata.DataReader(name='IBM', data_source='yahoo', start='2005-10-1')
ibmquotes['Volatility'] = np.log(ibmquotes['Close']/
    ibmquotes['Close'].shift(1))
ibmquotes[['Close', 'Volatility']].plot(figsize=(12,10), \
    subplots=True, color=colornow) 

以下截图是波动图的结果:

The volatility plot

现在,让我们看看如何衡量我们的波动性。波动性是衡量价格变化的尺度,可以有各种峰值。而且,指数函数让我们插入时间,给我们成长;对数(指数的倒数)让我们插入增长,并给我们时间度量。以下片段显示了衡量波动性的对数图:

%time
ibmquotes['VolatilityTest'] = 0.0
for I in range(1, len(ibmquotes)):
  ibmquotes['VolatilityTest'] = 
    np.log(ibmquotes['Close'][i]/ibmquotes['Close'][i-1])

如果我们对此进行计时,前面的片段将采用以下内容:

CPU times: user 1e+03 ns, sys: 0 ns, total: 1e+03 ns Wall time: 5.01 µs

为了细分并展示我们是如何做到的,我们使用%time并使用收盘值与收盘值变化的比率来分配波动性度量,如下所示:

%time
ibmquotes['Volatility'] = np.log(ibmquotes['Close']/ ibmquotes['Close'].shift(1))

如果我们对此进行计时,前面的片段将采用以下内容:

CPU times: user 2 µs, sys: 3 µs, total: 5 µs Wall time: 5.01 µs.

它们的价值差异越大,结果就越不稳定。在我们试图绘制波动率相对于行权价格的隐含波动率之前,让我们看看 VSTOXX 数据。该数据可从http://www.stoxx.comhttp://www.eurexchange.com/advanced-services/下载。VSTOXX 数据的示例行如下所示:

            V2TX   V6I1   V6I2   V6I3   V6I4   V6I5   V6I6   V6I7   V6I8 
  Date                                                                     
2015-05-18  21.01  21.01  21.04    NaN  21.12  21.16  21.34  21.75 21.84 
2015-05-19  20.05  20.06  20.15  17.95  20.27  20.53  20.83  21.38 21.50 
2015-05-20  19.57  19.57  19.82  20.05  20.22  20.40  20.63  21.25 21.44 
2015-05-21  19.53  19.49  19.95  20.14  20.39  20.65  20.94  21.38 21.55
2015-05-22  19.63  19.55  20.07  20.31  20.59  20.83  21.09  21.59 21.73

该数据文件由欧洲斯托克指数组成,所有这些指数都可以通过一个简单的过滤机制绘制,日期在 2011 年 12 月 31 日至 2015 年 5 月 1 日之间。以下代码可用于绘制 VSTOXX 数据:

import pandas as pd

url = 'http://www.stoxx.com/download/historical_values/h_vstoxx.txt'
vstoxx_index = pd.read_csv(url, index_col=0, header=2,
                           parse_dates=True, dayfirst=True,
                           sep=',')
vstoxx_short = vstoxx_index[('2011/12/31' < vstoxx_index.index)
                            & (vstoxx_index.index < '2015/5/1')]
# to plot all together
vstoxx_short.plot(figsize=(15,14))

当运行前面的代码时,它会创建一个比较欧洲斯托克指数波动性的图:

The volatility plot

前面的图显示了一起绘制的所有索引,但是如果它们要绘制在单独的子图上,您可能需要将以下子图设置为True:

# to plot in subplots separately
vstoxx_short.plot(subplots=True, grid=True, color='r',
     figsize=(20,20), linewidth=2)

The volatility plot

隐含波动率

布莱克-斯科尔斯-默顿模型是金融市场的统计模型。从这个模型中,可以找到对欧式期权价格的估计。这个公式被广泛使用,许多经验测试表明,布莱克-斯科尔斯价格“相当接近”观察到的价格。费希尔·布莱克和迈伦·斯克尔斯在他们 1973 年的论文《期权和公司负债的定价》中首次发表了这个模型。该模型背后的主要思想是通过以正确的方式买卖标的资产来对冲期权。

在这个模型中,非分散支付股票的欧式看涨期权的价格如下:

Implied volatilities

Implied volatilities

对于给定的欧式看涨期权, C g ,隐含波动率由前面的等式(对数收益率的标准差)计算得出。期权定价公式对波动率的偏导数称为 Vega 。这是一个数字,表明如果波动率为正的 1%且仅波动率为正,期权价格将向哪个方向移动以及移动到什么程度,如下式所示:

Implied volatilities

波动率模型(如 BSM)预测波动率以及该模型的金融用途,预测未来收益的特征。这种预测用于风险管理和对冲、市场时机、投资组合选择和许多其他金融活动。美式看涨或看跌期权为您提供了随时行权的权利,但对于欧式看涨或看跌期权,您只能在到期日行权。

Black-Scholes-Merton(BSM)没有封闭形式的解,但是使用牛顿法(也称为牛顿-拉夫森法),可以通过迭代获得近似值。每当涉及迭代方法时,都会有一定数量的阈值来确定迭代的终止条件。让我们看一下 Python 代码,通过迭代(牛顿法)找到这些值并绘制出来:

Implied volatilities

from math import log, sqrt, exp
from scipy import stats
import pandas as pd
import matplotlib.pyplot as plt

colors = [(31, 119, 180), (174, 199, 232), (255,128,0), 
 (255, 15, 14), (44, 160, 44), (152, 223, 138), (214, 39, 40), 
 (255, 152, 150),(148, 103, 189), (197, 176, 213), (140, 86, 75),
(196, 156, 148),(227, 119, 194), (247, 182, 210), (127, 127, 127),
(199, 199, 199),(188, 189, 34), (219, 219, 141), (23, 190, 207),
(158, 218, 229)]

# Scale the RGB values to the [0, 1] range, which is the format matplotlib accepts.
for i in range(len(colors)):
  r, g, b = colors[i]
  colors[i] = (r / 255., g / 255., b / 255.)

def black_scholes_merton(S, r, sigma, X, T):

  S = float(S) # convert to float
  logsoverx = log (S/X)
  halfsigmasquare = 0.5 * sigma ** 2
  sigmasqrtT = sigma * sqrt(T)

  d1 = logsoverx + ((r + halfsigmasquare) * T) / sigmasqrtT
  d2 = logsoverx + ((r - halfsigmasquare) * T) / sigmasqrtT

  # stats.norm.cdf —> cumulative distribution function
  value = (S * stats.norm.cdf(d1, 0.0, 1.0) – 
     X * exp(-r * T) *   stats.norm.cdf(d2, 0.0, 1.0))

  return value

def vega(S, r, sigma, X, T):

  S = float(S)
  logsoverx = log (S/X)
  halfsigmasquare = 0.5 * sigma ** 2
  sigmasqrtT = sigma * sqrt(T) 
  d1 = logsoverx + ((r + halfsigmasquare) * T) / sigmasqrtT
  vega = S * stats.norm.cdf(d1, 0.0, 1.0) * sqrt(T)

  return vega 

def impliedVolatility(S, r, sigma_est, X, T, Cstar, it):

  for i in range(it):
    numer = (black_scholes_merton(S, r, sigma_est, X, T) - Cstar)
    denom = vega(S,r, sigma_est, X, T)
    sigma_est -= numer/denom

  return sigma_est

我们已经准备好使用这些函数,它们可以在单独的文件中使用并导入,或者只运行一次,完全嵌入代码中。输入文件是从stoxx.com获得的一个名为vstoxx_data.h5的文件,如下代码所示:

h5 = pd.HDFStore('myData/vstoxx_data_31032014.h5', 'r')

futures_data = h5['futures_data'] # VSTOXX futures data
options_data = h5['options_data'] # VSTOXX call option data

h5.close()

options_data['IMP_VOL'] = 0.0
V0 = 17.6639  # the closing value of the index
r=0.04        # risk free interest rate
sigma_est=2
tol = 0.5     # tolerance level for moneyness

现在,让我们用options_datafutures_data值形式运行迭代:

for option in options_data.index:
  # iterating over all option quotes
  futureval = futures_data[futures_data['MATURITY'] == 
      options_data.loc[option]['MATURITY']]['PRICE'].values[0]

  # picking the right futures value 
  if (futureval * (1 - tol) < options_data.loc[option]['STRIKE'] 
    < futureval * (1 + tol)):
    impliedVol = impliedVolatility(V0,r,sigma_est,
            options_data.loc[option]['STRIKE'], 
            options_data.loc[option]['TTM'], 
            options_data.loc[option]['PRICE'],  #Cn
            it=100)                             #iterations 
    options_data['IMP_VOL'].loc[option] = impliedVol

plot_data = options_data[options_data['IMP_VOL'] > 0]
maturities = sorted(set(options_data['MATURITY']))

plt.figure(figsize=(15, 10))

i=0
for maturity in maturities:

  data = plot_data[options_data.MATURITY == maturity]

  # select data for this maturity
  plot_args = {'lw':3, 'markersize': 9}
  plt.plot(data['STRIKE'], data['IMP_VOL'], label=maturity.date(),
       marker='o', color=colors[i], **plot_args)
  i += 1

plt.grid(True)

plt.xlabel('Strike rate $X$', fontsize=18)
plt.ylabel(r'Implied volatility of $\sigma$', fontsize=18)
plt.title('Short Maturity Window (Volatility Smile)', fontsize=22)

plt.legend()
plt.show()

下图是运行前面程序的结果,该程序使用从http://vstoxx.com下载的数据展示了隐含波动率与罢工率的关系。或者,可以在http://knapdata.com/python/vstoxx_data_31032014.h5下载。下图显示了相对于欧洲斯托克指数的隐含波动率:

Implied volatilities

投资组合估值

对一个实体的投资组合估值的常见意义是估计其当前价值。估值通常适用于金融资产或负债,可以对股票、期权、企业或无形资产进行。为了理解估值和它们的可视化方法,我们将挑选共同基金并绘制它们,比较它们,并找到相关性。

让我们假设我们对以单一货币计价的所有投资组合进行估值。这大大简化了投资组合中的价值聚合。

我们将从 Vanguard 中挑选三只基金,如 Vanguard 美国 Total ( vus.to)、Vanguard 加拿大 cap(vre.to)和 Vanguard 新兴市场(vee.to)。下面的代码显示了三只 Vanguard 基金的比较。

import pandas as pd   #gets numpy as pd.np

from pandas.io.data import get_data_yahoo
import matplotlib.pyplot as plt

# get data
data = get_data_yahoo(["vus.to","vre.to","vee.to"], 
  start = '2014-01-01')['Adj Close']

data.plot(figsize=(10,10), lw=2) 
plt.show()

还有另一种从pandas.io.data使用 get_data_yahoo()获取数据的方式,如下图截图所示:

The portfolio valuation

除了绘制它们之外,还可以在将价格转换为日志回报后获得相关矩阵,以便缩放这些值,如以下代码所示:

#convert prices to log returns
retn=data.apply(pd.np.log).diff()

# make corr matrix
retn.corr()

#make scatterplot to show correlation
pd.tools.plotting.scatter_matrix(retn, figsize=(10,10))
plt.show()

# some more stats
retn.skew()
retn.kurt()
# Output
vee.to    0.533157 
vre.to    3.717143 
vus.to    0.906644 
dtype: float64

相关图如下图所示。这个是在应用skew()kurt()相关后,使用熊猫的scatter_matrix函数获得的:

The portfolio valuation

仿真模型

模型是结构和系统功能的表示。模型类似于它所代表的系统,更容易理解。系统的模拟是系统的工作模型。该模型通常是可重新配置的,以允许频繁的实验。该模型的运行对研究该模型是有用的。在建立现有系统之前,模拟是有用的,以减少故障的可能性,从而满足规格。

特定系统何时适合模拟模型?一般来说,每当需要对系统中的随机性进行建模和分析时,仿真是首选的工具。

几何布朗模拟

布朗运动是随机游走的一个例子,广泛用于物理过程建模,如扩散和生物过程以及社会和金融过程(如股票市场的动态)。

布朗运动是一种复杂的方法。这是基于 1827 年布朗在植物中发现的一个过程。它有一系列的应用,包括模拟图像中的噪声、生成分形、晶体的生长和股票市场模拟。出于此处内容相关性的目的,我们将选择后者,即股市模拟。

米(meter 的缩写))F.M .奥斯本研究了普通股价格和货币价值的对数,并表明它们在统计均衡中有一系列影响。利用统计数据和随机时间的股票选择价格,他能够推导出一个分布函数,该函数非常类似于布朗运动中粒子的分布。

几何布朗运动的定义:

一个随机过程( S t )如果满足以下随机微分方程,则称其遵循几何布朗运动:

Geometric Brownian simulation

综合双方,应用初始条件:St= So,可以得出上述方程的解如下:

Geometric Brownian simulation

利用前面的推导,我们可以插入值,得到以下布朗运动:

import matplotlib.pyplot as plt
import numpy as np

'''
Geometric Brownian Motion with drift!
u=drift factor 
sigma: volatility 
T: time span
dt: length of steps
S0: Stock Price in t=0
W: Brownian Motion with Drift N[0,1] 
'''
rect = [0.1, 5.0, 0.1, 0.1]
fig = plt.figure(figsize=(10,10))

T = 2
mu = 0.1
sigma = 0.04
S0 = 20
dt = 0.01
N = round(T/dt)
t = np.linspace(0, T, N)

# Standare normal distrib
W = np.random.standard_normal(size = N) 
W = np.cumsum(W)*np.sqrt(dt) 

X = (mu-0.5*sigma**2)*t + sigma*W

#Brownian Motion 
S = S0*np.exp(X) 

plt.plot(t, S, lw=2)
plt.xlabel("Time t", fontsize=16)
plt.ylabel("S", fontsize=16)
plt.title("Geometric Brownian Motion (Simulation)",
  fontsize=18)
plt.show()

布朗运动模拟的结果如下截图所示:

Geometric Brownian simulation

使用布朗运动模拟股票价格也显示在下面的代码中:

import pylab, random

class Stock(object):
    def __init__(self, price, distribution):
        self.price = price
        self.history = [price]
        self.distribution = distribution
        self.lastChange = 0

    def setPrice(self, price):
        self.price = price
        self.history.append(price)

    def getPrice(self):
        return self.price

    def walkIt(self, marketBias, mo):
        oldPrice = self.price
        baseMove = self.distribution() + marketBias
        self.price = self.price * (1.0 + baseMove)
        if mo:
            self.price = self.price + random.gauss(.5, .5)*self.lastChange
        if self.price < 0.01:
            self.price = 0.0
        self.history.append(self.price)
        self.lastChange = oldPrice - self.price

    def plotIt(self, figNum):
        pylab.figure(figNum)
        pylab.plot(self.history)
        pylab.title('Closing Price Simulation Run-' + str(figNum))
        pylab.xlabel('Day')
        pylab.ylabel('Price')

def testStockSimulation():
    def runSimulation(stocks, fig, mo):
        mean = 0.0
        for s in stocks:
            for d in range(numDays):
                s.walkIt(bias, mo)
            s.plotIt(fig)
            mean += s.getPrice()
        mean = mean/float(numStocks)
        pylab.axhline(mean)
    pylab.figure(figsize=(12,12))
    numStocks = 20
    numDays = 400
    stocks = []
    bias = 0.0
    mo = False
    startvalues = [100,500,200,300,100,100,100,200,200, 300,300,400,500,00,300,100,100,100,200,200,300]
    for i in range(numStocks):
        volatility = random.uniform(0,0.2)
        d1 = lambda: random.uniform(-volatility, volatility)
        stocks.append(Stock(startvalues[i], d1))
    runSimulation(stocks, 1, mo)

testStockSimulation()
pylab.show()

使用来自均匀分布的随机数据的收盘价模拟的结果如下截图所示:

Geometric Brownian simulation

基于扩散的模拟

随机模型提供了对反应扩散过程更详细的理解。这样的描述对于生物系统的建模通常是必要的。已经研究了各种各样的模拟模型,为了在本章的上下文中限制我们自己,我们将考虑平方根扩散。

由考克斯、英格索尔和罗斯(1985)为金融推广的平方根扩散被用于建模均值回复量(如利率和波动性)。这个过程的随机微分方程如下:

The diffusion-based simulation

x t 的值具有卡方分布,但是在离散版本中,它们可以用正态分布来近似。对于离散版本,我们指的是使用迭代方法应用欧拉数值近似方法,如下式所示:

The diffusion-based simulation

import numpy as np
import matplotlib.pyplot as plt
import numpy.random as npr

S0 = 100 # initial value
r = 0.05 
sigma = 0.25 
T = 2.0 

x0=0
k=1.8
theta=0.24
i = 100000
M = 50
dt = T / M
def srd_euler():
  xh = np.zeros((M + 1, i))
  x1 = np.zeros_like(xh)
  xh[0] = x0
  x1[0] = x0
  for t in range(1, M + 1):
   xh[t] = (xh[t - 1]
    + k * (theta - np.maximum(xh[t - 1], 0)) * dt
    + sigma * np.sqrt(np.maximum(xh[t - 1], 0)) * np.sqrt(dt) 
    * npr.standard_normal(i))
  x1 = np.maximum(xh, 0)
  return x1
x1 = srd_euler()

plt.figure(figsize=(10,6))
plt.hist(x1[-1], bins=30, color='#98DE2f', alpha=0.85)
plt.xlabel('value')
plt.ylabel('frequency')
plt.grid(False)

plt.figure(figsize=(12,10))
plt.plot(x1[:, :10], lw=2.2)
plt.title("Square-Root Diffusion - Simulation")
plt.xlabel('Time', fontsize=16)
plt.ylabel('Index Level', fontsize=16)
#plt.grid(True)
plt.show()

The diffusion-based simulation

阈值模型

阈值模型是任何模型,其中一些阈值用于区分值的范围,其中模型预测的行为以某种重要的方式收敛。谢林试图通过构建两个模拟模型来模拟个体相互作用时的分离动力学。

谢林的隔离模型

谢林的 隔离模式 ( SSM )最早由托马斯 C·谢林提出。这个模型是能够自组织的系统的第一个构造模型之一。

谢林通过将便士和一角硬币放在棋盘上,并根据各种规则移动它们来进行实验。在他的实验中,他使用了一个类似于城市的棋盘,棋盘的正方形代表住所,正方形代表邻居。便士和一角硬币(视觉上也不同)可以代表两个群体的吸烟者、不吸烟者、男性、女性、高管、非高管、学生或教师。

模拟规则将终止条件指定为没有任何代理因为开心而从当前位置移动,这意味着如果代理不开心就会移动

谢林模型被用来模拟教室的隔离,该模型表明,即使是对相邻同学的微弱偏好,隔离模式也可能发生。

假设我们根据学生的优先级有三种类型的学生类别:运动类高级熟练度学术类普通类,每种类型分别为 012

为了便于说明,我们假设一所高中有 250 名不同类型的学生。每个代理代表一名学生。这些代理人都住在一个单一的单位广场上(这可以想象成一所高中建筑)。代理人的位置只是一个点 (x,y) ,这里 0 < x,y < 1 。如果一个代理的 12 个最近的邻居中有一半或更多是同一类型的(从欧几里得距离来看是最近的),那么这个代理就是幸福的。每个代理的初始位置是从二元均匀分布中独立得出的,如以下代码所示:

from random import uniform, seed
from math import sqrt
import matplotlib.pyplot as plt

num = 250             # These many agents of a particular type
numNeighbors = 12     # Number of agents regarded as neighbors
requireSameType = 8   # At least this many neighbors to be same type

seed(10)  # for reproducible random numbers

class StudentAgent:

    def __init__(self, type):
        #Students of different type will be shown in colors
        self.type = type
        self.show_position()

    def show_position(self):
        # position changed by using uniform(x,y)
        self.position = uniform(0, 1), uniform(0, 1)

    def get_distance(self, other):
        #returns euclidean distance between self and other agent.
        a = (self.position[0] - other.position[0])**2
        b = (self.position[1] - other.position[1])**2
        return sqrt(a + b)

    def happy(self, agents):
        "returns True if reqd number of neighbors are the same type."
        distances = []

        for agent in agents:
            if self != agent:
                distance = self.get_distance(agent)
                distances.append((distance, agent))
        distances.sort()
        neighbors = [agent for d, agent in distances[:numNeighbors]]
        numSameType = sum(self.type == agent.type 
            for agent in neighbors)
        return numSameType >= requireSameType

    def update(self, agents):
        "If not happy, randomly choose new positions until happy."
        while not self.happy(agents):
            self.show_position()

def plot_distribution(agents, cycle_num):

    x1,y1 = [],[]
    x2,y2 = [],[]
    x3,y3 = [],[]

    for agent in agents:
        x, y = agent.position
        if agent.type == 0:
            x1.append(x); y1.append(y)
        elif agent.type == 1:
            x2.append(x); y2.append(y)        
        else:
            x3.append(x); y3.append(y)

    fig, ax = plt.subplots(figsize=(10,10))
    plot_args = {'markersize' : 8, 'alpha' : 0.65, 'markersize': 14}
    ax.set_axis_bgcolor('#ffffff')
    ax.plot(x1, y1, 'o', markerfacecolor='#1b62a5',  **plot_args)
    ax.plot(x2, y2, 'o', markerfacecolor='#279321', **plot_args)
    ax.plot(x3, y3, 'D', markerfacecolor='#fd6610', **plot_args)
    ax.set_title('Iteration {}'.format(cycle_num))
    plt.show()

agents = [StudentAgent(0) for i in range(num)]
agents.extend(StudentAgent(1) for i in range(num))
agents.extend(StudentAgent(2) for i in range(num))
count = 1
terminate=False
while terminate == False:
    plot_distribution(agents, count)
    count += 1
    no_one_moved = True
    for agent in agents:
        old_position = agent.position
        agent.update(agents)
        if agent.position != old_position:
            no_one_moved = False
    if no_one_moved:
        terminate=True

Schelling's Segregation Model

统计和机器学习概述

人工 智能 ( AI )这个领域并不新鲜,如果我们还记得三十年前研究 AI 的时候,除了机器人学的之外,当时对这个领域的未来几乎没有什么了解。现在,特别是在过去的十年里,人们对人工智能和机器学习的兴趣有了相当大的增长。从最广泛的意义上说,这些领域旨在“发现和学习一些有用的”环境知识。收集到的信息导致新算法的发现,进而引出“如何处理高维数据和处理不确定性”的问题。

机器学习旨在生成足够简单的分类表达式,供人类使用。他们必须充分模仿人类的推理,以提供对决策过程的见解。与统计方法类似,背景知识可以在开发阶段加以利用。统计学习在许多科学领域发挥着关键作用,学习科学在与工程和其他学科领域交叉的统计、数据挖掘和人工智能领域发挥着关键作用。

统计学和机器学习的区别在于统计学强调推理,而机器学习强调预测。当应用统计学时,一般的方法是推断数据产生的过程。对于机器学习来说,人们想要知道如何预测数据关于某个变量的未来特征。统计学习和机器学习之间有很多重叠之处,专家们经常会有一方提出相反的观点。让我们把这场辩论留给专家们,并在本章中选择几个领域进行讨论。在接下来的章节中,将会有机器学习的详细例子。以下是一些算法:

  • 回归或预测
  • 线性和二次判别分析
  • 分类
  • 最近邻
  • 奈伊夫拜厄斯
  • 支持向量机
  • 决策树
  • 使聚集

机器学习的算法大致分为监督学习、无监督学习、强化学习和深度学习。分类的监督学习方法是给测试数据贴标签,就像老师一样,给班级监督。无监督学习没有任何标记的训练数据,而有监督学习有完全标记的训练数据。半监督学习介于监督学习和非监督学习之间。这也利用了未标记的数据进行训练。

由于本书的上下文是数据可视化,我们将在下面的章节中只讨论一个少数算法。

K-最近邻

我们首先要看的机器学习算法是 k 近邻 ( k-NN )。k-NN 不根据训练数据建立模型。它将没有标签的新数据与现有数据进行比较。然后,获取最相似的数据(最近的邻居)并查看它们的标签。现在,从已知数据集中查看最上面的 k 条最相似的数据( k 是一个整数,通常小于 20)。下面的代码演示了 k 近邻图:

from numpy import random,argsort,sqrt
from pylab import plot,show
import matplotlib.pyplot as plt

def knn_search(x, data, K):

  """ k nearest neighbors """

  ndata = data.shape[1]
  K = K if K < ndata else ndata
  # euclidean distances from the other points
  sqd = sqrt(((data - x[:,:ndata])**2).sum(axis=0))
  idx = argsort(sqd) # sorting
  # return the indexes of K nearest neighbors
  return idx[:K]

data = random.rand(2,200) # random dataset
x = random.rand(2,1) # query point

neig_idx = knn_search(x,data,10)

plt.figure(figsize=(12,12))

# plotting the data and the input point
plot(data[0,:],data[1,:],'o,  x[0,0],x[1,0],'o', color='#9a88a1', 
   markersize=20)

# highlighting the neighbors
plot(data[0,neig_idx],data[1,neig_idx],'o', 
  markerfacecolor='#BBE4B4',markersize=22,markeredgewidth=1)

show()

k 近邻的方法如下:

  • 使用任何方法收集数据
  • 准备距离计算所需的数值
  • 用任何合适的方法进行分析
  • 无培训(不涉及任何培训)
  • 计算错误率的测试
  • 应用对计算出的 k 最近邻搜索采取一些行动,并识别查询的顶部 k 最近邻

为了测试出一个分类器,你可以从一些已知的数据开始,这样你就可以对分类器隐藏答案,并向分类器询问它的最佳猜测。

K-nearest neighbors

广义线性模型

回归是估计变量之间关系的统计过程。更具体地说,回归有助于理解当任何一个自变量发生变化时,因变量的典型值是如何变化的。

线性回归是可以应用插值的最古老的回归类型,但它不适合预测分析。这种回归对异常值和互相关很敏感。

贝叶斯回归是一种惩罚估计,比传统的线性回归更加灵活和稳定。它假设你有一些关于回归系数的先验知识,并且统计分析适用于贝叶斯推断的上下文。

我们将讨论一组方法,其中目标值( y )预计是一些输入变量的线性组合(xT5】1x 2 ,以及……xn)。换句话说,使用符号表示目标值如下:

Generalized linear models

现在,让我们来看看贝叶斯线性回归模型。人们可能会问的一个逻辑问题是“为什么是贝叶斯?”答案是:

  • 贝叶斯模型更加灵活
  • 贝叶斯模型在小样本中更准确(可能取决于先验)
  • 贝叶斯模型可以包含先验信息

贝叶斯线性回归

首先,让我们看一下线性回归的图形模型。在这个模型中,假设我们被赋予了数据值——D =((x1、y 1 )、(x2、yT22】2)、…(xT28】n【T29

Bayesian linear regression

这里, w 是权重向量,每个 Y i 呈正态分布,如上式所示。 Y i 都是随机变量,并且用一个新的变量 x 来条件化每个随机变量YT20】I= YIT28】从数据中,我们可以预测出相应的 y 为新的变量x【t33

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats

from sklearn.linear_model import BayesianRidge
from sklearn.linear_model import LinearRegression

np.random.seed(0)
n_samples, n_features = 200, 200

X = np.random.randn(n_samples, n_features)  # Gaussian data
# Create weights with a precision of 4.
theta = 4.
w = np.zeros(n_features)

# Only keep 8 weights of interest
relevant_features = np.random.randint(0, n_features, 8)
for i in relevant_features:
    w[i] = stats.norm.rvs(loc=0, scale=1\. / np.sqrt(theta))

alpha_ = 50.
noise = stats.norm.rvs(loc=0, scale=1\. / np.sqrt(alpha_), size=n_samples)
y = np.dot(X, w) + noise

# Fit the Bayesian Ridge Regression 
clf = BayesianRidge(compute_score=True)
clf.fit(X, y)

# Plot weights and estimated and histogram of weights
plt.figure(figsize=(11,10))
plt.title("Weights of the model", fontsize=18)
plt.plot(clf.coef_, 'b-', label="Bayesian Ridge estimate")
plt.plot(w, 'g-', label="Training Set Accuracy")
plt.xlabel("Features", fontsize=16)
plt.ylabel("Values of the weights", fontsize=16)
plt.legend(loc="best", prop=dict(size=12))
plt.figure(figsize=(11,10))
plt.title("Histogram of the weights", fontsize=18)
plt.hist(clf.coef_, bins=n_features, log=True)
plt.plot(clf.coef_[relevant_features], 5 * np.ones(len(relevant_features)),
         'ro', label="Relevant features")
plt.ylabel("Features", fontsize=16)
plt.xlabel("Values of the weights", fontsize=16)
plt.legend(loc="lower left")
plt.show()

以下两个图是程序的结果:

Bayesian linear regression

Bayesian linear regression

创建动画和互动绘图

有几个交互式绘图工具可供选择,如 Bokeh、Plotly 和 VisPy。

Bokeh 允许您通过 JavaScript 绘制 matplotlib 对象,这使得交互部分变得容易。例如,如果需要一个交互式的地图绘图,可以使用 Bokeh。Bokeh 使用 JavaScript,支持 D3.js 风格的绘图,并通过现代网络浏览器将可视化作为目标。Bokeh 在大数据集上提供了良好的性能。您可以通过condapip轻松安装bokeh,如下代码所示:

conda install bokeh
 OR
pip install bokeh

import collections 

from bokeh.sampledata import us_counties, unemployment
from bokeh.plotting import figure, show, output_file
from bokeh.models import HoverTool

county_coordinate_xs=[
us_counties.data[code]['lons'] for code in us_counties.data
if us_counties.data[code]['state'] == 'ca'
]
county_coordinate_ys=[
us_counties.data[code]['lats'] for code in us_counties.data
if us_counties.data[code]['state'] == 'ca'
]

colors = ["#e6f2ff", "#cce5ff", "#99cbff", "#b2d8ff", "#73abe5", "#5985b2"]
county_colors = []
for county_id in us_counties.data:
  if us_counties.data[county_id]['state'] != 'ca':
    continue
  try:
    rate = unemployment.data[county_id]
    idx = min(int(rate/2), 5)
    county_colors.append(colors[idx])
  except KeyError:
    county_colors.append("black")

output_file("california.html", title="california.py example")

TOOLS="pan,wheel_zoom,box_zoom,reset,hover,save"
p = figure(title="California Unemployment 2009", width=1000, height=1000, tools=TOOLS)

p.patches(county_coordinate_xs, county_coordinate_ys,
fill_color=county_colors, fill_alpha=0.7,
line_color="white", line_width=0.5)

mouse_hover = p.select(dict(type=HoverTool))
mouse_hover.point_policy = "follow_mouse"
mouse_hover.tooltips = collections.OrderedDict([
("index", "$index"), ("(x,y)", "($x, $y)"),
("fill color", "$color[hex, swatch]:fill_color"),
])
show(p)

为了查看结果,您可能需要使用浏览器打开California.html:

Creating animated and interactive plots

Plotly 是另一个允许互动绘图的选项,但是需要一个在线并且有 Plotly 账号。使用 Plotly 的绘图看起来非常好,而且是交互式的。以下代码显示了如何使用plotly创建交互式绘图:

from pylab import * 
import plotly
#py = plotly.plotly('me', 'mykey')

def to_plotly(ax=None):
    if ax is None:
        ax = gca()

    lines = []
    for line in ax.get_lines():
        lines.append({'x': line.get_xdata(),
                      'y': line.get_ydata(),
                      'name': line.get_label(),
                      })

    layout = {'title':ax.get_title(),
              'xaxis':{'title':ax.get_xlabel()},
              'yaxis':{'title':ax.get_ylabel()}
              }
    filename = ax.get_title()  if ax.get_title() != '' else 'Untitled'
    print filename
    close('all')
    #return lines, layout
    return py.iplot(lines,layout=layout, filename = filename)    

plot(rand(100), label = 'trace1')
plot(rand(100)+1, label = 'trace2')
title('Title')
xlabel('X label')
ylabel('Y label ')

response = to_plotly()
response

Creating animated and interactive plots

VisPy 是另一款使用 Python 和 OpenGL 构建的高性能交互工具;因此,它传递了现代 GPU 的力量。它是相当新的,随着它的成熟,它给用户留下了另一个好的可视化库可供选择。下面的例子显示了使用vispy可以创建一个可以交互缩放的图像:

import sys

from vispy import scene
from vispy import app
import numpy as np

canvas = scene.SceneCanvas(keys='interactive')
canvas.size = 800, 800
canvas.show()

# Set up a viewbox to display the image with interactive pan/zoom
view = canvas.central_widget.add_view()

# Create the image
img_data = np.random.normal(size=(100, 100, 3), loc=128,
                            scale=40).astype(np.ubyte)
image = scene.visuals.Image(img_data, parent=view.scene)

# Set 2D camera (the camera will scale to the contents in the scene)
view.camera = scene.PanZoomCamera(aspect=1)

if __name__ == '__main__' and sys.flags.interactive == 0:
    app.run()

Creating animated and interactive plots

前面的图像显示了第一次出现的图,但是当我们移动鼠标并放大它时,它显示如下:

Creating animated and interactive plots

总结

本章讨论了典型的金融示例,并在最后讨论了机器学习。简要介绍了确定性模型,使用毛利分析和节省抵押贷款支付进行了讨论。

利用期权形式的真实数据,还讨论了 VSTOXX 波动率指数上欧洲看涨期权的隐含波动率。我们还看了蒙特卡洛模拟。使用不同的实现方法,我们展示了使用蒙特卡罗方法的模拟方法、库存问题和篮球情况。

此外,您还以股市模型为例学习了模拟模型(如几何布朗和基于扩散的模拟)。这一章还侧重于如何利用扩散来显示漂移和波动。

我们还研究了贝叶斯线性回归和交互式绘图方法,可供选择。然后,我们讨论了 k 近邻算法、基于实例的学习性能和机器学习算法。这个例子只是为了引起人们对这个主题的兴趣,并让你对这些算法有所了解。然而,在下一章中,我们将研究更有趣的统计和机器学习算法。

六、统计和机器学习

机器学习使您能够创建和使用计算机算法,从这些算法中学习,纠正它们,并改进它们,以绘制任何过去未知的新模式。您还可以从这些从数据中发现的新模式中提取见解。例如,人们可能有兴趣教计算机如何识别图像中的邮政编码值。另一个例子是,如果我们有一个特定的任务,比如确定垃圾邮件,那么不要直接编写程序来解决这个问题,在这个范式中,你可以寻求学习的方法,并变得更擅长使用计算机获得准确的结果。

近年来,机器学习已经成为人工智能的一个重要组成部分。有了计算能力,我们很有可能能够使用机器学习方法构建智能系统。凭借我们今天拥有的计算能力,这些任务已经变得比二十年前简单得多。机器学习的主要目标是开发在现实世界中有应用价值的算法。除了时间和空间效率之外,这些学习算法所需的数据量也起着挑战性的作用。由于机器学习算法是由数据驱动的,你可以看到为什么今天在这个学科领域已经有这么多不同的算法。在本章的以下部分,我们将通过示例讨论以下主题:

  • 分类方法—决策树、线性和 k 近邻
  • 朴素贝叶斯、线性回归和逻辑回归
  • 支持向量机
  • 基于树的回归和无监督学习
  • 主成分分析
  • 基于相似度的聚类
  • 测量分类性能

分类方法

机器学习算法在许多现实应用中非常有用,例如,如果有人对准确预测气候或诊断疾病感兴趣。学习通常基于一些已知的行为或观察。这意味着机器学习是关于学习在过去的经验或观察的基础上改进未来的东西。

机器学习算法大致分为监督学习、无监督学习、强化学习和深度学习。分类的监督学习方法(其中测试数据被标记)类似于监督不同班级的老师。当我们指定目标变量时,监督学习依靠算法从数据中学习。构建准确的分类器需要以下特征:

  • 一组很好的训练例子
  • 在训练场上相当不错的表现
  • 一种与先前预期密切相关的分类方法

Classification methods

二进制分类器获取数据项并将其放入两个类中的一个(对于更高维度,数据项被放入 k 类中)。二元分类器的例子决定了一个人的结果是否可以被诊断为某种疾病的阳性或阴性。分类器算法是概率性的。在一定的误差范围内,某人可以被诊断为阳性或阴性。在这些算法中,有一种通用的方法可以实现这一点,其顺序如下:

  • 从可靠的来源收集数据。
  • 准备或重组具有特定结构的数据。对于二进制分类器,需要计算距离。
  • 用任何合适的方法分析数据。
  • 训练(这不适用于二进制分类器)。
  • 测试(计算错误率)。

在这一章中,的讨论将集中在什么工具可用于可视化输入和结果,但没有太多的关注机器学习的概念。关于这个主题的更深入的内容,你可以参考适当的材料。让我们看一个例子,并逐步了解各种选择。

理解线性回归

一个简单的场景是人们希望根据 GPA 分数和 SAT 分数的数据预测一个学生是否有可能被大学本科项目(如普林斯顿大学)录取,样本数据如下:

Understanding linear regression

为了能够考虑录取分数与 SAT 分数和 GPA 分数相结合的分数,这里只是为了举例说明(注意,这不像实际的录取过程),我们将尝试找出分隔线。由于 SAT 分数沿 x 轴从 21002390 不等,我们可以从y = 2490–2 * I * 2000中尝试五个值。在下面的例子中,我们有 2150 而不是 2000。沿 y 轴的 GPA 取极值为3.3**5.0;因此,我们从一个极端使用以 3.3 开始的增量值,从另一个极端使用 3.3+0.2i5.0-0.2i (步长为 0.2 )。

作为第一次尝试看到数据的视觉外观,我们将尝试使用matplotlibnumpy来探索它。使用在 xy 轴上的 SAT 和 GPA 分数,并应用散点图,我们将尝试在以下示例中找到分隔线:

import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np

mpl.rcParams['axes.facecolor']= '#f8f8f8' 
mpl.rcParams['grid.color'] = '#303030' 
mpl.rcParams['grid.color']= '#303030' 
mpl.rcParams['lines.linestyle'] = '--'
#SAT Score 
x=[2400,2350,2400,2290,2100,2380,2300,2280,2210,2390]

#High school GPA
y=[4.4,4.5,4.2,4.3,4.0,4.1,3.9,4.0,4.3,4.5]

a = '#6D0000'
r = '#00006F' 
#Acceptance or rejections core 
z=[a,a,a,r,r,a,r,r,a,a]

plt.figure(figsize=(11,11))
plt.scatter(x,y,c=z,s=600)

# To see where the separation lies
for i in range(1,5):
  X_plot = np.linspace(2490-i*2,2150+i*2,20)
  Y_plot = np.linspace(3.3+i*0.2,5-0.2*i,20)
  plt.plot(X_plot,Y_plot, c='gray')

plt.grid(True) 

plt.xlabel('SAT Score', fontsize=18) 
plt.ylabel('GPA', fontsize=18) 
plt.title("Acceptance in College", fontsize=20) 
plt.legend()

plt.show()

在前面的代码中,我们不会执行任何回归或分类。这只是试图理解数据的视觉外观。还可以画几条分隔线,直观了解线性回归的工作原理。

您可以看到,没有足够的数据来应用准确的方法来预测测试数据。然而,如果我们尝试获取更多的数据,并使用一些知名的包来应用机器学习算法,我们可以更好地理解结果。例如,增加课外活动(如体育和音乐)。

Understanding linear regression

线性回归

使用线性回归的主要目标是预测一个数值目标值。一种方法是写一个关于输入的目标值的方程。例如,假设我们试图预测一个参加体育和音乐,但属于低收入家庭的全面发展的学生的接受率。

一个可能的等式是接受度= 0.0015 收入+ 0.49(参与 _ 评分);这是一个回归方程。这使用简单的线性回归来预测具有单一特征的定量反应。它采用以下形式:

Linear regression

β 0β 1 合起来称为模型系数。要创建我们的模型,您必须学习这些系数的值。一旦你学会了这些系数,你就可以用这个模型来合理地预测接受率。

这些系数是使用最小二乘准则估计的,这意味着我们将在数学上找到分隔线,并最小化残差平方和。以下是以下示例中使用的部分数据:

Linear regression

下面的 Python 代码展示了如何尝试散点图来确定变量之间的相关性:

from matplotlib import pyplot as pplt

import pandas as pds

import statsmodels.formula.api as sfapi

df = pds.read_csv('/Users/myhomedir/sports.csv', index_col=0)
fig, axs = plt.subplots(1, 3, sharey=True)
df.plot(kind='scatter', x='sports', y='acceptance', ax=axs[0], figsize=(16, 8))
df.plot(kind='scatter', x='music', y='acceptance', ax=axs[1])
df.plot(kind='scatter', x='academic', y='acceptance', ax=axs[2])

# create a fitted model in one line
lmodel = sfapi.ols(formula='acceptance ~ music', data=df).fit()

X_new = pd.DataFrame({'music': [df.music.min(), df.music.max()]})
predictions = lmodel.predict(X_new)

df.plot(kind='scatter', x='music', y='acceptance', figsize=(12,12), s=50)

plt.title("Linear Regression - Fitting Music vs Acceptance Rate", fontsize=20)
plt.xlabel("Music", fontsize=16)
plt.ylabel("Acceptance", fontsize=16)

# then, plot the least squares line

Linear regression

如上图所示,蓝点为 (x,y) 的观测值,对角交叉的线为基于 (x,y) 值的最小二乘拟合,橙色线为残差,为观测值与最小二乘线之间的距离。

Linear regression

使用statsmodelspandasmatplotlib(如上图所示),我们可以假设是基于一所大学如何评价其学生对学术、体育和音乐的贡献的某种评分。

为了测试一个分类器,我们可以从一些已知的数据开始,在不知道答案的情况下,我们将从分类器中寻找答案进行最佳猜测。此外,我们可以将分类器出错的次数相加,然后除以总的测试次数,得到错误率。

下面是从前面的 Python 代码中导出的线性回归图。

Linear regression

还有许多其他 Python 库可以用于线性回归,scikit-learnseabornstatsmodelsmlpy是其中一些著名且流行的库。网络上已经有许多用这些软件包解释线性回归的例子。关于scikit-learn 包的详细信息,请参考http://sci kit-learn . org/stable/modules/generated/sklearn . linear _ model。LinearRegression.html

还有一种有趣的机器学习模型叫做决策树学习,有时也可以称为分类树。另一个类似的模型是回归树。在这里,我们将看到它们之间的差异,以及其中一个是否比另一个有意义。

决策树

分类树用于将数据分成属于响应变量的类。响应变量通常有两类: (1 或 0)和晴天或雨天。如果目标变量有两个以上的类别,那么 C4.5 可以适用。C4.5 改进了连续属性、离散属性和后期构建过程的 ID3 算法。

与大多数学习算法类似,分类树算法分析训练集,然后基于该训练构建分类器,以便在将来有新数据时,它可以正确地对训练和新数据进行分类。测试示例是输入对象,算法必须预测输出值。当响应或目标变量本质上是分类的时,使用分类树。

相反,当响应变量是连续的而不是离散的时,需要回归树。例如,产品的预测价格。回归树是通过二进制分区构建的。这是一个迭代过程,将数据拆分为分区或分支,然后随着方法向上移动每个分区或分支,继续将每个分区拆分为更小的组。换句话说,当问题涉及预测而不是分类时,回归树是适用的。有关这方面的更多细节,我们建议您参考关于分类和回归树的书籍。

当预测因子和反应之间的关系是线性时,标准回归树更合适,当预测因子和反应之间的关系是非线性时,则应使用 C4.5。此外,总结一下,当响应变量只有两个类别时,应该使用分类树算法。

一个例子

对于打网球或高尔夫的决策树算法来说,人们可以通过问一个问题来轻松梳理决策过程,即外面是下雨还是晴天?根据答案画出每个问题的决策图。比赛的性质几乎是一样的——网球对高尔夫——而且在任何体育赛事中,如果刮风下雨,很可能不会有比赛。

对于网球来说,如果前景晴朗,但湿度较高,那么建议不要打。同样,如果下雨刮风,那么网球比赛的整体动态将会相当糟糕。因此,在这种情况下打网球也没有什么乐趣。下图显示了所有可能的情况:

An example

我们还可以添加离散属性(如温度);在什么温度范围内打网球没有意义?大概,如果温度大于华氏 70 度,也就是温度热的话。我们可以将所有这些结合起来编写如下规则:

If (Outlook = Sunny) and (Humidity = High) then play=No
If (Outlook = Rain) and (Wind = Strong) then play=No
If (Outlook = Sunny) and (Humidity = Normal) or
  (Outlook = Overcast) or (Outlook=Rain and Wind=Weak) then play=Yes

使用以下训练集,我们可以运行算法来选择下一个最佳分类器:

|

观点

|

温度

|

湿度

|

|

玩?

|
| --- | --- | --- | --- | --- |
| 快活的 | 热的 | 高的 | 无力的 | 不 |
| 快活的 | 热的 | 高的 | 强烈的 | 不 |
| 遮蔽 | 热的 | 高的 | 无力的 | 是 |
| 遮蔽 | 凉爽的 | 常态 | 强烈的 | 是 |
| 快活的 | 温和的 | 高的 | 无力的 | 不 |
| 快活的 | 凉爽的 | 常态 | 无力的 | 是 |
| 雨 | 温和的 | 高的 | 无力的 | 是 |
| 雨 | 凉爽的 | 常态 | 无力的 | 是 |
| 雨 | 凉爽的 | 常态 | 强烈的 | 不 |
| 雨 | 温和的 | 常态 | 无力的 | 是 |
| 快活的 | 温和的 | 常态 | 强烈的 | 是 |
| 遮蔽 | 温和的 | 高的 | 强烈的 | 是 |
| 遮蔽 | 热的 | 常态 | 无力的 | 是 |
| 雨 | 温和的 | 高的 | 强烈的 | 不 |

决策树 ( ID3 )的自上而下归纳是遵循这些规则的方法:

  • 迭代叶节点直到停止条件:
    1. 确定遍历中下一个节点的最佳决策属性。

    2. 将步骤 1 中最佳节点指定为决策属性。

    3. 对于这些最佳节点的每个值,创建这些节点的新后代。

    4. 将训练数据分类到叶节点中。

    5. Stopping condition for iteration:

      如果训练数据被分类在阈值内

线性回归和决策树算法的一个明显区别是,决策边界平行于坐标轴,例如,如果我们有两个特征( x 1x 2 ,那么它只能创建规则,例如x1>= 5.2,x 2 决策树算法的优点是对错误具有鲁棒性,这意味着训练集可能有错误。还有,对算法影响不大。

使用来自scikit-learn(scikit-learn.org)的sklearn包和以下代码,我们可以绘制决策树分类器:

from sklearn.externals.six import StringIO
from sklearn import tree
import pydot 

# Four columns from the table above with values
# 1st col - 1 for Sunny, 2 for Overcast, and 3 for Rainy
# 2nd col - 1 for Hot, 2 for Mild, 3 for Cool
# 3rd col – 1 for High and 2 for Normal
# 4th col – 0 for Weak and 1 for Strong

X=[[1,1,1,0],[1,1,1,1],[2,1,1,0],[2,3,2,1],[1,2,1,0],[1,3,2,0],\
[3,2,1,0],[3,3,2,0],[3,3,2,1],[3,2,2,0],[1,2,2,1],[2,2,1,1],\
[2,1,2,0],[3,2,1,0]]  

# 1 for Play and 0 for Don't Play
Y=[0,0,1,1,0,1,1,1,0,1,1,1,1,0] 

clf = tree.DecisionTreeClassifier()
clf = clf.fit(X, Y)

dot_data = StringIO() 
tree.export_graphviz(clf, out_file=dot_data) 

graph = pydot.graph_from_dot_data(dot_data.getvalue()) 
graph.write_pdf("game.pdf")

使用sklearn的导出功能,可以将树形图转换为类似下图的图形形式:

An example

为了创建自己的树形结构,可以选择从matplotlib开始使用绘图方法。为了显示树形图,matplotlib有标注,允许您创建带有标签的树形结构,如以下代码所示:

import matplotlib.pyplot as plt

#create nodes here
branchNode = dict(boxstyle="sawtooth", fc="0.8")
leafNode = dict(boxstyle="round4", fc="0.8")
startNode = dict(boxstyle="sawtooth", fc="0.9")

def createPlot():
    fig = plt.figure(1, facecolor='white')
    fig.clf()
    createPlot.ax1 = plt.subplot(111, frameon=False) #ticks for demo purposes
    plotNode('from here', (0.3,0.8), (0.3, 0.8), startNode)
    plotNode('a decision node', (0.5, 0.1), (0.3, 0.8), branchNode)
    plotNode('a leaf node', (0.8, 0.1), (0.3, 0.8), leafNode)
    plt.show()
...

这通常是一个如何使用matplotlib从头开始创建树结构的想法。基本上,前面的例子显示了三个节点的创建,并将它们连接起来形成一个小树。该代码的结果如下所示:

An example

贝叶斯定理

为了首先理解贝叶斯定理,在我们试图看一看天真的贝叶斯分类方法之前,我们应该考虑这个例子。我们假设在 U 宇宙的所有人中,患乳腺癌的那一组人被设定为 A ,而设定为 B 的那一组人进行了筛查试验,结果不幸被诊断为乳腺癌阳性。这在下图中显示为重叠区域A∪B:

The Bayes theorem

有两个不寻常的领域需要关注:B–A∪B或无乳腺癌且诊断为阳性的人和事件A–A∪B或乳腺癌且诊断为阴性的人。现在,让我们尝试回答我们是否知道随机选择的人的测试是阳性的。那么,人得乳腺癌的概率是多少呢?这在视觉上转化为我们是否知道一个人在 B 区域可见,那么同一个人出现在A∪B的概率是多少?从数学上讲,这转化为概率(给定的一个 B )。条件概率方程如下所示:

The Bayes theorem

同样,如果我们知道一个随机选择的人得了癌症,诊断测试出来阳性的概率是多少?这转化为概率(B 给 A) ,如下代码所示:

The Bayes theorem

因此,我们推导出贝叶斯定理,其中 ABP (B) 非零的事件。

朴素贝叶斯分类器

朴素贝叶斯分类器技术基于贝叶斯定理,适用于输入维数较高的情况。虽然它看起来很简单,但在技术上比其他分类方法表现得更好。

(更多信息可在http://scikit-learn.org/stable/modules/naive_bayes.htmlhttp://sebastianaschka . com/Articles/2014 _ naive _ Bayes _ 1 . html获取)。

让我们看看下面这个用红色和蓝色显示对象的例子。如图所示,红色显示的对象代表患有乳腺癌的人群,蓝色显示的对象代表被诊断为乳腺癌阳性的人群。我们的任务是能够标记任何新数据,在这种情况下,新数据是基于对象的现有结构或类别出现的新人,并识别新数据或新人所属的组或类。

在贝叶斯中,先验概率更倾向于接近于当前如何表征对象的模式或行为。这主要是因为先前这个词在这里是先前经验的同义词;因此,如果红色物体比蓝色物体占更大的百分比,那么这就给了我们一个优势,我们可以预期红色物体的预测结果应该更高。

这里的方法是朴素贝叶斯和 k-最近邻算法的结合。对于纯粹天真的贝叶斯分类,我们将讨论另一个使用TextBlob(http://textblob.readthedocs.org/en/dev/)的例子。

下图直观地显示了一个尚未分类的新人:

The Naïve Bayes classifier

利用红色和蓝色的先验概率,可以计算出 x 为红色或蓝色的后验概率,如下代码所示:

The Naïve Bayes classifier

新人最有可能被归类为乳腺癌确诊阳性者。

使用文本块的朴素贝叶斯分类器

TextBlob是一个有趣的库,其中有一组用于文本处理的工具。它附带了用于自然语言处理 ( 自然语言处理)任务的应用编程接口,如分类、名词短语提取、词性标注和情感分析。

要确保一个人可以使用,需要几个步骤。任何使用自然语言处理的图书馆都需要一些语料库;因此,在尝试使用这个有趣的库之前,需要完成以下安装和配置顺序:

  • 安装TextBlob(通过condapip)
  • 下载语料库

安装文本块

使用 binstar search -t conda textblob,可以为水蟒用户找到安装的地方。更多详情可参见附录前进探索可视化

下载语料库

以下命令将让一个人下载corpora:

$ python -m textblob.download_corpora

[nltk_data] Downloading package brown to
[nltk_data]     /Users/administrator/nltk_data...
[nltk_data]   Unzipping corpora/brown.zip.
[nltk_data] Downloading package punkt to
[nltk_data]     /Users/administrator/nltk_data...
[nltk_data]   Unzipping tokenizers/punkt.zip.
[nltk_data] Downloading package wordnet to
[nltk_data]     /Users/administrator/nltk_data...
[nltk_data]   Unzipping corpora/wordnet.zip.
[nltk_data] Downloading package conll2000 to
[nltk_data]     /Users/administrator/nltk_data...
[nltk_data]   Unzipping corpora/conll2000.zip.
[nltk_data] Downloading package maxent_treebank_pos_tagger to
[nltk_data]     /Users/administrator/nltk_data...
[nltk_data]   Unzipping taggers/maxent_treebank_pos_tagger.zip.
[nltk_data] Downloading package movie_reviews to
[nltk_data]     /Users/administrator/nltk_data...
[nltk_data]   Unzipping corpora/movie_reviews.zip.
Finished.

使用文本块的朴素贝叶斯分类器

TextBlob 可以轻松创建自定义文本分类器。为了更好地理解这一点,人们可能需要用他们的训练和测试数据做一些实验。在 TextBlob 0.6.0 版本中,以下分类器可用:

  • BaseClassifier
  • DecisionTreeClassifier
  • MaxEntClassifier
  • NLTKClassifier *
  • NaiveBayesClassifier
  • PositiveNaiveBayesClassifier

标有*的分类器是包裹nltk.classify模块的抽象类。

对于情感分析,可以使用朴素贝叶斯分类器,并用该分类器和textblob.en.sentiments.PatternAnalyzer训练系统。一个简单的例子如下:

from textblob.classifiers import NaiveBayesClassifier
from textblob.blob import TextBlob

from textblob.classifiers import NaiveBayesClassifier
from textblob.blob import TextBlob

train = [('I like this new tv show.', 'pos'),
 # similar train sentences with sentiments goes here]
test = [ ('I do not enjoy my job', 'neg'),
 # similar test sentences with sentiments goes here]
]

cl = NaiveBayesClassifier(train)
cl.classify("The new movie was amazing.") # shows if pos or neg

cl.update(test)

# Classify a TextBlob
blob = TextBlob("The food was good. But the service was horrible. "
                "My father was not pleased.", classifier=cl)
print(blob)
print(blob.classify())

for sentence in blob.sentences:
    print(sentence)
    print(sentence.classify())

以下是运行上述代码时将显示的结果:

pos
neg
The food was good.
pos
But the service was horrible.
neg
My father was not pleased.
pos

可以从文本格式或 JSON 格式的文件中读取训练数据。JSON 文件中的示例数据如下所示:

[
  {"text": "mission impossible three is awesome btw","label": "pos"},
  {"text": "brokeback mountain was beautiful","label":"pos"},
  {"text": " da vinci code is awesome so far","label":"pos"},
  {"text": "10 things i hate about you + a knight's tale * brokeback mountain","label":"neg"},
  {"text": "mission impossible 3 is amazing","label":"pos"},

    {"text": "harry potter = gorgeous","label":"pos"},  
    {"text": "i love brokeback mountain too: ]","label":"pos"},
]

from textblob.classifiers import NaiveBayesClassifier
from textblob.blob import TextBlob
from nltk.corpus import stopwords

stop = stopwords.words('english')

pos_dict={}
neg_dict={}
with open('/Users/administrator/json_train.json', 'r') as fp: 
     cl = NaiveBayesClassifier(fp, format="json")
print "Done Training"

rp = open('/Users/administrator/test_data.txt','r')
res_writer = open('/Users/administrator/results.txt','w')
for line in rp:
    linelen = len(line)
    line = line[0:linelen-1]
    sentvalue = cl.classify(line)
    blob = TextBlob(line)
    sentence = blob.sentences[0]
    for word, pos in sentence.tags:
       if (word not in stop) and (len(word)>3 \
            and sentvalue == 'pos'): 
         if pos == 'NN' or pos == 'V':  
           pos_dict[word.lower()] = word.lower()
       if (word not in stop) and (len(word)>3 \
            and sentvalue == 'neg'): 
         if pos == 'NN' or pos == 'V':  
           neg_dict[word.lower()] = word.lower()

    res_writer.write(line+" => sentiment "+sentvalue+"\n")

    #print(cl.classify(line))
print "Lengths of positive and negative sentiments",len(pos_dict), len(neg_dict)  

Lengths of positive and negative sentiments 203 128 

我们可以从语料库中添加更多的训练数据,并用以下代码评估分类器的准确性:

test=[
("mission impossible three is awesome btw",'pos'),
("brokeback mountain was beautiful",'pos'),
("that and the da vinci code is awesome so far",'pos'),
("10 things i hate about you =",'neg'),
("brokeback mountain is a spectacularly beautiful movie",'pos'),
("mission impossible 3 is amazing",'pos'),
("the actor who plays harry potter sucks",'neg'),
("harry potter = gorgeous",'pos'),
('The beer was good.', 'pos'),
('I do not enjoy my job', 'neg'),
("I ain't feeling very good today.", 'pos'),
("I feel amazing!", 'pos'),
('Gary is a friend of mine.', 'pos'),
("I can't believe I'm doing this.", 'pos'),
("i went to see brokeback mountain, which is beautiful(",'pos'),
("and i love brokeback mountain too: ]",'pos')
]

print("Accuracy: {0}".format(cl.accuracy(test)))

from nltk.corpus import movie_reviews

reviews = [(list(movie_reviews.words(fileid)), category)
for category in movie_reviews.categories()
for fileid in movie_reviews.fileids(category)]
new_train, new_test = reviews[0:100], reviews[101:200]

cl.update(new_train)
accuracy = cl.accuracy(test + new_test)
print("Accuracy: {0}".format(accuracy))

# Show 5 most informative features
cl.show_informative_features(4)

输出如下:

Accuracy: 0.973913043478 
Most Informative Features 
contains(awesome) = True         pos : neg    =     51.9 : 1.0 
contains(with) = True            neg : pos    =     49.1 : 1.0 
contains(for) = True             neg : pos    =     48.6 : 1.0 
contains(on) = True              neg : pos    =     45.2 : 1.0 

首先,训练集有 250 个样本,准确率为0.813,后来它又增加了 100 个电影评论样本。准确率上升到0.974。因此,我们尝试使用不同的测试样本,并绘制样本大小与准确度的关系图,如下图所示:

The Naïve Bayes classifier using TextBlob

用词云看积极情绪

单词云给予在任何给定文本中更频繁出现的单词更大的突出。它们也被称为标签云或加权词。从出现的次数来看,一个单词力量的重要性在视觉上映射到它的外观大小。换句话说,在可视化中出现最多的单词是在文本中出现最多的单词。

除了以形状和颜色显示单词的出现之外,单词云在社交媒体和营销方面还有以下几个有用的应用:

  • 企业可以了解他们的客户,以及他们如何看待自己的产品。一些组织使用了一种非常有创意的方式,让他们的粉丝或追随者发表他们对自己品牌的看法,将所有这些话带到一个词云,以了解他们对产品品牌最常见的印象是什么。
  • 通过识别一个在网上很受欢迎的品牌来寻找了解竞争对手的方法。从他们的内容中创建一个词云,以更好地理解哪些词和主题吸引了产品目标市场。

为了创建单词云,可以编写 Python 代码或使用已经存在的东西。NYU 数据科学中心的安德烈亚斯·穆勒用 Python 创建了一个单词云。这非常简单易用。RemachineScript.ttf字体文件可从http://www . font s101 . com/fonts/view/Script/63827/remmachine _ Script下载。

STOPWORDS由极其常见的单词组成,例如aantheiswasatin等等。以下代码使用STOPWORDS列表创建一个单词云,以便忽略它们:

from wordcloud import WordCloud, STOPWORDS
import matplotlib.pyplot as plt
from os import path

d = path.dirname("__file__")
text = open(path.join(d, '/Users/MacBook/kirthi/results.txt')).read()

wordcloud = WordCloud(
    font_path='/Users/MacBook/kirthi/RemachineScript.ttf',
    stopwords=STOPWORDS,
    background_color='#222222',
    width=1000,
    height=800).generate(text)

为了绘制这个,首先设置图形大小,并使用imshow()将文字云显示为图像。

# Open a plot of the generated image.
plt.figure(figsize=(13,13))

plt.imshow(wordcloud)
plt.axis("off")

plt.show()

综上所述,我们首先从TextBlob例子中提取情感,并假设提取的结果在results.txt中。然后,我们将使用这些单词将数据可视化为带有matplotlib包的单词云。

wordcloud的结果如下图所示:

Viewing positive sentiments using word clouds

k 近邻

k 最近邻 ( k-NN )分类是最容易理解的分类方法之一(尤其是在对数据分布的先验知识很少或没有的情况下)。k-最近邻分类有一种方法可以存储所有已知案例,并基于相似性度量(例如,欧几里德距离函数)对新案例进行分类。k-NN 算法因其简单性而在统计估计和模式识别中受到欢迎。

对于1-最近邻 ( 1-NN ),一个特定点的标签被设置为最近的训练点。当将其扩展为更高的 k 值时,测试点的标签是由最近的 k 训练点测量的。k-NN 算法被认为是一种懒惰的学习算法,因为优化是在本地完成的,并且计算被延迟到分类。

这种方法有优点也有缺点。优点是精度高,对异常值不敏感,对数据没有假设。k-NN 的缺点是计算量大,需要大量内存。

可以使用以下距离度量之一:

k-nearest neighbors

让我们考虑一个例子,给我们一大筐水果,里面只有苹果、香蕉和梨。我们将假设苹果是红苹果,而不是绿苹果。有一个特征可以区分这些水果:颜色。苹果是红色的,香蕉是黄色的,梨是绿色的。这些水果也可以用它们各自的重量来描述。以下假设是为了说明这个例子:

形状特征分类如下:

  • 对于苹果来说,形状值在 1 到 3 之间,而重量在 6 到 7 盎司之间
  • 对于梨来说,形状值在 2 到 4 之间,而重量在 5 到 6 盎司之间
  • 对于香蕉来说,形状值在 3 到 5 之间,而重量在 7 到 9 盎司之间

我们有关于篮子里水果的数据如下:

k-nearest neighbors

如果我们有一个已知重量和颜色类别的未标记水果,那么应用 k-最近邻方法(带有任何距离公式)将最有可能找到最近的 k 邻居(如果它们是绿色、红色或黄色,则未标记的水果最有可能分别是梨、苹果或香蕉)。下面的代码演示了使用水果形状和重量的 k 最近邻算法:

import csv
import matplotlib.patches as mpatches
import matplotlib.pyplot as plt

count=0
x=[]
y=[]
z=[]

with open('/Users/myhome/fruits_data.csv', 'r') as csvf:
  reader = csv.reader(csvf, delimiter=',')
  for row in reader:
    if count > 0:
      x.append(row[0])
      y.append(row[1])
      if ( row[2] == 'Apple' ): z.append('r')
      elif ( row[2] == 'Pear' ): z.append('g')
      else: z.append('y')
    count += 1

plt.figure(figsize=(11,11))

recs=[]
classes=['Apples', 'Pear', 'Bananas']
class_colours = ['r','g','y']
plt.title("Apples, Bananas and Pear by Weight and Shape", fontsize=18)

plt.xlabel("Shape category number", fontsize=14)
plt.ylabel("Weight in ounces", fontsize=14)

plt.scatter(x,y,s=600,c=z)

k-nearest neighbors

让我们挑选四个未标记的水果,它们的 x 和 y 值分别为 A(3.5,6.2)B(2.75,6.2)C(2.9,7.6)D(2.4,7.2) ,代码如下:

from math import pow, sqrt
dist=[]
def determineFruit(xv, yv, threshold_radius):
  for i in range(1,len(x)):
    xdif=pow(float(x[i])-xv, 2)
    ydif=pow(float(y[i])-yv, 2)
    sqrtdist = sqrt(xdif+ydif))
    if ( xdif < threshold_radius and 
         ydif < thresholdradius and sqrtdist < threshold_radius):
      dist.append(sqrtdist)
    else:
      dist.append(99)
  pear_count=0
  apple_count=0
  banana_count=0
  for i in range(1,len(dist)):
      if dist[i] < threshold_radius:
        if z[i] == 'g': pear_count += 1
        if z[i] == 'r': apple_count += 1
        if z[i] == 'y': banana_count += 1
  if ( apple_count >= pear_count and apple_count >= banana_count ):
    return "apple"
  elif ( pear_count >= apple_count and pear_count >= banana_count):
    return "pear"
  elif ( banana_count >= apple_count and banana_count >= pear_count):
    return "banana"

dist=[]
determine = determineFruit(3.5,6.2, 1)
print determine

'pear'

逻辑回归

正如我们之前看到的一样,线性回归的一个问题是它往往会使数据不足。这给了我们无偏估计量的最小均方误差。使用欠信息技术模型,我们不会得到最好的预测。有一些方法可以通过给我们的估计量增加一些偏差来减少均方差。

逻辑回归是为有真或假反应的数据拟合模型的方法之一。线性回归不能直接预测所有的概率,但逻辑回归可以。此外,与朴素贝叶斯的结果相比,预测概率可以更好地校准。

对于本次讨论,通过保持我们对二进制响应的关注,我们可以将1的值设置为true,将0的值设置为false。逻辑回归模型假设输入变量可以通过对数反函数进行缩放;因此,另一种看待这个问题的方法是,观测到的 y 值的对数可以表示为 n 输入变量与 x 的线性组合,如下式所示:

Logistic regression

由于对数函数的反函数是指数函数,右侧的表达式看起来像是 x 变量线性组合的 sigmoid 版本。这意味着分母永远不可能是 1 (除非 z0 )。因此 P(x) 的值严格大于 0 ,小于 1 ,如下代码所示:

import matplotlib.pyplot as plt
import matplotlib
import random, math
import numpy as np
import scipy, scipy.stats
import pandas as pd

x = np.linspace(-10,10,100)
y1 = 1.0 / (1.0+np.exp(-x))
y2 = 1.0 / (1.0+np.exp(-x/2))
y3 = 1.0 / (1.0+np.exp(-x/10))

plt.title("Sigmoid Functions vs LineSpace")
plt.plot(x,y1,'r-',lw=2)
plt.plot(x,y2,'g-',lw=2)
plt.plot(x,y3,'b-',lw=2)
plt.xlabel("x")
plt.ylabel("y")
plt.show()

下图显示了标准 sigmoid 函数:

Logistic regression

下面是一个展示快乐悲伤概率的例子。

Logistic regression

Kaggle 主办了所有的机器学习比赛。它通常提供训练和测试数据。不久前根据真实数据预测泰坦尼克号的幸存者在卡格尔号上有争议。titanic_train.csvtitanic_test.csv文件分别用于培训和测试。使用scikit-learn中包含逻辑回归的linear_model包,我们可以看到以下代码是赢得比赛的作者版本的修改版本:

Import numpy as np
import pandas as pd
import sklearn.linear_model as lm
import sklearn.cross_validation as cv
import matplotlib.pyplot as plt

train = pd.read_csv('/Users/myhome/titanic_train.csv')
test = pd.read_csv('/Users/myhome/titanic_test.csv')
train[train.columns[[2,4,5,1]]].head()

data = train[['Sex', 'Age', 'Pclass', 'Survived']].copy()
data['Sex'] = data['Sex'] == 'female'
data = data.dropna()

data_np = data.astype(np.int32).values
X = data_np[:,:-1]
y = data_np[:,-1]

female = X[:,0] == 1
survived = y == 1

# This vector contains the age of the passengers.
age = X[:,1]
# We compute a few histograms.
bins_ = np.arange(0, 121, 5)
S = {'male': np.histogram(age[survived & ~female], 
                          bins=bins_)[0],
     'female': np.histogram(age[survived & female], 
                            bins=bins_)[0]}
D = {'male': np.histogram(age[~survived & ~female], 
                          bins=bins_)[0],
     'female': np.histogram(age[~survived & female], 
                            bins=bins_)[0]}
bins = bins_[:-1]
plt.figure(figsize=(15,8))
for i, sex, color in zip((0, 1),('male', 'female'), ('#3345d0', '#cc3dc0')):
    plt.subplot(121 + i)
    plt.bar(bins, S[sex], bottom=D[sex], color=color,
            width=5, label='Survived')
    plt.bar(bins, D[sex], color='#aaaaff', width=5, label='Died', alpha=0.4)
    plt.xlim(0, 80)
    plt.grid(None)

    plt.title(sex + " Survived")
    plt.xlabel("Age (years)")
    plt.legend()

(X_train, X_test, y_train, y_test) = cv.train_test_split(X, y, test_size=.05)
print X_train, y_train

# Logistic Regression from linear_model
logreg = lm.LogisticRegression();
logreg.fit(X_train, y_train)
y_predicted = logreg.predict(X_test)

plt.figure(figsize=(15,8));
plt.imshow(np.vstack((y_test, y_predicted)),
           interpolation='none', cmap='bone');
plt.xticks([]); plt.yticks([]);
plt.title(("Actual and predicted survival outcomes on the test set"))

以下是显示泰坦尼克号男女幸存者的线性回归图:

Logistic regression

我们已经看到scikit-learn有很好的机器学习功能集合。它们还附带了一些标准数据集,例如,虹膜数据集和数字数据集用于分类,波士顿房价数据集用于回归。机器学习是关于学习数据的属性并将这些属性应用到新的数据集。

支持向量机

支持向量机 ( SVM )是可应用于回归或分类的监督学习方法。这些学习方法是非线性模型的一种扩展,它在经验上提供了良好的性能,并在许多应用中取得了成功,如生物信息学、文本、图像识别等。这些方法计算量小,易于实现,但容易出现拟合不足,精度可能较低。

让我们了解一下 SVM 的目标。这里的目标是在 x 和 y 之间映射或找到一个模式,我们希望从xT4→T6】y(xT10】ϵT12】x 和yt16】ϵt18】y 执行映射。在这里, x 可以是对象,而 y 可以是标签。另一个简单的例子是 X 是 n 维实值空间,而 y 是一组 -1,1

SVM 的一个经典例子是,当给出一只老虎和一个人的两张照片时, X 成为像素图像的集合,而 Y 成为回答问题的标签,即“这是老虎还是人?”当一张未知的图片出现时。下面是字符识别问题的另一个例子:

Support vector machines

网上已经有很多 SVM 的例子,但在这里,我们将展示如何使用scikit-learn ( sklearn)将可视化方法应用于包括 SVM 在内的各种机器学习算法。在sklearn中,sklearn.svm套装包括以下 SVR 车型:

import numpy as np
from sklearn.svm import SVR
import matplotlib.pyplot as plt

X = np.sort(5 * np.random.rand(40, 1), axis=0)
y = (np.cos(X)+np.sin(X)).ravel()
y[::5] += 3 * (0.5 - np.random.rand(8))

svr_rbfmodel = SVR(kernel='rbf', C=1e3, gamma=0.1)
svr_linear = SVR(kernel='linear', C=1e3)
svr_polynom = SVR(kernel='poly', C=1e3, degree=2)
y_rbfmodel = svr_rbfmodel.fit(X, y).predict(X)
y_linear = svr_linear.fit(X, y).predict(X)
y_polynom = svr_polynom.fit(X, y).predict(X)

plt.figure(figsize=(11,11))
plt.scatter(X, y, c='k', label='data')
plt.hold('on')
plt.plot(X, y_rbfmodel, c='g', label='RBF model')
plt.plot(X, y_linear, c='r', label='Linear model')
plt.plot(X, y_polynom, c='b', label='Polynomial model')
plt.xlabel('data')
plt.ylabel('target')
plt.title('Support Vector Regression')
plt.legend()
plt.show()

Support vector machines

主成分分析

主成分分析 ( 主成分分析)使用简单的重排和旋转变换来变换未标记数据的属性。看着没有任何意义的数据,可以想办法这样降维。例如,当特定数据集在与轴成特定角度运行时看起来类似于椭圆,而在另一个变换表示中沿着 x 轴移动,并且明显没有沿着 y 轴变化的迹象,则可以忽略这一点。

k-means 聚类适用于未标记数据的聚类。有时,可以使用主成分分析将数据投影到更低的维度,然后将其他方法(如 k 均值)应用到更小、更小的数据空间。

然而,仔细地执行降维是非常重要的,因为任何降维都可能导致信息的丢失,并且算法在丢弃噪声的同时保留数据的有用部分是至关重要的。在这里,我们将从至少两个角度激励主成分分析,并解释为什么保持最大可变性是有意义的:

  • 相关性和冗余性
  • 形象化

假设我们收集了一个校园里学生的数据,包括性别、身高、体重、看电视时间、运动时间、学习时间、平均绩点等等。在使用这些维度对这些学生进行调查时,我们发现身高和体重的相关性产生了一个有趣的理论(通常,学生越高,由于骨骼重量而产生的重量越多,反之亦然)。在更大的人群中可能不是这种情况(更重的体重不一定意味着更高)。这种相关性也可以可视化如下:

Principal component analysis

import matplotlib.pyplot as plt
import csv

gender=[]
x=[]
y=[]
with open('/Users/kvenkatr/height_weight.csv', 'r') as csvf:
  reader = csv.reader(csvf, delimiter=',')
  count=0
  for row in reader:
    if count > 0:
        if row[0] == "f": gender.append(0)
        else:  gender.append(1)
        height = float(row[1])
        weight = float(row[2])
        x.append(height)
        y.append(weight)
    count += 1

plt.figure(figsize=(11,11))
plt.scatter(y,x,c=gender,s=300)
plt.grid(True)
plt.xlabel('Weight', fontsize=18)
plt.ylabel('Height', fontsize=18)
plt.title("Height vs Weight (College Students)", fontsize=20)
plt.legend()

plt.show()

再次使用sklearn``preprocessing``datasetsdecomposition包,您可以编写如下简单的可视化代码:

from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt

data = load_iris()
X = data.data

# convert features in column 1 from cm to inches
X[:,0] /= 2.54
# convert features in column 2 from cm to meters
X[:,1] /= 100
from sklearn.decomposition import PCA

def scikit_pca(X):

    # Standardize
    X_std = StandardScaler().fit_transform(X)

    # PCA
    sklearn_pca = PCA(n_components=2)
    X_transf = sklearn_pca.fit_transform(X_std)

    # Plot the data
    plt.figure(figsize=(11,11))
    plt.scatter(X_transf[:,0], X_transf[:,1], s=600, color='#8383c4', alpha=0.56)
    plt.title('PCA via scikit-learn (using SVD)', fontsize=20)
    plt.xlabel('Petal Width', fontsize=15)
    plt.ylabel('Sepal Length', fontsize=15)
    plt.show()

scikit_pca(X)

该图显示了使用scikit-learn包的主成分分析:

Principal component analysis

安装 sci kit-学习

以下命令将帮助安装scikit-learn包:

$ conda install scikit-learn
Fetching package metadata: ....
Solving package specifications: .
Package plan for installation in environment /Users/myhomedir/anaconda:

The following packages will be downloaded:

 package                    |            build
 ---------------------------|-----------------
 nose-1.3.7                 |           py27_0         194 KB
 setuptools-18.0.1          |           py27_0         341 KB
 pip-7.1.0                  |           py27_0         1.4 MB
 scikit-learn-0.16.1        |       np19py27_0         3.3 MB
 ------------------------------------------------------------
 Total:         5.2 MB

The following packages will be UPDATED:

 nose:         1.3.4-py27_1      --> 1.3.7-py27_0 
 pip:          7.0.3-py27_0      --> 7.1.0-py27_0 
 scikit-learn: 0.15.2-np19py27_0 --> 0.16.1-np19py27_0
 setuptools:   17.1.1-py27_0     --> 18.0.1-py27_0 

Proceed ([y]/n)? y
Fetching packages ...

对于蟒蛇来说,由于命令行界面都是通过conda进行的,所以可以使用conda进行安装。在其他方面,默认情况下,人们总是试图使用pip install。但是,无论如何,您都应该查看安装文档。由于所有的scikit-learn套餐都很受欢迎,并且已经存在了一段时间,所以没有太大的变化。现在,在下一节中,我们将探索 k-means 聚类来结束这一章。

k-均值聚类

k-means 聚类 起源于信号处理,是数据挖掘中一种流行的方法。k-means 聚类的主要目的是找到数据集的一些 m 点,这些点可以最好地表示数据集中一些 m 区域的中心。

k-means 聚类也称为分区聚类。这意味着在启动任何群集过程之前,需要指定群集的数量。您可以定义一个目标函数,该函数使用数据点与其最近的聚类质心之间的欧氏距离之和。人们可以遵循一个系统的过程,通过找到一组全新的可以迭代降低目标函数值的聚类中心,迭代地最小化这个目标函数。

k-means 聚类是聚类分析中一种流行的方法。它不需要任何假设。这意味着当给定一个数据集并且预定数量的聚类被标记为 k 时,并且当应用 k-means 算法时,它最小化了距离的平方和误差。

算法很容易理解,如下所示:

  • 给出了一组 n(x,y) 和一组 k 质心
  • 对于每个 (x,y) ,找到最接近该点的质心(这决定了 (x,y) 所属的星团)
  • 在每个聚类中,找到中间值,并将其设置为该聚类的质心,然后重复该过程

让我们来看一个简单的例子(这可以应用于大量的点集合),使用来自sklearn.cluster包的 k-means。这个例子表明,用最少的代码,您可以使用scikit-learn库完成 k-means 聚类:

import matplotlib.pyplot as plt

from sklearn.cluster import KMeans

import csv

x=[]
y=[]

with open('/Users/myhomedir/cluster_input.csv', 'r') as csvf:
  reader = csv.reader(csvf, delimiter=',')
    for row in reader:
      x.append(float(row[0]))
      y.append(float(row[1]))

data=[]
for i in range(0,120):
  data.append([x[i],y[i]])

plt.figure(figsize=(10,10))

plt.xlim(0,12)
plt.ylim(0,12)

plt.xlabel("X values",fontsize=14)
plt.ylabel("Y values", fontsize=14)

plt.title("Before Clustering ", fontsize=20)

plt.plot(x, y, 'k.', color='#0080ff', markersize=35, alpha=0.6)

kmeans = KMeans(init='k-means++', n_clusters=3, n_init=10)
kmeans.fit(data)

plt.figure(figsize=(10,10))

plt.xlabel("X values",fontsize=14)
plt.ylabel("Y values", fontsize=14)

plt.title("After K-Means Clustering (from scikit-learn)", fontsize=20)

plt.plot(x, y, 'k.', color='#ffaaaa', markersize=45, alpha=0.6)

# Plot the centroids as a blue X
centroids = kmeans.cluster_centers_

plt.scatter(centroids[:, 0], centroids[:, 1], marker='x', s=200,
  linewidths=3, color='b', zorder=10)

plt.show()

聚类前绘制数据如下所示:

k-means clustering

k-means clustering

在这个例子中,如果我们为五个集群设置 k=5 ,那么这个集群保持不变,但是另外两个集群被分成两个以获得五个集群,如下图所示:

k-means clustering

总结

本章举例说明了流行的机器学习算法。简单介绍了线性回归和逻辑回归。使用线性回归的大学录取标准和逻辑回归的泰坦尼克号幸存者,本章还说明了如何使用这些回归方法的statsmodels.formula.apipandassklearn.linear_model软件包。在这两个例子中,matplotlib被用于可视化方法。

你了解了决策树。使用体育示例(高尔夫和网球),我们使用sklearnpydot包查看决策树。进一步,我们讨论了贝叶斯定理和朴素贝叶斯分类器。使用TextBlob包和来自nltk语料库的电影评论数据,我们查看了使用wordcloud包的单词云的示例。

你学习了 k 近邻算法。在这里,我们看了一个根据重量和形状对水果进行分类的例子,通过颜色直观地将水果分开。

我们还以最简单的形式查看了 SVM 的插图,并举例说明了如何从sklearn.svm包生成数据,以及如何使用matplotlib库绘制结果。您学习了主成分分析,如何确定冗余,以及消除一些变量。我们将 iris 示例与sklearn.preprocesing库一起使用,以了解如何可视化结果。最后,我们用一个使用sklearn.cluster的随机点的例子来看 k-means 聚类,因为这是实现聚类的最简单方法(用最少的代码)。在下一章中,我们将讨论生物信息学、遗传学和网络的各种例子。

七、生物信息学、遗传学和网络模型

科学应用有多个黑盒,这些黑盒里的东西很复杂,通常被认为是神奇的。然而,它们都遵循一套系统的协议。这些协议在研究界众所周知。例如,网络模型被广泛用于表示复杂的结构化数据,如蛋白质网络、分子遗传学和化学结构。研究界另一个有趣的领域是生物信息学。这是一个不断发展的领域,最近在研究中取得了相当大的突破。

在生物学领域,有许多不同的复杂结构,如 DNA 序列、蛋白质结构等。为了进行比较,让我们来看看这些结构中的一些未知元素。有一个能直观显示它们的模型是有帮助的。类似地,在图论或网络的任何应用中,能够可视化复杂的图结构本质上是有益的。

在本章的后面,我们将讨论一些有趣的例子,例如社交网络、现实生活中的有向图例子、适合这些问题的数据结构以及网络分析。为了演示示例,这里我们将使用特定的库,如 metaseq、NetwX、matplotlib、Biopython 和 ETE 工具包,涵盖以下主题:

  • 有向图和多重图
  • 图的聚类系数
  • 社交网络分析
  • 平面图测试和有向无环图测试
  • 最大流量和最小流量
  • 遗传编程示例
  • 随机块模型和随机图

有向图和多重图

首先,我们将回顾有向图和多重图。稍后,我们将在 Python 中找出生成它们的选项。另外,我们将看一个例子,其中您可能需要有向图。在我们从概念上描述图和有向图之前,让我们看一下理解何时可以使用图和有向图的不同方法。

在大学校园区域内相互连接的计算机可以被认为是一个连通图,其中连接中的每台计算机都被视为一个节点或顶点。连通路径是一条边,在某些情况下,如果只有单向连接,那么它就是一个有向图。例如,一个非常受限制的联邦网络不允许任何外部连接进入,但很可能不会反过来限制。以下是显示地点之间距离的简单图表:

Directed graphs and multigraphs

在前面的例子中,带有城市标签 AF 的图是有向图,右侧的另一个图是无向图。在有向图中,如果箭头指向两个方向,就有一条路可以走两个方向,而在无向图中,两条路都是假设的。如果我们用某种数据结构来表示这些图表,那会是什么呢?此外,如果我们要绘制这些类型的图表,我们使用哪些库以及如何实现?

存储图形数据

图形数据通常表示为邻接矩阵,除非是稀疏的。邻接矩阵是具有 V 2 行的矩阵,假设图具有一个 V 顶点或一个节点。例如,对于上图中显示的两个图形,邻接矩阵类似于下表:

|   |

A

|

B

|

C

|

D

|

E

|

F

|
| --- | --- | --- | --- | --- | --- | --- |
| A | Zero | Twenty-five | Twenty-six |   |   |   |
| B |   | Zero | eighty-five | five | Ten |   |
| C | Twenty-six | eighty-five | Zero |   |   | Ten |
| D |   |   |   | Zero |   | Eleven |
| E |   |   |   | nine | Zero | Eighty-eight |
| F |   |   |   | Eleven | Eighty-eight | Zero |

|   |

芝加哥

|

波士顿

|

纽约

|

洗 DC

|

迈阿密

|

达拉斯

|
| --- | --- | --- | --- | --- | --- | --- |
| 芝加哥 | Zero | One thousand six hundred and thirteen |   | One thousand one hundred and forty-five |   |   |
| 波士顿 | One thousand six hundred and thirteen | Zero | Three hundred and thirty-eight | Seven hundred and twenty-five |   |   |
| 纽约 |   | Three hundred and thirty-eight | Zero | Three hundred and eighty-three | Two thousand one hundred and forty-five |   |
| 洗 DC | One thousand one hundred and forty-five | Seven hundred and twenty-five | Three hundred and eighty-three | Zero | One thousand seven hundred and nine | Two thousand one hundred and thirteen |
| 迈阿密 |   |   | Two thousand one hundred and forty-five | One thousand seven hundred and nine | Zero | Two thousand one hundred and sixty-one |
| 达拉斯 |   |   |   | Two thousand one hundred and thirteen | Two thousand one hundred and sixty-one | Zero |

对于无向图,通过对称性,使用一半的存储就足够了(不需要存储从 ABBA 的所有信息)。空白条目显示没有足够的距离数据。如果矩阵是稀疏的,其中大多数条目没有被填充,那么您可以将其存储为列表。幸运的是,scipy中有处理稀疏矩阵的便捷方法。以下代码仅适用于上图所示的第一个图形:

import scipy.sparse as sparse

matrixA = sparse.lil_matrix((6,6))

matrixA = sparse.lil_matrix( [[0,25,26,0,0,0], [0,0,85,5,10,0],
   [26,85,0,0,0,10], [0,0,0,0,0,11],[0,0,0,9,0,88],[0,0,0,11,88,0]])
print matrixA   
(0, 1)  25   
(0, 2)  26   
(1, 2)  85   
(1, 3)  5   
(1, 4)  10   
(2, 0)  26   
(2, 1)  85   
(2, 5)  10
(3, 5)  11   
(4, 3)  9   
(4, 5)  88   
(5, 3)  11   
(5, 4)  88

显示图形

前面的示例仅显示了如何使用scipy库(尤其是scipy.sparse包)来表示图形。但是,在下一节中,我们将看到如何显示这些图表。虽然有许多 Python 包可供选择来显示图形,但其中最受欢迎的前三个选择是NetworkXigraph(来自igraph.org)和graph-tool。让我们看一个使用这三个包的图形显示的例子。

igraph

最初,igraph是为 R 用户准备的,但是后来增加了 Python 版本。对于较小的图形,可以添加顶点和边并非常容易地显示它们,但在大多数情况下,图形并不小;因此,igraph提供了方便地从文件中读取图形数据并显示的功能。

目前,igraph提供几种格式,如dimacsdledgelistgramlgraphdbgmllglncolpajek。GraphML 是一种基于 XML 的文件格式,可用于大型图形,而 NCOL 图形格式适用于带有加权边列表的大型图形。LGL 图格式也可以用于带有加权边的大型图布局。其他大多数使用简单的文本格式。igraph只完全支持 DL 文件格式,其他所有的igraph只支持部分文件格式。

与许多其他 Python 包类似,igraph的好处在于它提供了非常方便的方法来配置和显示图形,并将它们存储为 SVG 格式,以便它们可以嵌入到 HTML 文件中。

我们来看看一个涉及pajek格式的例子(关于pajek的更多细节,可以参考http://vlado.fmf.uni-lj.si/pub/networks/pajek/)。还有很多其他参数。其中一些是labelcolorvertexsizeradius用于一些顶点形状。我们将在这里看到两个例子。第一个示例为一个小图形分配了标签和边,而第二个示例从文件中读取图形的数据并显示它。以下示例显示了使用igraph包的标记图形:

from igraph import *

vertices = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J"]

edges = [(0,1),(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,1),
         (1,8),  (8,2),(2,4),(4,9),(9,5),(5,7),(7,0)]

graphStyle = { 'vertex_size': 20}
g = Graph(vertex_attrs={"label": vertices}, edges=edges, directed=True)
g.write_svg("simple_star.svg", width=500, height=300, **graphStyle)

星形图中有 10 个顶点,形成五个三角形和一个五边形。还有,有 15 条边,因为五个三角形完成了边的集合。这是一个非常简单的图,其中每条边由从零开始的相关顶点数定义。以下标记图形是前面 Python 示例的结果:

igraph

第二个示例不仅说明了如何从文件中读取图形数据,还说明了如何以 SVG 格式保存绘图,以便您可以将 SVG 数据嵌入到 HTML 中:

from igraph import read  

g=read("ragusa.net",format="pajek")  

g.vs["color"]="#3d679d" 
g.es["color"]="red" 

graphStyle={ 'vertex_size': 12, 'margin': 6} 
#graphStyle["layout"]=g.layout("fr")  # optional

g.write_svg("ragusa_graph.svg", width=600, height=600,**graphStyle)

使用igraph的读取功能读取pajek格式文件。设置好边和顶点颜色,就可以生成图形的 SVG 格式。igraph提供了几种不同的布局,你可以尝试一下。下图显示了通过从文件中读取图形数据使用igraph包创建的图形:

igraph

pajek格式的图形数据是从pajek网络网站(http://vlado.fmf.uni-lj.si/pub/networks/pajek/data/gphs.htm)的一个名为Rgausa16.net的文件中获得的。一旦从这里下载了一个数据文件,您就可以以类似的方式使用它并显示图表,如上图所示。如果我们使用tinamatr.net数据并将设置为圆形布局,那么图形将以圆形布局出现,如下代码所示:

graphStyle["layout"]=g.layout("circle")

网络 x

这个 Python 包被称为NetworkX的原因之一是因为它是一个网络和图形分析的库。从寻找从源节点或顶点到目的节点或顶点的最短路径,寻找度分布来绘制与结点相似的节点,以及寻找图的聚类系数,有几种方法可以进行图分析。

对图形的研究已经有一段时间了,并且适用于神经生物学、化学、社会网络分析、页面排名以及当今许多类似的有趣领域。社交网络在加入相似的附属成员的意义上是真正分类的,而生物网络则相反。换句话说,脸书用户或院士(他们是合著者)之间的友谊可以很容易地通过图形可视化。Python 包为用户提供了许多选择。通常,用户选择其中的几个来结合他们各自的最佳功能。

NetworkX提供图形构建和分析功能。您可以以标准和非标准数据格式读写网络数据,生成图形网络,分析它们的结构,并构建几个模型。下面的 Python 代码展示了如何使用matplotlib创建有向图:

import matplotlib.pyplot as plt
import pylab
from pylab import rcParams

import networkx as nx
import numpy as np

# set the graph display size as 10 by 10 inches
rcParams['figure.figsize'] = 10, 10

G = nx.DiGraph()

# Add the edges and weights
G.add_edges_from([('K', 'I'),('R','T'),('V','T')], weight=3)
G.add_edges_from([('T','K'),('T','H'),('I','T'),('T','H')], weight=4)
G.add_edges_from([('I','R'),('H','N')], weight=5)
G.add_edges_from([('R','N')], weight=6)

# these values to determine node colors
val_map = {'K': 1.5, 'I': 0.9, 'R': 0.6, 'T': 0.2}
values = [val_map.get(node, 1.0) for node in G.nodes()]

edge_labels=dict([((u,v,),d['weight'])
                 for u,v,d in G.edges(data=True)])

#set edge colors
red_edges = [('R','T'),('T','K')]
edge_colors = ['green' if not edge in red_edges else 'red' for edge in G.edges()]

pos=nx.spring_layout(G)

nx.draw_networkx_edges(G,pos,width=2.0,alpha=0.65)
nx.draw_networkx_edge_labels(G,pos,edge_labels=edge_labels)

nx.draw(G,pos, node_color = values, node_size=1500,
 edge_color=edge_colors, edge_cmap=plt.cm.Reds)

pylab.show()

下图说明了如何使用NetworkX配置图形的边缘权重和视觉美感。在几种显示有向图的方法中,NetworkX采用了不同的方法,在末尾显示粗条,而不是使用箭头符号来确定图形的方向。

NetworkX

当有一项科学研究涉及到表示事物或人的元素集合时,它们之间的关联更好地以图形的形式表示,其中这些元素是顶点或节点。在大多数情况下,中心性直观地标识了非常重要的节点。Python 包(如NetworkX)对于图形分析有许多有用的功能,包括在图形中寻找小团体。对于较小的图形来说,更容易直观地检查复杂的细节,但是对于较大的图形来说,人们会想要识别一种行为模式,例如孤立的集群组。

通常,节点和边的标签取决于您试图显示为图形的内容。例如,蛋白质相互作用可以显示为图表。一个更复杂的例子是序列空间图,其中一个图节点代表一个蛋白质序列,而一条边代表一个单一的脱氧核糖核酸突变。科学家更容易放大这些图像来看到图案,如下图所示。这个例子没有使用 Python,而是使用交互式编程来缩放和查看复杂的细节。

NetworkX

上图来自http://publications.csail.mit.edu/

有时,您可能想要在地图上突出显示不同的路线。例如,如果正在显示一张路线图,并且您必须在这张地图上显示奥运自行车队今年将遵循的路线,您可以执行类似于以下代码的操作:

import networkx as nx
from pylab import rcParams

# set the graph display size as 10 by 10 inches
rcParams['figure.figsize'] = 10, 10

def genRouteEdges(r):
    return [(r[n],r[n+1]) for n in range(len(r)-1)]

G=nx.Graph(name="python")
graph_routes = [[11,3,4,1,2], [5,6,3,0,1], [2,0,1,3,11,5]]
edges = []
for r in graph_routes:
    route_edges = genRouteEdges(r)
    G.add_nodes_from(r)
    G.add_edges_from(route_edges)
    edges.append(route_edges)

print("Graph has %d nodes with %d edges" %(G.number_of_nodes(),    
G.number_of_edges()))

pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G,pos=pos)
nx.draw_networkx_labels(G,pos=pos)

colors = ['#00bb00', '#4e86cc', 'y']
linewidths = [22,14,10]

for ctr, edgelist in enumerate(edges):
    nx.draw_networkx_edges(G,pos=pos,edgelist=edgelist,
      edge_color = colors[ctr], width=linewidths[ctr])

使用方便的方法从NetworkX开始,对于特定的路线,可以很容易地突出显示不同颜色和线宽的路线,如下图所示:

NetworkX

如上图所示,通过控制路线的高光,您可以在地图上识别不同的路线。

此外,从到度分布到聚类系数的最短路径,NetworkX提供了多种执行图形分析的方法。下面的代码显示了一种查看最短路径的简单方法:

import networkx as nx

g = nx.Graph()
g.add_edge('m','i',weight=0.1)
g.add_edge('i','a',weight=1.5)
g.add_edge('m','a',weight=1.0)
g.add_edge('a','e',weight=0.75)
g.add_edge('e','h',weight=1.5) 
g.add_edge('a','h',weight=2.2)

print nx.shortest_path(g,'i','h')
nx.draw(g)

#printed shortest path as result 
['i', 'a', 'h']

使用NetworkX(特别是读取 GML 格式的数据)的另一个例子是“在悲惨世界小说中的人物的共同出现”,这是我们从位于https://gephi.org/datasets/lesmiserables.gml.zipgephi.org的数据集下载的。

NetworkX

前面的绘图是程序读取《悲惨世界》的人物关联,创建网络图的结果,如下代码所示:

import networkx as nx
from pylab import rcParams
rcParams['figure.figsize'] = 12, 12

G = nx.read_gml('/Users/kvenkatr/Downloads/lesmiserables.gml', relabel=True)
G8= G.copy()
dn = nx.degree(G8)
for n in G8.nodes():
  if dn[n] <= 8:
    G8.remove_node(n)
pos= nx.spring_layout(G8)
nx.draw(G8, node_size=10, edge_color='b', alpha=0.45, font_size=9, pos=pos)
labels = nx.draw_networkx_labels(G8, pos=pos)

图形工具

在三个包、igraphnetworkxgraph-tool中,graph-tool包是最难安装的,尤其是在 Mac OS 上。Graph-tool有许多方便的功能,并且被认为在中心性相关算法方面非常高效。这包括 k 核、PageRank、最小生成树和单源最短路径。对照表可在https://graph-tool.skewed.de/performance查阅。前面提到的包含中心性相关算法的模块是graph_tool.centrality

import graph_tool.all as gtool

gr = gtool.collection.data["polblogs"]
gr = gtool.GraphView(gr, vfilt=gtool.label_largest_component(gr))

cness = gtool.closeness(gr)

gtool.graph_draw(gr, pos=gr.vp["pos"], vertex_fill_color=cness,
               vertex_size=gtool.prop_to_size(cness, mi=5, ma=15),
               vorder=cness, vcmap=matplotlib.cm.gist_heat,
               output="political_closeness.pdf")

Graph-tool

“中心性”一词中的前缀 centra 实际上意味着某个实体(在本文中,这将是一个节点或顶点)是中心的。此外,许多其他实体连接到中央实体。所以,我们可以问一个合理的问题,那就是,使一个顶点变得重要的特征是什么?在graph_tool中心性模块中,提供了九种与中心性相关的算法, PageRank 是其中之一,此外还有贴近度

页面排名

graph_tool.centrality .pagerank()函数生成 v 顶点的页面行。大多数了解谷歌 PageRank 的人都知道这项措施是如何运作的。简而言之,这是一种衡量网页 A 重要程度的方法(根据有多少外部网站 B 依赖于网页 A 以及有多少网页 A 依赖——在图论中它们被称为入度和出度)。除此之外,谷歌还应用了许多其他外部因素来对网页进行排名。在前面的例子中,如果我们用 PageRank 替换查找接近度的行,如下所示:

pagerank = gtool.pagerank(gr)

这将生成一个以 PageRank 为重点的图表。除了中心性度量,还有一个因素叫做图的聚类系数。

图的聚类系数

图中一个节点或顶点的聚类系数取决于相邻节点的接近程度,从而形成一个团(或一个小的完全图),如下图所示:

The clustering coefficient of graphs

有一个众所周知的公式来聚集系数,它看起来相当沉重的数学符号。然而,简单地说,看看下面的等式:

The clustering coefficient of graphs

这包括跟踪每个顶点上的链接,并计算每个顶点上的聚类指数,其中最明显的节点邻居是离该节点只有一条链接的节点。聚类指数计算如下所示:

The clustering coefficient of graphs

The clustering coefficient of graphs

以下代码说明了如何显示《悲惨世界》小说中的人物,以及每个人物是如何与其他人物联系在一起的:

import networkx as nx
from pylab import rcParams
rcParams['figure.figsize'] = 12, 12

G = nx.read_gml('/Users/kvenkatr/Downloads/lesmiserables.gml', relabel=True)
G8= G.copy()

dn = nx.degree(G8)

for n in G8.nodes():
  if dn[n] <= 8:
    G8.remove_node(n)

pos= nx.spring_layout(G8)
nx.draw(G8, node_size=10, edge_color='b', alpha=0.45, font_size=9, pos=pos)
labels = nx.draw_networkx_labels(G8, pos=pos)

def valuegetter(*values):
    if len(values) == 1:
        item = values[0]
        def g(obj):
            return obj[item]
    else:
        def g(obj):
            return tuple(obj[item] for item in values)
    return g

def clustering_coefficient(G,vertex):
    neighbors = G[vertex].keys()
    if len(neighbors) == 1: return -1.0
    links = 0
    for node in neighbors:
        for u in neighbors:
            if u in G[node]: links += 1
    ccoeff=2.0*links/(len(neighbors)*(len(neighbors)-1))
    return links, len(neighbors),ccoeff 

def calculate_centrality(G):
    degc = nx.degree_centrality(G)
    nx.set_node_attributes(G,'degree_cent', degc)
    degc_sorted = sorted(degc.items(), key=valuegetter(1), reverse=True)
    for key, value in degc_sorted[0:10]:
        print "Degree Centrality:", key, value
    return G, degc

print "Valjean", clustering_coefficient(G8,"Valjean")
print "Marius", clustering_coefficient(G8,"Marius")
print "Gavroche", clustering_coefficient(G8,"Gavroche")
print "Babet", clustering_coefficient(G8,"Babet")
print "Eponine", clustering_coefficient(G8,"Eponine")
print "Courfeyrac", clustering_coefficient(G8,"Courfeyrac")
print "Comeferre", clustering_coefficient(G8,"Combeferre")
calculate_centrality(G8)

前面的代码有两个结果;第一部分是打印的文本输出,而第二部分是绘制的网络图,如以下代码和图表所示:

#Text Results printed
Valjean (82, 14, 0.9010989010989011)
Marius (94, 14, 1.032967032967033)
Gavroche (142, 17, 1.0441176470588236)
Babet (60, 9, 1.6666666666666667)
Eponine (36, 9, 1.0)
Courfeyrac (106, 12, 1.606060606060606)
Comeferre (102, 11, 1.8545454545454545)

Degree Centrality: Gavroche 0.708333333333
Degree Centrality: Valjean 0.583333333333
Degree Centrality: Enjolras 0.583333333333
Degree Centrality: Marius 0.583333333333
Degree Centrality: Courfeyrac 0.5
Degree Centrality: Bossuet 0.5
Degree Centrality: Thenardier 0.5
Degree Centrality: Joly 0.458333333333
Degree Centrality: Javert 0.458333333333
Degree Centrality: Feuilly 0.458333333333

图表结果如下所示。

The clustering coefficient of graphs

很明显,到目前为止,我们已经发现comefere恰好具有更大的聚类系数(0.927)。通常,当我们绘制二维大图时,不容易在视觉上看到聚类系数。

社交网络分析

几年前,从社交网络(如领英、脸书或推特)获取数据要简单得多。现在,大多数 API 都有限制。此外,访问方法也有点复杂。首先,必须获得身份验证(甚至更早以前就是这种情况),然后使用访问朋友或连接的方法。我们在这里只选择了 Twitter 来演示对社交网络数据的分析,但是你也可以用类似的方式找到其他社交媒体数据。

为了访问 Twitter 数据,正如我们在前面几章中注意到的(当我们讨论 word clouds 时),您必须获得身份验证密钥才能访问它们的 API。有四个键:CONSUMER_KEYCONSUMER_SECRETACCESS_TOKEN_KEYSACCESS_TOKEN_SECRET。一旦这些凭证通过 Python 验证成功,就可以调用GetFriends()GetFollowers()获取好友和关注者列表。Python 中有许多包可以用来访问 Twitter 数据。因此,使用哪一个非常令人困惑。我们在过去的例子中使用了 tweepy。这里,在下面的代码中,我们将使用Python-Twitter,因为它有方便的模块来获取数据,将其汇总,存储在cPickle中,然后可视化。

import cPickle
import os
import twitter  # https://github.com/ianozsvald/python-twitter

# Usage:
# $ # setup CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN_KEY, ACCESS_TOKEN_SECRET
# as environment variables
# $ python get_data.py  # downloads friend and follower data to ./data

# Errors seen at runtime:
# raise URLError(err)
# urllib2.URLError: <urlopen error [Errno 104] Connection reset by peer>

DATA_DIR = "data"  # storage directory for friend/follower data

# list of screen names that we'll want to analyze
screen_names = [ 'KirthiRaman', 'Lebron' ]

def get_filenames(screen_name):
    """Build the friends and followers filenames"""
    return os.path.join(DATA_DIR, "%s.friends.pickle" % (screen_name)), os.path.join(DATA_DIR, "%s.followers.pickle" % (screen_name))

if __name__ == "__main__":

    # deliberately stripped my keys
    t = twitter.Api(consumer_key='k7atkBNgoGrioMS...',
                  consumer_secret='eBOx1ikHMkFc...',
                  access_token_key='8959...',
                  access_token_secret='O7it0...');

    print t.VerifyCredentials()

    for screen_name in screen_names:
        fr_filename, fo_filename = get_filenames(screen_name)
        print "Checking for:", fr_filename, fo_filename
        if not os.path.exists(fr_filename):
            print "Getting friends for", screen_name
            fr = t.GetFriends(screen_name=screen_name)
            cPickle.dump(fr, open(fr_filename, "w"), protocol=2)
        if not os.path.exists(fo_filename):
            print "Getting followers for", screen_name
            fo = t.GetFollowers(screen_name=screen_name)
            cPickle.dump(fo, open(fo_filename, "w"), protocol=2)

好友和关注者信息被转储到 cPickle。通过运行以下命令(如https://github.com/ianozsvald/python-twitter中所述),您可以运行以下代码:

python get_data.py
python summarise_data.py
python draw_network.py

Analysis of social networks

平面图形测试

平面图是可以在没有任何相交边的平面上绘制的图。为了绘制它们,您必须从一个顶点开始,从一边到另一边绘制,并在绘制继续时跟踪面。根据 Kuratowski 的观点,如果一个图不包含在五个顶点上是完全图的一部分的子图,那么这个图就是平面的。

下面是平面图的一个简单示例:

The planar graph test

欧拉公式连接了许多顶点、边和面。根据欧拉公式,如果在没有任何相交边的平面上画一个有限连通的平面图,如果 v 代表顶点数, e 代表边数, f 代表面数,那么v e+f = 2

除了MayaviNetworkXplanarity之外,还可以使用gamera包来创建和显示图形。但是,gamera只在 Windows 上可用。我们这里有一个简单的例子使用planarityNetworkX:

import planarity 
import networkx as nx 

# complete graph of 8 nodes, K8 
G8=nx.complete_graph(8) 

# K8 is not planar 
print(planarity.is_planar(G8)) 

# Will display false because G8 is not planar subgraph 
K=planarity.kuratowski_subgraph(G8) 

# Will display the edges
print(K.edges())

#Will display the graph
nx.draw(G8)

False
[(0, 4), (0, 5), (0, 7), (2, 4), (2, 5), (2, 7), (3, 5), (3, 6), (3, 7), (4, 6)]

此示例说明了以下八个节点的完整图形不是平面的:

The planar graph test

上图显示,只有八个节点的平面图看起来可能很乱,所以节点越多的图看起来越复杂。

有向无环图测试

先来看看什么是有向无环图( DAG )。有向无环图是有向的图,这意味着从给定顶点 AB 的边将指向特定的方向( A- > B 或 B- > A ),并且是无环的。非循环图是那些不循环的图,这也意味着没有循环(它们不循环)。

什么是 DAG 的好例子?一棵树甚至一棵树。我们都知道它们是什么,因为这本书的前几章已经讨论过了。使用 trie 的一个很好的例子是存储字典中的单词,并有一个拼写检查算法。我们不会对此进行更深入的讨论,但是在可视化的背景下,为了检查一个图是否是非循环的,我们将确定提供方法来测试一个图是否是非循环的 Python 包。

NetworkX有一个方便功能叫做is_directed_acyclic_graph (Graph)。这是一个非循环图的例子;使用这个函数,我们将测试它是否返回 true:

import matplotlib.pyplot as plt
import pylab
from pylab import rcParams

import networkx as nx
import numpy as np

# set the graph display size as 10 by 10 inches
rcParams['figure.figsize'] = 10, 10

G = nx.DiGraph()

# Add the edges and weights
G.add_edges_from([('K', 'I'),('R','T'),('V','T')], weight=3)
G.add_edges_from([('T','K'),('T','H'),('T','H')], weight=4)
# these values to determine node colors
val_map = {'K': 1.5, 'I': 0.9, 'R': 0.6, 'T': 0.2}
values = [val_map.get(node, 1.0) for node in G.nodes()]

edge_labels=dict([((u,v,),d['weight'])
                 for u,v,d in G.edges(data=True)])

#set edge colors
red_edges = [('R','T'),('T','K')]
edge_colors = ['green' if not edge in red_edges else 'red' for edge in G.edges()]

pos=nx.spring_layout(G)

nx.draw_networkx_edges(G,pos,width=2.0,alpha=0.65)
nx.draw_networkx_edge_labels(G,pos,edge_labels=edge_labels)

nx.draw(G,pos, node_color = values, node_size=1500,
 edge_color=edge_colors, edge_cmap=plt.cm.Reds)

pylab.show()
nx.is_directed_acyclic_graph(G) 

True

本例的非循环图如下图所示:

The directed acyclic graph test

最大流量和最小流量

流网络是从源到目的地的有向图,沿着每个边分配容量。正如我们可以将街道地图建模为有向图,以便找到从一个地方到另一个地方的最短路径一样,我们也可以将有向图解释为“流动网络”。流动网络的一些例子是液体流过管道,电流流过电网,以及数据通过通信网络传输。下面是一个示例图形流程图:

Maximum flow and minimum cut

G 图的边预计具有指示该边可以支持多少流量的容量。如果该容量不存在,则假设它具有无限容量。流网最大流量 G 这里是 4

NetworkX 包中,maximum_flow_value(Graph, from, to)函数计算图形的最大流量,如下代码所示:

import networkx as nx
G = nx.DiGraph()
G.add_edge('p','y', capacity=5.0)
G.add_edge('p','s', capacity=4.0)
G.add_edge('y','t', capacity=3.0)
G.add_edge('s','h', capacity=5.0)
G.add_edge('s','o', capacity=4.0)

flow_value = nx.maximum_flow_value(G, 'p', 'o')

print "Flow value", flow_value
nx.draw(G, node_color='#a0cbe2')

Flow value 4.0

前一个代码的图表正在测试maximum_flow_value,该图表的显示如下图所示:

Maximum flow and minimum cut

一个遗传编程的例子

CnvKit 也有可用,但它是 CLI,不太好用。除此之外,由 NCBI 国家卫生研究院的研究人员开发的 PyCogent 是一个有用的工具。然而,它们并不容易使用。我们将使用名为Bio(https://github.com/biopython/biopython/tree/master/Bio)的包和来自 Python 编程的库来进行生物学

一般来说,每个实验、研究项目或研究都有序列作为生物信息学中使用的关键对象。作为一名数学家,我对序列的视觉思考与具有特定模式(如ATAGCATATGCT)的字符串有关。首先,这里有一个简单的例子,显示了序列,气相色谱比和密码子:

from Bio.Seq import Seq 
from Bio.Alphabet import IUPAC 
from Bio.SeqUtils import GC 

def DNACodons(seq):
    end = len(seq) - (len(seq) % 3) – 1
    codons = [seq[i:i+3] for i in range(0, end, 3)]     
    return codons DNACodons(my_seq)
my_seq = Seq('GGTCGATGGGCCTAGCAGCATATCTGAGC', IUPAC.unambiguous_dna) 
print "GC Result==>", GC(my_seq)  

DNACodons(my_seq)
[Seq('GGT', IUPACUnambiguousDNA()),
 Seq('CGA', IUPACUnambiguousDNA()), 
 Seq('TGG', IUPACUnambiguousDNA()), 
 Seq('GCC', IUPACUnambiguousDNA()), 
 Seq('TAG', IUPACUnambiguousDNA()), 
 Seq('CAG', IUPACUnambiguousDNA()), 
 Seq('CAT', IUPACUnambiguousDNA()), 
 Seq('ATC', IUPACUnambiguousDNA()), 
 Seq('TGA', IUPACUnambiguousDNA())]

GC Result==> 58.6206896552

让我们考虑两种分子结构,收集某些原子,并尝试用它们的φψ角度绘制它们的位置。允许的分子结构是脱氧核糖核酸、核糖核酸和蛋白质。使用PythonForBiology库中的ModellingMaths模块,我们将尝试并排绘制这些结构:

A genetic programming example

这两个图使用了来自两个文件的数据:testTransform.pdb1A12.pub。这里面包含了人类染色体凝聚 ( RCC1 )的调节器,如下代码所示:

# bio_1.py
#
import matplotlib.pyplot as plt
from phipsi import getPhiPsi
from Modelling import getStructuresFromFile

def genPhiPsi(fileName):
  struc = getStructuresFromFile(fileName)[0]

  phiList = []
  psiList = []
  for chain in struc.chains:
    for residue in chain.residues[1:-1]:
      phi, psi = getPhiPsi(residue)
      phiList.append(phi)
      psiList.append(psi)

  return phiList, psiList

if __name__ == '__main__':

  phiList = []
  psiList = []
  phiList, psiList = genPhiPsi('examples/testTransform.pdb')

  phiList2 = []
  psiList2 = []
  phiList2, psiList2 = genPhiPsi('examples/1A12.pdb')

  plt.figure(figsize=(12,9))
  f, (ax1, ax2) = plt.subplots(1, 2, sharey=True, figsize=(12,9))

  ax1.scatter(phiList, psiList, s=90, alpha=0.65)
  ax1.axis([-160,160,-180,180])
  ax1.set_title('Ramachandran Plot for Two Structures')
  ax2.scatter(phiList2, psiList2, s=60, alpha=0.65, color='r')
  plt.show()

在这个例子中使用的库将与一个名为PythonForBiology.zip的文件中的代码例子一起提供。假设您已经安装了numpymatplotlib,您可以提取它并通过命令行运行这段代码。

随机区块模型

在前面的章中,我们已经讨论了使用蒙特卡罗模拟的随机模型。到目前为止,我们一直在讨论图表和网络,所以纯粹从这个角度来看,社区结构也可以被视为图表。在这样的图中,节点通常作为密集连接的子图聚集在一起。通常,两个这样的节点之间的边的概率是该节点所属的簇的函数。

这种网络划分的一种流行选择是随机块模型。随机块模型的简单定义以标量 n 为特征。这表示组的数量或集群的数量,以及显示节点及其连接的矩阵。更严谨的数学定义,可以参考统计学书籍。

在支持随机模型的几个 Python 包中,PyMC 提供了马尔可夫链蒙特卡罗 ( MCMC )和概率模型的三个构建块,如随机、确定性和潜力。除了 PyMC,还有另一个有趣的包叫做 StochPy 用于随机建模。 SSA 模块特别提供了方便的方法(http://stochpy.sourceforge.net/examples.html)。第一个示例使用具有正态分布的pymc来显示复合图,另一个示例使用 MCMC 模型,如以下代码所示:

import pymc as mc

from pylab import rcParams

# set the graph display size as 10 by 10 inches
rcParams['figure.figsize'] = 12, 12
z = -1.

#instead of 0 and 1, some unknown mu and std goes here:
X = mc.Normal( "x", 0, 1, value = -3\. ) 

#Here below, one can place unknowns here in place of 1, 0.4
@mc.potential
def Y(x=X, z=z): 
  return mc.lognormal_like( z-x, 1, 0.4,  )

mcmc = mc.MCMC([X])
mcmc.sample(10000,500)
mc.Matplot.plot(mcmc)

此处显示的示例说明了如何用很少的代码行显示复杂的模型:

Stochastic block models

PyMC 中有disaster_model的例子,加上 MCMC 和 5 万次简单迭代,模型显示如下:

from pymc.examples import disaster_model
from pymc import MCMC

from pylab import hist, show, rcParams

rcParams['figure.figsize'] = 10, 10

M = MCMC(disaster_model)
M.sample(iter=65536, burn=8000, thin=16)

hist(M.trace('late_mean')[:], color='#b02a2a')

show()

如果我们要显示模型平均值的直方图,这是使用 PyMC 的一个选项:

Stochastic block models

以下代码使用stochpy时间序列轨迹数据进行模拟:

import stochpy as stp
smod = stp.SSA()

from pylab import rcParams
# set the graph display size as 10 by 10 inches
rcParams['figure.figsize'] = 12, 12

smod.Model('dsmts-003-04.xml.psc')
smod.DoStochSim(end=35,mode='time',trajectories=2000)
smod.GetRegularGrid()
smod.PlotAverageSpeciesTimeSeries()

StochPy 有几种方便的方法来模拟随机模型并显示结果,如下图所示:

Stochastic block models

总结

本章举例说明了网络和生物信息学的例子,以及能够绘制结果的 Python 包的选择。我们看了图和多重图的简单介绍,并使用稀疏矩阵和距离图来说明如何使用几个不同的包来存储和显示图,例如NetworkXigraph(来自【igraph.org】)和graph-tool

图的聚类系数和中心性演示了如何计算聚类系数,以便它们能够知道图中节点或顶点的重要性。我们还使用Python-Twitter包和NetworkX库,通过可视化的推特好友和关注者的插图,观察了社交网络数据的分析。

您还学习了遗传编程示例,演示了如何在脱氧核糖核酸序列中看到密码子,以及如何使用生物包计算气相色谱比。除此之外,我们还演示了如何显示脱氧核糖核酸、核糖核酸或蛋白质的结构。

讨论了平面图测试、非循环图测试和使用NetworkX包的最大流,以及如何测试所有这些的几行代码。此外,您可以使用几种选择绘制随机块模型,例如 PyMC 或 StochPy。在下一章中,我们将以高级可视化方法结束,您可以从中进行选择。

八、高级可视化

可视化方法已经从几十年前的传统条形图和饼图转变为最近更具创造性的形式。设计可视化并不像从特定工具提供的众多选择中选择一个那样简单。正确的可视化传达了正确的信息,错误的可视化可能会扭曲、混淆或传达错误的信息。

其中的计算机和存储设备不仅有助于使用数据结构存储大块数据,而且有助于通过算法使用计算能力。根据 D3.js 的创建者和领先的可视化专家迈克尔·博斯托克的说法,我们应该可视化算法,而不仅仅是输入其中的数据。算法是任何过程或计算模型背后的核心引擎;因此,该算法成为可视化的重要用例。

可视化算法只是在最近几年才得到认可,探索这个概念的一个有趣的地方是visualgo.net,他们有一些先进的算法来教授数据结构和算法。Visualgo 包含的算法可以在史蒂芬·哈利姆博士的著作《T2》中找到。来自三藩市大学(https://www.cs.usfca.edu/~galles/visualization/)的大卫·盖尔斯教授提供了另一种类似的有趣的可视化方法。还有其他类似的贡献来教授算法和数据。

我们讨论了许多不同的领域,包括数值计算、金融模型、统计和机器学习以及网络模型。在本章的后面,我们将讨论一些关于可视化的新的和创造性的想法,以及一些模拟和信号处理的例子。此外,我们将涵盖以下主题:

  • 计算机模拟、信号处理和动画示例
  • 使用 HTML5 的一些有趣的可视化方法
  • 朱莉娅和 Python 有什么不同?—优点和缺点
  • 与 Python 相比,为什么 D3.js 是最受欢迎的可视化工具
  • 创建仪表板的工具

计算机模拟

计算机模拟是一门流行了几十年的学科。它是一个试图模拟抽象模型的计算机程序。计算机模拟模型可以帮助创建复杂系统,作为理解和评估隐藏或未知场景的一种方式。计算机模拟建模的一些显著例子是天气预报和用于训练飞行员的飞机模拟器。

计算机模拟已经成为物理、化学、生物学、经济学、工程学、心理学和社会科学等不同领域系统数学建模的一个非常有效的部分。

以下是模拟模型的好处:

  • 更好地理解正在研究的算法或过程
  • 识别流程和算法中的问题区域
  • 评估任何与算法模型相关的变化的影响

模拟模型的类型如下:

  • 离散模型:在这种情况下,系统的变化只发生在特定的时间
  • 连续模型:在这种情况下,系统的状态会在一段时间内连续变化
  • 混合模型:包含离散和连续元素

为了进行模拟,通常使用随机概率输入,因为在进行任何此类模拟实验之前,您不太可能拥有真实数据。因此,模拟实验涉及随机数是很常见的,无论它是否针对确定性模型。

首先,让我们考虑一下在 Python 中生成随机数的几个选项,并举例说明模拟中的一个或多个例子。

Python 的随机包

Python 提供了一个名为random的包,它有几个方便的功能,可用于以下目的:

  • 生成介于 0.0 和 1.0 之间或特定起始值和结束值之间的随机实数
  • 生成特定数字范围之间的随机整数
  • 从数字或字母列表中获取随机值列表
import random

print random.random() # between 0.0 and 1.0
print random.uniform(2.54, 12.2) # between 2.54 and 12.2
print random.randint(5,10)  # random integer between 5 and 10

print random.randrange(25)  # random number between 0 and 25
#  random numbers from the range of 5 to 500 with step 5
print random.randrange(5,500,5) 

# three random number from the list 
print random.sample([13,15,29,31,43,46,66,89,90,94], 3) 
# Random choice from a list
random.choice([1, 2, 3, 5, 9])

SciPy 的随机函数

NumPy 和 SciPy 是 Python 模块,由数学和数值例程组成。数值T5】Python(NumPy)包提供了操纵数值数据的大数组和矩阵的基本例程。scipy包用算法和数学技术扩展了 NumPy。

NumPy 内置了伪随机数发生器。这些数字是伪随机的,这意味着它们是由单个种子号确定性地生成的。使用相同的种子号,您可以生成相同的随机数集,如以下代码所示:

Import numpy as np
np.random.seed(65536)

不提供种子值可以生成不同的随机序列。每次运行具有以下代码的程序时,NumPy 都会自动选择一个不同的随机种子(基于时间):

np.random.seed()

间隔[0.0, 1.0]中的五个随机数的数组可以如下生成:

import numpy as np
np.random.rand(5)
#generates the following
array([ 0.2611664,  0.7176011,  0.1489994,  0.3872102,  0.4273531])

rand功能也可以是用来生成随机二维数组,如下代码所示:

np.random.rand(2,4) 
array([
[0.83239852, 0.51848638, 0.01260612, 0.71026089],        
[0.20578852, 0.02212809, 0.68800472, 0.57239013]])

要生成随机整数,可以使用randint (min, max),其中minmax定义数字的范围,其中随机整数必须绘制,如下代码所示:

np.random.randint(4,18) 

使用以下代码绘制离散泊松分布 λ = 8.0 :

np.random.poisson(8.0)

要从平均值为 μ = 1.25 且标准差为 σ = 3.0 的连续正态(高斯)分布中提取,请使用以下代码:

np.random.normal(2.5, 3.0)

#for mean 0 and variance 1
np.random.mormal()

仿真示例

在第一个例子中,我们将选择几何布朗运动,也称为指数布朗运动,用随机微分方程 ( SDE )来建模股价行为:

Simulation examples

在上式中, Wt 为布朗运动, μ 为百分比漂移, σ 为百分比波动率。以下代码显示了布朗运动图:

from numpy.random import standard_normal
from numpy import zeros, sqrt
import matplotlib.pyplot as plt

S_init = 20.222
T =1
tstep =0.0002
sigma = 0.4
mu = 1
NumSimulation=6

colors = [ (214,27,31), (148,103,189), (229,109,0), (41,127,214), 
(227,119,194),(44,160,44),(227,119,194), (72,17,121), (196,156,148)]  

# Scale the RGB values to the [0, 1] range.

for i in range(len(colors)):  
    r, g, b = colors[i]  
    colors[i] = (r / 255., g / 255., b / 255.)

plt.figure(figsize=(12,12))

Steps=round(T/tstep); #Steps in years
S = zeros([NumSimulation, Steps], dtype=float)
x = range(0, int(Steps), 1)

for j in range(0, NumSimulation, 1):

    S[j,0]= S_init
    for i in x[:-1]:
       S[j,i+1]=S[j,i]+S[j,i]*(mu-0.5*pow(sigma,2))*tstep+ \
          sigma*S[j,i]*sqrt(tstep)*standard_normal()
    plt.plot(x, S[j], linewidth=2., color=colors[j])

plt.title('%d Simulation using %d Steps, \n$\sigma$=%.6f $\mu$=%.6f $S_0$=%.6f ' % (int(NumSimulation), int(Steps), sigma, mu, S_init), 
          fontsize=18)
plt.xlabel('steps', fontsize=16)
plt.grid(True)
plt.ylabel('stock price', fontsize=16)
plt.ylim(0,90)

plt.show()

下图显示了使用布朗运动的六次模拟的结果:

Simulation examples

这里的另一个模拟示例演示了如何应用霍德里克-普雷斯科特滤波器来获得属于时间序列数据类别的股票价格数据的平滑曲线表示:

Simulation examples

这里,我们将使用matplotlib中的金融子包来生成一系列日期的股价数据,起始日期为 2012 年 5 月,终止日期为 2014 年 12 月。使用matplotlibhold方法,可以将平滑后的曲线和股价图一起显示出来,如下代码所示:

from matplotlib import finance
import matplotlib.pyplot as plt

import statsmodels.api as sm

titleStr='Stock price of FB from May. 2012 to Dec. 2014'
plt.figure(figsize=(11,10))

dt1 = datetime.datetime(2012, 05, 01)
dt2 = datetime.datetime(2014, 12, 01)
sp=finance.quotes_historical_yahoo('FB',dt1,dt2,asobject=None)

plt.title(titleStr, fontsize=16) 
plt.xlabel("Days", fontsize=14) 
plt.ylabel("Stock Price", fontsize=14)

xfilter = sm.tsa.filters.hpfilter(sp[:,2], lamb=100000)[1]

plt.plot(sp[:,2])
plt.hold(True)
plt.plot(xfilter,linewidth=5.)

除了这些例子,你可以模拟一个队列系统或者任何基于事件的进程。例如,你可以模拟一个神经网络,一个有助于快速建模的软件包可以在http://briansimulator.org获得。看看他们的演示程序,了解更多细节。

信号处理

信号处理中有很多的例子你可以想到,但是我们会选择一个涉及卷积的具体例子。两个信号的卷积是组合它们以产生滤波的第三信号的一种方式。在现实生活中,信号卷积用于平滑图像。在很大程度上,卷积也被用于计算信号干扰。关于更多的细节,你可以参考一本关于微波测量的书,但是我们将尝试给你看一些简单的例子。

让我们考虑三个简单的例子。第一个示例说明了数字信号的卷积信号,并使用汉明来模拟模拟信号,如以下代码所示:

import matplotlib.pyplot as plt
from numpy import concatenate, zeros, ones, hamming, convolve

digital = concatenate ( (zeros(20), ones(25), zeros(20)))
norm_hamming = hamming(80)/sum(hamming(80))
res = convolve(digital, norm_hamming)
plt.figure(figsize=(10,10))
plt.ylim(0, 0.6)
plt.plot(res, color='r', linewidth=2)
plt.hold(True)
plt.plot(data, color='b', linewidth=3)
plt.hold(True)
plt.plot(norm_hamming, color='g', linewidth=4)
plt.show()

在这个例子中,我们将使用连接和来自numpy的 0 和 1 来产生数字信号,汉明来产生模拟信号,卷积来应用卷积。

如果我们绘制所有三个信号,即数字信号、模拟汉明和卷积结果信号(res),结果信号将如预期的那样偏移,如下图所示:

Signal processing

在另一个例子中,我们将使用随机信号,即random_data并应用快速傅立叶 变换 ( FFT )如下:

import matplotlib.pyplot as plt
from scipy import randn
from numpy import fft

plt.figure(figsize=(10,10))
random_data = randn(500)
res = fft.fft(random_data)
plt.plot(res, color='b')
plt.hold(True)
plt.plot(random_data, color='r')
plt.show()

使用scipy中的randn生成随机信号数据,使用numpy中的fft执行快速傅里叶变换,变换结果用蓝色绘制,原始随机信号用红色绘制,如下图所示:

Signal processing

在第三个示例中,显示了如何使用scipy包创建反转图像的简单示例。在我们开始实际的 Python 代码和结果之前,让我们试着分析一下倒排图像将如何帮助可视化数据。

有争议的是,在某些情况下,倒置的颜色对我们的视觉造成的压力较小,并且看起来很舒服。令人惊讶的是,如果我们将原始图像和反转图像并排放置,反转图像将有助于可视化某些区域,否则这些区域在原始图像中可能是困难的,如果不是所有图像的话,至少在某些情况下是如此。下面的代码展示了如何使用scipy.misc.pilutil.Image()将图像转换为反转图像:

import scipy.misc as scm 
from scipy.misc.pilutil import Image  

# open original image 
orig_image = Image.open('/Users/kvenkatr/Desktop/filter.jpg')

# extract image data into array
image1 = scm.fromimage(orig_image)
# invert array values 
inv_image = 255 - image1

# using inverted array values, convert image 
inverted_image = scm.toimage(inv_image) 

#save inverted image
inverted_image.save('/Users/kvenkatr/Desktop/filter_invert.jpg').

反转图像结果与原始图像一起显示在此处:

Signal processing

类似地,使用以下一些功能,可以将其他过滤机制应用于任何图像:

convolve()         Multidimensional convolution.
correlate()        Multi-dimensional correlation.
gaussian_filter()  Multidimensional Gaussian filter

功能的完整列表显示在 http://tinyurl.com/3xubv9p T2。

动画

您可以使用matplotlib在 Python 中完成动画,但是结果会保存在 MP4 格式的文件中,以便以后回放。动画的基本设置如下:

import numpy as np 
import matplotlib.pyplot as plt 
from matplotlib import animation  

# Set up the figure, axis, and the plot element to be animated 
fig = plt.figure() 
ax = plt.axes(xlim=(0, 3.2), ylim=(-2.14, 2.14)) 
line, = ax.plot([], [], lw=2)

确保动画包从matplotlib导入,设置轴,并准备必要的绘图变量(这只是一个空行),如下所示:

# initialization function: plot the background of each frame
def init():
    line.set_data([], [])
    return line,

绘图的初始化需要在开始任何动画之前执行,因为它会创建一个基本帧,如以下代码所示:

# animation function.  This is called sequentially
def animate(i):
    x = np.linspace(0, 2, 1000)
    xval = 2 * np.pi * (x - 0.01 * i)
    y = np.cos(xval) # Here we are trying to animate cos function
    line.set_data(x, y)
    return line,

这里是动画功能,以帧数为输入,定义改变的xy值,并设置绘图变量:

anim = animation.FuncAnimation(fig, animate, init_func=init,\
            frames=200, interval=20, blit=True)
anim.save('basic_animation.mp4', fps=30)
plt.show()

实际动画对象通过FuncAnimation创建,并通过init()animate()功能,以及帧数、每秒帧数 ( fps )和时间间隔参数。blit=True参数告诉您只需要重新绘制显示的更改部分(否则,可能会看到闪烁)。

在你尝试执行一个动画之前,你必须确定mencoderffmpeg已经安装;否则,在没有ffmpegmencoder的情况下运行该程序将导致以下错误:ValueError: Cannot save animation: no writers are available. Please install mencoder or ffmpeg to save animations.。下图显示了三角曲线的动画,例如正弦曲线或余弦曲线:

Animation

您可以将这个 MP4 文件嵌入到一个 HTML 中进行显示,并按下左下角的播放按钮来观看动画。

https://jakevdp . github . io/blog/2012/08/18/matplotlib-animation-tutorial/有一个有趣的双摆动画演示,在http://matplotlib . org/examples/animation/dynamic _ image 2 . html有一个动态图像动画演示。

在本书中,到目前为止,我们已经讨论了可视化方法,涉及如何在 Python 中绘图或创建外部格式(如 MP4)。基于 JavaScript 的可视化方法受欢迎的原因之一是因为您可以在网络上呈现它们,并且还可以将一些事件驱动的动画与它们相关联。支持向量图形 ( SVG )获得人气的原因有很多,其中之一就是能够缩放到任意大小 而不丢失细节

使用 HTML5 的可视化方法

下面的代码简单说明了使用feGaussianBlur显示圆的 SVG:

  <svg width="230" height="120"  xmlns:xlink="http://www.w3.org/1999/xlink">
    <filter id="blurMe">
       <feGaussianBlur in="SourceGraphic" stdDeviation="5" />
    </filter>

    <circle cx="60"  cy="80" r="60" fill="#E90000" />
    <circle cx="190" cy="80" r="60" fill="#E90000"
      filter="url(#blurMe)" />
    <circle cx="360"  cy="80" r="60" fill="#4E9B01" />
    <circle cx="490" cy="80" r="60" fill="#4E9B01"
      filter="url(#blurMe)" />
    <circle cx="660"  cy="80" r="60" fill="#0080FF" />
    <circle cx="790" cy="80" r="60" fill="#0080FF"
      filter="url(#blurMe)" />
  </svg>

前两个圆的半径为 60,用相同的颜色填充,但第二个圆使用模糊滤镜。同样,绿色和蓝色的相邻圆圈也遵循相同的行为(对于彩色效果,请参考http://knapdata.com/dash/html/svg_circle.html,如下图所示:

Visualization methods using HTML5

当数据表示在可视化中需要整体的一部分,但没有组合成一个整体时,我们如何使用这种模糊的概念。这是什么意思?让我们考虑两个例子。在第一个例子中,我们将考虑一个以外语(在某些情况下,不止一种语言)注册的学生班级。如果我们将分布表示如下,我们会怎么做?

Visualization methods using HTML5

您可以通过 Python 程序生成 SVG 格式,如以下代码所示:

import os
display_prog = 'more' # Command to execute to display images.
svcount=1

class Scene:
    def __init__(self,name="svg",height=400,width=1200):
        self.name = name
        self.items = []
        self.height = height
        self.width = width
        return

    def add(self,item): self.items.append(item)

    def strarray(self):
        var = [ "<html>\n<body>\n<svg height=\"%d\" width=\"%d\" >\n" % (self.height,self.width),
               "  <g id=\"setttings\">\n",
               "    <filter id=\"dropshadow\" height=\"160%\">\n",
               "     <feGaussianBlur in=\"SourceAlpha\" stdDeviation=\"5\"></feGaussianBlur>\n",
               "       <feOffset dx=\"0\" dy=\"3\" result=\"offsetblur\"></feOffset>\n",
               "       <feMerge>\n",
               "          <feMergeNode></feMergeNode>\n",
               "          <feMergeNode in=\"SourceGraphic\"></feMergeNode>\n",
               "       </feMerg>\n",
               "    </filter>\n"]
        for item in self.items: var += item.strarray()            
        var += [" </g>\n</svg>\n</body>\n</html>"]
        return var

    def write_svg(self,filename=None):
        if filename:
            self.svgname = filename
        else:
            self.svgname = self.name + ".html"
        file = open(self.svgname,'w')
        file.writelines(self.strarray())
        file.close()
        return

    def display(self,prog=display_prog):
        os.system("%s %s" % (prog,self.svgname))
        return        

def colorstr(rgb): return "#%x%x%x" % (rgb[0]/16,rgb[1]/16,rgb[2]/16)

class Text:
    def __init__(self, x,y,txt, color, isItbig, isBold):
        self.x = x
        self.y = y
        self.txt = txt
        self.color = color
        self.isItbig = isItbig 
        self.isBold = isBold
    def strarray(self):
        if ( self.isItbig == True ):
          if ( self.isBold == True ):
            retval = [" <text y=\"%d\" x=\"%d\" style=\"font-size:18px;font-weight:bold;fill:%s\">%s</text>\n" %(self.y, self.x, self.color,self.txt) ]
          else:
            retval = [" <text y=\"%d\" x=\"%d\" style=\"font-size:18px;fill:%s\">%s</text>\n" %(self.y, self.x, self.color,self.txt) ]
        else:
          if ( self.isBold == True ):
            retval = [" <text y=\"%d\" x=\"%d\" style=\"fill:%s;font-weight:bold;\">%s</text>\n" %(self.y, self.x, self.color,self.txt) ]
          else:
            retval = [" <text y=\"%d\" x=\"%d\" style=\"fill:%s\">%s</text>\n" %(self.y, self.x, self.color,self.txt) ]
        return retval

class Circle:
    def __init__(self,center,radius,color, perc):
        self.center = center #xy tuple
        self.radius = radius #xy tuple
        self.color = color   #rgb tuple in range(0,256)
        self.perc = perc
        return

    def strarray(self):
        global svcount
        diam = self.radius+self.radius
        fillamt = self.center[1]-self.radius - 6 + (100.0 - self.perc)*1.9
        xpos = self.center[0] - self.radius
        retval = ["  <circle cx=\"%d\" cy=\"%d\" r=\"%d\"\n" %\
                (self.center[0],self.center[1],self.radius),
                "    style=\"stroke: %s;stroke-width:2;fill:white;filter:url(#dropshadow)\"  />\n" % colorstr(self.color),
               "  <circle clip-path=\"url(#dataseg-%d)\" fill=\"%s\" cx=\"%d\" cy=\"%d\" r=\"%d\"\n" %\
                (svcount, colorstr(self.color),self.center[0],self.center[1],self.radius),
                "    style=\"stroke:rgb(0,0,0);stroke-width:0;z-index:10000;\"  />\n",
               "<clipPath id=\"dataseg-%d\"> <rect height=\"%d\" width=\"%d\" y=\"%d\" x=\"%d\"></rect>" %(svcount,diam, diam,fillamt,xpos),
               "</clipPath>\n"
                ]
        svcount += 1
        return retval

def languageDistribution():
    scene = Scene('test')
    scene.add(Circle((140,146),100,(0,128,0),54))
    scene.add(Circle((370,146),100,(232,33,50),42))
    scene.add(Circle((600,146),100,(32,119,180),65))
    scene.add(Circle((830,146),100,(255,128,0),27))
    scene.add(Text(120,176,"English", "white", False, True))
    scene.add(Text(120,196,"Speaking", "#e2e2e2", False, False))
    scene.add(Text(340,202,"German", "black", False, True))
    scene.add(Text(576,182,"Spanish", "white", False, True))
    scene.add(Text(804,198,"Japanese", "black", False, True))

    scene.add(Text(120,88,"54%", "black", True, True))
    scene.add(Text(350,88,"42%", "black", True, True))
    scene.add(Text(585,88,"65%", "black", True, True))
    scene.add(Text(815,88,"27%", "black", True, True))

    scene.write_svg()
    scene.display()
    return

if __name__ == '__main__': languageDistribution()

前面的例子给出了一个为可视化创建自定义svg方法的想法。今天 Python 中还有很多其他的svg 作者,但是他们都没有展示我们在这里展示的方法。也有许多不同的方法来创建其他语言的自定义可视化方法,例如 Julia。这种技术已经存在了将近三年,被认为适合数值和科学计算。

朱莉娅和 Python 有什么不同?

Julia 是一种动态的编程语言。不过在性能方面与 C 相当,因为 Julia 是一个基于低级虚拟机的即时编译器(JIT 编译器)。众所周知,在 Python 中,为了将 C 和 Python 结合起来,你可能不得不使用 Cython。

朱莉娅的一些显著优点如下:

  • 性能与 C 相当
  • 内置的包管理器
  • 有类似 lisp 的宏
  • 可以使用 PyCall 包调用 Python 函数
  • 可以直接调用 C 函数
  • 专为分布式计算设计
  • 用户定义的类型和内置类型一样快

唯一的缺点是你必须学习一门新的语言,尽管与 C 和 Python 有一些相似之处。

D3.js(其中 D3 简称表示 DDD,代表文档驱动的数据)是 Python 中用于可视化的竞争框架之一。

用于可视化的 D3.js

D3.js 是一个 JavaScript 库,用于在 Web 上呈现数据,并使用 HTML、SVG 和 CSS 帮助显示数据。

D3.js 将数据附加到文档对象模型 ( DOM )元素;因此,您可以使用 CSS3、HTML 和 SVG 来展示他们的数据。此外,由于 JavaScript 有事件侦听器,您可以使数据具有交互性。

迈克·博斯托克在斯坦福可视化小组攻读博士期间创建了 D3.js。首先,迈克与斯坦福可视化小组合作制作了 Protivis,然后最终成为 D3。迈克·博斯托克、瓦迪姆·奥吉夫茨基和杰弗里·赫尔发表了一篇题为 D3:数据驱动文档的论文,可在http://vis.stanford.edu/papers/d3查阅。

实际上,D3.js 的基本原理是使用 CSS 样式选择器从 DOM 节点中进行选择,然后使用 jQuery 样式对其进行操作。这里有一个例子:

d3.selectAll("p")            // select all <p> elements
  .style("color", "#FF8000") // set style "color" to value "#FF8000"
  .attr("class", "tin")      // set attribute "class" to value "tin"
  .attr("x", 20);            // set attribute "x" to 20px

D3 的许多优点之一是,通过简单地访问 DOM 的机制,您可以创建惊人的数据表示。另一个优点是,通过充分利用 JavaScript 的强大功能结合当今的计算能力,您可以轻松快速地添加导航行为。在 http://bost.ocks.org/mike/有大量这样的可视化工具。这里显示了 D3 可视化图的一个示例:

D3.js for visualization

你可以制作很多可视化的例子,在图库中的例子中(http://christophviau . com/d3list/gallery . html # visualization type =棒棒糖),我最喜欢的是讲述使用多系列多轴的不同聚合的故事,可以在http://tinyurl.com/p988v2u查看(上图也有显示)。

仪表盘

Python 相比 D3 有很多优势。当你把这两者结合在一起时,你可以把两者都利用起来。例如,Python 为数值和科学计算提供了一些非常好的软件包选项。由于这个原因,它一直很受学术界的欢迎。

最近出现的有趣的数据可视化和协作工具非常少,其中一个工具就是 Plotly(https://Plotly . ly)。Python 仪表盘集合可在https://plot.ly/python/dashboard/访问。由于这是相当新的,我们还没有有机会进一步探索,看看一个人能做什么。Splunk 提供了一个在http://dev.splunk.com/view/SP-CAAADSR创建基于 Python 的仪表板的 SDK,Pyxley 是一个包的集合,它结合了 Python 和 JavaScript 的力量来创建基于网络的仪表板。Splunk 仪表板中的一个示例如下所示:

Dashboards

上图显示了 Plotly 的一个示例。它演示了如何生成一个看起来漂亮、易于理解并且可以在http://tinyurl.com/pwmg5zr导航的可视化效果。

总结

本章说明了前几章没有涉及的其他主题,例如使用 Python 的信号处理和动画。此外,我们还将 Python 与 D3.js 和 Julia 进行了比较,确定了它们的优势。讨论了信号处理的几个例子。我们还使用numpymatplotlib研究了模拟和数字信号频谱的卷积。

我们还看了一个动画示例,并演示了如何通过 Python 生成 MP4 格式的动画。我们还将 Julia 与 Python 进行了比较,并列出了 Julia 相对于 Python 的几个优点,并对它们进行了比较,以看出不同之处。

此外,我们展示了 D3.js 的优势,强调了这个基于 JavaScript 的可视化工具和 Python 之间的区别。最后,我们讨论了仪表板可用的选项,并列出了创建基于 Python 的仪表板的几个选项。

九、附录 a:向前探索可视化

Python 自 1991 年以来一直存在,并在科学家和工程师群体中广受欢迎。在众多的图书馆中,numpyscipymatplotlib已经广泛应用于科学计算。Sage 通过 IPython 使用简单的浏览器界面,涵盖了代数、组合学、数值数学、数论和微积分等领域。另一个流行的软件包pandas可以用来存储和处理复杂的数据集。

有多种工具可以运行和编辑 Python 程序,其中之一是来自 Continuum 的 Anaconda。Anaconda 的优势之一是它不需要花费任何费用,并且内置了大多数必要的包。管理环境和 Python 包的底层命令行工具是conda,编辑器是 Spyder。

在过去,安装 Spyder 是复杂的,因为它涉及到下载和安装它在一个多步骤的过程。最近版本的安装非常简单,人们可以一步自动下载并安装所有组件。

conda 概述

Conda 是一个命令行工具,负责管理环境和 Python 包,而不是使用pip。有多种方法可以查询和搜索包,必要时创建新的环境,以及在现有的 conda 环境中安装和更新 Python 包。这个命令行工具还跟踪包和平台细节之间的依赖关系,帮助您从不同的包组合中创建工作环境。要查看正在运行的conda版本,可以在 Python 中输入conda --version,它会显示例如conda 3.18.2作为版本。

conda 环境是一个文件系统目录,其中包含一组特定的conda包。要开始使用环境,只需设置 PATH 变量,将其指向 bin 目录。

以下是使用conda从命令行安装软件包的示例:

$ conda install scipy

Fetching package metadata: ....
Solving package specifications: .
Package plan for installation in environment /Users/MacBook/anaconda:

The following packages will be downloaded:

 package                |       build
 ---------------------|-----------------
 flask-0.10.1         |       py27_1         129 KB
 itsdangerous-0.23    |       py27_0          16 KB
 jinja2-2.7.1         |       py27_0         307 KB
 markupsafe-0.18      |       py27_0          19 KB
 werkzeug-0.9.3       |       py27_0         385 KB

The following packages will be linked:

 package              |            build
 ---------------------|-----------------
 flask-0.10.1         |       py27_1
 itsdangerous-0.23    |       py27_0
 jinja2-2.7.1         |       py27_0
 markupsafe-0.18      |       py27_0
 python-2.7.5         |       2
 readline-6.2         |       1
 sqlite-3.7.13        |       1
 tk-8.5.13            |       1
 werkzeug-0.9.3       |       py27_0
 zlib-1.2.7           |       1

Proceed ([y]/n)? 

我们正在安装的软件包上的任何依赖项都将被自动识别、下载和链接。

下面是一个使用conda从命令行更新包的例子:

$ conda update matplotlib
Fetching package metadata: ....
Solving package specifications: .
Package plan for installation in environment /Users/MacBook/anaconda:

The following packages will be downloaded:

 package                    |            build
 ---------------------------|-----------------
 freetype-2.5.2             |                0         691 KB
 conda-env-2.1.4            |           py27_0          15 KB
 numpy-1.9.2                |           py27_0         2.9 MB
 pyparsing-2.0.3            |           py27_0          63 KB
 pytz-2015.2                |           py27_0         175 KB
 setuptools-15.0            |           py27_0         436 KB
 conda-3.10.1               |           py27_0         164 KB
 python-dateutil-2.4.2      |           py27_0         219 KB
 matplotlib-1.4.3           |       np19py27_1        40.9 MB
 ------------------------------------------------------------
 Total:        45.5 MB

The following NEW packages will be INSTALLED:

 python-dateutil: 2.4.2-py27_0 

The following packages will be UPDATED:

 conda:             3.10.0-py27_0    --> 3.10.1-py27_0 
 conda-env:       2.1.3-py27_0     --> 2.1.4-py27_0 
 freetype:   2.4.10-1         --> 2.5.2-0 
 matplotlib:     1.4.2-np19py27_0 --> 1.4.3-np19py27_1
 numpy:          1.9.1-py27_0     --> 1.9.2-py27_0 
 pyparsing:      2.0.1-py27_0     --> 2.0.3-py27_0 
 pytz:           2014.9-py27_0    --> 2015.2-py27_0 
 setuptools:     14.3-py27_0      --> 15.0-py27_0 

Proceed ([y]/n)?

在某些情况下,通过conda安装软件包需要更多的步骤。例如,要安装wordcloud,您必须执行本代码中给出的步骤:

#step-1 command
conda install wordcloud

Fetching package metadata: ....
Error: No packages found in current osx-64 channels matching: wordcloud

You can search for this package on Binstar with
# This only means one has to search the source location
binstar search -t conda wordcloud

Run 'binstar show <USER/PACKAGE>' to get more details:
Packages:
 Name | Access       | Package Types   | 
 ------------------------- | ------------ | --------------- |
 derickl/wordcloud | public       | conda           |
Found 1 packages

# step-2 command
binstar show derickl/wordcloud

Using binstar api site https://api.binstar.org
Name:    wordcloud
Summary:
Access:  public
Package Types:  conda
Versions:
 + 1.0

To install this package with conda run:
conda install --channel https://conda.binstar.org/derickl wordcloud

# step-3 command
conda install --channel https://conda.binstar.org/derickl wordcloud

Fetching package metadata: ......
Solving package specifications: .
Package plan for installation in environment /Users/MacBook/anaconda:

The following packages will be downloaded:

 package                    |            build
 ---------------------------|-----------------
 cython-0.22                |           py27_0         2.2 MB
 django-1.8                 |           py27_0         3.2 MB
 pillow-2.8.1               |           py27_1         454 KB
 image-1.3.4                |           py27_0          24 KB
 setuptools-15.1            |           py27_1         435 KB
 wordcloud-1.0              |       np19py27_1          58 KB
 conda-3.11.0               |           py27_0         167 KB
 ------------------------------------------------------------
 Total:         6.5 MB

The following NEW packages will be INSTALLED:
 django:     1.8-py27_0
 image:      1.3.4-py27_0
 pillow:     2.8.1-py27_1
 wordcloud:  1.0-np19py27_1

The following packages will be UPDATED:
 conda:      3.10.1-py27_0 --> 3.11.0-py27_0
 cython:     0.21-py27_0   --> 0.22-py27_0
 setuptools: 15.0-py27_0   --> 15.1-py27_1

Finally, the following packages will be downgraded:

 libtiff:    4.0.3-0       --> 4.0.2-1

Proceed ([y]/n)? y

Anaconda 是用于科学计算的免费 Python 发行版。该发行版附带经过测试和优化的 Python 2.x 或 Python 3.x 和 100+跨平台 Python 包。Anaconda 还可以创建混合和匹配不同 Python 版本的自定义环境。

装有蟒蛇的包装

以下命令将显示 Anaconda 环境中所有包的列表:

conda list 

Anaconda 的特色套餐有AstropyCythonh5pyIPythonLLVMLLVMpymatplotlibMayaviNetworkXNLTKNumexprNumbanumpypandasPytablesscikit-imagescikit-learnscipySpyderQt/PySideVTK

要检查 Anaconda 安装的软件包,请导航到命令行并输入conda list命令,以快速显示默认环境中安装的所有软件包的列表。或者,您也可以查看 Continuum Analytics,了解当前和最新版本中可用软件包列表的详细信息。

此外,您始终可以通过常用方式安装软件包,例如,使用pip install命令或从使用setup.py文件的来源安装。虽然conda是首选的打包工具,但是 Anaconda 没有什么特别的地方阻止使用标准的 Python 打包工具。

IPython 不是必需的,但强烈建议使用。IPython 应该在安装 Python、GNU Readline 和 PyReadline 之后安装。默认情况下,Anaconda 和 Canopy 会执行这些操作。本书中的所有示例都使用了 Python 包,这是有充分理由的。在下一节中,我们更新了列表。

打包网站

以下是我们在本书中提到的 Python 包及其各自网站的列表,在这里可以找到最新的信息:

关于 matplotlib

matplotlib包提供了许多创建可视化图表的方便方法。这本书只探讨了其中的一小部分。您必须从以下来源进一步探索matplotlib:

还应该参考上一节列出的其他包,它们是使绘图更有吸引力的库。

posted @ 2025-10-26 09:01  绝不原创的飞龙  阅读(1)  评论(0)    收藏  举报