点击关注 异步图书,置顶公众号
每天与你分享 IT好书 技术干货 职场知识
参与文末话题讨论,每日赠送异步图书
——异步小编
每个数据科学家都需要处理存储在磁盘中的数据,这些数据涉及的格式有ASCII文本、PDF、XML、JSON等。此外,数据还可以存储在数据库表格中。在对数据进行分析之前,数据科学家首先要做的是从这些数据源获取各种格式的数据,并对这些数据进行清洗,去除其中的噪声。今天推荐的图书是《Java数据科学指南》一书,并从中节选第一章内容,从本文中我们将学习这些内容,即了解如何从不同数据源获取各种格式的数据。
在这一过程中,我们将用到外部Java库(Java归档文件,简称JAR文件),这些库的使用不仅限于本文,还贯穿于《Java数据科学指南》一书。这些库由不同开发者或组织开发,方便了大家的使用。编写代码时,我们会用到Eclipse IDE工具,它是Windows平台下最好的集成开发环境,全书都会使用它。接下来,我们将讲解如何导入任意一个外部JAR文件,以下各个部分将指导你把外部JAR文件导入到项目中,跟随步骤动手去做即可。
对于一个Eclipse项目,你可以采用如下方法添加JAR文件:首先依次单击“Project|Build Path|Configure Build Path”,在Libraries选项卡中,单击“Add External JARs”,选择你想添加到项目的外部JAR文件,如图1-1所示。
1.2 使用Java从分层目录中提取所有文件名
这部分内容(以及后面各部分内容)是为那些想从复杂目录结构中提取文件路径与名称的数据科学家准备的,以方便进一步进行后续分析。这里的复杂目录结构是指在一个根目录下包含大量目录与文件。
准备工作
开始之前,需要做如下准备工作。
1.创建复杂的目录结构(目录层数你自己决定)。
2.在其中一些目录中创建文本文件,而在另一些目录中留空。
操作步骤
1.首先编写一个static方法,即listFiles(File rootDir),它带有一个File类型的参数,该参数可以是根目录或起始目录。这个方法将返回一系列文件,这些文件存在于参数所指定的根目录(以及其他所有下级子目录)中。
2.然后,创建一个HashSet对象,用来包含文件信息。
3.在创建好HashSet对象之后,要检查参数指定的根目录及其子目录是否为null。当为null时,直接把HashSet对象返回即可,不需要进行进一步处理。
4.接着,检查根目录中的每个目录(或文件),判断它是文件还是目录。如果是文件,就把它添加到HashSet中;如果是一个目录,就递归地调用本方法,并把当前目录路径与名称传递给它。
5.最后,把HashSet返回给该方法的调用者。
listFiles(File rootDir)方法的完整代码如下,包含执行该方法所需要的类与驱动方法。
请注意,代码中的HashSet用来存储文件路径与名称。这意味着我们不会有任何重复项,这是因为Java中的Set这种数据结构不包含重复项。
1.3 使用Apache Commons IO从多层目录中提取所有文件名
你可以使用前面一部分演示的操作步骤,采用递归方法把多层目录中的文件名列出来。除此之外,我们还有另外一种更简单、更方便的方法来完成它,那就是使用Apache Commons IO,并且只需编写少量代码即可。
准备工作
开始之前,需要做如下准备。
1.本部分会用到一个名称为Commons IO的Java库,它来自于Apache基金会。全书中,我们会使用Commons IO 2.5版本,请从Commons官网下载JAR文件。
2.在Eclipse中,把下载的JAR文件包含到你的项目中(作为外部JAR文件)。
操作步骤
1.创建listFiles方法,它带有一个参数,用来指定层级目录的根目录。
2.创建一个文件对象,并把根目录名传递给它。
3.Apache Commons库的FileUtils类中包含一个名称为listFiles()方法。使用这个方法提取所有文件名,并且把它们放入一个带有<File>泛型的列表变量中。使用TrueFileFilter.INSTANCE来匹配所有目录。
4.我们可以像下面这样把文件名显示在标准输出中。由于我们把文件名放入了一个列表之中,所以我们可以通过某种方法对这些文件中的数据进行进一步处理。
5.关闭方法。
完整代码包括方法代码、类代码,以及驱动方法,如下所示:
如果你想把带有一些特定扩展名的文件列出来,还可以使用Apache Commons库中的listFiles方法。但是这个方法的参数有些不同,它拥有3个参数,分别为文件目录、扩展名(String[])、递归与否。在这个库中还有一个有趣的方法,即listFilesAndDirs(File directory, IOFileFilter fileFilter, FileFilter dirFilter),如果你想把文件与目录全部列出来,可以使用它。
1.4使用Java 8从文本文件一次性读取所有内容
在许多场合下,数据科学家所拥有的数据是文本格式的。我们有很多方法可以用来读取文本文件的内容,这些方法各具优缺点:一些方法执行起来耗时、耗内存,而另一些方法执行速度很快,也不需要消耗太多计算机内存;一些方法可以把全部文本内容一次性读出,而另一些方法则只能一行行地读取文本文件。至于到底要选择哪种方法,则取决于你所面对的任务,以及你决定采用何种方法来处理这个任务。
在这部分中,我们将演示使用Java 8把文本文件的全部内容一次性读出来的方法。
操作步骤
1.首先,创建一个String对象,用来保存待读取的文本文件的目录与名称。
2.使用Paths类的get()方法,可以得到待读文件的路径。get()方法的参数是String对象,用来指定文件名,它的输出作为lines()方法的输入。lines()方法包含于Files类之中,用来读取一个文件的所有行,并且返回Stream,也就是说,这个方法的输出定向到一个Stream变量。因为我们的dummy.txt
文件中包含字符串数据,所以把Stream变量的泛型设置为String。整个读取过程需要放入一个try...catch块中,用来应对读取过程中可能发生的异常,比如当试图读取的文件不存在或已损坏时,就会抛出异常。
下面代码用来把dummy.txt文件中的内容全部显示出来。在stream变量中包含着文本文件的所有行,所以需要使用它的forEach()方法显示出每行内容。
1.5使用Apache Commons IO从文本文件一次性读取所有内容
在上一节中我们学习了使用Java8从文本文件中一次性读取所有内容,其实我们也可以使用Apache Commons IO API一次性读取文本文件的所有内容。
准备工作
开始之前,需要做如下准备。
1.本部分,我们会用到一个名为Apache Commons IO的Java库。
2.在Eclipse中,把下载好的JAR文件包含到你的项目中。
操作方法
1.假设你要读取的文件为dummy.txt,它位于C:/目录之下。首先,需要创建一个文件对象,用来访问这个文件,如下所示:
2.接着,创建一个字符串对象,用来保存文件中的文本内容。这里我们要使用readFileToString()
方法,它来自于Apache Commons IO库,是FileUtils类的一个成员方法。调用这个方法的方式有很多,但是现在,你只需知道我们要传递两个参数给它,第一个参数是file对象,用来指定要读取的文件,第二个参数是文件的编码,在示例中,我们将其设置为UTF-8。
3.只要使用上面两行代码,我们就可以读取文本文件内容,并将它们存入一个String变量中。但是,你可不是一个普通的数据科学家,你比其他人要聪明得多。所以,你在上面两行代码的前后又添加了几行代码,用来处理Java方法抛出的异常,比如你试图读取的文件不存在或者已经损坏,就会触发异常。为此,我们需要把上面两行代码放入到一个try...catch块之中,如下所示:
1.6 使用Apache Tika提取PDF文本
在解析与提取数据时,最难搞的文件类型之一是PDF文件。有些PDF文件甚至无法解析,因为它们有密码保护,而其他一些则包含着扫描的文本与图像。所以,这种动态文件类型有时会成为数据科学家的梦魇。本部分演示如何使用Apache Tika从PDF文件提取文本,当然前提是PDF文件没有被加密,也没有密码保护,而只包含非扫描的文本。
准备知识
开始之前,需要先做如下准备。
1.下载Apache Tika 1.10 JAR文件,并且将其作为外部Java库包含到你的Eclipse项目中。
2.把任意一个未锁定的PDF文件保存到C:/目录之下,并且命名为testPDF.pdf。
操作步骤
1.创建一个名称为convertPdf(String)的方法,它带有一个字符串参数,用来指定PDF文件名称。
2.创建一个输入流,用来以字节流的形式包含PDF数据。
3.创建一个try块,如下所示
4.把文件指派给刚刚创建好的stream。
5.在Apache Tika包中包含着许多不同的解析器。如果你不知道该选用哪一个,或者说你还有其他类型的文档需要转换,那么你应该使用AutoDetectParser解析器,如下所示:
6.创建一个handler,用来处理文件的正文内容。请注意,创建时需要把构造函数的参数设为-1
。通常,Apache Tika会对处理的文件进行限制,要求它至多包含100 000个字符。使用-1让这个handler忽略这个限制。
7.创建一个metadata对象。
8.调用解析器对象的parser()方法,并把上面创建的这些对象传递给它。
9.使用handler对象的tostring()方法,获取从文件中提取的正文文本。
10.关闭try块,并且添加catch与finally块。最后,关闭整个方法,如下所示:
下面代码包含convertPdf(String)方法的完整代码,以及相应的类与驱动方法。在调用convertPdf(String)方法时,你需要提供待转换的PDF文件的路径与名称,即把该方法的参数指定为C:/testPDF.pdf。
1.7 使用正则表达式清洗ASCII文本文件
ASCII文本文件中通常会包含一些非必要的字符,这些字符通常产生于转换过程中,比如把PDF转换为文本或把HTML转换为文本的过程中。并且,这些字符常常被看作噪声,它们是数据处理的主要障碍之一。本部分,我们学习使用正则表达式为ASCII文本数据清洗一些噪声的方法。
操作步骤
1.创建一个名为cleanText(String)的方法,它带有一个String类型的参数,用来指定要清洗的文本。
2.在你的方法中,添加如下几行代码,而后把清洗后的文本返回,并关闭方法。在如下代码中,第一行代码用来去掉非ASCII字符,紧接的一行用来把连续的空格字符替换为单个空格字符。第三行用来清除所有ASCII控制字符。第四行用来去除ASCII非打印字符。最后一行用来从Unicode移除非打印字符。
以下代码是方法的完整代码,包含相应类与驱动方法。
1.8 使用Univocity解析CSV文件
对数据科学家来说,另一种经常处理的文件格式是CSV(逗号分隔)文件,在这种文件中数据之间通过逗号进行分隔。CSV文件非常流行,因为大部分电子表格应用程序都可以读取它,比如MS Excel。
本部分,我们将学习解析CSV文件,以及处理所提取的数据点的方法。
准备工作
开始之前,需要先做如下准备。
1.下载Univocity JAR文件,并将其作为外部库添加到你的Eclipse项目中。
2.使用Notepad创建一个CSV文件,它包含如下数据。创建好之后,把文件的扩展名修改为.csv
,并把它保存到C盘之下,即C:/testCSV.csv。
操作步骤
1.创建一个名为parseCsv(String)的方法,它带有一个String类型的参数,用来指定待解析的文件名。
2.而后创建一个配置对象,该对象用来提供多种配置选项。
3.借助于配置对象,你可以打开解析器的自动检测功能,让它自动侦测输入中包含何种行分隔符序列。
4.创建一个RowListProcessor对象,用来把每个解析的行存储在列表中。
5.你可以使用RowProcessor来配置解析器,以对每个解析行的值进行处理。你可以在com.univocity.parsers.common.processor包中找到更多RowProcessors,但是你也可以自己创建。
6.如果待解析的CSV文件包含标题头,你可以把第一个解析行看作文件中每个列的标题。
7.接下来,使用给定的配置创建一个parser实例。
8.parser实例的parse()方法用来解析文件,并把每个经过解析的行指定给前面定义的RowProcessor。
9.如果解析中包含标题,则可使用如下代码获取这些标题。
10.随后,你可以很容易地处理这个字符串数组,以获取这些标题值。
11.另一方面,我们在列表中可以找到行值。只要使用一个for循环即可把列表打印出来,如下所示。
12.最后,关闭方法。
整个方法的完整代码如下所示:
有很多采用Java编写的CSV解析器。但是,相比较而言,Univocity是执行速度最快的一个。
本文摘自《Java数据科学指南》
[加]鲁什迪·夏姆斯(Rushdi Shams) 著
点击封面购买纸书
学习MLlib、DL4j和Weka等开源库,掌握实用的Java数据科学技能
本书旨在通过Java编程来引导读者更好地完成数据科学任务。本书通过9章内容,详细地介绍了数据获取与清洗、索引的建立和检索数据、统计分析、数据学习、信息的提取、大数据处理、深度学习、数据可视化等重要主题。
今日互动
你对本书的看法?为什么?截止时间5月31日17时,留言+转发本活动到朋友圈,小编将抽奖选出3名读者 赠送纸书1本和2张e读版80元异步社区代金券,(留言点赞最多的自动获得一张)。异步图书后台回复“5月新书”进入新书交流群,获得第一手新书信息, 点击此处直接参加活动。
推荐阅读
长按二维码,可以关注我们哟
每天与你分享IT好文。
在“异步图书”后台回复“关注”,即可免费获得2000门在线视频课程;推荐朋友关注根据提示获取赠书链接,免费得异步e读版图书一本。赶紧来参加哦!
点击阅读原文,购买《Java数据科学指南》