Java数据分析指南

978-7-115-49486-3
作者: [美]约翰·哈伯德(John R. Hubbard)
译者: 高蓉 李茂
编辑: 胡俊英

图书目录:

详情

本书包含11章内容,详细介绍了数据分析、数据预处理、数据可视化、数据统计、关系型数据库、回归分析、分类法、聚类法、推荐系统、NoSQL数据库以及Java大数据分析等内容。由浅入深地带领读者了解数据分析的各项要点,并结合具体的代码示例讲解如何通过Java及相关编程方法实现数据分析。

图书摘要

版权信息

书名:Java数据分析指南

ISBN:978-7-115-49486-3

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

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

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

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

著    [美] 约翰•哈伯德(John R. Hubbard)

译    高 蓉  李 茂

责任编辑 胡俊英

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

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

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

读者服务热线:(010)81055410

反盗版热线:(010)81055315


Copyright ©2017 Packt Publishing. First published in the English language under the title

Java Data Analysis.

All rights reserved.

本书由英国Packt Publishing公司授权人民邮电出版社出版。未经出版者书面许可,对本书的任何部分不得以任何方式或任何手段复制和传播。

版权所有,侵权必究。


当今,数据科学已经成为一个热门的技术领域,例如数据处理、信息检索、机器学习、自然语言处理、数据可视化等都得到了广泛的应用和发展。而Java作为一门经典的编程语言,在数据科学领域也有着卓越的表现。

本书旨在通过Java编程来引导读者更好地进行数据分析。本书包含11章内容,详细地介绍了数据科学导论、数据预处理、数据可视化、统计、关系数据库、回归分析、分类分析、聚类分析、推荐系统、NoSQL数据库以及Java大数据分析等重要主题。

本书适合想通过Java解决数据科学问题的读者,也适合数据科学领域的专业人士以及普通的Java开发者阅读。通过阅读本书,读者将能够对数据分析有更加深入的理解,并且掌握实用的数据分析技术。


高蓉,博士,毕业于南开大学,现任教于杭州电子科技大学。研究领域包括资产定价、实证金融、数据科学应用,已出版著作多部,发表论文数篇。感谢浙江省教育厅科研项目(编号Y201840396)、浙江省自然科学基金项目(编号LY17G030033)、南开大学基本科研费(编号63185010)对翻译本书的支持。

李茂,毕业于北京师范大学,现任教于天津理工大学。热爱数据科学,从事与统计和数据分析相关的教学和研究工作。


约翰•哈伯德(John R. Hubbard)任教于宾夕法尼亚州和弗吉尼亚州的高校,从事计算机数据分析工作长达40余年。他拥有宾州州立大学的计算机科学硕士学位和密歇根大学的数学博士学位。目前,他在里士满大学担任数学和计算机科学的名誉教授,他在该校讲授数据结构、数据库系统、数值分析和大数据。

哈伯德博士出版了许多著作并发表过多篇论文,除了本书,他还出版过6本计算领域的著作。其中某些著作已经翻译为德文、法文、中文和其他5种语言。此外,他还是一位业余音乐家。

我要感谢本书审阅者的宝贵意见与建议。此外,我还要感谢Packt出版社的强大团队出版并完善本书。最后,我要感谢我的家庭对我无微不至的支持。


埃林•帕奇奥可夫斯基(Erin Paciorkowski)是乔治亚理工学院的计算机科学专家,一位优秀学者。她在国防部从事Java开发超过8年,也是乔治亚理工学院在线计算机硕士项目的研究生助教。她是一位经过认证的敏捷专家,同时持有Security+、Project+和ITIL基金会证书。在2016年,她获得了格蕾丝•霍珀荣誉奖学金。她的兴趣包括数据分析和信息安全。

阿列克谢•季诺维也夫(Alexey Zinoviev)是EPAM系统的首席工程师、Java和大数据培训师,精通Apache Spark、Apache Kafka、Java并发以及JVM内部机制。他在机器学习、大型图形处理以及分布式可扩展Java应用开发领域具有深厚经验。你可以通过@zaleslaw关注他,或通过GitHub关注他。

感谢我的妻子阿娜斯塔娅和可爱的儿子罗曼,在相当长的时间里包容我专心审阅本书。


“有人说,技术只有教给别人,自己才能真正理解。而真相是,除非教给计算机,即作为算法实现,否则依然无法真正理解。”

——高德纳(Donald Knuth)

正如高德纳的名言,理解某种技术的最佳方法是实现。本书通过展示Java编程语言的实现,帮助你理解一些最重要的数据科学算法。

本书介绍的算法和数据管理技术通常归入以下领域:数据科学、数据分析、预测分析、人工智能、商业智能、知识发现、机器学习、数据挖掘,以及大数据。其中很多新颖的方法都会令人震惊!例如,ID3分类算法、K-均值和K-中心点聚类算法、亚马逊的推荐系统以及谷歌的PageRank算法,这些技术几乎影响着每一个使用网络电子设备的人。

第1章,“数据科学导论”,介绍本书主题,概述了数据分析的历史发展及其在解决社会关键问题方面扮演的重要角色。

第2章,“数据预处理”,阐述存储数据的多种格式、数据集的管理,以及基本的预处理技术,比如排序、合并和散列。

第3章,“数据可视化”,包含图形、图表、时间序列、移动平均、正态和指数分布以及Java应用。

第4章,“统计”,概述基本的概率和统计原理,包括随机性、多元分布、二项分布、条件概率、独立性、列联表、贝叶斯定理、协方差和相关性、中心极限定理、置信区间以及假设检验。

第5章,“关系数据库”,介绍关系数据库的开发与访问,包括外键、SQL、查询、JDBC、批处理、数据库视图、子查询以及索引。你将学习如何使用Java和JDBC分析存储在关系数据库中的数据。

第6章,“回归分析”,讲述预测分析的一个重要部分,包括线性回归、多项式回归以及多元线性回归。你将学习在Java中如何使用Apache Commons Math Library实现这些技术。

