Julia数据科学应用

978-7-115-47328-8
作者: [美] 扎卡赖亚斯•弗格里斯(Zacharias Voulgaris)
译者: 陈光欣
编辑: 陈冀康

图书目录:

详情

本书介绍了Julia在数据科学领域的应用以及相关的几个基本的数据科学原理,然后,详细介绍了如何安装Julia并发挥其功能强大的库。本书提供了众多的示例,展示了使用Julia命令、数据集和函数所能够解决的问题。本书每一章都带有练习和问题,帮助读者掌握Julia这一数据分析工具。

图书摘要

版权信息

书名:Julia数据科学应用

ISBN:978-7-115-47328-8

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。


• 著    [美] 扎卡赖亚斯•弗格里斯(Zacharias Voulgaris)

  译    陈光欣

  责任编辑 陈冀康

• 人民邮电出版社出版发行  北京市丰台区成寿寺路11号

  邮编 100164  电子邮件 315@ptpress.com.cn

  网址 http://www.ptpress.com.cn

• 读者服务热线:(010)81055410

  反盗版热线:(010)81055315


Simplified Chinese translation copyright ©2017 by Posts and Telecommunications Press

ALL RIGHTS RESERVED

Julia for Data Scientist,by Zacharias Voulgaris, PhD ,ISBN 9781634621304

Copyright © 2016 by Technics Publications, LLC

本书中文简体版由Technics Publications授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。

版权所有,侵权必究。


数据科学通过各种统计学和机器学习的技术与方法,将数据转换为有用的信息或知识。Julia是一种在数据科学领域逐渐流行起来的语言。

本书会提出一系列在数据科学流程中常见的、有代表性的实际问题,并指导读者使用Julia去解决这些问题。全书共13章,涵盖了Julia基础知识、工作环境搭建、语言基础和高级内容、数据科学应用、数据可视化、机器学习方法(包括无监督式学习方法和监督式学习方法)、图分析方法等重要的话题。附录部分给出了学习和使用Julia的一些有用的参考资料,还给出了各章的思考题的答案。

本书适合对数据科学的知识和应用方法感兴趣的读者阅读,特别适合有志于学习Julia并从事数据科学相关工作的人员学习参考。


我是在几年前发现Julia的,从此就被它的强大能力与巨大潜力所吸引。Julia具有用户友好的集成开发环境(Integrated Development Environment,IDE),这使它很容易上手;它还具有高级的逻辑表达能力(非常类似Matlab和其他高级语言)和极高的性能,这使它的功能非常强大。但是,当时我正致力于研究其他更成熟的平台,比如R和Java,未能给予Julia太多的关注。

因此,我只了解了Julia的一些基础知识,以及当时的教程中提供的一些具体应用,并没有进行更多的研究。除了Julia之外,我还知道不断有一些新的有趣的语言被开发出来,但大多数是昙花一现。

那么,为什么我现在又对Julia感兴趣了呢?一个原因就是,这些年它一直保持着良好的发展势头,Julia会议的参与人数每年都有显著的增长。尽管我曾经很熟悉它的基本知识,但当我重拾Julia时,发现有很多新的知识需要学习。从我初识Julia之后,它已经有了很大的发展。

更重要的原因是,Julia已经跨过了大西洋,引起了欧洲从业者的极大兴趣,其中一位已经为这种相当年轻的语言创建了一系列视频和练习资料。

在试用了Julia 0.2版之后,我开始琢磨,除了快速分解质因数和计算第n个斐波那契数之外,是否能使用Julia来做些真正有用的事情。虽然0.2版仅有几个软件包,文档也做得很差,我只能找到零星几个介绍这门语言的视频,多数还是来自某个Python会议上的发言。但是,我还是在计算机上保留着Julia,并时不时地用它写个脚本,来解决Project Euler、Programming Praxis或类似站点上的编程问题。当时我是个项目经理,所以没有很大的积极性去掌握一门新的编程语言。我在Julia上所做的一切都是出于兴趣。

但是,几个月之后,我重新开始从事数据科学工作,并更加正式地使用Julia编程。我很快就发现,使用Julia编写代码比使用Python更容易,例如,即使使用Python完成一个基本的数据加工任务,也需要一大堆扩展包。

在使用Julia解决小问题之后,我决定使用Julia独立完成一个完整的数据科学项目。在经历了不可避免的学习曲线和成长阵痛之后,我终于达到了目标。这并不是我最得意的成果,但它证明了在进行一些训练、尝试和纠错之后,Julia可以高效地完成正式的数据科学任务。

在本书中,我会分享在这个项目以及随后的项目中获得的经验,阐述如何在数据科学的各个环节使用Julia。尽管现在已经有了一些介绍Julia的书籍,但还没有一本全面介绍如何在数据科学领域内应用Julia的专著。我曾非常期待有这样一本书,但有了多年使用Julia的经验之后,我决定亲自上阵,撰写这样的一本书。

我完全清楚,撰写一本介绍正处于发展时期的语言的书风险有多大,但是Julia这门语言不会停止发展,如果我等待它完全成熟,这本书就永远不会完成。

我并不期待你能够全面掌握Julia,或成为一个成熟的数据科学家。如果你渴望扩展技能,学习解决老问题的新方法,并严格按照本书的进度进行学习,那么Julia就会成为你进行数据分析的一个有效工具。


现在的编程语言有几十种,有些是通用的,有些则专注于某个领域,但每种语言都号称比其他语言更优秀。最强大的语言(能够快速执行复杂运算的语言)学习起来应该很难(要想掌握就更难了),它们的用户仅局限于那些对编程具有天赋的“硬核”程序员。雄心勃勃的数据科学家不得不面对这样一种前景:花费大量时间和精力学习了一门语言,却对他们的工作帮助甚微,写下了一行又一行复杂的代码,却实现不了一种可用的算法。

“即插即用”的编程语言是另外一种情况,它们将所有的编程复杂性都进行了精心的封装。那些最单调乏味的(一般也是应用最广泛的)算法都被预先包装好了,供用户方便地使用,几乎不需要学习过程。这些语言的问题是,它们的速度会很慢,而且对内存和运算能力有很苛刻的要求。数据科学家们又面临了一种与前面相反的困境:语言学习没有陡峭的学习曲线,这是个优点,但想用这些语言来完成任务,却困难重重。

Julia正是位于这两种极端情况中间的一种语言,它最大程度地综合了上面两类语言的优点。其实,它就是一门设计用来进行技术计算的编程语言,它计算速度快,易于使用,并内置了许多数据处理工具。尽管它还处于初级阶段,那些对它进行了充分测试的人们已经感受到了它的巨大潜力,并确信它在技术计算和数据科学领域内有很大的用武之地。

以下一些特点使Julia在众多编程语言中脱颖而出。

在学习和使用Julia的过程中,你肯定会发现它的更多优点,尤其是在数据科学方面。

“数据科学”是个相当含糊的名词,自从它成为科学领域一门学科后,就具有很多不同的意义。在本书中,我们这样来定义它:数据科学通过各种统计学和机器学习的技术与方法,将数据转换为有用的信息或知识。

由于数据的快速增长,数据科学必须利用各种工具的强大功能来应对大数据的挑战。因为数据科学的一大部分任务就是运行脚本来处理规模庞大、结构复杂的数据集(通常被称为“数据流”),所以一门高性能的编程语言对于数据科学来说不是奢侈品,而是必需品。

考虑一下某种特定的数据处理算法,它通过传统语言实现,需要运行几个小时。那么算法性能的适度提高就可以对数据处理过程的整体速度造成相当大的影响。作为一门新语言,Julia做的就是这样一件事情,这使它成为了数据科学应用的理想工具,既适合经验丰富的数据科学家,也适用于入门者。