第7章,“分类分析”,包括决策树、熵、ID3算法及其Java实现、ARFF文件、贝叶斯分类器及其Java实现、支持向量机(SVM)算法、logistic回归、K-最近邻以及模糊分类算法。你将学习在Java中如何使用Weka库实现这些算法。

第8章,“聚类分析”,包括分层聚类、K-均值聚类、K-中心点聚类以及仿射传播聚类。你将学习如何在Java中使用Weka库实现这些算法。

第9章,“推荐系统”,包括效用矩阵、相似性度量、余弦相似性、亚马逊的item-to-item推荐系统、大型稀疏矩阵以及具有历史意义的网飞大奖赛(Netflix Prize competition)。

第10章,“NoSQL数据库”,主要介绍MongoDB数据库系统,同时介绍地理空间数据库和基于MongoDB的Java开发。

第11章,“Java大数据分析”,包括谷歌的PageRank算法及其MapReduce框架。需要特别注意两个典型示例——WordCount和矩阵操作,它们都是MapReduce的完整Java实现。

附录,“Java工具”,指导你安装本书用到的所有软件:NetBeans、MySQL、Apache Commons Math Library、javax.json、Weka以及MongoDB。

本书的重点是帮助读者理解数据分析的基本应用原理和算法,方式是指导读者通过Java编程不断深化对基本原理和算法的理解。因此,读者需要具备一定Java编程经验。如果读者还具有一些基础的统计学知识和数据库工作经验,那么学习起来会感觉更轻松。

如果你是一名学生或者从业者,希望深入理解数据分析,并希望在该领域提高Java的算法开发能力,本书正是为你而写!

本书通过不同的文本样式区分不同种类的信息。这里举例说明这些样式并解释其含义。

书中的代码、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟URL、用户输入,都会按这样的字体显示:“我们可以通过使用include指令包含其他环境。”

代码块设置如下:

Color = {RED, YELLOW, BLUE, GREEN, BROWN, ORANGE}
Surface = {SMOOTH, ROUGH, FUZZY}
Size = {SMALL, MEDIUM, LARGE}

命令行的输入和输出格式如下:

mongo-java-driver-3.4.2.jar 
mongo-java-driver-3.4.2-javadoc.jar

新术语和重要词汇以黑体表示。例如,你会在屏幕上看到在菜单或对话框中出现这样的文字:“点击下一个按钮可以移到下一屏。”

 

警告或重要的注释形式如下。

 

提示和技巧形式如下。