人们认为数据科学是由多个环节组成的一个流程,每个环节都与手头的数据和分析目标密切相关。很多时候,这个目标是实现一个仪表盘或某种智能可视化结果(通常是可交互的),这通常被称为“数据产品”。

数据科学包括从真实世界(比如HDFS系统中的数据流,CSV文件中的数据集,或者关系数据库中的数据)中获取数据,对数据进行处理并得到有用的信息,以及将信息以一种精炼和可操作的形式返回到真实世界中。最终结果通常是数据产品的形式,但也不是必须的。举例来说,你可能被要求在公司的内部数据上面应用数据科学,但只要将结果以可视化的方式与公司管理者共享就可以了。

看一个小公司的例子,这个公司正在对博客订阅者进行问卷调查,从而进行数据驱动的市场研究。这个数据科学过程包括以下5个步骤。

1.从营销团队获取数据。

2.进行数据准备,将数据转换成可以用于预测分析的形式。

3.对数据进行探索性分析,分辨出是否某些人更倾向于购买某些特定产品。

4.对工作进行规范化,使整个工作过程达到资源有效和无误差。

5.开发模型,深入研究公司客户对哪些产品最感兴趣,以及他们期望为这些产品付多少钱。

我们会在第5章中对这个过程进行更详细的介绍。图1.1是数据科学过程的

图1.1 数据科学过程概览。3 个堆叠起来的圆形表示可以应用Julia 的阶段

一幅完整图景,其中也包含了Julia语言的适用范围。我们通常用三个堆叠起来的圆形表示Julia,在图中,这个符号指示出了Julia的用武之地。很明显,除了数据产品开发和数据获取,Julia几乎可以用于数据科学过程的各个阶段。

想想看,Julia可以在多大程度上简化你的工作流程?你不需要从其他平台上抓取代码来修补自己的流程,从而造成令人困扰的瓶颈。而且,只要你在Julia中调试通过了代码,也没有必要将其转换为像C++或Java之类的语言,因为这样做不会有性能上的提高。这一点非常重要,在用R和Matlab之类的语言构建原型时,这种转换是个必需的步骤。

你可能心生疑虑:“既然Julia是这么完美的一种语言,为什么还没有被数据科学社区广泛接受呢?”我们完全可以期待,Julia这种功能丰富的语言崭露头角,未来会像R和Python一样,在数据科学领域内占有一席之地。

尽管Julia对于任何类型的数据处理项目来说都是一种明智的选择,但是与其他更成熟的语言相比,它提供的扩展包还不够多(尽管随着用户数量的增加,可用的Julia扩展包的数量也在平稳地增长)。主要原因是Julia是一门年轻的语言,而且随着时间的推移,必定还会发展变化。

更重要的是,数据科学从业者与学习者还没有确信Julia可以像Python和R那样容易地学习和掌握。这两种语言都以具有庞大的用户社区为傲,用户社区使编程不仅简单,而且具有乐趣。想想Python Challenge吧:一系列编程任务,使学习Python就像是在游戏里面闯关。

毫无疑问,总有一天Julia也会发展出同等规模的粉丝圈,但现在它的影响力还不够,特别是在数据科学从业者中间。尽管Julia潜力巨大,但很多人发现,在Julia中写出清晰的代码并将初始程序调试通过是一件相当困难的事情。对新手来说,整个开发过程令人望而却步,甚至半途而废。

预先开发好的程序通常以“库”或“包”的形式来提供。尽管Julia提供了足够多的包来完成数据科学任务,但是还缺少一些算法,需要自己编程实现。在网页发展的初级阶段,HTML和CSS也面临了同样的问题,但是当它们的深奥技术逐渐成为主流,形势就一片大好了。对Julia的先驱者来说,可能会发生同样的事情。即使你没有积极地参与Julia编程社区,但在对这门语言逐渐熟练的过程中,你也肯定会受益匪浅。而且,当社区不断增长完善时,Julia用户完成任务会越来越容易,特别是对先驱者来说。

尽管现在能够扩展Julia功能的库文件(通常称为“包”)还比较少,但是Julia资源正在不断地增加。从2015年初到2016年中,Julia包的数量翻了一番,而且还看不出减慢的迹象。由于Julia的用户多数是从事高级计算的,所以这些包都是用来满足他们的需求的。包的更新就更加频繁了,这使得这门语言的鲁棒性不断提高。最后,因为Julia社区规模较小,并且联系紧密,所以很少有重复开发。

“现有的包的质量如何?”你可能会问这个问题。因为开发这些包的用户大多经验丰富,他们会尽力写出高质量的代码,从GitHub用户奖励的“星星”就可以反映出这一点。值得注意的是,从2015年末到我写完这本书为止,各种Julia包获得的星星数量增加了50%。很明显,在这个著名的程序仓库中,上传的Julia代码受到了越来越多的青睐。

很多GitHub中的包(不管是什么语言)都有一个有趣的特点,就是通过测试来建立程序的完整性和覆盖率等指标,这样在你开始使用这些程序之前,就可以清楚地了解它们的可靠性。对于最新版本的Julia(0.4),测试结果相当感人:在所有610个包中,63%的包通过了所有测试,只有11%的包没有通过测试(其余的包还没有进行测试,或者是不可测试的)。

如果想了解Julia包开发情况的最新信息,你可以访问http://pkg.julialang. org/pulse.html。此外,在本书的末尾,我们给出了一个参考列表,其中包括了数据科学应用中最常用的包。值得注意的是,尽管Julia不像其他语言那样有那么多类型的扩展包,但是对数据分析而言,Julia的扩展包完全够用了。本书的主要内容就是阐述这些扩展包的工作原理,以及如何使用它们来解决艰巨的数据科学问题。

如果你正在阅读本书(并计划按照书中的示例进行练习),那么你至少应该对数据科学领域有所涉猎。我假设你具有基本的编程经验,并对数据结构、GitHub仓库和数据分析过程有所了解。如果你曾经独立实现过某种算法,完整地开发过某种程序,或使用过GitHub上的现成程序解决过实际问题(哪怕是个简单的问题),那么你就有了一个良好的开端。

最重要的是,我希望你有一个脚踏实地的态度,在遇到问题时,可以熟练使用各种技术文档和论坛来寻求解决方案。最后,你必须对学习这门语言有种发自内心的兴趣,并将其与你的数据分析项目紧密地结合起来。

读到现在,你应该知道学习本书的最大收益就是:在掌握用于数据科学的Julia语言方面取得显著进步。你可能不会成为Julia开发专家,但你学到的知识足以使你看懂新的脚本,并完全可以使用Julia完成一些有趣的数据分析项目。其中的一些数据工程任务,如果使用其他编程语言来完成的话,会不厌其烦。

本书会提出一系列在数据科学流程中常见的、有代表性的实际问题,并指导你使用Julia去解决这些问题。你不需要去重新发明轮子,因为可以使用现有的内置功能和扩展包解决绝大多数问题。而且,你还可以使用几个真实数据集进行练习,以别人的成功经验为指导,不用在盲人摸象的情况下不断重复试错的过程。

本书将要介绍的内容如下。

1.可以用于Julia开发的几种现成的IDE(集成开发环境),以及如何使用文本编辑器来创建和编辑Julia脚本。

2.通过几个相对简单的示例程序,介绍Julia语言特性(主程序结构和函数)。

3.使用Julia完成数据工程任务的几种不同方式,包括数据的导入、清洗、格式化和存储,以及如何进行数据预处理。

4.数据可视化,以及几种简单但很强大的用于数据探索目的的统计方法。

5.通过各种技术去除不必要的变量,实现数据降维。在这部分内容中,我们还将涉及特征评估技术。

6.机器学习方法,包括无监督式学习方法(各种聚类技术)和监督式学习方法(决策树、随机森林、基本神经网络、回归树、极限学习机等)。

7.图分析方法,研究如何在现有数据上应用目前最流行的算法,并确定不同实体之间的联系。