本书由异步社区出品,社区(https://www.epubit.com/)为您提供相关资源和后续服务。

本书提供配套源代码,要获得该配套资源,请在异步社区本书页面中单击,跳转到下载界面,按提示进行操作即可。注意:为保证购书读者的权益,该操作会给出相关提示,要求输入提取码进行验证。

如果您是教师,希望获得教学配套资源,请在社区本书页面中直接联系本书的责任编辑。

作者和编辑尽最大努力来确保书中内容的准确性,但难免会存在疏漏。欢迎您将发现的问题反馈给我们,帮助我们提升图书的质量。

当您发现错误时,请登录异步社区,按书名搜索,进入本书页面,单击“提交勘误”,输入勘误信息,单击“提交”按钮即可。本书的作者和编辑会对您提交的勘误进行审核,确认并接受后,您将获赠异步社区的100积分。积分可用于在异步社区兑换优惠券、样书或奖品。

我们的联系邮箱是contact@epubit.com.cn。

如果您对本书有任何疑问或建议,请您发邮件给我们,并请在邮件标题中注明本书书名,以便我们更高效地做出反馈。

如果您有兴趣出版图书、录制教学视频,或者参与图书翻译、技术审校等工作,可以发邮件给我们;有意出版图书的作者也可以到异步社区在线提交投稿(直接访问www.epubit.com/selfpublish/submission即可)。

如果您是学校、培训机构或企业,想批量购买本书或异步社区出版的其他图书,也可以发邮件给我们。

如果您在网上发现有针对异步社区出品图书的各种形式的盗版行为,包括对图书全部或部分内容的非授权传播,请您将怀疑有侵权行为的链接发邮件给我们。您的这一举动是对作者权益的保护,也是我们持续为您提供有价值的内容的动力之源。

“异步社区”是人民邮电出版社旗下IT专业图书社区,致力于出版精品IT技术图书和相关学习产品,为作译者提供优质出版服务。异步社区创办于2015年8月,提供大量精品IT技术图书和电子书,以及高品质技术文章和视频课程。更多详情请访问异步社区官网https://www.epubit.com。

“异步图书”是由异步社区编辑团队策划出版的精品IT专业图书的品牌,依托于人民邮电出版社近30年的计算机图书出版积累和专业编辑团队,相关图书在封面上印有异步图书的LOGO。异步图书的出版领域包括软件开发、大数据、AI、测试、前端、网络技术等。

异步社区

微信服务号


数据分析是对数据进行组织、清洗、转换和建模的过程,目的是获取有价值的信息和新知识。数据分析、商业分析、数据挖掘、人工智能、机器学习、知识发现和大数据,这些术语也可以用来描述相似的过程。这些领域之间的区别更多体现在应用领域,而非基础本质。有人认为,这些领域都是数据科学新学科的一部分。

在从组织化数据中获取有效信息的过程中,关键步骤是应用计算机科学算法进行管理。而本书的重点就是这些算法。

数据分析是一个历久弥新的领域。它起源于数值方法和统计分析的数学领域,可以追溯至18世纪。近年来,随着互联网愈加普遍和海量数据逐渐可得,许多数据科学方法受到越来越多的关注,随后我们将研究这些算法。

在第1章中,我们来讲述数据分析史上的一些著名案例。这些案例可以帮助我们理解这门科学的重要性和未来前景。

数据与文明一样历史悠久,甚至年代更为古老。1.7万年前,法国拉斯科的原始居民为了纪念他们最伟大的狩猎胜利,尝试以洞穴壁画的形式记录这些胜利。这些记录为我们提供了旧石器时代人类活动的数据。从现代意义上讲,这些数据并没有被分析,也没有为我们提供新知识。但是,这些数据的存在本身就证明了人类需要使用数据保存自己的思想。

5000年前,美索不达米亚的苏美尔人在泥板上记录了更为重要的数据。那些楔形文字记录了与日常商业交易相关的大量会计数据。为了运用数据,苏美尔人不仅发明了文字,还发明了人类文明史上的第一个数字系统。

在1086年,威廉国王(译者注:1066年,诺曼底公爵威廉征服英格兰)为了确定王室和臣民的土地与财产范围,下令收集大量数据。因为这是对人们(物质)生活的最终盘点,因此被称为“末日审判书”。威廉国王分析这些数据,并确定了随后几个世纪中土地的所有权和纳税义务。

在1572年11月11日,一位名叫第谷·布拉赫的丹麦贵族青年观测到一颗超新星,我们现在称这颗超新星为SN1572。第谷从那时起一直到30年后逝世,将毕生的财富与精力都奉献给积累天文数据的事业。他有一位年轻的德国助手,名叫约翰尼斯·开普勒,开普勒(见图1-1)最终在1618年阐明了行星运动三大定律,而此前他花了18年的时间分析这些数据。

图1-1 开普勒

开普勒的成就通常被科学家视为科学革命的开始。从那时起,观察自然、收集数据、分析数据、形成理论,然后用更多的数据来检验这个理论,这成为了科学方法的基本步骤。请注意,这里的核心步骤是数据分析。

当然,今天的数据分析师熟悉的现代工具,包括算法和实现算法的计算机,当年的开普勒一样都没有。但是,他运用了对数,这项技术突破帮助他解决了数值计算的问题。开普勒曾在1620年说到,纳皮尔在1614年发明的对数对于他发现行星运动三大定律发挥着至关重要的作用。

开普勒的成就深深地影响了后辈伊萨克·牛顿。这两位大师都凭借科学方法获得了无与伦比的成功!

牛顿的朋友为数不多,埃德蒙·哈雷是其中一位。哈雷首次计算出彗星的轨道,后来这颗彗星以他的名字命名。哈雷博学多识,精通天文学、数学、物理学、气象学、地球物理学,以及制图学。

哈雷在1693年分析了死亡率数据,这些数据由德国布雷斯劳的卡斯帕·诺依曼编制。正如90年前布拉赫的数据启发了开普勒的工作一样,哈雷的分析也启发了新知识。根据他发表的结果,英国政府可以为年金制定合适的价格并进行出售,其依据是领取年金者的年龄。

尽管今天的大部分数据都是数值型的,但我们研究的大多数算法都可以运用于类型更加广泛的数据,包括文本、图像、音频以及视频文件,甚至互联网上的整个网页。

1821年,一位名叫查理·巴贝奇(见图1-2)的剑桥大学青年学生钻研着一些刚刚手算出的三角函数表和对数表。当他发现数据中的错误举不胜举时,怒喊出“我真希望用蒸汽来计算”的设想。他建议这些表格可以使用蒸汽机驱动的机械自动计算。

图1-2 巴贝奇

巴贝奇是一位业余数学家,担任剑桥大学的卢卡斯数学教授,在此150年前,伊萨克·牛顿曾担任这个教席,在此150年后,斯蒂芬·霍金也担任这个教席。但是,巴贝奇一生的大部分时间中都致力于自动计算。他提出可编程计算机的概念后,被人们尊为第一位计算机科学家,而他的助手阿达·勒芙蕾丝被认为是第一位计算机程序员。

巴贝奇的目标是建立一台分析数据并可获取有价值信息的机器,这个功能是数据分析的核心步骤。如果这个步骤实现自动化,就可以在更大的数据集上更快地运行。巴贝奇对三角函数表和对数表相当有兴趣,这与他希望改进航海导航方法的目标紧密相关,而这个目标是大英帝国版图扩张的关键。

1854年,一场霍乱在伦敦的贫民区爆发。这场传染病之所以迅速传播,部分原因是没有人知道疾病的根源。但是,一位名叫约翰·斯诺的医生怀疑传染的根源是受污染的水。在那时,大部分伦敦人的生活用水来自公共水井,公共水井直接来自泰晤士河。图1-3展示了斯诺绘制的地图,黑色矩形标示霍乱发生的频率。

图1-3 斯诺医生的霍乱地图

如果你仔细看,还可以看到9个公共水泵的位置,用黑点标记并标注PUMP。根据这个数据,我们可以轻松看出宽街和剑桥街拐角处的水泵位于疾病的传染中心。这个数据分析启发斯诺去探索那个水泵的供水情况,结果他发现污水未经处理就通过管道的破口直接流入水泵。

斯诺通过在地图上定位公共水泵,提出疾病的源头很可能就是宽街和剑桥街拐角处的水泵。在公共卫生领域,这是一项最早的重大数据分析应用案例。霍乱在19世纪导致高达数百万人的死亡,詹姆斯·诺克斯·波尔克总统和作曲家彼得·伊里奇·柴可夫斯基也在其中。但这种疾病到今天依然普遍,全世界每年大约有10万人死于霍乱。

1789年,美国国会委托展开十年一度的人口普查,目的是确定众议员代表数量和征税的分配。第一次人口普查在1790年展开,当时美国的人口不足400万,普查仅仅计算自由人的数量。但美国人口的数量到1880年已经超过5000万。同时人口普查本身也变得更为复杂,它还需要记录家属、父母、出生地、财产和收入。

1880年的人口普查编制花了整整8年时间,因此美国人口普查局认识到需要使用某种自动化方式改进1890年的普查。他们雇佣一位名叫赫尔曼·何乐礼(见图1-4)的年轻工程师,他提议制造一台使用打孔卡片记录数据的电子制表机系统。

图1-4 何乐礼

这是自动化数据处理的首次应用,结果非常成功。普查制表才开始6周,报告的美国总人口就接近6200万。

因为这个成就,何乐礼获得了MIT的博士学位。他在1911年建立了计算—制表—记录公司,这家公司在1924年改名为IBM。IBM最近制造了超级计算机沃森,它可能是数据挖掘和人工智能领域最成功的大规模应用。

在第二次世界大战期间,美国海军战列舰的火炮能将2700磅(约1225千克)的炮弹发射到24英里(约38.6千米)以外。炮弹在射程中飞行接近90秒。除了火炮的仰角、幅角以及发射初速度,炮弹的轨迹还会受到舰船运动、天气条件甚至是地球自转运动的影响。轨迹的精确计算非常重要。

为了解决计算问题,美国军队在宾夕法尼亚大学成立了一个工程师团队来建造ENIAC,也就是第一台可编程的完全电子化数字计算机。尽管直到第二次世界大战结束这个项目都没有完成,但它本身就是巨大的成功。

ENIAC(见图1-5)体积庞大,占了一个大房间,还需要一组工程师和程序员来操作。这台计算机的输入和输出数据都记录在何乐礼卡片上,其他的机器也可以自动读取这些卡片和打印内容。

图1-5 ENIAC

ENIAC在氢弹开发中发挥了重要作用,它取代了炮兵射表而用于氢弹模拟项目的第一次测试运行,使用的卡片多达100多万张。

1979年,哈佛学生丹·布里克林在上课时看到教授在黑板上修改财务数据表格条目的过程非常冗繁。教授一旦完成某个条目错误的修正,接着又要修正条目对应的边际条目。布里克林想到,这么单调的工作完全可以在他的新苹果II微型计算机上更轻松更准确地完成。后来他发明了VisiCalc软件包,这是第一个用于微型计算机的电子表格计算机程序。许多人都认为,这项发明将微型计算机从业余爱好者的游戏平台转向了正规的商业工具。

布里克林的VisiCalc引发了商业计算的范式转变。此前电子表格计算是商业数据处理的一种基本形式,计算过程需要庞大且昂贵的大型计算中心,而现在相同的工作只需要一个人使用一台个人计算机就可以完成。IBM PC发行两年以后,VisiCalc就成为商业和会计的基本软件。

1854年的霍乱传染案例是理解数据、信息和知识之间差异的一个典型的例子。斯诺医生使用的数据,包括霍乱爆发以及水泵位置的数据,都是已知的,但它们之间的关系是未知的。斯诺医生通过在一张城市地图上绘制两个数据集,确定宽街和剑桥街的水泵就是污染源。因此,关系就是新信息。新信息最终带来了新知识,一个新知识是疾病通过污水传播,另一个新知识是随后这种疾病的预防方式。

10多年来,Java一直都是世界主流的编程语言,同时越来越受欢迎,其理由相当充分:

其他的流行软件各有优势,Python的学习更轻松,R的运行更简单,JavaScript的网站开发更容易,C/C++的速度更快,但就通用编程来说,Java无可替代。

太阳计算机系统有限公司(Sun Microsystems)的詹姆斯·高斯林领导的团队在1995年开发了Java。甲骨文公司在2010年以7.4亿美元的价格收购了该公司,从此支持Java。本书撰写时的主流版本是2014年发布的Java 8。但当你购买本书时,Java 10应该可以找到,它在2018年3月发布。

正如本书书名所示,书中所有示例都使用Java编写。

 

附录包括使用Java设置计算机的方式说明。

为了简化Java软件开发,许多程序员会使用集成开发环境(Integrated Development Environment, IDE)。网上有多种优质的免费JavaIDE可以下载,包括:

这些IDE的工作原理类似,因此你只要使用过一个,就很容易使用别的。

尽管本书的所有Java示例都可以在命令行中运行,但我们还是选择在NetBeans上展示程序运行。这个IDE有许多优点,包括:

清单1-1是NetBeans中标准的“Hello World”程序。

清单1-1 Hello World 程序

当这个程序在NetBeans中运行时,可以看到它的一些语法着色:评论是灰色的,保留字是蓝色的,对象是绿色的,字符串是橘色的。

在大部分情况下,我们为了节省篇幅会省略展示清单中的头注释和包指定,仅仅展示程序,就像清单1-2这样:

清单1-2 缩短的Hello World程序

或者,有时我们仅展示main()方法,就像清单1-3这样:

清单1-3 进一步缩短的Hello World程序

无论如何,所有的完整源代码文件都可以从异步社区网站下载。

图1-6是“Hello World”程序的输出结果。

图1-6 Hello World程序的输出结果

 

附录阐述NetBeans的安装与启动。

本章的第1章讲述了一些引发数据分析发展的重大历史事件:古代商业记录的保管、英国皇家对土地和财富的资料汇编,以及天文学、物理和航海的精确数学模型。正是这些活动启发巴贝奇发明了计算机。从霍乱根源的识别,到经济数据的管理和海量数据的现代化处理,文明前进的步伐推动着数据分析的发展。

本书选择Java编程语言实现所研究的数据分析算法,本章简单解释了选择Java的理由,并在最后介绍了本书会一直使用的集成开发环境(IDE)——NetBeans。


在分析数据之前,通常需要对其形式进行标准化处理。本章描述这些处理的过程。

数据可以划分为不同的类型。数据类型不仅标识数据的形式,也标识可以对数据执行哪种操作。例如,算数运算可以对数值型数据执行,但不能对文本数据执行。

数据类型也决定了数据需要的计算机存储空间大小。例如,像3.14这样的十进制数值通常存储在一个32位(4字节)内存中,而像https://google.com这样的网址则占用160位内存。

下面是本书将要处理的数据主要类型,对应的Java类型显示在括号中:

计算机科学将变量视为数据值的存储位置。Java通过对变量声明特定的类型来引入变量。例如,考虑下列语句:

String lastName;

该语句声明变量lastName具有类型String

另外,声明变量时还可以使用具体值进行初始化,就像这样:

double temperature = 98.6;

该语句可以这样理解,命名为temperature的存储空间中包含double类型值98.6

结构化变量还可以在一个语句中同时进行声明和初始化:

int[] a= {88, 11, 44, 77, 22};

这个语句声明int数组类型的变量int[]包含5个指定元素。

数据分析很容易将数据视为信息点。例如在一组个人信息中,每个数据点包含某个人的信息。考虑下列数据点:

("Adams", "John","M", 26, 704601929)

它代表一位叫作John Adams的26岁男性,ID号为704601929

数据点中的单个数据值称为字段(或属性)。每个数据值都有自己的类型。上例中5个字段的类型是3个文本的和两个数值的。

数据点的字段数据类型序列叫作类型签名,上例的类型签名是(文本、文本、文本、数值、数值)。该类型签名对应在Java中是(String, String, String, int, int)。

数据集是数据点的集合,一个数据集中所有数据点的类型签名都相同。例如代表一组人的一个数据集,其中每个点都代表组中的唯一成员。集合中所有点的类型签名都相同,因此签名就是数据集本身的特征。

NULL值

有这样一种特殊的数据值,类型未定,还可以充当任何类型,这种数据值就是null值,它表示未知。例如,前面描述过的数据集包含的数据点("White", null, "F", 39, 440163867),代表一位39岁的人,姓氏是White、ID号码是440163867,女性,但她的名字缺少信息(或者未指定)。

在关系数据库中,每个数据集都可以看作一张表,每个数据点都是表中的一行。数据集签名定义表列。

表2-1是关系数据库表的例子,这个表有4行5列,表示包含5个字段4个数据点的数据集。

表2-1

性别

年龄

 ID 

Adams

John

26

704601929

White

null

39

440163867

Jones

Paul

49

602588410

Adams

null

30

120096334

 

 

这张表有两个null字段。

正如数据集中数据点的顺序无关紧要,数据库表在本质上是行集合,因此行顺序也一样无关紧要。同理,数据库表不会包含重复的行,数据集也不会包含重复的数据点。

数据集可以要求指定字段的所有值都不重复。这样的字段称为数据集的关键字段。在前面的示例中,ID数字字段可以当作关键字段。

在约束数据集(或数据库表)的关键字段时,设计者应该预见到数据集可能存储的数据类型。例如,前面表中的“First name”字段就是一种关键字段的糟糕的选择,因为许多人都会同名。

键值用于搜索。比方如果想知道保罗·琼斯的年龄,可以先找到ID为602588410的数据点(也就是行),再检查这个数据点的年龄。

数据集也可以不指定单个字段而指定字段的一个子集作为关键字段。例如在一个地理数据的数据集中,我们可以指定Longitude(经度)和Latitude(纬度)字段联合作为关键字段。

指定的字段子集(或单个字段)关键字段可以视为一组键—值对(Key-Value Pairs, KVP)。从这种角度看,每个数据点都包含两部分:它的键和对应的键值(实际中也用到属性值对这个术语)。

在前面的示例中,键是ID,值是Last nameFirst nameSexAge

在之前提过的地理数据集中,键可以是LongitudeLatitude,而值可以是AltitudeAverage TemperatureAveragePrecipitation

有时候,键—值对可以视为一种输入输出结构,关键字段是输入,字段的值是输出。例如,在地理数据集中,给定经度和纬度,数据集就返回对应的海拔、温度和平均降水量。

键值对的数据集通常会作为哈希表实现。健对于这种数据结构,就像索引对于集合,这很像书中的页码或表中的行数。这种直接访问比顺序访问快得多,后者就像在一本书中逐页搜索一个特定的词或者短语。

键值对数据集在Java中通常使用java.util.HashMap<Key,Value>类实现。类型参数KeyValue是指定的类。(还有一个更古老的HashTable类,但过时了。)

图2-1是7个南美国家的数据文件。

图2-1 南美国家的数据文件

清单2-1是将这个数据载入HashMap对象的Java程序。

清单2-1 HashMap示例

Countries.dat文件在data文件夹中。第15行实例化一个名为dataFilejava.io.File对象来代表这个文件。第16行实例化一个名为datasetjava.util.HashMap对象。这个结构化文件具有String类型的键和Integer类型的值。在try代码块内部,我们实例化一个Scanner对象来读取文件。第22行每个数据点的载入都使用HashMap类的put()方法。

数据集载入后,我们在第27行打印它的大小,在第28行打印Peru的值(格式化代码“%,d”表示打印逗号分隔的整型数值。)

图2-2是HashMap程序的输出结果。

图2-2 HashMap程序的输出结果

注意在上一个示例中,get()方法是如何实现哈希表键值结构的输入输出内容。在第28行,输入是名称Peru,输出的结果是该国家的人口29907003

哈希表改变某个特定键的值很容易。清单2-2是同一个HashMap示例,只是多3行代码。

清单2-2 改进后的HashMap示例

第29行将Peru的值改为31000000

图2-3是程序修改后的输出结果。

图2-3 HashMap程序修改后的结果

注意,哈希表的大小不变!但键值更新后,put()方法加入了一个新的数据点。

上一个示例中的Countries.dat文件是一个平面文件,即没有特殊结构或格式的普通文本文件,是最简单的数据文件类型。

另一种常见的简单数据文件格式是逗号分隔值文件(Comma Separated Values ,CSV),也是一种文本文件,但数据值的分隔使用逗号而非空格。图2-4是CSV格式的数据,内容与上一个示例相同。

图2-4 CSV文件

 

这个示例增加了一个标题行,从而可以根据名称CountryPopulation识别列。

为了Java可以进行正确的处理,必须告诉Scanner对象把逗号当作分隔符。清单2-3的第18行在input对象实例化后完成了这个任务。

清单2-3 读取CSV数据的程序

图2-5 CSV程序的输出

格式化代码%-10s表示以10列字段格式打印字符串并左对齐。 格式化代码%,12d表示以12列字段格式打印字符串并右对齐,每3位数字之前加逗号(为了可读性)。

 

第34~35行代码实例化HSSFWorkbookHSSF Sheet对象。这些代码以及第38~39行的代码要求从外部包org.apache.poi.hssf.usermodel导入3个类,这3个导入语句是:

POI-3.16相应的Java文档可以从https://poi.apache.org/ download.html下载。相应的NetBeans安装指令参见附录。

从微软Excel读取和写入数据的最好方式是使用Apache软件基金会的POI开源API库。你可以从这里下载这个库https://poi.apache.org/download.html。选择当前的poi-bin压缩文件。

本节展示两个Java程序,用来在Map数据结构和Excel工作簿文件之间来回复制数据。我们使用TreeMap而没有使用HashMap的目的仅仅在于展示前者根据键值保持数据点顺序的方式。

import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel. HSSFSheet;
import org.apache.poi.hssf. usermodel. HSSFWorkbook;

第一个程序命名为FromMapToExcel.java,清单2-4是它的main()方法。

清单2-4 FromMapToExcel程序

第23行的load()方法将图2-1显示的Countries数据文件载入map。第24行的print()方法打印map的内容。第25行的storeXL()方法在我们的data文件夹中创建excel工作簿Countries.xls,先在这个工作簿中创建一个工作表,再把map数据存储在工作表中。

Excel工作簿和工作表的结果如图2-6所示。

注意这个数据与图2-1显示的数据相同。唯一的区别在于巴西的人口超过100000000,Excel以四舍五入的形式进行展示,并以指数符号表示:2.01E+08。

load()方法的代码与清单2-1的第15~26行相同(除了第16行)。

清单2-5是print()方法的代码:

图2-6 由FromMapToExcel程序创建的Excel表格

清单2-5 FromMapToExcel中的print()方法

清单2-5的第45行从map提取键(countries)的集合,接着第47行对每个国家提取对应的人口数量,第48行进行打印。

清单2-6是storeXL()方法的代码。

第60~63行实例化outworkbookworksheetcountries对象,接着for循环的每次迭代都在worksheet对象中载入一行,这段代码含义清晰。

下一个程序从Excel表载入Java map结构,与之前程序的工作正好相反。

清单2-6 FromMapToExcel程序中的storeXL()方法

清单2-7 FromExcelToMap程序

它仅仅调用loadXL()方法并打印map结果。

清单2-8 FromExcelToMap程序中的loadXL()方法

第37~45行的循环对Excel工作表的每一行迭代一次。每次迭代得到这一行中两个单元格的值,并在第44行将数据对放入map中。

Excel是一个优秀的数据编辑可视化环境。但正如上面的例子所示,它无法令人满意地处理结构化数据,尤其是当数据像经过网络服务器那样自动传输时。

Java作为面向对象语言,可以很好地处理结构化数据,比如列表、表和图。但作为编程语言,Java不能在内部存储数据。而文件、电子表格和数据库都需要存储数据。

机器可读、结构化数据的标准化文件概念可以追溯到20世纪60年代。这种思想把数据嵌入代码块,每块都需要通过打开和关闭的标签进行标识。这些标签基本定义了这种结构的语法。

这也是IBM开发通用标记语言Generalized Markup Language,GML)和之后的标准通用标记语言Standard Generalized Markup Language,SGML)的原因。SGML在军事、航空航天、出版以及技术参考行业应用广泛。