除了上面这些内容,我们会继续讨论数据科学中的一些基本知识,这样,在深入钻研数据科学的各个环节之前,你会对数据科学的整体流程有一个清晰的认识。而且,书中的所有资料都带有补充信息,这对Julia初学者是非常重要的,补充信息介绍了将Julia安装到计算机上的方法,以及学习这门语言的一些资源。

在本书中,你将会接触到很多示例和问题,它们可以加强你对每章内容的理解和掌握。如果你确信已经掌握了书中的大部分知识,就可以编写自己的程序,充分发挥这门非凡编程语言的巨大威力。

本书会指导你如何以并行的方式运行Julia(如果你不能使用集群,在单机上也可以)。对于那些勇气十足、希望接受挑战的人,在最后一章可以利用本书中学到的所有知识和技能,使用Julia从零开始构建一项完整的数据科学应用。你准备好了吗?


就像每个名副其实的应用科学家一样,如果你想使用Julia研究数据科学,那么就需要建立一个工作环境,在里面探索数据、提取精华。同其他多数编程语言类似,Julia自带的界面是非常简单的,通常只有Read、Evaluate、Print、Loop(REPL)等几项功能。这个基础界面适合于对Julia进行试用,或者创建一些基础的脚本。

如果你想使用Julia做一些正式的工作,那么就需要一种为Julia量身定做的IDE。如果你想做些事情在单位里一鸣惊人,那么除了Julia基础程序,你还需要安装一些扩展包。

在本章中,我们将介绍如下内容。

在开始建立工作环境之前,你必须在计算机上安装Julia,附录A会指导你如何安装Julia。

我强烈建议你安装一个IDE,如果你习惯于使用文本编辑器,那么我建议你选择一个能够识别Julia代码的编辑器,这样你可以更容易地创建和编辑Julia脚本。

还有,从现在开始,你应该熟悉Julia选择扩展包的方法,并知道如何将扩展包安装在你的计算机上。你还可以掌握一些Julia的使用经验,使用Julia运行一些简单的算法,同时熟悉Julia的IO功能。

Juno是一个非常简单但又功能强大的IDE,它基于Light Table,专门用于编写Julia脚本。Juno具有自动完成功能,可以预测你要输入的函数或变量,这和多数智能手机的文本输入预测功能一样。自动完成功能可以提高编码的速度和准确性。

Juno界面直观、容易掌握,是Julia编码的明智选择。如果想了解它的更多功能,你可以参考它丰富的在线文档,通过“Help”菜单就可以访问。在图2.1中你可以看到Juno的屏幕截图。

当Julia启动或运行脚本的时候,Juno控制台中会给出一个方便的动态提示,这使你可以清楚地知道现在正在进行什么工作(在图2.1中你可以看到这种提示,就在行号下面)。遗憾的是,你不能像使用其他IDE(比如用于Python的Canopy)一样,在控制台中直接输入。但是,这并没有什么不方便,因为你可以在脚本窗口中轻松地运行单条命令,输入命令之后,按Ctrl+Enter(或Shift+Enter)组合键就可以了。

图2.1 所有功能窗口打开时的Juno屏幕截图

需要注意的是,当你想在Juno中运行脚本时,必须保证脚本的扩展名是.jl,这是IDE识别并运行Julia代码的唯一方式。

当脚本未按照预期运行时,Juno使用一种直截了当的方式来通知你:在控制台中使用红色高亮文本,如图2.2所示。

图2.2 Juno的局部屏幕截图,重点在于IDE中显示错误信息的部分。
以高亮的红色文本表示遇到的问题

尽管Juno还处于开发阶段,但它已经具有了很多久经考验的优良品质。Atom软件(一种可用于各种语言的通用IDE)近期已经可以集成Julia内核,这使得它可以识别并运行Julia脚本(参见http://bit.ly/2akTBua 获得详细信息)。

还有,Atom允许用户在文本编辑器(通常在这里编写所有的脚本)和REPL之间来回切换,这使得它成为测试代码的理想工具,你可以在测试成功后将代码正式加入程序。这样,Atom的Julia插件就成为了Julia IDE的另一种可行的选择,它正逐渐地被集成到Juno项目中。所以我们要密切注意这些项目的进展,因为它们可能成为Julia IDE的新的标准规范。你可以在Julia官方博客(http://bit.ly/29Nzsf1)中获得这些项目的更多信息。

在基于Atom的IDE成熟(最终会使Juno成为开发Julia脚本的首选IDE)之前,我们还是有其他开发充分、性能可靠的IDE可以选择。一种选择就是IJulia,也就是运行在网页浏览器中的Julia。如果你具有Python编程经验,并使用过IPython notebook,那么你会选择使用IJulia开始工作。实际上,我们在本书中就要使用这个IDE,使用这个IDE的一个理由就是它更加成熟(Julia开发者和专家在他们的报告中大多使用IJulia),另一个理由是它可以更好地展示代码。

说来奇怪,IJulia这种编码方式与其他任何一种IDE相比,都显得更老式,因为它本质上就是为Python开发的一个笔记本软件(现在称为Jupyter,以前叫IPython,可以在https://jupyter.org 下载),唯一的区别就是后端运行的是Julia内核。你可以将Jupyter看作是一种可以装配多种引擎的汽车:尽管它通常使用Python引擎,但使用Julia引擎也完全没问题。

IJulia可以方便地制作展示品和教程,还可以用来做数据探索。在使用Python进行工作的数据科学家中间,Jupyter能够流行一点都不奇怪。

所有的IJulia笔记都是运行在网页浏览器上的,尽管它们的代码文件是保存在本地的。你可以在这个站点找到相应的安装文件:http://bit.ly/1aA7oeg 。当然,你也可以通过JuliaBox(https://juliabox.com)在云上运行IJulia,这时你就需要一个Google账户。你可以在图2.3中看一下IJulia的界面。

图2.3 IJulia在线版本(JuliaBox)的屏幕截图。这种交互式版本适合在展示时
分享代码、创建教程,以及进行更高级的编程

除了Juno和IJulia之外,开发了Julia Studio的公司近期又发布了另一款Julia IDE。Julia Studio是一款优秀的IDE,可以使Julia像R和Matlab一样友好,并有可能使用户像在“back in the day”中那样爱上Julia。但是,这个项目有段时间没有任何进展了,尽管如此,一些Julia教程还是将它列为Julia的首选IDE。

最新发布的IDE称为Epicenter。对于现有的IDE,它是非常值得期待的一种备选方式,但是,它的免费版本现在功能还非常有限。如果你想创建更加精巧的Julia应用,你可以考虑购买Epicenter的付费版本。你可以在这个网址获得更多信息:http://bit.ly/29Nzvrv 。

还有一款IDE由著名的Tutorials Point网站(http://bit.ly/29KlxWp)开发,名字就叫作Julia IDE,但是现在已经淡出了人们的视线。它和JuliaBox有点像,都是基于云的,但并不要求你登录。它的界面很直观,可以和Juno媲美。此外,它允许你直接使用控制台,这样你就可以在一个窗口内进行脚本开发和脚本测试(不用在编辑器和REPL之间来回切换,也不用在Juno编辑器的两个标签页之间来回切换)。但是,它并不能一直支持最新版本的Julia,因此不能正确运行最新的扩展包,因此我建议你仅使用它来编写简单的脚本。

最后,如果你习惯于使用REPL配合简单的文本编辑器来进行Julia开发,并且你是个Windows用户,那么你可以使用Notpad++,它现在也已经有了Julia语言包(http://bit.ly/29Y9SWL)。请一定不要忘了使用正确的扩展名(.jl)来保存脚本文件,这样文本编辑器才能识别出它们是Julia脚本,并使用正确的高亮显示。另外,如果你使用Linux或Mac OS操作系统,那么你可以使用Emacs来编辑Julia脚本,这也需要安装相应的语言包(http://bit.ly/29Y9CqH )。

在前一章中我们已经知道,对于任何一种编程语言,扩展包都是非常重要而且广受欢迎的。原因非常简单:扩展包可以提供一些非常有用的附加功能,帮助你更有效率地完成任务,这些任务如果不使用扩展包,完全靠自己编程实现的话,一般会花费大量时间。扩展包的最大好处是,它们可以帮助我们完成那些最无聊和最浪费时间的任务。在你熟练掌握Julia之前,都要依赖扩展包的功能来处理你的数据。

在使用Julia平台时,找到所需的扩展包非常容易。最安全的方法就是使用Julia官方网站的相应网页(http://pkg.julialang.org),这里提供了一个列表,列出了所有被Julia生态系统正式采用的扩展包。这些扩展包也可以在GitHub上找到:https://github.com/JuliaLang 。

如果你想更加大胆地使用扩展包,那么你可以在GitHub上漫游一番,找到一些实验性的Julia代码库。但是请记住,使用这样的扩展包存在风险,请慎重。为了达到好的用户体验,请一定安装所有先决条件。

为你的任务选择恰当的扩展包也是一个简单直接的过程,只是带有一些主观因素。我的建议是仔细阅读扩展包中README.md文件中提供的文档。如果你认定了这个扩展包适合你的需求,就再查看一下文件列表下面的测试统计。图2.4给出了一个统计示例。

图2.4 一个相对成熟的Julia扩展包的测试统计。尽管这个扩展包看上去还需要
完善一些,但它通过了所有功能测试,不论是对于最近的稳定版本(左下角),
还是对于最新的未经充分测试的版本(右下角)

如果对于你想使用的Julia版本,扩展包通过了大多数测试,那么它就很适合你。甚至就算它现在有一些不稳定,为未来着想,你知道这个扩展包也没什么坏处,因为Julia扩展包的发展非常迅速,没准过不了多长时间,它就完全达到你的期望值了。

不管你正在使用Juno、IJulia、Epicenter、文本编辑器,还是 REPL,在某个时候总会意识到Julia基础包的功能是不够用的(尽管它比Python基础包的功能强大很多——至少在数据分析方面),必须安装扩展包。要在Julia中安装扩展包,你需要执行以下的代码:

Pkg.add("mypackage")

这里的mypackage是你要安装的扩展包的名称。必须使用双引号,因为Julia将其看作是字符串变量。依据扩展包体积的不同,在你的计算机上下载并安装扩展包需要不同的时间。在Windows系统下,扩展包会安装到C:\Users\username.julia\v0.4目录下,在基于UNIX的系统下,扩展包会安装到~/.julia/v0.4目录下。

非正式的扩展包(主要因为它们还在开发过程中)需要手工添加。幸运的是,Pkg.clone()命令会使整个过程变得非常简单。要安装非正式的扩展包,你只需要运行这个命令,将扩展包的GitHub URL作为参数传给它:

Pkg.clone("git://github.com/AuthorName/SomeCoolPackage.jl.git")

有些非正式扩展包可能不会正确地工作(特别是有一段时间没有更新的时候),甚至完全不工作。但是,熟悉这些扩展包还是很重要的,如果你想处于Julia技术最前沿的话!

尽管不是必需的,我们还是应该在安装完扩展包之后,不断地更新它们,因为我们最好使用扩展包的最新版本。你可以通过以下代码更新扩展包:

Pkg.update()

要完成这行命令需要一点时间,特别是在第一次运行的时候。如果你要安装很多扩展包,那么只需在安装完所有扩展包后运行这条命令,因为没有仅更新单个扩展包的方法。还有,我们应该定期运行Pkg.update()命令,来保证我们一直使用安装了的扩展包的最新版本。在REPL中运行这条命令时,出现的问题显示为红色,在其他情况下,显示为蓝色。

扩展包安装好之后,你就可以使用其中的功能了。在每个需要使用扩展包功能的Julia会话中执行以下代码即可:

using mypackage

扩展包安装好之后,它会被Julia识别为关键字,所以将它加载到内存中时,你不需要把它放在双引号中。

如果你对自己的Julia编程技术非常自信,就可以在不需要懂得C语言的情况下,试着自己去改善现有扩展包的功能。要达到这个目的,你需要修改扩展包的源代码,然后将它重新加载到内存中(使用前面提到的命令)。例如,在Windows系统下,扩展包CoolPackage的源代码应该是C:\Users\username.julia\v0.4\CoolPackage\src目录下的所有.jl文件。如果是基于UNIX的系统,路径就是!/username/.julia/v0.4/ CoolPackage。

1.创建笔记本

在IJulia中创建一个新文件(称为IJulia笔记本)非常简单。IJulia笔记本只能在IJulia中使用,在本地或云端都可以。如果你想创建一个脚本文件,与使用其他IDE的用户共享,那么你就需要将IJulia笔记本导出为.jl文件(我们马上就会介绍这种文件)。

如果你已经打开了IJulia,那么可以单击右上角的“New”按钮,然后在展开的菜单底部选择“Julia”选项。如果你正在处理一个笔记本文件,那么可以使用“File”菜单创建一个新的笔记本文件。图2.5展示了这两种方法。

图2.5 在IJulia中创建一个新笔记本:在主界面中(上图),或者在处理另一个
笔记本文件时(下图)

2.保存笔记本

你可以在IJulia中保存Julia笔记本,单击左上角的磁盘按钮,或者在“File”菜单中选择第5项命令,如图2.6所示。

图2.6 在IJulia中保存笔记本:通过“File”菜单(上图)或者通过磁盘按钮(下图)

在保存笔记本时,IJulia收集所有的文本和代码,并将它们放到一个扩展名为.ipynb的文本文件中。这种文件可以被Jupyter程序读取(即使计算机上没有安装IJulia)。我建议你将所有的Julia脚本放在一个文件夹中,以方便存取和引用。

Jupyter会定时自动保存你的笔记本,即使你关闭了笔记本,只要IDE仍在运行,就不会丢失任何东西。IJulia笔记本中包含的不只是代码,还有很多别的信息。所以当你保存这种文件时(使用扩展名.ipynb),同时保存了你做的所有标记(例如,小节标题和HTML文本)、Julia代码、以及在你保存笔记本时代码产生的所有结果(包括文本和图形)。

3.重命名笔记本

在IJulia中有两种方法来重命名笔记本(除了在操作系统中重命名实际文件)。最简单的方法是单击界面上方、Jupyter图标右侧的笔记本名称(缺省为“Untitled”),然后在弹出的文本框中输入新名称。通过选择“File”菜单中的第4个选项,也可以完成同样的任务,如图2.7所示。

4.加载笔记本

有很多种方法可以在IJulia中加载一个Julia笔记本。在主界面中,只需单击笔记本文件即可(文本文件也可以)。或者,如果你正在处理一个文件,那么就可以使用“File”菜单(这个操作会在Jupyter主界面中打开一个新的标签页)来加载另一个文件,如图2.8所示。

图2.7 在IJulia中重命名笔记本

图2.8 在IJulia中加载笔记本

5.导出笔记本

笔记本文件可以使用多种方法导出,这取决于下一步你想做什么。下面是几种选择。

以上的所有导出方式,以及一些其他导出方式,都可以通过“File”菜单实现,如图2.9所示。

图2.9 在IJulia中导出笔记本

因为Julia是一门函数式编程语言,所以你找到的或创建的脚本通常是以函数的形式来编写的。多个函数可以在一个.jl文件中以互不影响的方式和平共处。相关的函数应该放在同一个文件中。

举例来说,如果你有一个函数fun(x::Int64),还有另一个函数fun(x::Array{Int64, 1}),那么就应该把它们放在一起,因为它们通过多分派特性可以互相扩展。还有,你应该把依赖于fun()的函数MyFun(x::Int64, y::Int64)也放在同一个.jl文件中。我建议你将所有测试代码封装在一个函数中,比如main(),来保证它们不会被运行,除非你确实想运行它们。

如果你使用IJulia,那么上面的那些都不重要,但是如果你想与其他Julia用户共享代码时,那么就应该知道如何组织代码。因为所有的.jl文件都可以通过IJulia笔记本运行(使用include()命令),那么将经常使用的程序保存为.jl文件就是一个好的做法,特别是你想随时修改这些程序的时候。

实现和运行简单的算法是非常容易的。但是,有些时候你会想建立更精巧的算法,这时就需要使用保存在其他.jl文件中的函数。当然,你总是可以将相应的代码复制粘贴到你的.jl文件中,但是这种操作会产生不必要的冗余代码。所以,应该通过引用相应的.jl文件来调用你需要的代码。这可以通过以下命令来实现:

include("my other script.jl")

如果被引用的.jl文件位于另一个目录内,那么请一定在命令中包含完整的路径。请记住文件夹分割符不是传统的斜杠(\),而是双斜杠(\),或是反斜杠(/)。Julia将双斜杠看作是一个字符,尽管它包含两个字符。你可以通过输入length(“\”)来检验一下。

如果你运行了include()命令,那么这个文件中的所有函数都可以被调用:

include("D:\\Julia scripts\\my other script.jl")

重要的一点是,在引用的文件中,函数中没有任何不必要的代码。否则,在运行程序时,会产生混淆。

在编写Julia脚本时,有时你会希望识别出Julia认为你正在工作的位置,并且可能会改变这个位置。要完成以上的任务,可以使用两个简单的命令:pwd()和cd()。

pwd()命令是“print working directory”(打印工作目录)的缩写,它的功能也正是这样:

In[1]: pwd()
Out[1]: "c:\\users\\Zacharias\\Documents\\Julia"

如果你想改变所在的目录,可以使用cd()命令,就像在命令行窗口中一样:

In[2]: cd("d:\\")
In[3]: pwd()
Out[3]: "d:\\"

Julia的缺省工作目录记录在juliarc.jl文件中,这个文件位于Julia安装目录中,在目录中具体的路径是\resources\app\julia\etc\julia。如果你想修改这个文件,我建议你先创建一个备份文件,因为对这个文件的修改可能会严重影响Julia的功能。

如果没有数据,那么工作环境就很难说是完整的。通常,数据来自于多种渠道的综合。在本书中,我们假定你已经从数据源中提取出了数据,并已经将所有数据保存在了一系列数据文件中,这些数据文件主要是.csv格式的(逗号分隔值文件)。对于那些格式化不是太好的数据,我们会介绍如何解析这种数据,从中提取出有价值的信息,然后保存在结构化的文件中,比如.csv文件或其他带有分隔符的文件(在第5章中有详细的介绍)。我们还会介绍如何处理半结构化的数据,它们以原始数据的方式保存在.txt文件中。

在本书的大多数案例中,我们会使用两个.csv文件:magic04.csv和OnlineNewsPopularity.csv(来自于UCI机器学习库,http://archive. ics.uci.edu/ml)。我们还会使用SpamAssassin数据集,它与那些公开程序库中的标准数据集相比,是最贴近实际生活中的问题的。

1.Magic数据集

Magic(或Magic04)数据集指的是由Magic望远镜通过成像技术收集到的数据。它包含了大约19 000个数据点和10个特征。我们需要预测的属性(位于数据集中的最后一列)表示每个数据点对应的辐射类型:或者是伽马,或者是强子(分别用g和h来表示)。因此,这是个分类问题。下面是数据集中的几行数据,你可以看一下:

22.0913,10.8949,2.2945,0.5381,0.2919,15.2776,18.2296,7.3975,21.068,
    123.281,g
100.2775,21.8784,3.11,0.312,0.1446,-48.1834,57.6547,-
    9.6341,20.7848,346.433,h

2.OnlineNewsPopularity数据集

恰如其名,OnlineNewsPopularity数据集由来自于各个新闻网站的数据组成,数据集中的目标变量是流行度,使用文章被分享的次数来测量。这个数据集包含了大约40 000个数据点和59个特征(不包括URL属性,它更像是每个数据点的标识符)。在这个案例中,因为我们要预测的属性是连续的,所以这是个经典的回归问题。下面是这个数据集中的几条数据:

http://mashable.com/2013/01/07/beewi-smart-toys/, 731.0, 10.0,
 370.0, 0.559888577828, 0.999999995495, 0.698198195053, 2.0,  
     2.0, 0.0, 0.0, 4.35945945946, 9.0, 0.0, 0.0, 0.0, 0.0,   
     1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 8500.0,  
     8500.0, 8500.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,  
     0.0222452755449, 0.306717575824, 0.0222312775078,  
     0.0222242903103, 0.626581580813, 0.437408648699,  
     0.0711841921519, 0.0297297297297, 0.027027027027,  
     0.52380952381, 0.47619047619, 0.350609996065, 0.136363636364,  
     0.6, -0.195, -0.4, -0.1, 0.642857142857, 0.214285714286,  
     0.142857142857, 0.214285714286, 855
http://mashable.com/2013/01/07/bodymedia-armbandgets-update/,  
     731.0, 8.0, 960.0, 0.418162618355, 0.999999998339,  
     0.54983388613, 21.0, 20.0, 20.0, 0.0, 4.65416666667, 10.0, 1.0,  
     0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,  
     0.0, 0.0, 545.0, 16000.0, 3151.15789474, 1.0, 0.0, 0.0, 0.0,  
     0.0, 0.0, 0.0, 0.0, 0.0200816655822, 0.114705387413,  
     0.0200243688545, 0.0200153281713, 0.825173249979,  
     0.514480300844, 0.268302724212, 0.0802083333333,  
     0.0166666666667, 0.827956989247, 0.172043010753,  
     0.402038567493, 0.1, 1.0, -0.224479166667, -0.5, -0.05, 0.0,  
     0.0, 0.5, 0.0, 556

3.Spam Assassin数据集

最后一个数据集由.txt文件组成,是3 298封电子邮件,其中有501封是垃圾邮件(spam)。其余是正常邮件(称为ham),可以分为两类:easy ham和hard ham,这取决于准确检测它们的难度。正如你所料,这是个经典的分类问题。

在这个案例中,因为数据集本身没有特征,所以我们需要做些额外的工作。我们需要使用邮件中的文本数据从头开始创建特征。下面是一封邮件示例。邮件标题以粗体显示,它将是我们处理邮件时的关注重点。

Return-Path: <Online#3.19578.34 -  
         UgGTgZFN19NAr9RR.1.b@newsletter.online.com> 
Received: from acmta4.cnet.com (abv-sfo1-acmta4.cnet.com  
         [206.16.1.163])
by dogma.slashnull.org (8.11.6/8.11.6) with ESMTP id g69MseT08837
for <qqqqqqqqqq-cnet-newsletters@example.com>; Tue, 9 Jul 2002  
         23:54:40 +0100
Received: from abv-sfo1-ac-agent2 (206.16.0.224) by acmta4.cnet.com  
         (PowerMTA(TM) v1.5); Tue, 9 Jul 2002 15:49:15 -0700 (envelope-  
         from <Online#3.19578.34-  
         UgGTgZFN19NAr9RR.1.b@newsletter.online.com>)
Message-ID: <1100198.1026255272511.JavaMail.root@abv-sfo1-ac-  
         agent2>
Date: Tue, 9 Jul 2002 15:54:30 -0700 (PDT)
From: "CNET News.com Daily Dispatch" <Online#3.19578.34-  
         UgGTgZFN19NAr9RR.1@newsletter.online.com>
To: qqqqqqqqqq-cnet-newsletters@example.com
Subject: CNET NEWS.COM: Cable companies cracking down on Wi-Fi
Mime-Version: 1.0
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
X-Mailer: Accucast (http://www.accucast.com)
X-Mailer-Version: 2.8.4-2

我们之所以选择上面的数据集,是因为它们的复杂性很合适,既可以从中得到一些有趣的结论,又不至于太难,令人摸不着头脑。它们应用得也非常普遍,很多数据科学应用中都使用了这些数据集。你可以使用下面的任何一种方法来下载它们。

下载完成并解压缩之后,你可以使用电子表格软件(比如MS Excel或Gnumeric,这取决于你使用何种操作系统)看看这些数据集,甚至使用文本编辑器(数据保存在.csv文件中,所以可以使用一般的文本编辑器打开,系统自带的文本编辑器就可以)也可以打开这些数据集。阅读一下相应的.names文件,你可以得到关于这些数据集的更多信息。

1.CSV文件

为了更容易地访问上述的数据集,我建议你将提取出的.csv文件移动到Julia工作目录中,如果你是个Julia新手,就更应该这样做。要想知道Julia工作目录,只要运行我们前面提到过的pwd()命令即可:

In[1]: pwd() #A

#A println(pwd())也可以完成同样的任务,特别是使用其他IDE和REPL的时候。

你可以使用下面的简单命令将.csv文件加载到Julia工作区(Julia操作的计算机内存):

In[2]: data = readcsv("magic04.csv");

命令末尾的分号不是必须的,但是如果你不想控制台窗口被上载文件中的数据填满的话,最好还是加上。文件中的数据被保存到一个名为“data”的二维数组(矩阵)中。你也可以将数据加载到其他形式的数据结构中(比如我们的最爱:数据框),但是我们稍后再来讨论这个问题。还有,请记住你可以在文件名中包含文件路径。如果你的.csv文件保存在D:\data\目录,那么你就应该运行下面的代码:

In[3]: data = readcsv("D:\\data\\magic04.csv");

2.文本文件

你可以非常容易地将文本数据(例如:一封垃圾邮件)加载到Julia工作区中,使用以下命令即可:

In[1]: f = open(filename, "r")
lines = readlines(f);
close(f)

上面的代码片段会将一个文本文件加载到IO对象f中,这个文件的路径和名称保存在filename中。参数r告诉Julia,你创建对象的目的只是想从中读取数据。

要使用IO对象f,你需要调用一个能够接受这个对象作为输入的函数,比如readlines()。readlines()可以解析整个文件,并将其中的内容分割为字符串,然后返回一个字符串数组作为函数输出。

下面是上述方法的一个变体,使用for循环依次对文件中的每一行进行解析:

f = open(filename, "r")
for line in eachline(f)
     [some code]
end
close(f)

如果你想将文本文件中的所有内容保存在一个字符串内,那么你可以使用下面的代码片段来完成这个操作:

f = open(filename, "r")
text = readall(f);
close(f)

尽管这种方法看上去更加优雅,但实际上不是最好的选择。原因有两点。第一,在数据科学领域内,你需要处理的文本文件经常会很大,将它们保存在一个变量(或一个数组)中会占用大量内存(这通常会引起成本的上升)。第二,我们通常不需要整个文件,所以我们可以一行接一行地处理文件,不需要在内存中保存多于一行的数据。

为了使你更好地熟悉新的工作环境,我们从一个相对简单的例子开始。如果你以前没有用过Julia,那么即使你没有完全理解这个例子,也无需担心,因为这个例子的目的只是让你熟悉一下Julia的界面和风格。当你适应了Julia的编码风格后,我们希望你可以重温一下这个示例。

现在,你可以自己输入代码,也可以使用kNN.jl文件,与这个文件在一起的是本书中其余的Julia代码文件。在IJulia或一个文本编辑器中打开这个文件,拷贝所有内容,然后粘贴到一个新的IJulia笔记本中。

我们选择一个最简单的但是依旧非常有用的算法。作为一个数据科学家,你应该学会编码,并应用在实际工作中,比如实现一个可以完成分类任务的算法。对于magic数据集,我们就是要应用这样的算法,将望远镜观测到的辐射进行分类,类别为gamma(伽马)或hadron(强子),分别用g和h来表示,是数据集中的最后一个属性。

为了完成这个示例,我们会使用数据集其他属性中的信息。最终结果是对未知观测的一个预测,预测它们属于两个类别中的哪一类,并使用这些观测的实际类别对预测进行检验。

这一节的重点不是使你成为一个分类算法专家,我们会在后续章节中讨论分类算法,所以如果有些内容你不理解的话,也是很正常的。目前,你只需把注意力放在Julia上面,尽量弄清楚程序是如何运行起来的。

我们要应用的算法称为“k最近邻”(kNN,k Nearest Neighbor),是机器学习初期的基本分类算法。尽管是个古老的算法,但在图像分析、推荐系统以及其他领域内,还有着广泛地应用。kNN是一种基于距离的分类器。尽管这种算法没有训练阶段,但当速度是最关键因素的时候,它的鲁棒性还是很好的。

这种算法非常简单易懂:要对一个给定的(未知的)数据点X进行分类,先找到与它最相似的k个数据点,然后进行多数投票。相似度通常与距离是相反的,所以与X距离最近的那些数据点被选择出进行投票。算法伪代码如代码清单2.1所示。

代码清单2.1 kNN算法伪代码

Inputs: training data input values (X), training data labels (x),
     testing data input values (Y), number of neighbors (k)
Output: predicted labels for testing data (y)
for each element i in array Y
     calculate distances of Yi to all training points Xj         #A
     find indexes of k smallest distances
     break down these data points based on their classes
     find the class with the majority of these k data points      #B
     assign this class as the label of Yi
end for
#A   distance function
#B   classify function

图2.10给出了更详细的算法流程。

图2.10 kNN算法(基础的机器学习分类算法)流程

要在Julia中实现kNN算法,我们需要编写两个函数:距离计算函数和分类函数。它们可以封装在主函数内,主函数称为kNN()。为简单起见,在这次对kNN算法的实现中,我们使用欧式距离(在实际工作中,你可以使用任何一种你喜欢的距离测量方式)。一般来说,最好从辅助函数开始,在本例中,辅助函数就是distance()和classify()。如果你使用单个脚本运行所有代码的话,你必须先定义好所有的函数,然后才能使用它们,正如在前一章中我们介绍的那样。

首先,我们需要考虑一下每个函数使用什么作为它们的输入,以及生成何种结果作为输出。对于distance()函数,很明显:它需要接受两个向量化的数据点(一维数组)作为输入,并生成一个数值(浮点数)作为输出。对于classify()函数,它需要接受一个包含所有数据点的距离的向量,还有一个包含所有数据点的标号的向量,以及要检查的近邻的数量(一个数值),最终生成一个独立元素(可以是一个数值,也可以是一个字符串,这取决于数据集中标号的方式)。

在Julia中,尽管对元素进行定义是非常重要的,但是如果定义得太具体的话,可能会导致令人沮丧的错误。举例来说,如果我们需要两个数值x和y作为输入,而且并不关心它们是何种类型的数值,那么我们就可以仅将x和y定义为数值,而不是定义为浮点数或整数。这样会使函数的功能更加丰富。但是,如果有些数值类型作为输入是没有意义的,我们就必须定义得更具体一些。

例如,对于函数classify(),我们不希望k是个带小数点的数值,也不希望它是个分数或是个复数,它应该是个整数。所以在相应函数中(在本例中,就是封装函数kNN()以及classify()函数),把它定义成整数相对来说更好。至于要在kNN()中使用的数据集,它应该是矩阵的形式,因此应该被定义成矩阵。

我们从辅助函数开始实现算法,这样你可以熟悉如何在Julia中实现一种算法。首先,我们会定义如何计算两个数据点之间的距离,如代码清单2.2(有更好的方法来实现这个功能,但这种方法更容易理解)所示。

代码清单2.2 实现kNN算法的一个辅助函数,负责计算x和y两个点之间的距离,x和y用向量表示。

In[1]: function distance{T<:Number}(x::Array{T,1}, y::Array{T,1})
dist = 0                        #A 
for i in 1:length(x)            #B
     dist += (x[i] - y[i])^2 
end
dist = sqrt(dist)
return dist
end
#A initialize distance variable
#B repeat for all dimensions of x and y

我们可以将这些代码写在主方法中,因为它们很短,但是如果我们想换一种距离测量方式,那将怎么办呢?我们可以将代码分成若干个辅助函数,每个函数都可以独立工作,并且易于理解和编辑,这样再做上面的修改就非常容易了。

现在我们进入算法的关键部分:基于计算出的距离,对一个数据点进行分类。我们多次调用距离函数,生成一个一维数组distances,我们使用这个数组作为classify()函数的一个输入。classify()函数的代码如代码清单2.3所示。

代码清单2.3 实现kNN算法的另一个辅助函数,基于一个数据点与数据集中已知数据点之间的距离,对这个数据点进行分类。

In[2]: function classify{T<:Any}(distances::Array{Float, 1},
     labels::Array{T, 1, k::Int64}
     class = unique(labels)                        #A 
     nc = length(class)                            #B
     indexes = Array(Int,0)                        #C
     M = maxtype(typeof(distances[1]))             #D 
     class_count = Array(Int, nc)
     for i in 1:k
           indexes[i] = inmin(distances)
           distances[indexes[i]] = M               #E
     end
     klabels = labels[indexes]

     for i in 1:nc
           for j in 1:k
                 if klabels[j] == class[i]
                       class_count[i] += 1
                       break
          end
     end
end
index = inmax(class_count)
return class[index]
  end
#A find all the distinct classes
#B number of classes
#C initialize vector of indexes of the nearest neighbors
#D the largest possible number that this vector can have
#E make sure this element is not selected again

现在,可以通过实现主函数(通常称为包装器函数)将所有过程组合起来了。当我们想使用kNN算法时,就调用这个函数(我们也可以分别调用每个子函数,这在调试阶段非常有用)。所以,让我们输入代码清单2.4中的代码,以完成我们的项目。

代码清单2.4 实现kNN算法的主函数(包装器)。

In[3]: function apply_kNN{T1<:Number, T2<:Any}(X::Array{T1,2},
     x::Array{T2,1}, Y::Array{T1,2}, k::Int)
N = size(X,1)                        #A 
n = size(Y,1)                        #B 
       D = Array(Float, N)           #C 
       z = Array(typeof(x[1]), n)    #D    
for i in 1:n
     for j in 1:N
           D[j] = distance(X[j,:], Y[i,:])
     end

z[i] = classify(D, x, k)
end
return z
     end
#A number of known data points
#B number of data points to classify
#C initialize distance vector
#D initialize labels vector (output)

要应用这个算法,肯定需要一些数据。所以,我们加载前面小节中介绍过的magic数据集:

In[4]: data = readcsv("magic04.csv")

这行代码会将数据保存在一个矩阵中。为了便于处理,我们先将数据重新组织一下,分成输入(特征)和输出(标号)。你可以使用下面的命令来完成这个操作:

In[5]: I = map(Float64, data[:, 1:(end-1)])     #A
In[6]: O = data[:, end]                         #B
#A take all the columns of the data matrix, apart from the last one
    and convert everything into a Float. Result = 10-dim Array of
    Float numbers
#B take only the last column of the data matrix. Result = 1-dim
    Array

现在,如果你想使用这些数据来测试分类器(这里就是kNN算法),那么这些数组都应该分成训练集和测试集。这是一项值得深入研究的技术,所以我们会在后续章节中继续讨论。眼下,我们可以使用基本的随机抽样来完成这个任务。

首先,我们得到训练集数据索引的一个随机集合(比如,数据点总数的一半)。然后,我们分别从I和O中选取出相应的数据点和它们的标号,并保存在两个数组中。在此之后,我们将剩余的数据点和它们的标号放在另外两个数组中。你可以使用代码清单2.5中的代码完成以上的操作(我们希望你能在IJulia或其他JuliaIDE中输入这些代码)。

代码清单2.5 kNN算法实现的测试代码,使用预先加载的Magic数据集。

In[7]: N = length(O)               #A
In[8]: n = round(Int64, N/2)       #B
In[9]: R = randperm(N)             #C
In[10]: indX = R[1:n]              #D 
In[11]: X = I[indX,:]              #E 
In[12]: x = O[indX]                #F 
In[13]: indY = R[(n+1):end] 
In[14]: Y = I[indY,:]              #E
In[15]: y = O[indY]                #F
#A number of data points in the whole dataset (which is equivalent
    to the length of array O)
#B the half of the above number
#C a random permutation of all the indexes (essential for sampling)
#D get some random indexes for the training set 
#E input values for training and testing set respectively
#F target values for training and testing set respectively
#G some random indexes for the testing set

现在你可以看看刚刚建立的kNN分类器在实战中的表现了。只需运行以下命令:

In[16]: z = apply_kNN(X, x, Y, 5)             #A
In[17]: println( sum(y .== z[1]) / n )         
println(z[1][1:5], z[2][1:5])                 #B
#A predicted values and the accompanying probabilities (output of
    the classifier)
#B accuracy rate of classification and small sample of the
    classifier’s output

在运行这段代码时,你可以在IJulia笔记本中看到以下内容(因为进行了随机抽样,所以每次运行的结果都会有些变化):

Out[17]: 0.805888538380652
    Any["g","g","g","g","g"][0.6,0.8,0.6,0.8,0.6]

可喜可贺!你已经使用Julia完成了你的第1次数据科学实验!请一定将这些代码保存在一个IJulia笔记本中(或保存为其他格式,比如.jl文件),因为你以后可能还会用到它们。

既然你已经得到了一些结果,那么你可能想把它们保存在什么地方,以备未来引用或进一步处理。和R不同,Julia不在你退出时保存工作区,如果你想在下次启动Julia内核时还能够引用某些特定的变量,最好亲自将它们保存在某个地方。幸运的是,你可以使用下面两种有效的方法来完成这个任务。

每种方法都有各自的优点,我们会在下面的小节中进行更详细介绍。

这可能是最简单的也是使用得最多的保存方式。这种方法不需要任何扩展包,生成的文件易于其他程序存取,文件格式也是多数人熟悉的格式。你可以使用下面的writedlm()函数将数据(比如,一个数组A)保存为分号分隔值文件,使用分号(;)作为数据域之间的分隔符:

writedlm("/data/mydata.dat", A, ";")

如你所见,writedlm()函数的第1个参数是文件名(包括路径),第2个参数是要保存的数组,第3个参数是分隔符。分隔符通常是一个字符,尽管它可以是任意字符串(比如,:)。这个参数的缺省值是一个制表符(用\t表示),得到的结果是.tsv文件。然而,你创建的分隔值文件的扩展名是由你来指定的,Julia不会做任何假定,即使在你看来这是很显然的事情。

分隔值文件的一种特殊情况是.csv文件,当涉及到数值型数据时,经常使用这种文件来保存数据。你可以通过选择逗号作为分隔符来将数据保存到.csv文件中,但是有一个更简单的方法:writecsv()函数。你可以按照下面的方法使用这个函数:

writecsv("/data/mydata.dat", A)

分隔值文件不是最节约资源的保存数据集的方法,但是很多时候,我们必须使用这种方法。当你的数据量不断增加时,或者你希望保留相关变量的元数据的时候,最好使用另一种保存数据文件的方法。因为本书示例使用的数据大小都在可控范围内,所以我们在输出数据时,总是使用分隔值文件。

很多时候,使用一门语言本身的格式文件来保存数据更容易。例如,Graphlab使用SFrames、SArrays和SGraphs文件,R使用.RData文件,Matlab使用.mat文件。这并不是说其他语言不能使用这些格式的文件,只是说这些文件更适合使用那些创建它们的语言来处理。Julia数据格式文件(.jld文件)就是这样一种文件,它由Simon Kornblith和Tim Holy根据他们自己的需要而开发的,使用的是通用的HDF5数据格式。开发这样一种格式的文件可不是闹着玩的,他们花费了很多精力才完成了这项任务。

要使用这种数据格式,你必须先添加JLD和HDF5扩展包,然后才能使用它们创建.jld文件来保存数据(比如一个由浮点数组成的数组A,和一个整数b)。你可以使用下面的代码来完成这些操作:

Pkg.add("HDF5")
Pkg.add("JLD")
using JLD
f = open("mydata.jld", "w")
@write f A
@write f b
close(f)

字符@在Julia中有特殊意义,当它应用于一个函数时,会改变函数的语法,使函数功能更加丰富。我们建议你在完全掌握了Julia基础知识之后再来深入研究@的用法:大概在你使用Julia解决了几个实际问题,并且对本书中介绍的函数完全熟悉之后。

另外,我们可以不使用上面最后4行代码,而是用以下的等价代码来替换它们:

save("mydata.jld", "var_A", A, "var_b", b)

在上面的代码中,引号中的参数对应着输出文件名和变量的存储名(这里我们使存储名和原来的变量名不同,来避免混淆)。如果你想将工作区中的所有变量保存下来,你只需要输入以下命令:

save("mydata.jld")

如果想提前出保存在.jld文件中的数据,可以使用以下代码:

D = load("mydata.jld")

加载.jld文件会创建一个字典,字典中包含了文件的所有内容,字典中的键就是变量名。如果你想从.jld文件中只提取出一个特定的变量(例如,这个变量的名称为“var_b”),那也非常简单,你可以使用以下代码:

b = load("mydata.jld", "var_b")

还可以使用另外一种方法从.jld文件中提取变量,如下所示:

f = jldopen("mydata.jld","r")
dump(f, 20)

这段代码会从.jld文件中提取出前20个变量,并把它们保存在字典中(例如,变量名:变量类型和维度)。当你不确定要提取出哪个变量时,这种方法特别有用。

JLD扩展包是一个非常新的扩展包,在本书写作时,它的文档还很不完备。我们希望你能主动地对它多了解一些,随着时间的推移,它会越来越完善。这个扩展包非常有价值,因为它可以使数据存储和提取更加容易。你可以通过它的文档获得更多信息,文档的GitHub链接为:http://bit.ly/29fVavH 。

如果数据是高度非结构化的,不适用于前面的任何一种保存方式,那么你可以将数据保存为纯文本文件。但是请注意,将数据保存为文本文件,如果以后想提取数据,就要进行一些额外的工作(我们会在第6章中讨论这种情况)。要将数据保存为.txt文件,只需使用以下代码:

f = open("/data/mydata.txt", "w")
write(f, SomeStringVariable)
write(f, AnotherStringVariable)
.
.
.
close(f)

在数据文件中,为了在每对连续变量之间留出间隔,你应该在每个字符串后面加上换行符(在Julia中用\n来表示)。所以,如果你想在数据文件(已经打开了)中保存字符串数据“Julia rocks!”,那么你应该做出如下的修改:

data = string(data, "\n")

当然,这不只限于字符串变量,不管你向文本文件中保存什么类型的数据,最终都会被转换成字符串类型。所以,如果你有一个数组A,你可以使用如下代码来保存它:

A = [123, 34423.23, -322,
     4553452352345234523452345345261709106832734]
f = open(("/data/mydata.txt", "w")
for a in A
write(f, string(a, "\n"))
end
close(f)

于是,这个数组就会被保存在data文件夹中的mydata.txt文件中,每个元素占一行。这样,在文本编辑器中读取文件的内容时会更加容易,而使用其他语言编写脚本来处理这个文件时,也会更加容易。

不管你是什么样的专业背景,你总会遇到一些自己搞不定、需要寻求帮助的情况,最常见的情况就是如何使用函数。当遇到这种情况时,先不要急着上Stackoverflow去提问,你应该先查看一下Julia文档。要查看Julia文档,只需使用如下命令:

help(somefunction)

或者,你也可以使用如下方法:

? somefunction

尽管这个函数的输出不是很简单易懂,但的确很有帮助。这个函数你用得越多,就越容易理解它的输出内容。Julia的发展日新月异,熟悉它的文档是非常必要的。你应该将其视为全面学习Julia的最好资料来源,应该特别关注其中的数据类型、操作符和函数部分。

如果你遇到的是一般性的问题,那么你可以搜索Julia手册,它的链接为:http://bit.ly/29bWHU2,也可以搜索Julia的维基教科书,它的链接为:http://bit.ly/29cIges 。我更喜欢后者,因为其中有更多的示例,也更易于阅读。对于那些主观性的问题(比如“我应该如何评价这段代码的性能?”),你最好还是去咨询专业人士。你可以从Stackoverflow开始(使用的标签是“Julia-lang”),Google上的Julia用户群也是一个不错的选择。无论如何,坚持就是胜利!和任何一种新语言一样,Julia有自己的特点,需要一定的时间来适应和习惯。正因为这个原因,在本书后面的章节中,我们会使代码尽量简化,这样你就可以集中注意力来体会这门语言的精彩之处和价值所在。

1.如果你想利用Juia的最新功能,那么应该使用哪种IDE?

2.如果因为缺少管理员权限,你不能在计算机上安装Julia,那么你应该使用哪种IDE?

3.相对于REPL,使用IDE(例如,Juno,或tutorialspoint.com提供的在线IDE)有哪些好处?相对于IDE和REPL,IJulia有哪些优点?

4.为什么应该在程序中使用辅助函数?

5.什么是包装器函数?

6.sqrt()、indmin()和length()函数的功能是?它们的参数是什么类型?(提示:对每个函数使用help()命令。)

7.表达式sum(y == z)/length(y)的取值范围是?

8.假设你有一个数组A,想将其保存在.csv文件中,应该如何实现?

9.工作区中有很多不同类型的数组和单值变量。因为操作系统要安装一批系统更新(又来了!),所以你必须关机。你想将最重要的变量保存起来,并保持数据类型不变,以便你能在操作系统更新完成之后继续工作,那么你应如何操作?

10.函数max()和maximum()有什么区别?如果想找出两个数值中的最大值,应该使用哪个函数?(提示:再一次使用help()命令,运行一下示例代码,看看二者到底有什么区别。)

11.如何安装和更新NMF扩展包?如何将其加载到内存中?

12.你能使用kNN算法分析一下你手头的文本数据吗?说说你的做法。

13.小组中的资深数据科学家认为,在分析数据集时,应该使用曼哈顿距离(又称城市街区距离),而不是欧氏距离(你可以在这个网站获得关于这种距离的更多信息:http://bit.ly/29J8D0Y)。不用重新从头开始实现kNN算法,你应该如何操作?

注:以上所有问题的答案都可以在附录F中找到。


相关图书

SPSS医学数据统计与分析
SPSS医学数据统计与分析
首席数据官知识体系指南
首席数据官知识体系指南
大数据实时流处理技术实战——基于Flink+Kafka技术
大数据实时流处理技术实战——基于Flink+Kafka技术
大数据安全治理与防范——流量反欺诈实战
大数据安全治理与防范——流量反欺诈实战
搜索引擎与程序化广告:原理、设计与实战
搜索引擎与程序化广告:原理、设计与实战
医疗大数据挖掘与可视化
医疗大数据挖掘与可视化

相关文章

相关课程