可扩展标记语言(Extensible Markup Language,XML)是SGML在20世纪90年代的衍生语言,主要目的是满足万维网的传输需求。图2-7是XML文件的一个示例。

图2-7 XML数据文件

图2-7中显示了3个对象,每个对象的字段个数都不同。注意,每个字段都以一个打开标签开始,并以匹配的关闭标签结束。例如,字段2017具有打开标签和关闭标签。

XML的特点是简单、灵活和易于处理,因此成为了主流的数据传输协议。

JavaScript对象标记(JavaScript Object Notation,JSON)格式是在21世纪初开发的,脚本语言JavaScript流行后不久就兴起了JSON。JSON继承了XML的良好思想,并经过修改适应了Java(和JavaScript)程序的轻松管理。JSON中的J代表JavaScript,但任何编程语言都可以使用JSON。

JSON的主流Java API有两种:javax.jasonorg.json。同时Google在com.google.gson中还有一个GSON版本。我们将使用官方的Java EE版本javax.jason

JSON是一种数据交换格式,一种用于在自动信息系统内传递信息的文本文件语法。正如XML,JSON与CSV文件一样使用逗号分隔文本。但不同之处在于JSON可以很好地处理结构化数据。

JSON文件的所有数据都可以表示为名—值对(name-value pairs),比如:

"firstName" : "John"
"age" : 54"
"likesIceCream": true

接着,对这些名—值对进行嵌套可以形成结构化数据,与XML的原理一样。

图2-8展示了与图2-7中XML文件数据结构相同的JSON文件。

图2-8 JSON数据文件

根对象是一个以books命名的名—值对。这个JSON对象的值是一个包含3个元素的JSON数组,其中每个元素都是代表一本书的JSON对象。注意,每个对象的结构都稍有不同。例如,前两个JSON对象有authors字段,是另一个JSON数组,而第三个JSON对象有标量author字段。此外,后两个JSON对象没有edition字段,而最后一个JSON对象没有isbn字段。

首先,每对大括号{}定义一个JSON对象。最外边的大括号定义JSON文件本身。其次,每个JSON对象也是一对大括号,中间有一个字符串、一个冒号和一个JSON值。这个JSON值可以是JSON数据值、JSON数组或者另一个JSON对象。JSON数组是一对中括号[],中间是一串JSON对象或JSON数组组成的序列。最后,JSON数据值可以是字符串、数字、JSON对象、JSON数组、TrueFalsenull。一般来说,null表示未知。

JSON可以在HTML页面中使用,将以下语句包括在<head>部分:

<script src =" js/ libs/ json2. js"> </script>

如果JSON文件结构已知,那么可以使用JsonReader对象读取,如清单2-10所示。否则可以使用JsonParser对象,如清单2-11所示。

解析器是一个对象,可以读取输入流中的令牌并识别类型。例如,在图2-7表示的JSON文件中,头3个标记是{、books以及[。它们的类型分别是START_OBJECT、KEY_NAME和START_ARRAY,从清单2-9的输出可以看出。注意JSON解析器将令牌作为事件进行调用。

清单2-9 识别JSON事件类型

通过这种方式识别令牌可以确定如何处理它们。如果它是START_OBJECT,那么下一个令牌一定是KEY_NAME。如果它是KEY_NAME,那么下一个令牌要么是键值,要么是START_OBJECT或者START_ARRAY。如果它是START_ARRAY,那么下一个令牌一定是另一个START_ARRAY或者另一个START_OBJECT。

清单2-10讲解了解析,它包含着双重目标:一是提取键—值对(真实数据),二是提取数据集的全部数据结构。

清单2-10 解析JSON文件

清单2-11是getMap()方法。

清单2-11 解析JSON文件用的getMap()方法

清单2-12是getList()方法。

清单2-12 解析JSON文件用的getList()方法

 

实际数据无论是名称还是值,都是通过方法parser. getString()和parser.getInt()获取的。

下面是程序的无格式化输出,目的仅仅是进行检查:

{books=[{year=2004, isbn=0-13-093374-0, publisher=Prentice Hall, title=Data Structures with Java, authors=[John R. Hubbard, Anita Huray]}, {year=2006, isbn=0-321-34980-6, edition=4, publisher=Addison Wesley, title=The Java Programming Language, authors=[Ken Arnold, James Gosling, David Holmes]}, {year=2017, author=John R. Hubbard, publisher=Packt, title=Data Analysis with Java}]}

举个例子说明Java打印键—值对的默认方法,year=2004,其中year是键,2004是值。

如果需要运行这样的Java程序,可以从https://mvnrepository.com/ artifact/org. glassfish/javax.json/1.0.4下载文件javax.json-1.0.4.jar。

点击Download (BUNDLE)即可完成下载。

如何在NetBeans中完成安装,请参见附录。

将下载的jar文件(目前是json-lib-2.4-jdk15.jar)拷贝到方便使用的文件夹中(例如,Mac的Library/Java/Extensions/)。如果你正在使用NetBeans,选择Tools | Libraries载入IDE,再在项目图标上点击鼠标右键并先后选择PropertiesLibraries,选择Add JAR/Folder,继续操作并选择javax.json-1.0.4.jar

使用Java生成数值测试数据集非常容易,可以使用java.util.Random对象生成随机数。清单2-13是生成随机数的代码。

清单2-13 生成随机数

这个程序生成了图2-9中的CSV文件,有8行5列,每个元素都是随机小数。

图2-9 测试数据文件

元数据是关于数据的数据。例如,前面生成的文件可以描述为“8行逗号分隔的小数,每行5个”,这就是元数据。有时你会需要这种信息,比如写程序读取这个文件时。

这个示例相当简单:数据无结构,数值类型相同。而结构化数据的元数据需要描述数据的结构。

数据集的元数据可以作为数据本身包括在同一个文件中。上面的例子可以使用一个标题行修正,如图2-10所示。

图2-10 文件头有元数据的测试文件

 

在Java中读取数据文件时,可以使用Scanner对象的nextLine()方法扫描前几行,如清单2-15第32行所示。

数据清洗,也叫作数据清理,描述了从数据集中寻找不可靠的数据值并进行修正或删除的过程。不可靠的数据常常源于数据输入或转录时的粗心。

数据清洗过程可以辅以各种软件工具的帮助。例如,微软Excel有CLEAN()函数用来消除文本文件中不可打印的字符。大多数统计系统有许多更通用的清洗函数,比如R和SAS。

Spellcheckers提供了大部分人会用到的数据清洗功能,但是它们无法帮助解决像把form拼成from和把their拼成there这样的错误。

统计离群点也很好认出,例如,当ExcelCountries表中巴西的人口显示为2.10而非2.01E+08时。

程序的约束有助于停止错误的数据输入。例如,某些变量只能从指定的集合取值,如国家的ISO标准两字母缩写(CN代表中国、FR代表法国,等等)。类似的是文本数据预期会符合预定格式,比如电话号码和电子邮件地址,在输入过程中可以自动检查。

数据清洗过程的基本要素是避免利益冲突。如果一位研究者为了支持某个已有理论而收集数据,那么原始数据的任何替换都必须采取最透明和最客观的方式。例如在新药物的测试过程中,制药实验室必须维护任何数据清洗的公共日志。

对数值数据进行缩放可以令它更有意义。数据缩放也称为数据标准化,这等同于对数据集的一个字段中所有值使用同一个数学函数变换。

摩尔定律的数据就是一个好例子。图2-11展示了绘制的几十个数据点,刻画了从1971年到2011年微处理器使用的晶体管数量。晶体管数量从2300增长到2.6亿。如果对计数字段使用线性缩放,那么大部分点会聚集在刻度下方,从而数据无法显示。当然,事实上晶体管数目是指数型增长而非线性增长的。因此,只有进行对数缩放才适合该数据的可视化。换句话说,应该对每个数据点(x, y)绘制点(x, log y)。微软Excel允许使用缩放函数。

图2-11 摩尔定律

过滤通常指数据集的子集选择,选择会基于数据字段上的某些条件进行。例如想在Countries数据集中选择国土面积超过100万平方公里的内陆国家。

例如,图2-12显示的Countries数据集:

图2-12 Countries数据集

为了进行有效的处理,首先定义展示在清单2-14中的Country类。在第20~27行,构造函数从指定Scanner对象扫描文件的下一行中读取新Country对象的4个字段。在第29~33行,负责重写的toString()方法会返回String对象,格式与输入文件的每行格式相同。

清单2-14 Country类的代码

清单2-15展示了过滤数据的主要程序。

清单2-15 过滤输入数据的代码

在第28~41行中,readDataset()方法使用了第34行的自定义构造函数,将指定文件的所有数据读入一个HashSet对象,这个对象在第19行被命名为dataset。真正的过滤在第21行完成。循环仅仅打印出面积至少100万平方公里的内陆国家,如图2-13所示。

在微软Excel中,你可以通过选择Data | Filter或Data | Advance| Advanced Filter来过滤数据。

另一种数据过滤是检测数据集并消除噪声的过程,这里的噪声指的是破坏数据的任何类型的独立随机传输干扰。这个术语源于录音的背景噪声现象,类似的情形也发生在图像文件和视频录制中,这种过滤方法更为高级。

图2-13 经过滤的数据

有时侯,对处理好的数据进行排序或重新排序很有必要。例如,图2-1中的Countries.dat文件已经按name字段排序,而你还想按population字段重新排序。

Java的一种排序方法是使用TreeMap(而非HashMap),如清单2-16所示。在第17行实例化dataset对象的映射中,键类型指定Integer,值类型指定String。这是因为排序按population字段进行,而它是整数型。

清单2-16 使用不同字段进行重排序

TreeMap数据结构按照关键字段的顺序保持数据的排序。因此,当它在第29行打印时,输出是按人口数量升序排列。

当然,在任何映射数据结构中,关键字段的值必须唯一。如果两个国家的人口相同,这种方法可能会失效。

更一般的方法是定义一个实现java.util.Comparable接口的DataPoint类,根据排序列的值比较对象。然后把完整的数据集载入一个ArrayList中,再在Collections类中运用sort()方法进行简单排序,就像Collections.sort(list)。

在Excel中,你可以从主菜单选择Data | Sort来对数据的一列进行排序。

另一种预处理任务是将多个排好序的文件合并成单个排好序的文件。清单2-18展示了实现该任务的Java程序。它在图2-14和图2-15展示的两个Countries文件上运行。注意它们的排序依据是人口数量。

图2-14 非洲国家

图2-15 南美洲国家

为了合并这两个文件,我们定义Java类代表每个数据点,如清单2-17所示。这里的类与清单2-14相同,但在第30~38行增加了两个方法。

清单2-17 Country类的代码

通过实现java.util.Comparable接口(在第10行)可以比较Country对象。如果隐式参数(this)的人口小于显式参数的人口,compareTo()方法(在第34~38行)会返回一个负整数,这允许我们根据它们的人口规模排序Country对象。

第30~32行的isNull()方法仅仅用来确定到达输入文件底部的时间。

清单2-18[1]的程序比较了两个文件的Country对象,再在第27行或第30行的输出文件中打印人口数量较少的国家。当两个国家中的某一个扫描结束时,将有一个Country对象包含null字段,结果第24行停止while循环。其余两个while循环中的一个将停止扫描其他文件。合并文件后的结果如图2-16所示。

清单2-18 合并两个已排序的文件

 

这个程序会生成数量巨大的新Country对象。

例如,如果一个文件包含100万条记录,另一个文件包含一条population字段取最大值的记录,那么合并会创建100万个无用(null)对象。这是另一个选择Java处理文件的好理由。在Java中,未引用过的对象占据的空间会自动释放并回到可用的内存堆。而在缺乏垃圾收集协议实现的编程语言中,程序很可能因为超过内存限制而崩溃。

图2-16 合并过的文件

散列法是将标识数字指派给数据对象的过程。术语“哈希”(hash)本身就表示这些号码随机置乱,仿佛一盘用剩肉、土豆、洋葱和香料做成的普通炖菜。

好的哈希函数具备以下两个特性。

Java为每个实例化对象自动指派一个哈希代码,这也是使用Java做数据分析的理由。对象obj的哈希代码由obj.hashCode()给出。例如,在清单2-15合并程序的第24行加入如下代码:

System.out.println(country1.hashCode());

可以得到Paraguay对象的哈希代码是685,325,104。

Java根据对象内容的哈希代码计算对象的哈希代码。例如,字符串AB的哈希代码是2081,这等于31*65 + 66,即A的哈希代码的31倍加上B的哈希代码(65和66分别是A和B的Unicode值。)

当然,哈希代码可以用来实现哈希表。最初的想法是在数组a[]中存储一组对象,其中对象x会在索引i = h mod n处存储,由于685,325,104 mod 255 = 109,Paraguay对象会接着存储在a[109]中。

mod表示取余数。例如,25 mod 7 = 4因为25 = 3·7 + 4。

[1] 译者注:原文此处有误!

本章讨论数据分析准备工作的各种组织过程。在计算机程序中,每个数据都会被指派相应的数据类型,刻画特征并定义可实施的操作类型。

数据存储在关系数据库中会组织成表,表中每行对应一个数据点,每列对应指定类型的单个字段。键字段的值唯一,可以作为搜索索引。

类似的观点是将数据组织成键—值对。正如在关系数据库表中,键字段必须唯一。哈希表实现了键—值范式,通过哈希函数确定键相关数据的存储位置。

数据文件根据文件类型的规格进行相应的格式化。逗号分隔值类型(CSV)是最常见的结构化数据文件类型,其他常见的类型还包括XML和JSON。

刻画数据结构的信息称为元数据,数据的自动处理需要这种信息。

本章描述的特定数据处理包括数据清洗和过滤(移除错误的数据)、数据放缩(根据指定比例调整数值型的值)、排序、合并以及散列。


相关图书

Effective Java中文版(原书第3版)
Effective Java中文版(原书第3版)
Java核心技术速学版(第3版)
Java核心技术速学版(第3版)
Java编程动手学
Java编程动手学
Java研发自测入门与进阶
Java研发自测入门与进阶
Java开发坑点解析:从根因分析到最佳实践
Java开发坑点解析:从根因分析到最佳实践
Java EE企业级应用开发实战(Spring Boot+Vue+Element)
Java EE企业级应用开发实战(Spring Boot+Vue+Element)

相关文章

相关课程