Java 数据科学秘籍(一)
零、前言
数据科学是当今专业化的热门领域,涵盖了广泛的人工智能领域,如数据处理、信息检索、机器学习、自然语言处理、大数据、深度神经网络和数据可视化。在本书中,我们将了解既现代又聪明的技术,并以简单易懂的方式介绍了 70 多个问题。
考虑到对高质量数据科学家的高需求,我们使用核心 Java 以及用 Java 编写的著名、经典和最先进的数据科学库编写了食谱。我们从数据收集和清理过程开始。然后,我们看看如何对获得的数据进行索引和搜索。之后,我们将介绍描述性统计和推断性统计以及它们在数据中的应用。然后,我们有两个连续的章节讨论机器学习在数据上的应用,这些数据可以成为构建任何智能系统的基础。现代信息检索和自然语言处理技术也包括在内。大数据是一个新兴领域,也涵盖了这个热门领域的几个方面。我们还涵盖了使用深度神经网络进行深度学习的基础知识。最后,我们学习如何使用有意义的视觉或图形来表示数据和从数据中获得的信息。
这本书的目标读者是对数据科学感兴趣并计划使用 Java 应用数据科学以更好地理解底层数据的任何人。
这本书涵盖了什么
第 1 章,获取和清理数据,涵盖了不同的数据读写方式以及清理数据以去除噪声的方式。它还让读者熟悉不同的数据文件类型,如 PDF、ASCII、CSV、TSV、XML 和 JSON。本章还介绍了提取 web 数据的方法。
第 2 章,索引和搜索数据,讲述了如何使用 Apache Lucene 索引数据以进行快速搜索。本章描述的技术可以被看作是现代搜索技术的基础。
第 3 章,统计分析数据,涵盖了 Apache Math API 的应用,从数据中收集和分析统计数据。本章还涵盖了更高层次的概念,如统计显著性测试,这是研究人员将他们的结果与基准进行比较时的标准工具。
第 4 章,从数据中学习-第 1 部分,涵盖了使用 Weka 机器学习工作台的基本分类、聚类和特征选择练习。
第 5 章,从数据中学习-第 2 部分,是后续章节,涵盖了使用另一个名为 Java 机器学习(Java-ML)库的 Java 库进行数据导入和导出、分类和特性选择。本章还涵盖了斯坦福分类器和大规模在线访问(MOA)的基本分类。
第 6 章,从文本数据中检索信息,涵盖了数据科学在文本数据信息检索中的应用。它涵盖了核心 Java 的应用以及流行的库,如 OpenNLP、Stanford CoreNLP、Mallet 和 Weka,用于将机器学习应用于信息提取和检索任务。
第七章、处理大数据,涵盖了机器学习的大数据平台应用,如 Apache Mahout、Spark-MLib 等。
第 8 章、从数据中深度学习,涵盖了使用 Java 深度学习(DL4j)库进行深度学习的基础知识。我们涵盖了 word2vec 算法、信念网络和自动编码器。
第 9 章、可视化数据,涵盖了基于数据生成有吸引力的信息显示的 GRAL 包。在软件包的许多功能中,选择了基本和基本图。
这本书你需要什么
我们已经用 Java 解决了现实世界的数据科学问题。我们的重点是为那些想知道如何用 Java 解决问题的人提供有效的内容。需要 Java 的基本知识,例如类、对象、方法、参数和参数、异常以及导出 Java 归档(JAR)文件。代码由叙述、信息和提示很好地支持,以帮助读者理解上下文和目的。在本书中解决的问题背后的理论,在许多情况下,没有被彻底讨论,但在必要时为感兴趣的读者提供参考。
这本书是给谁的
这本书是为那些想知道如何使用 Java 解决与数据科学相关的现实世界问题的人准备的。这本书从覆盖面的角度来看非常全面,对于已经从事数据科学并寻求使用 Java 解决项目中的问题的从业者来说也非常有用。
章节
在这本书里,你会发现几个经常出现的标题(准备好,怎么做...,它是如何工作的...,还有更多...,另请参见)。
为了给出如何完成配方的明确说明,我们使用以下章节:
准备就绪
本节将告诉您制作方法的内容,并描述如何设置制作方法所需的任何软件或任何初步设置。
怎么做...
本节包含遵循配方所需的步骤。
它是如何工作的...
这一部分通常包括对前一部分发生的事情的详细解释。
还有更多...
这一节包含了关于配方的附加信息,以使读者对配方有更多的了解。
参见
这个部分提供了一些有用的链接,可以链接到食谱的其他有用信息。
习俗
在这本书里,你会发现许多区分不同种类信息的文本样式。下面是这些风格的一些例子和它们的含义的解释。
文本中的码字、数据库表名、文件夹名、文件名、文件扩展名、路径名、伪 URL、用户输入、Twitter 句柄显示如下:“其中,你会找到一个名为lib的文件夹,这是你感兴趣的文件夹。”
代码块设置如下:
classVals = new ArrayList<String>();
for (int i = 0; i < 5; i++){
classVals.add("class" + (i + 1));
}
任何命令行输入或输出都按如下方式编写:
@relation MyRelation
@attribute age numeric
@attribute name string
@attribute dob date yyyy-MM-dd
@attribute class {class1,class2,class3,class4,class5}
@data
35,'John Doe',1981-01-20,class3
30,'Harry Potter',1986-07-05,class1
新术语和重要词汇以粗体显示。您在屏幕上看到的文字,例如在菜单或对话框中看到的文字,出现在如下文本中:“从管理面板中选择系统信息”
注意
警告或重要提示出现在这样的框中。
Tip
提示和技巧是这样出现的。
读者反馈
我们随时欢迎读者的反馈。让我们知道你对这本书的看法——你喜欢或不喜欢什么。读者的反馈对我们来说很重要,因为它有助于我们开发出真正让你受益匪浅的图书。
要给我们发送总体反馈,只需给 feedback@packtpub.com 发电子邮件,并在邮件主题中提及书名。
如果有一个你擅长的主题,并且你有兴趣写一本书或者为一本书投稿,请查看我们在www.packtpub.com/authors的作者指南。
客户支持
既然您已经是 Packt book 的骄傲拥有者,我们有许多东西可以帮助您从购买中获得最大收益。
下载示例代码
你可以从你在www.packtpub.com的账户下载本书的示例代码文件。如果你在其他地方购买了这本书,你可以访问 www.packtpub.com/support 的并注册,让文件直接通过电子邮件发送给你。
您可以按照以下步骤下载代码文件:
- 使用您的电子邮件地址和密码登录或注册我们的网站。
- 将鼠标指针悬停在顶部的支持选项卡上。
- 点击代码下载&勘误表。
- 在搜索框中输入书名。
- 选择您要下载代码文件的书。
- 从下拉菜单中选择您购买这本书的地方。
- 点击代码下载。
您也可以通过点击 Packt Publishing 网站上该书网页上的代码文件按钮来下载代码文件。在搜索框中输入书名即可进入该页面。请注意,您需要登录到您的 Packt 帐户。
下载文件后,请确保使用最新版本的解压缩或解压文件夹:
- WinRAR / 7-Zip for Windows
- 适用于 Mac 的 Zipeg / iZip / UnRarX
- 用于 Linux 的 7-Zip / PeaZip
这本书的代码包也托管在 GitHub 的 https://GitHub . com/packt publishing/Java-Data-Science-Cookbook 上。我们在 github.com/PacktPublis…
下载这本书的彩色图片
我们还为您提供了一个 PDF 文件,其中包含本书中使用的截图/图表的彩色图像。彩色图像将帮助您更好地理解输出中的变化。你可以从https://www . packtpub . com/sites/default/files/downloads/javadatasciencecoookbook _ color images . pdf下载这个文件。
勘误表
尽管我们已尽一切努力确保内容的准确性,但错误还是会发生。如果您在我们的某本书中发现了一个错误——可能是文本或代码中的错误——如果您能向我们报告,我们将不胜感激。这样做,你可以让其他读者免受挫折,并帮助我们改进本书的后续版本。如果您发现任何勘误表,请通过访问www.packtpub.com/submit-erra…,选择您的图书,点击勘误表提交表链接,并输入您的勘误表的详细信息。一旦您的勘误表得到验证,您的提交将被接受,该勘误表将被上传到我们的网站或添加到该标题的勘误表部分下的任何现有勘误表列表中。
要查看之前提交的勘误表,请前往www.packtpub.com/books/conte…并在搜索栏中输入图书名称。所需信息将出现在勘误表部分。
盗版
互联网上版权材料的盗版是所有媒体都存在的问题。在 Packt,我们非常重视版权和许可证的保护。如果您在互联网上发现我们作品的任何形式的非法拷贝,请立即向我们提供地址或网站名称,以便我们采取补救措施。
请联系我们在 copyright@packtpub.com 与涉嫌盗版材料的链接。
我们感谢您帮助保护我们的作者,以及我们为您带来有价值内容的能力。
问题
如果你对这本书的任何方面有问题,你可以在 questions@packtpub.com 联系我们,我们会尽最大努力解决问题。
一、获取和清理数据
在本章中,我们将介绍以下配方:
- 使用 Java 从分层目录中检索所有文件名
- 使用 Apache Commons IO 从分层目录中检索所有文件名
- 使用 Java 8 从文本文件中一次性读取内容
- 使用 Apache Commons IO 一次从文本文件中读取所有内容
- 使用 Apache Tika 提取 PDF 文本
- 使用正则表达式清理 ASCII 文本文件
- 使用 Univocity 解析逗号分隔值文件
- 使用 Univocity 解析选项卡分隔值文件
- 使用 JDOM 解析 XML 文件
- 使用 JSON.simple 编写 JSON 文件
- 使用 JSON.simple 读取 JSON 文件
- 使用 JSoup 从 URL 中提取 web 数据
- 使用 Selenium 从网站提取网络数据
Webdriver - 从 MySQL 数据库读取表数据
简介
每个数据科学家都需要处理以多种格式存储在磁盘上的数据,比如 ASCII 文本、PDF、XML、JSON 等等。此外,数据可以存储在数据库表中。在进行任何分析之前,数据科学家的首要任务是从这些数据源和这些格式中获取数据,并应用数据清理技术去除其中存在的噪声。在这一章中,我们将看到完成这一重要任务的方法。
我们将不仅在本章,而且在整本书中使用外部 Java 库(Java 归档文件或简单的 JAR 文件)。这些库是由开发人员或组织创建的,目的是让每个人的生活更轻松。我们将使用 Eclipse IDE 进行代码开发,最好是在 Windows 平台上,并贯穿全书。这里是你如何包含任何外部 JAR 文件,在许多食谱中,我指导你将外部 JAR 文件包含到你的项目中,这是你需要做的。
在 Eclipse 中右键单击项目 | 构建路径 | 配置构建路径,可以在项目中添加一个 JAR 文件。在库选项卡下,点击添加外部 jar...,并选择您将用于特定项目的外部 JAR 文件:
使用 Java 从分层目录中检索所有文件名
这个方法(以及下面的内容)是为想要从一个复杂的目录结构中检索文件路径和名称(用于将来的分析)的数据科学家准备的,这个复杂的目录结构包含根目录中的许多目录和文件。
准备就绪
为了执行此配方,我们需要以下内容:
- 在目录中创建目录(尽可能多的层)。
- 在其中的一些目录中创建文本文件,同时将一些目录留空以获得更多刺激。
怎么做...
-
我们将创建一个接受一个
File参数的static方法,这个参数最终是根目录或开始的目录。该方法将返回一组在这个根目录(以及所有其他后续目录)中找到的文件:public static Set<File> listFiles(File rootDir) { -
首先,创建一个包含文件信息的
HashSet:Set<File> fileSet = new HashSet<File>(); -
一旦创建了
HashSet,我们需要检查根目录或其中的目录是否是null。对于这种情况,我们不需要进一步处理:if (rootDir == null || rootDir.listFiles() == null){ return fileSet; } -
我们一次从根目录中考虑一个目录(或文件),并检查我们是在处理一个文件还是一个目录。对于一个文件,我们将它添加到我们的
HashSet中。在目录的情况下,我们通过发送目录的路径和名称再次递归调用这个方法:for (File fileOrDir : rootDir.listFiles()) { if (fileOrDir.isFile()){ fileSet.add(fileOrDir); } else{ fileSet.addAll(listFiles(fileOrDir)); } } -
Finally, we return the
HashSetto the caller of this method:return fileSet; }完整的方法,以及运行它的类和驱动程序方法如下:
import java.io.File; import java.util.HashSet; import java.util.Set; public class TestRecursiveDirectoryTraversal { public static void main(String[] args){ System.out.println(listFiles(new File("Path for root directory")).size()); } public static Set<File> listFiles(File rootDir) { Set<File> fileSet = new HashSet<File>(); if(rootDir == null || rootDir.listFiles()==null){ return fileSet; } for (File fileOrDir : rootDir.listFiles()) { if (fileOrDir.isFile()){ fileSet.add(fileOrDir); } else{ fileSet.addAll(listFiles(fileOrDir)); } } return fileSet; } }
注意
注意使用HashSet来存储文件的路径和名称。这意味着我们不会有任何重复,因为 Java 中的Set数据结构不包含重复条目。
使用 Apache Commons IO 从分层目录中检索所有文件名
分层目录中的文件名列表可以递归地完成,如前面的方法所示。然而,使用 Apache Commons IO 库,这可以用更简单、更方便的方式和更少的代码来完成。
准备就绪
为了执行此配方,我们需要以下内容:
- 在这个菜谱中,我们将使用 Apache 的一个名为 Commons IO 的 Java 库。在整本书中,我们将使用 2.5 版本。从这里下载您选择的 JAR 文件:https://commons . Apache . org/proper/commons-io/download _ io . CGI
- 将 JAR 文件包含在项目中,即 Eclipse 中的一个外部 JAR。
怎么做...
-
创建一个方法,将目录层次结构中的根目录作为输入:
public void listFiles(String rootDir){ -
创建一个根目录名为
File dir = new File(rootDir);的文件对象
-
Apache Commons 库的
FileUtils类包含一个名为listFiles()的方法。用这个方法检索所有的文件名,并把这些名字放在一个带有<File>泛型的列表变量中。使用TrueFileFilter.INSTANCE匹配所有目录:List<File> files = (List<File>) FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); -
文件名可以显示在标准输出中,如下所示。现在我们有了列表中的名字,我们有了进一步处理这些文件中的数据的方法:
for (File file : files) { System.out.println("file: " + file.getAbsolutePath()); } -
Close the method:
}这个配方中的方法、它的类以及运行它的驱动程序方法如下:
import java.io.File; import java.util.List; import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.TrueFileFilter; public class FileListing{ public static void main (String[] args){ FileListing fileListing = new FileListing(); fileListing.listFiles("Path for the root directory here"); } public void listFiles(String rootDir){ File dir = new File(rootDir); List<File> files = (List<File>) FileUtils.listFiles(dir, TrueFileFilter.INSTANCE, TrueFileFilter.INSTANCE); for (File file : files) { System.out.println("file: " + file.getAbsolutePath()); } }
Tip
如果您想列出带有某些特定扩展名的文件,Apache Commons 库中也有一个名为listFiles的方法。但是,参数不同;该方法带三个参数,即文件目录、String[]扩展名、布尔递归。这个库中另一个有趣的方法是 listFilesAndDirs (File directory,IOFileFilter fileFilter,IOFileFilter dirFilter),如果有人不仅对列出文件而且对列出目录感兴趣的话。详细信息可在 commons.apache.org/proper/comm…](commons.apache.org/proper/comm…)
使用 Java 8 从文本文件中一次性读取内容
在许多情况下,数据科学家都有文本格式的数据。读取文本文件内容的方法有很多种,它们各有利弊:有些方法耗费时间和内存,有些方法速度很快,不需要太多的计算机内存;有些人一次读取所有文本内容,而有些人逐行读取文本文件。选择取决于手头的任务和数据科学家完成该任务的方法。
这个菜谱演示了如何使用 Java 8 一次读取所有文本文件内容。
怎么做...
-
首先,创建一个
String对象来保存将要读取的文本文件的路径和名称:String file = "C:/dummy.txt"; -
使用
Paths类的get()方法,我们得到我们试图读取的文件的路径。这个方法的参数是指向文件名的String对象。这个方法的输出被提供给另一个名为lines()的方法,它在Files类中。该方法从文件中读取所有行作为一个Stream,因此,该方法的输出指向一个Stream变量。因为我们的dummy.txt文件包含字符串数据,所以Stream变量的泛型被设置为String。
整个读取过程需要一个try...catch块来尝试读取一个不存在或损坏的文件等等。
下面的代码段显示了我们的dummy.txt文件的内容。stream变量包含文本文件的行,因此使用变量的forEach()方法显示每行内容:
try (Stream<String> stream = Files.lines(Paths.get(file))) {
stream.forEach(System.out::println); } catch (IOException e) {
System.out.println("Error reading " + file.getAbsolutePath());
}
使用 Apache Commons IO 从文本文件中一次性读取内容
使用 Apache Commons IO API 可以实现上一个配方中描述的相同功能。
准备就绪
为了执行此配方,我们需要以下内容:
- 在这个菜谱中,我们将使用 Apache 的一个名为 Commons IO 的 Java 库。从这里下载您选择的版本:https://commons . Apache . org/proper/commons-io/download _ io . CGI
- 将 JAR 文件包含在项目中,即 Eclipse 中的一个外部 JAR。
怎么做...
-
比方说,您正试图读取位于您的
C:/ drive中名为dummy.txt的文件的内容。首先,你需要创建一个文件对象来访问这个文件,如下:File file = new File("C:/dummy.txt"); -
接下来,创建一个 string 对象来保存文件的文本内容。我们将从 Apache Commons IO 库中使用的方法称为
readFileToString,它是名为FileUtils的类的成员。有许多不同的方法可以调用这个方法。但是现在,只需要知道我们需要给这个方法发送两个参数。首先是file对象,这是我们将要读取的文件,然后是文件的编码,在这个例子中是UTF-8:String text = FileUtils.readFileToString(file, "UTF-8"); -
前面两行足以读取文本文件内容并将其放入变量中。然而,你不仅仅是一个数据科学家,你还是一个聪明的数据科学家。因此,您需要在代码前后添加几行代码,以便处理 Java 方法在试图读取一个不存在或已损坏的文件时抛出的异常。前面代码的完整性可以通过引入如下的
try...catch块来实现:File file = new File("C:/dummy.txt"); try { String text = FileUtils.readFileToString(file, "UTF-8"); } catch (IOException e) { System.out.println("Error reading " + file.getAbsolutePath()); }
使用 Apache Tika 提取 PDF 文本
解析和提取数据最困难的文件类型之一是 PDF。有些 pdf 甚至无法解析,因为它们受密码保护,而有些 pdf 包含扫描文本和图像。因此,这种动态文件类型有时会成为数据科学家的噩梦。这个菜谱演示了如何使用 Apache Tika 从 PDF 文件中提取文本,因为该文件没有加密或密码保护,并且包含没有扫描的文本。
准备就绪
为了执行此配方,我们需要以下内容:
- 从archive.apache.org/dist/tika/t…下载 Apache Tika 1.10 JAR 文件,并将其作为外部 Java 库包含在您的 Eclipse 项目中。
- 在您的
C: drive上将任何解锁的 PDF 文件另存为testPDF.pdf。
怎么做...
-
创建一个名为
convertPdf(String)的方法,该方法将待转换的 PDF 文件的名称作为参数:public void convertPDF(String fileName){ -
创建一个包含 PDF 数据的字节流输入流:
InputStream stream = null; -
创建一个
try块,如下所示:try{ -
将文件分配给刚刚创建的
stream:stream = new FileInputStream(fileName); -
Apache Tika 包中提供了许多不同的解析器。如果您不知道要使用哪个解析器,或者说您不仅有 pdf,还有其他类型的文档要转换,您应该使用如下的【T0:】T1
-
创建一个处理程序来处理文件的主体内容。注意
-1是构造函数的参数。通常,Apache Tika 仅限于处理最多 100,000 个字符的文件。-1值确保这个限制被主体处理程序忽略:BodyContentHandler handler = new BodyContentHandler(-1); -
创建元数据对象:
Metadata metadata = new Metadata(); -
用您刚刚创建的所有这些对象调用解析器对象的
parser()方法:parser.parse(stream, handler, metadata, new ParseContext()); -
使用 handler 对象的
tostring()方法获取从文件中提取的正文:System.out.println(handler.toString()); -
Close the
tryblock and complement it with acatchblock andfinallyblock, and close the method as follows:
```java
}catch (Exception e) {
e.printStackTrace();
}finally {
if (stream != null)
try {
stream.close();
} catch (IOException e) {
System.out.println("Error closing stream");
}
}
}
```
一个类中带有驱动方法的完整方法如下。您刚刚创建的方法可以通过向它发送您需要转换的 PDF 文件的路径和名称来调用,该文件在您的`C: drive`中保存为`testPDF.pdf`:
```java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.sax.BodyContentHandler;
public class TestTika {
public static void main(String args[]) throws Exception {
TestTika tika = new TestTika();
tika.convertPdf("C:/testPDF.pdf");
}
public void convertPdf(String fileName){
InputStream stream = null;
try {
stream = new FileInputStream(fileName);
AutoDetectParser parser = new AutoDetectParser();
BodyContentHandler handler = new BodyContentHandler(-1);
Metadata metadata = new Metadata();
parser.parse(stream, handler, metadata, new
ParseContext());
System.out.println(handler.toString());
}catch (Exception e) {
e.printStackTrace();
}finally {
if (stream != null)
try {
stream.close();
} catch (IOException e) {
System.out.println("Error closing stream");
}
}
}
}
```
使用正则表达式清理 ASCII 文本文件
ASCII 文本文件可能包含最终在转换过程中引入的不必要的字符单元,如 PDF 到文本的转换或 HTML 到文本的转换。这些字符通常被视为噪音,因为它们是数据处理的主要障碍之一。这个方法使用正则表达式清除了 ASCII 文本数据中的一些干扰。
怎么做...
-
创建一个名为
cleanText(String)的方法,它采用String格式的文本:public String cleanText(String text){ -
Add the following lines in your method, return the cleaned text, and close the method. The first line strips off non-ASCII characters. The line next to it replaces continuous white spaces with a single white space. The third line erases all the
ASCIIcontrol characters. The fourth line strips off theASCIInon-printable characters. The last line removes non-printable characters from Unicode:text = text.replaceAll("[^p{ASCII}]",""); text = text.replaceAll("s+", " "); text = text.replaceAll("p{Cntrl}", ""); text = text.replaceAll("[^p{Print}]", ""); text = text.replaceAll("p{C}", ""); return text; }类中包含驱动程序方法的完整方法如下所示:
public class CleaningData { public static void main(String[] args) throws Exception { CleaningData clean = new CleaningData(); String text = "Your text here you have got from some file"; String cleanedText = clean.cleanText(text); //Process cleanedText } public String cleanText(String text){ text = text.replaceAll("[^p{ASCII}]",""); text = text.replaceAll("s+", " "); text = text.replaceAll("p{Cntrl}", ""); text = text.replaceAll("[^p{Print}]", ""); text = text.replaceAll("p{C}", ""); return text; } }
使用 Univocity 解析逗号分隔值(CSV)文件
数据科学家处理的另一种非常常见的文件类型是逗号分隔值 ( CSV )文件,其中数据由逗号分隔。CSV 文件非常受欢迎,因为它们可以被大多数电子表格应用程序读取,如 MS Excel。
在这个菜谱中,我们将看到如何解析 CSV 文件并处理从中检索到的数据点。
准备就绪
为了执行此配方,我们需要以下内容:
-
从http://OSS . sonatype . org/content/repositories/releases/com/univo city/univo city-parsers/2 . 2 . 1/univo city-parsers-2 . 2 . 1 . JAR下载 Univocity JAR 文件。将 JAR 文件作为外部库包含在 Eclipse 的项目中。
-
使用记事本从以下数据创建一个 CSV 文件。文件扩展名应该是
.csv。你将文件保存为C:/testCSV.csv:Year,Make,Model,Description,Price 1997,Ford,E350,"ac, abs, moon",3000.00 1999,Chevy,"Venture ""Extended Edition""","",4900.00 1996,Jeep,Grand Cherokee,"MUST SELL! air, moon roof, loaded",4799.00 1999,Chevy,"Venture ""Extended Edition, Very Large""",,5000.00 ,,"Venture ""Extended Edition""","",4900.00
怎么做...
-
创建一个名为
parseCsv(String)的方法,将文件名作为字符串参数:public void parseCsv(String fileName){ -
然后创建一个设置对象。该对象提供了许多配置设置选项:
CsvParserSettings parserSettings = new CsvParserSettings(); -
您可以配置解析器来自动检测输入中的行分隔符序列:
parserSettings.setLineSeparatorDetectionEnabled(true); -
创建一个
RowListProcessor,将每个解析过的行存储在一个列表中:RowListProcessor rowProcessor = new RowListProcessor(); -
您可以配置解析器使用一个
RowProcessor来处理每个解析行的值。你会在com.univocity.parsers.common.processor包中找到更多的RowProcessors,但是你也可以自己创建:parserSettings.setRowProcessor(rowProcessor); -
如果您将要解析的 CSV 文件包含标题,您可以将第一个解析的行视为文件中每一列的标题:
parserSettings.setHeaderExtractionEnabled(true); -
现在,用给定的设置创建一个
parser实例:CsvParser parser = new CsvParser(parserSettings); -
parse()方法将解析文件,并将每个解析的行委托给您定义的【T1:】T2 -
如果您已经解析了头部,那么可以如下找到【T0:】T1
-
然后,您可以轻松地处理这个字符串数组来获取头值。
-
另一方面,行值可以在列表中找到。可以使用 for 循环打印列表,如下所示:
```java
List<String[]> rows = rowProcessor.getRows();
for (int i = 0; i < rows.size(); i++){
System.out.println(Arrays.asList(rows.get(i)));
}
```
12. Finally, close the method:
```java
}
```
整个方法可以写成如下形式:
```java
import java.io.File;
import java.util.Arrays;
import java.util.List;
import com.univocity.parsers.common.processor.RowListProcessor;
import com.univocity.parsers.csv.CsvParser;
import com.univocity.parsers.csv.CsvParserSettings;
public class TestUnivocity {
public void parseCSV(String fileName){
CsvParserSettings parserSettings = new CsvParserSettings();
parserSettings.setLineSeparatorDetectionEnabled(true);
RowListProcessor rowProcessor = new RowListProcessor();
parserSettings.setRowProcessor(rowProcessor);
parserSettings.setHeaderExtractionEnabled(true);
CsvParser parser = new CsvParser(parserSettings);
parser.parse(new File(fileName));
String[] headers = rowProcessor.getHeaders();
List<String[]> rows = rowProcessor.getRows();
for (int i = 0; i < rows.size(); i++){
System.out.println(Arrays.asList(rows.get(i)));
}
}
public static void main(String[] args){
TestUnivocity test = new TestUnivocity();
test.parseCSV("C:/testCSV.csv");
}
}
```
注意
有许多 CSV 解析器是用 Java 编写的。然而,相比之下,大学是最快的。详细对比结果见此:github.com/uniVocity/c…
使用 Univocity 解析制表符分隔值(TSV)文件
与 CSV 文件不同,制表符分隔值 ( TSV )文件包含由制表符分隔的数据。这个菜谱向您展示了如何从 TSV 文件中检索数据点。
准备就绪
为了执行此配方,我们需要以下内容:
- 从http://OSS . sonatype . org/content/repositories/releases/com/univo city/univo city-parsers/2 . 2 . 1/univo city-parsers-2 . 2 . 1 . JAR下载 Univocity JAR 文件。将 JAR 文件包含在 Eclipse 外部库的项目中。
- 使用记事本从以下数据创建 TSV 文件。文件扩展名应该是
.tsv。您将文件保存为C:/testTSV.tsv:
Year Make Model Description Price
1997 Ford E350 ac, abs, moon 3000.00
1999 Chevy Venture "Extended Edition" 4900.00
1996 Jeep Grand Cherokee MUST SELL!nair, moon roof, loaded 4799.00
1999 Chevy Venture "Extended Edition, Very Large" 5000.00
Venture "Extended Edition" 4900.00
怎么做...
-
创建一个名为
parseTsv(String)的方法,将文件名作为字符串参数:public void parseTsv(String fileName){ -
在这个菜谱中,TSV 文件的行分隔符是一个换行符或
n。要将该字符设置为行分隔符,请修改设置:settings.getFormat().setLineSeparator("n"); -
使用这些设置,创建一个 TSV 解析器:
TsvParser parser = new TsvParser(settings); -
一次性解析 TSV 文件的所有行,如下所示:
List<String[]> allRows = parser.parseAll(new File(fileName)); -
遍历列表对象,打印/处理这些行,如下所示:
for (int i = 0; i < allRows.size(); i++){ System.out.println(Arrays.asList(allRows.get(i))); } -
最后,关闭方法:
}
类中包含驱动程序方法的完整方法如下所示:
import java.io.File;
import java.util.Arrays;
import java.util.List;
import com.univocity.parsers.tsv.TsvParser;
import com.univocity.parsers.tsv.TsvParserSettings;
public class TestTsv {
public void parseTsv(String fileName){
TsvParserSettings settings = new TsvParserSettings();
settings.getFormat().setLineSeparator("n");
TsvParser parser = new TsvParser(settings);
List<String[]> allRows = parser.parseAll(new File(fileName));
for (int i = 0; i < allRows.size(); i++){
System.out.println(Arrays.asList(allRows.get(i)));
}
}
}
使用 JDOM 解析 XML 文件
与通常是非结构化的文本数据不同,在 XML 文件中组织数据是一种以结构化方式准备、传递和利用数据的流行方法。有几种方法可以解析 XML 文件的内容。在本书中,我们将把我们的方法限制在一个名为 JDOM 的用于 XML 解析的外部 Java 库。
准备就绪
为了执行此配方,我们需要以下内容:
- 从www.jdom.org/downloads/i…下载 JDOM 的 2.06 版 JAR 文件。
- 在 Eclipse 中,创建一个项目并将 JAR 文件包含在一个外部 JAR 中。
- 打开记事本。创建一个名为
xmldummy的新文件,扩展名为.xml。该文件的内容将简单如下:
<?xml version="1.0"?>
<book>
<author>
<firstname>Alice</firstname>
<lastname>Peterson</lastname>
</author>
<author>
<firstname>John</firstname>
<lastname>Doe</lastname>
</author>
</book>
怎么做...
-
创建一个名为
builder:SAXBuilder builder = new SAXBuilder();的
SAXBuilder对象 -
现在您需要创建一个
File对象来指向您将要解析的 XML 文件。如果您已经将 XML 文件保存在了C:/驱动器中,那么输入下面的代码段:File file = new File("c:/dummyxml.xml"); -
在一个
try块中,您将创建一个Document对象,这将是您的 XML 文件:try { Document document = (Document) builder.build(file); -
当您解析 XML 时,由于它是树结构的,您需要知道文件的根元素来开始遍历树(换句话说,开始系统地解析)。因此,您创建了一个类型为
Element的rootNode对象来保存根元素,在我们的例子中是<book>节点:Element rootNode = document.getRootElement(); -
然后,您将检索根节点中所有名为
author的子节点。这些名字以列表的形式出现,因此,您将使用一个列表变量来保存它们:List list = rootNode.getChildren("author"); -
接下来,您将使用一个
for循环来遍历这个列表,以获得这个列表中条目的元素。每个元素将保存在一个名为 node 的Element类型变量中。该变量有一个名为getChildText()的方法,该方法以其子变量的名字作为参数;该方法返回命名的子元素的文本内容,如果没有这样的子元素,则返回null。这个方法很方便,因为调用getChild().getText()可以抛出一个NullPointerException:for (int i = 0; i < list.size(); i++) { Element node = (Element) list.get(i); System.out.println("First Name : " + node.getChildText("firstname")); System.out.println("Last Name : " + node.getChildText("lastname")); } -
Finally, you will be closing the
tryblock; put the followingcatchblocks to handle exceptions:} catch (IOException io) { System.out.println(io.getMessage()); } catch (JDOMException jdomex) { System.out.println(jdomex.getMessage()); }食谱的完整代码如下:
import java.io.File; import java.io.IOException; import java.util.List; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.input.SAXBuilder; public class TestJdom { public static void main(String[] args){ TestJdom test = new TestJdom(); test.parseXml("C:/dummyxml.com"); } public void parseXml(String fileName){ SAXBuilder builder = new SAXBuilder(); File file = new File(fileName); try { Document document = (Document) builder.build(file); Element rootNode = document.getRootElement(); List list = rootNode.getChildren("author"); for (int i = 0; i < list.size(); i++) { Element node = (Element) list.get(i); System.out.println("First Name : " + node.getChildText("firstname")); System.out.println("Last Name : " + node.getChildText("lastname")); } } catch (IOException io) { System.out.println(io.getMessage()); } catch (JDOMException jdomex) { System.out.println(jdomex.getMessage()); } } }
注意
有许多不同类型的 XML 解析器,每种都有自己的好处 Dom 解析器:这些解析器将文档的完整内容加载到内存中,并在内存中创建其完整的层次树。 SAX 解析器:这些解析器不会将完整的文档加载到内存中,而是基于事件触发器来解析文档。 JDOM 解析器 : JDOM 解析器以类似于 DOM 解析器的方式解析文档,但是更方便。 StAX 解析器 : 这些解析器处理文档的方式与 SAX 解析器相似,但效率更高。 XPath 解析器 : 这些解析器基于表达式解析文档,广泛用于 XSLT。 DOM4J 解析器 : 这是一个 Java 库,使用 Java 集合框架解析 XML、XPath 和 XSLT,提供对 DOM、SAX 和 JAXP 的支持。
使用 JSON.simple 编写 JSON 文件
就像 XML 一样,JSON 也是一种轻量级的人类可读的数据交换格式。它代表 JavaScript 对象符号。这正成为现代 web 应用程序生成和解析的流行格式。在这个菜谱中,您将看到如何编写 JSON 文件。
准备就绪
为了执行此配方,我们需要以下内容:
- 从code.google.com/archive/p/j…下载
json-simple-1.1.1.jar,并将 JAR 文件作为外部库包含到您的 Eclipse 项目中。
怎么做...
-
创建一个名为
writeJson(String outFileName)的方法,该方法采用我们将生成的 JSON 文件的名称作为输出,并带有这个菜谱中的 JSON 信息。 -
创建一个 JSON 对象,并使用该对象的
put()方法填充一些字段。例如,假设你的领域是书籍和它们的作者。下面的代码将创建一个 JSON 对象,并填充哈利波特系列中的一本书的名称及其作者的姓名:JSONObject obj = new JSONObject(); obj.put("book", "Harry Potter and the Philosopher's Stone"); obj.put("author", "J. K. Rowling"); -
接下来,假设我们有三个书评人对这本书的评论。它们可以放在一个 JSON 数组中。该数组可以按如下方式填充。首先,我们使用数组对象的
add()来添加评论。当所有的评论都被添加到数组中时,我们将把数组放到我们在上一步中创建的 JSON 对象中:JSONArray list = new JSONArray(); list.add("There are characters in this book that will remind us of all the people we have met. Everybody knows or knew a spoilt, overweight boy like Dudley or a bossy and interfering (yet kind-hearted) girl like Hermione"); list.add("Hogwarts is a truly magical place, not only in the most obvious way but also in all the detail that the author has gone to describe it so vibrantly."); list.add("Parents need to know that this thrill-a-minute story, the first in the Harry Potter series, respects kids' intelligence and motivates them to tackle its greater length and complexity, play imaginative games, and try to solve its logic puzzles. "); obj.put("messages", list); -
我们将把 JSON 对象中的信息写到一个输出文件中,因为这个文件将用于演示我们如何读取/解析 JSON 文件。下面的
try...catch代码块将信息写到一个 JSON 文件中:try { FileWriter file = new FileWriter("c:test.json"); file.write(obj.toJSONString()); file.flush(); file.close(); } catch (IOException e) { //your message for exception goes here. } -
JSON 对象的内容也可以显示在标准输出上,如下所示:
System.out.print(obj); -
最后,关闭方法:
}
整个类、该配方中描述的方法以及调用带有输出 JSON 文件名的方法的驱动程序方法如下:
import java.io.FileWriter;
import java.io.IOException;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
public class JsonWriting {
public static void main(String[] args) {
JsonWriting jsonWriting = new JsonWriting();
jsonWriting.writeJson("C:/testJSON.json");
}
public void writeJson(String outFileName){
JSONObject obj = new JSONObject();
obj.put("book", "Harry Potter and the Philosopher's Stone");
obj.put("author", "J. K. Rowling");
JSONArray list = new JSONArray();
list.add("There are characters in this book that will remind us
of all the people we have met. Everybody knows or knew a
spoilt, overweight boy like Dudley or a bossy and interfering
(yet kind-hearted) girl like Hermione");
list.add("Hogwarts is a truly magical place, not only in the most
obvious way but also in all the detail that the author has gone
to describe it so vibrantly.");
list.add("Parents need to know that this thrill-a-minute story,
the first in the Harry Potter series, respects kids'
intelligence and motivates them to tackle its greater length
and complexity, play imaginative games, and try to solve
its logic puzzles. ");
obj.put("messages", list);
try {
FileWriter file = new FileWriter(outFileName);
file.write(obj.toJSONString());
file.flush();
file.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.print(obj);
}
}
输出文件将包含如下数据。请注意,此处显示的输出已经过修改,以增加可读性,实际输出是一段大而平的文本:
{
"author":"J. K. Rowling",
"book":"Harry Potter and the Philosopher's Stone",
"messages":[
"There are characters in this book that will remind us of all the people we have met. Everybody knows or knew a spoilt, overweight boy like Dudley or a bossy and interfering (yet kind-hearted) girl like Hermione",
"Hogwarts is a truly magical place, not only in the most obvious way but also in all the detail that the author has gone to describe it so vibrantly.",
"Parents need to know that this thrill-a-minute story, the first in the Harry Potter series, respects kids' intelligence and motivates them to tackle its greater length and complexity, play imaginative games, and try to solve its logic puzzles."
]
}
使用 JSON.simple 读取 JSON 文件
在这个菜谱中,我们将看到如何读取或解析 JSON 文件。作为我们的样本输入文件,我们将使用我们在前一个菜谱中创建的 JSON 文件。
准备就绪
为了执行此配方,我们需要以下内容:
- 使用前面的方法创建一个 JSON 文件,其中包含图书、作者和评论者的评论信息。该文件将用作该配方解析/读取的输入。
怎么做...
-
因为我们将读取或解析一个 JSON 文件,首先,我们将创建一个 JSON 解析器:
JSONParser parser = new JSONParser(); -
然后,在
try块中,我们将检索 book 和 author 字段中的值。然而,要做到这一点,我们首先使用解析器的parse()方法来读取输入的 JSON 文件。parse()方法将文件的内容作为对象返回。因此,我们将需要一个Object变量来保存内容。然后,object将被分配给一个 JSON 对象进行进一步处理。注意在赋值期间Object变量的类型转换:try { Object obj = parser.parse(new FileReader("c:test.json")); JSONObject jsonObject = (JSONObject) obj; String name = (String) jsonObject.get("book"); System.out.println(name); String author = (String) jsonObject.get("author"); System.out.println(author); } -
从输入 JSON 文件中检索的下一个字段是 review 字段,这是一个数组。我们迭代这个字段如下:
JSONArray reviews = (JSONArray) jsonObject.get("messages"); Iterator<String> iterator = reviews.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } -
最后,我们创建 catch 块来处理解析期间可能出现的三种类型的异常,然后关闭该方法:
} catch (FileNotFoundException e) {
//Your exception handling here
} catch (IOException e) {
//Your exception handling here
} catch (ParseException e) {
//Your exception handling here
}
}
整个类、该配方中描述的方法以及运行该方法的驱动程序方法如下:
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Iterator;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
public class JsonReading {
public static void main(String[] args){
JsonReading jsonReading = new JsonReading();
jsonReading.readJson("C:/testJSON.json");
}
public void readJson(String inFileName) {
JSONParser parser = new JSONParser();
try {
Object obj = parser.parse(new FileReader(inFileName));
JSONObject jsonObject = (JSONObject) obj;
String name = (String) jsonObject.get("book");
System.out.println(name);
String author = (String) jsonObject.get("author");
System.out.println(author);
JSONArray reviews = (JSONArray) jsonObject.get("messages");
Iterator<String> iterator = reviews.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
} catch (FileNotFoundException e) {
//Your exception handling here
} catch (IOException e) {
//Your exception handling here
} catch (ParseException e) {
//Your exception handling here
}
}
}
成功执行代码后,您将能够在标准输出中看到输入文件的内容。
使用 JSoup 从 URL 中提取 web 数据
如今,大量的数据可以在网上找到。这些数据有时是结构化的、半结构化的,甚至是非结构化的。因此,提取它们需要非常不同的技术。有许多不同的方法来提取 web 数据。最简单方便的方法之一是使用名为 JSoup 的外部 Java 库。这个菜谱使用 JSoup 中提供的一些方法来提取 web 数据。
准备就绪
为了执行此配方,我们需要以下内容:
- 去jsoup.org/download,下载
jsoup-1.9.2.jar文件。将 JAR 文件添加到 Eclipse 项目的外部库中。 - 如果您是 Maven 爱好者,请按照下载页面上的说明将 JAR 文件包含到您的 Eclipse 项目中。
怎么做...
-
创建一个名为
extractDataWithJsoup(String url)的方法。该参数是调用该方法所需的任何网页的 URL。我们将从这个网址提取网页数据:public void extractDataWithJsoup(String href){ -
使用
connect()方法,将 URL 发送到我们想要连接的地方(并提取数据)。然后,我们会用它链接更多的方法。首先,我们将链接以毫秒为参数的timeout()方法。之后的方法定义连接期间的用户代理名称,以及是否尝试忽略连接错误。与前两个方法相联系的下一个方法是最终返回一个Document对象的get()方法。因此,我们将在Document类的doc中保存这个返回的对象:doc = Jsoup.connect(href).timeout(10*1000).userAgent ("Mozilla").ignoreHttpErrors(true).get(); -
As this code throws
IOException, we will be using atry...catchblock as follows:Document doc = null; try { doc = Jsoup.connect(href).timeout(10*1000).userAgent ("Mozilla").ignoreHttpErrors(true).get(); } catch (IOException e) { //Your exception handling here }Tip
We are not used to expressing time in milliseconds. Therefore, when milliseconds are the time unit in coding, it is a good practice to write 10*1000 to represent 10 seconds. This enhances the readability of the code.
-
对于一个
Document对象,可以找到大量的方法。如果要提取 URL 的标题,可以使用如下的标题方法:if(doc != null){ String title = doc.title(); -
为了只提取 web 页面的文本部分,我们可以将
body()方法与Document对象的text()方法链接起来,如下:String text = doc.body().text(); -
如果想提取一个 URL 中的所有超链接,可以使用带有
a[href]参数的Document对象的select()方法。这一次给你所有的链接:Elements links = doc.select("a[href]"); -
也许你想单独处理网页中的链接?这也很简单——你需要遍历所有的链接来得到单个的链接:
for (Element link : links) { String linkHref = link.attr("href"); String linkText = link.text(); String linkOuterHtml = link.outerHtml(); String linkInnerHtml = link.html(); System.out.println(linkHref + "t" + linkText + "t" + linkOuterHtml + "t" + linkInnterHtml); } -
最后,用大括号结束 if 条件。用大括号结束该方法:
}
}
完整的方法、其类和驱动程序方法如下:
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class JsoupTesting {
public static void main(String[] args){
JsoupTesting test = new JsoupTesting();
test.extractDataWithJsoup("Website address preceded by http://");
}
public void extractDataWithJsoup(String href){
Document doc = null;
try {
doc = Jsoup.connect(href).timeout(10*1000).userAgent
("Mozilla").ignoreHttpErrors(true).get();
} catch (IOException e) {
//Your exception handling here
}
if(doc != null){
String title = doc.title();
String text = doc.body().text();
Elements links = doc.select("a[href]");
for (Element link : links) {
String linkHref = link.attr("href");
String linkText = link.text();
String linkOuterHtml = link.outerHtml();
String linkInnerHtml = link.html();
System.out.println(linkHref + "t" + linkText + "t" +
linkOuterHtml + "t" + linkInnterHtml);
}
}
}
}
使用 Selenium Webdriver 从网站提取 web 数据
Selenium 是一个基于 Java 的工具,用于帮助自动化软件测试或质量保证。有趣的是,Selenium 可以用来自动检索和利用 web 数据。这个食谱告诉你如何做。
准备就绪
为了执行此配方,我们需要以下内容:
- 从 selenium-release.storage.googleapis.com/index.html?…](selenium-release.storage.googleapis.com/index.html?…) 。从后者中,提取出
selenium-java-2.53.1.jar文件。将这两个 JAR 文件包含在您的 eclipse 项目外部 Java 库中。 - 从ftp.mozilla.org/pub/firefox…下载并安装火狐 47.0.1,选择适合你操作系统的版本。
Tip
由于 Selenium 和 Firefox 之间的版本冲突问题,一旦您运行特定版本的代码,请关闭 Firefox 中的自动更新下载和安装选项。
怎么做...
-
创建一个名为
extractDataWithSelenium(String)的方法,该方法将一个String作为参数,它最终是我们要从中提取数据的 URL。我们可以从 URL 中提取许多不同类型的数据,比如标题、标题和下拉框中的值。这个菜谱只集中提取网页的文字部分:public String extractDataWithSelenium(String url){ -
接下来,使用下面的代码创建一个 Firefox web 驱动程序:
WebDriver driver = new FirefoxDriver(); -
通过传递 URL:
driver.get("http://cogenglab.csd.uwo.ca/rushdi.htm");来使用
WebDriver对象的get()方法 -
The text of the webpage can be found using
xpath, where the value ofidis content: -
用
findElement()方法找到这个特殊的元素。这个方法返回一个WebElement对象。创建一个名为webElement的WebElement对象来保存返回值:WebElement webElement = driver.findElement(By.xpath("//* [@id='content']")); -
对象有一个名为
getText()的方法。调用该方法检索网页文本,并将文本放入一个String变量中,如下:String text = (webElement.getText()); -
Finally, return the String variable and close the method:
}配方的 driver main()方法的完整代码段如下所示:
import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; public class TestSelenium { public String extractDataWithSelenium(String url) { WebDriver driver = new FirefoxDriver(); driver.get("http://cogenglab.csd.uwo.ca/rushdi.htm"); WebElement webElement = driver.findElement(By.xpath("//* [@id='content']")); System.out.println(webElement.getText()); return url; } public static void main(String[] args){ TestSelenium test = new TestSelenium(); String webData = test.extractDataWithSelenium ("http://cogenglab.csd.uwo.ca/rushdi.htm"); //process webData } }
注意
Selenium 和 Firefox 有兼容性问题。一些 Selenium 版本不能与一些 Firefox 版本一起使用。此处提供的配方适用于配方中提到的版本。但它不能保证能与其他 Selenium 或 Firefox 版本兼容。
由于 Selenium 和 Firefox 之间的版本冲突问题,一旦您使用两者的特定版本运行代码,请关闭 Firefox 中的自动更新下载和安装选项。
从 MySQL 数据库读取表格数据
数据也可以存储在数据库表中。这个菜谱演示了我们如何从 MySQL 的一个表中读取数据。
准备就绪
为了执行此配方,我们需要以下内容:
-
从 dev.mysql.com/downloads/m… MySQL 社区服务器。这个配方使用的版本是 5.7.15。
-
Create a database named
data_science. In this database, create a table namedbooksthat contains data as follows:字段类型的选择对于这个配方来说并不重要,但是字段的名称需要与这里展示的名称完全匹配。
-
从dev.mysql.com/downloads/c…下载平台无关的 MySql JAR 文件,并将其作为外部库添加到您的 Java 项目中。这个配方使用的版本是 5.1.39。
怎么做...
-
创建一个 public void
readTable(String user, String password, String server)方法,它将 MySQL 数据库的用户名、密码和服务器名作为参数:public void readTable(String user, String password, String server){ -
创建一个 MySQL 数据源,并使用该数据源设置用户名、密码和服务器名:
MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser(user); dataSource.setPassword(password); dataSource.setServerName(server); -
在
try块中,为数据库创建一个连接。使用该连接,创建一个语句,该语句将用于执行一个SELECT查询来从表中获取信息。查询的结果将存储在一个结果集中:try{ Connection conn = dataSource.getConnection(); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM data_science.books"); -
现在,遍历结果集,通过提及列名来检索每一列数据。请注意方法的使用,它为我们提供了您在使用它们之前需要知道的字段类型。例如,我们知道 ID 字段是整数,我们可以使用
getInt()方法:while (rs.next()){ int id = rs.getInt("id"); String book = rs.getString("book_name"); String author = rs.getString("author_name"); Date dateCreated = rs.getDate("date_created"); System.out.format("%s, %s, %s, %sn", id, book, author, dateCreated); } -
迭代后关闭结果集、语句和连接:
rs.close(); stmt.close(); conn.close(); -
在从表中读取数据的过程中,尽可能捕捉一些异常,并关闭该方法:
}catch (Exception e){
//Your exception handling mechanism goes here.
}
}
执行该方法的完整方法、类和驱动程序方法如下:
import java.sql.*;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
public class TestDB{
public static void main(String[] args){
TestDB test = new TestDB();
test.readTable("your user name", "your password", "your MySQL
server name");
}
public void readTable(String user, String password, String server)
{
MysqlDataSource dataSource = new MysqlDataSource();
dataSource.setUser(user);
dataSource.setPassword(password);
dataSource.setServerName(server);
try{
Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM
data_science.books");
while (rs.next()){
int id = rs.getInt("id");
String book = rs.getString("book_name");
String author = rs.getString("author_name");
Date dateCreated = rs.getDate("date_created");
System.out.format("%s, %s, %s, %sn", id, book,
author, dateCreated);
}
rs.close();
stmt.close();
conn.close();
}catch (Exception e){
//Your exception handling mechanism goes here.
}
}
}
这段代码显示您创建的表中的数据。
二、索引和搜索数据
在本章中,我们将介绍以下配方:
- 用 Apache Lucene 索引数据
- 用 Apache Lucene 搜索索引数据
简介
在这一章中,你将学到两个非常重要的食谱。第一个配方演示了如何索引数据,第二个配方与第一个配方密切相关,演示了如何搜索索引数据。
对于索引和搜索,我们将使用 Apache Lucene。Apache Lucene 是一个免费的开源 Java 软件库,主要用于信息检索。它由 Apache 软件基金会支持,并根据 Apache 软件许可证发布。
许多不同的现代搜索平台,如 Apache Solr 和 ElasticSearch,或爬行平台,如 Apache Nutch,在后端使用 Apache Lucene 进行数据索引和搜索。因此,任何学习这些搜索平台的数据科学家都将从本章的两个基本食谱中受益。
使用 Apache Lucene 索引数据
在这个菜谱中,我们将演示如何用 Apache Lucene 索引大量数据。索引是快速搜索数据的第一步。实际上,Lucene 使用了一个倒排全文索引。换句话说,它考虑所有的文档,将它们分成单词或标记,然后为每个标记建立一个索引,这样它就可以提前知道如果搜索一个术语,应该查找哪个文档。
准备就绪
以下是要实施的步骤:
-
To download Apache Lucene, go to lucene.apache.org/core/downlo…, and click on the Download button. At the time of writing, the latest version of Lucene was 6.4.1. Once you click on the Download button, it will take you to the mirror websites that host the distribution:
-
Choose any appropriate mirror for downloading. Once you click a mirror website, it will take you to a directory of distribution. Download the
lucene-6.4.1.zipfile onto your system: -
Once you download it, unzip the distribution. You will see a nicely organized folder distribution, as follows:
-
Open Eclipse, and create a project named
LuceneTutorial. To do that, open Eclipse and go to File. Then go to New... and Java Project. Take the name of the project and click on Finish: -
Now you will be inserting JAR files necessary for this recipe as external libraries into your project. Right-click on your project name in the Package Explorer. Select Build Path and then Configure Build Path... This will open properties for your project:
-
Click on the Add External Jars button, and then add the following JAR files from Lucene 6.4.1 distributions:
lucene-core-6.4.1.jar,可以在解压后的 Lucene 发行版的lucene-6.4.1\core中找到lucene-queryparser-6.4.1.jar,可以在解压后的 Lucene 发行版的lucene-6.4.1\queryparser中找到Lucene-analyzers-common-6.4.1.jar,可以在解压后的 Lucene 发行版的lucene-6.4.1\analysis\common中找到
添加完 JAR 文件后,点击 OK :
-
对于索引,你将使用文本格式的威廉莎士比亚的作品。打开浏览器,进入norvig.com/ngrams/。这将打开一个名为自然语言语料库数据:美丽数据的页面。在下载部分的文件中,您会发现一个名为 shakespeare 的. txt 文件。将此文件下载到系统中的任何位置。
-
Unzip the files and you will see that the distribution contains three folders,
comedies,historical, andtragedies: -
Create a folder in your project directory. Right-click on your project in Eclipse and go to New, and then click Folder. As the folder name, type in input and click on Finish:
-
将步骤 8 中的
shakespeare.txt复制到您在步骤 9 中创建的文件夹中。 -
按照步骤 9 中的说明创建另一个名为 index 的文件夹。在此阶段,您的项目文件夹将如下所示:
现在您已经为编码做好了准备。
怎么做...
-
Create a package in your project named
org.apache.lucene.demo, and create a Java file in the package namedIndexFiles.java: -
在这个 Java 文件中,您将创建一个名为
IndexFiles:public class IndexFiles {的类
-
您将编写的第一个方法称为
indexDocs。该方法使用给定的索引编写器对任何给定的文件进行索引。如果将目录作为参数提供,该方法将递归地遍历在给定目录下找到的文件和目录。这个方法为每个输入文件索引一个文档:提示
这种方法相对较慢,因此为了获得更好的性能,可以将多个文档放入输入文件中
static void indexDocs(final IndexWriter writer, Path path) throws IOException {- writer 是编写索引的索引编写器,给定的文件或目录信息将存储在该索引中
- path 是要索引的文件,或包含将为其创建索引的文件的目录
-
如果提供了一个目录,该目录将被递归迭代或遍历:
if (Files.isDirectory(path)) { Files.walkFileTree(path, new SimpleFileVisitor<Path>() { -
然后,您将重写一个名为
visitFile的方法,根据给定的路径和基本文件属性来访问文件或目录:@Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { -
接下来,您将调用一个稍后将创建的名为
indexDoc的静态方法。我们故意将 catch 块留空,因为我们让您决定如果文件不能被索引时该做什么:try { indexDoc(writer, file, attrs.lastModifiedTime().toMillis()); } catch (IOException ignore) { } -
从
visitFile方法返回:return FileVisitResult.CONTINUE; } -
关闭区块:
} ); } -
在 else 块中,调用
indexDoc方法。请记住,在else块中,您处理的是文件,而不是目录:else { indexDoc(writer, path, Files.getLastModifiedTime(path).toMillis()); } -
关闭
indexDocs()方法:
```java
}
```
11. 现在创建一个方法来处理单个文档的索引:
```java
static void indexDoc(IndexWriter writer, Path file, long
lastModified) throws IOException {
```
12. 首先,创建一个try块来创建一个新的空文档:
```java
try (InputStream stream = Files.newInputStream(file)) {
Document doc = new Document();
```
13. 接下来,将文件的路径添加为一个字段。键入"path"作为字段名。该字段将是可搜索的或索引的。但是,请注意,您没有对字段进行标记,也没有对术语频率或位置信息进行索引:
```java
Field pathField = new StringField("path", file.toString(),
Field.Store.YES);
doc.add(pathField);
```
14. 添加文件的最后修改日期,一个名为"modified" :
```java
doc.add(new LongPoint("modified", lastModified));
```
的字段
15. 将文件内容添加到名为"contents"的字段中。您指定的阅读器将确保文件的文本被标记化和索引,但不被存储:
```java
doc.add(new TextField("contents", new BufferedReader(new
InputStreamReader(stream, StandardCharsets.UTF_8))));
```
### 注意
如果文件不是用`UTF-8`编码,那么搜索特殊字符将会失败
16. 为文件创建索引:
```java
if (writer.getConfig().getOpenMode() == OpenMode.CREATE) {
System.out.println("adding " + file);
writer.addDocument(doc);
}
```
17. 文档可能已经被索引了。你的else区块将处理这些情况。您将使用updateDocument而不是替换与精确路径匹配的旧路径,如果存在:
```java
else {
System.out.println("updating " + file);
writer.updateDocument(new Term("path", file.toString()),
doc);
}
```
18. 关闭 try 块和方法:
```java
}
}
```
19. 现在让我们为这个类创建 main 方法。
```java
public static void main(String[] args) {
```
20. You will be providing three options from the console when you run your program:
* 第一个选项是 index,参数将是包含索引的文件夹
* 第二个选项是 docs,参数是包含文本文件的文件夹
* 最后一个选项是 update,该参数将表示您是想要创建新索引还是更新旧索引
为了保存这三个参数的值,创建并初始化三个变量:
```java
String indexPath = "index";
String docsPath = null;
boolean create = true;
```
21. 设置三个选项的值:
```java
for(int i=0;i<args.length;i++) {
if ("-index".equals(args[i])) {
indexPath = args[i+1];
i++;
} else if ("-docs".equals(args[i])) {
docsPath = args[i+1];
i++;
} else if ("-update".equals(args[i])) {
create = false;
}
}
```
22. 设置文件目录:
```java
final Path docDir = Paths.get(docsPath);
```
23. 现在,您将开始索引目录中的文件。首先,设置计时器,因为您将计时索引延迟:
```java
Date start = new Date();
```
24. 对于索引,创建一个目录并创建一个分析器(在这种情况下,您将使用一个基本的、标准的分析器和一个索引编写器配置器):
```java
try {
Directory dir = FSDirectory.open(Paths.get(indexPath));
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
```
25. 配置好索引编写器后,根据关于索引创建或更新的输入,设置索引的打开模式。如果选择创建新的索引,打开模式将被设置为CREATE。否则就是CREATE_OR_APPEND :
```java
if (create) {
iwc.setOpenMode(OpenMode.CREATE);
} else {
iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
}
```
26. 创建索引编写器:
```java
IndexWriter writer = new IndexWriter(dir, iwc);
indexDocs(writer, docDir);
```
27. 关闭writer :
```java
writer.close();
```
28. 至此,您差不多完成了编码。只需完成对索引时间的跟踪:
```java
Date end = new Date();
System.out.println(end.getTime() - start.getTime() + " total
milliseconds");
```
29. 关闭try块。我们有意将catch块留为空白,以便您可以决定在索引过程中出现异常的情况下应该做什么:
```java
} catch (IOException e) {
}
```
30. 关闭 main 方法并关闭类:
```java
}
}
```
31. Right-click on your project in Eclipse, select Run As, and click on Run Configurations...:
32. Go to the Arguments tab in the Run Configurations window. In the Program Arguments option, put
-docs input\ -index index\. Click on Run:
33. 代码的输出如下所示:
它是如何工作的...
食谱的完整代码如下:
package org.apache.lucene.demo;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Date;
public class IndexFiles {
static void indexDocs(final IndexWriter writer, Path path) throws
IOException {
if (Files.isDirectory(path)) {
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException {
try {
indexDoc(writer, file,
attrs.lastModifiedTime().toMillis());
} catch (IOException ignore) {
}
return FileVisitResult.CONTINUE;
}
}
);
} else {
indexDoc(writer, path,
Files.getLastModifiedTime(path).toMillis());
}
}
static void indexDoc(IndexWriter writer, Path file, long
lastModified) throws IOException {
try (InputStream stream = Files.newInputStream(file)) {
Document doc = new Document();
Field pathField = new StringField("path", file.toString(),
Field.Store.YES);
doc.add(pathField);
doc.add(new LongPoint("modified", lastModified));
doc.add(new TextField("contents", new BufferedReader(new
InputStreamReader(stream, StandardCharsets.UTF_8))));
if (writer.getConfig().getOpenMode() == OpenMode.CREATE) {
System.out.println("adding " + file);
writer.addDocument(doc);
} else {
System.out.println("updating " + file);
writer.updateDocument(new Term("path", file.toString()),
doc);
}
}
}
public static void main(String[] args) {
String indexPath = "index";
String docsPath = null;
boolean create = true;
for(int i=0;i<args.length;i++) {
if ("-index".equals(args[i])) {
indexPath = args[i+1];
i++;
} else if ("-docs".equals(args[i])) {
docsPath = args[i+1];
i++;
} else if ("-update".equals(args[i])) {
create = false;
}
}
final Path docDir = Paths.get(docsPath);
Date start = new Date();
try {
System.out.println("Indexing to directory '" + indexPath +
"'...");
Directory dir = FSDirectory.open(Paths.get(indexPath));
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
if (create) {
iwc.setOpenMode(OpenMode.CREATE);
} else {
iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
}
IndexWriter writer = new IndexWriter(dir, iwc);
indexDocs(writer, docDir);
writer.close();
Date end = new Date();
System.out.println(end.getTime() - start.getTime() + " total
milliseconds");
} catch (IOException e) {
}
}
}
使用 Apache Lucene 搜索索引数据
现在您已经索引了您的数据,您将在这个菜谱中使用 Apache Lucene 搜索数据。这个配方中的搜索代码依赖于您在前一个配方中创建的索引,因此,只有按照前一个配方中的说明执行,它才能成功执行。
准备就绪
- Complete the previous recipe. After completing the previous recipe, go to the index directory in your project that you created in step 11 of that recipe. Make sure that you see some indexing files there:
- Create a Java file named
SearchFilesin theorg.apache.lucene.demopackage you created in the previous recipe: - 现在您已经准备好在
SearchFiles.java文件中键入一些代码。
怎么做...
-
在 Eclipse 的编辑器中打开
SearchFiles.java并创建下面的类:public class SearchFiles { -
您需要创建两个常量字符串变量。第一个变量将包含您在之前的配方中创建的
index的路径。第二个变量将包含您要搜索的字段内容。在我们的例子中,我们将在index:public static final String INDEX_DIRECTORY = "index"; public static final String FIELD_CONTENTS = "contents";的
contents字段中进行搜索 -
开始创建你的主方法:
public static void main(String[] args) throws Exception { -
通过打开
index目录中的索引创建一个indexreader:IndexReader reader = DirectoryReader.open(FSDirectory.open (Paths.get(INDEX_DIRECTORY))); -
下一步是创建一个搜索器来搜索索引:
IndexSearcher indexSearcher = new IndexSearcher(reader); -
作为您的分析仪,创建一个标准分析仪:
Analyzer analyzer = new StandardAnalyzer(); -
通过向
QueryParser构造函数提供两个参数来创建一个查询解析器,这两个参数是您将要搜索的字段和您已经创建的解析器:QueryParser queryParser = new QueryParser(FIELD_CONTENTS, analyzer); -
在这个菜谱中,您将使用一个预定义的搜索词。在这个搜索中,您试图找到同时包含
"over-full"和"persuasion":String searchString = "over-full AND persuasion";的文档
-
使用搜索字符串创建一个查询:
Query query = queryParser.parse(searchString); -
搜索者将查看索引,看是否能找到搜索项。你也提到了会有多少搜索结果,在我们的例子中是
5:
```java
TopDocs results = indexSearcher.search(query, 5);
```
11. 创建一个数组来保存hits :
```java
ScoreDoc[] hits = results.scoreDocs;
```
12. 请注意,在索引过程中,我们只使用了一个文档shakespeare.txt。因此,在我们的例子中,这个数组的长度最大为 1。
13. 您可能还想知道找到的匹配的文档数量:
```java
int numTotalHits = results.totalHits;
System.out.println(numTotalHits + " total matching documents");
```
14. 最后,遍历命中结果。您将获得找到匹配项的文档 ID。有了文档 ID,您就可以创建文档,并打印文档的路径和 Lucene 为您使用的搜索词计算的分数:
```java
for(int i=0;i<hits.length;++i) {
int docId = hits[i].doc;
Document d = indexSearcher.doc(docId);
System.out.println((i + 1) + ". " + d.get("path") + " score="
+ hits[i].score);
}
```
15. 关闭方法和类:
```java
}
}
```
16. If you run the code, you will see the following output:
17. 打开项目文件夹的输入文件夹中的
shakespeare.txt文件。手动搜索,你会发现"over-full"和"persuasion"都出现在文档中。
18. 更改步骤 8 中的searchString,如下:
```java
String searchString = "shakespeare";
```
19. By keeping the rest of the codes as they are, whether you run the code, you will see the following output:
20. 再次打开
Shakespeare.txt文件,仔细检查莎士比亚这个词是否出现在其中。你将一无所获。
这个食谱的完整代码如下:
package org.apache.lucene.demo;
import java.nio.file.Paths;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
public class SearchFiles {
public static final String INDEX_DIRECTORY = "index";
public static final String FIELD_CONTENTS = "contents";
public static void main(String[] args) throws Exception {
IndexReader reader = DirectoryReader.open(FSDirectory.open
(Paths.get(INDEX_DIRECTORY)));
IndexSearcher indexSearcher = new IndexSearcher(reader);
Analyzer analyzer = new StandardAnalyzer();
QueryParser queryParser = new QueryParser(FIELD_CONTENTS,
analyzer);
String searchString = "shakespeare";
Query query = queryParser.parse(searchString);
TopDocs results = indexSearcher.search(query, 5);
ScoreDoc[] hits = results.scoreDocs;
int numTotalHits = results.totalHits;
System.out.println(numTotalHits + " total matching documents");
for(int i=0;i<hits.length;++i) {
int docId = hits[i].doc;
Document d = indexSearcher.doc(docId);
System.out.println((i + 1) + ". " + d.get("path") + " score="
+ hits[i].score);
}
}
}
注意
你可以访问lucene.apache.org/core/2_9_4/…获取 Apache Lucene 支持的查询语法。
三、统计分析数据
在本章中,我们将介绍以下配方:
- 生成描述性统计数据
- 生成汇总统计数据
- 从多个分布生成汇总统计数据
- 计算频率分布
- 统计字符串中的词频
- 用 Java 8 计算字符串中的词频
- 计算简单回归
- 计算普通最小二乘回归
- 计算广义最小二乘回归
- 计算两组数据点的协方差
- 计算两组数据点的皮尔逊相关性
- 进行配对 t 检验
- 进行卡方检验
- 进行单向 ANOVA 检验
- 进行柯尔莫哥洛夫-斯米尔诺夫试验
简介
统计分析是数据科学家的日常活动之一。这种分析包括但不限于描述性统计、频率分布、简单和多重回归、相关性和协方差以及数据分布中的统计显著性的分析。幸运的是,Java 有许多只需几行代码就能对数据进行强大的统计分析的库。这一章用 15 种方法概述了数据科学家如何使用 Java 进行这种分析。
请注意,本章的重点仅在于使用 Java 进行基本的数据统计分析,尽管也可能使用线性代数、数值分析、特殊函数、复数、几何、曲线拟合、微分方程。
为了执行本章中的配方,我们需要以下内容:
- 阿帕奇公共数学 3.6.1。所以需要从http://commons . Apache . org/proper/commons-math/download _ math . CGI下载 JAR 文件。
- If you want to use older versions, the older versions are in the archive at archive.apache.org/dist/common…, as shown in the following screenshot:
- 将它作为外部 JAR 文件包含在您的 eclipse 项目中:
Apache Commons Math 3.6.1 的stat包非常丰富,优化得很好。该包可以生成以下描述性统计数据:
- 算术和几何平均
- 方差和标准差
- 和、积、对数和、平方和
- 最小值、最大值、中间值和百分位数
- 偏斜度和峰度
- 一阶、二阶、三阶和四阶矩
此外,根据他们的网站http://commons . Apache . org/proper/commons-math/user guide/stat . html,这些方法被优化并使用尽可能少的内存。
除了百分位数和中位数,所有这些统计数据都可以计算,而无需在内存中保存输入数据值的完整列表。
生成描述性统计数据
描述性统计用于总结一个样本,通常不是基于概率理论开发的。相比之下,推断统计学主要用于从人口的代表性样本中得出关于人口的结论。在这个菜谱中,我们将看到如何使用 Java 从小样本中生成描述性统计数据。
在不扩大这个食谱范围的情况下,我们将只关注这里列出的描述性统计数据的一个子集。
怎么做...
-
创建一个将
double数组作为参数的方法。该数组将包含您要计算其描述性统计数据的值:public void getDescStats(double[] values){ -
创建一个
DescriptiveStatistics类型的对象:DescriptiveStatistics stats = new DescriptiveStatistics(); -
遍历双数组的所有值,并将它们添加到
DescriptiveStatistic对象:for( int i = 0; i < values.length; i++) { stats.addValue(values[i]); } -
Apache Commons 数学库的
DescriptiveStatistics类中有计算一组值的平均值、标准差和中值的方法。调用这些方法来获取值的描述性统计信息。最后,关闭方法:
double mean = stats.getMean();
double std = stats.getStandardDeviation();
double median = stats.getPercentile(50);
System.out.println(mean + "\t" + std + "\t" + median);
}
包括驱动方法在内的完整代码如下所示:
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
public class DescriptiveStats {
public static void main(String[] args){
double[] values = {32, 39, 14, 98, 45, 44, 45, 34, 89, 67, 0,
15, 0, 56, 88};
DescriptiveStats descStatTest = new DescriptiveStats();
descStatTest.getDescStats(values);
}
public void getDescStats(double[] values){
DescriptiveStatistics stats = new DescriptiveStatistics();
for( int i = 0; i < values.length; i++) {
stats.addValue(values[i]);
}
double mean = stats.getMean();
double std = stats.getStandardDeviation();
double median = stats.getPercentile(50);
System.out.println(mean + "\t" + std + "\t" + median);
}
}
Tip
为了以线程安全的方式计算统计数据,您可以创建一个SynchronizedDescriptiveStatistics实例,如下所示:DescriptiveStatistics stats = new SynchronizedDescriptiveStatistics();
生成汇总统计
我们可以通过使用SummaryStatistics类来生成数据的汇总统计。这类似于前面配方中使用的DescriptiveStatistics类;主要区别在于,与DescriptiveStatistics类不同,SummaryStatistics类不在内存中存储数据。
怎么做...
-
像前面的方法一样,创建一个将
double数组作为参数的方法:public void getSummaryStats(double[] values){ -
创建一个
SummaryStatistics:类的对象SummaryStatistics stats = new SummaryStatistics(); -
将所有值添加到
SummaryStatistics类的这个对象:for( int i = 0; i < values.length; i++) { stats.addValue(values[i]); } -
最后,使用
SummaryStatistics类中的方法为这些值生成汇总统计数据。使用完统计数据后,关闭该方法:
double mean = stats.getMean();
double std = stats.getStandardDeviation();
System.out.println(mean + "\t" + std);
}
驱动程序方法的完整代码如下所示:
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
public class SummaryStats {
public static void main(String[] args){
double[] values = {32, 39, 14, 98, 45, 44, 45, 34, 89, 67, 0, 15,
0, 56, 88};
SummaryStats summaryStatTest = new SummaryStats();
summaryStatTest.getSummaryStats(values);
}
public void getSummaryStats(double[] values){
SummaryStatistics stats = new SummaryStatistics();
for( int i = 0; i < values.length; i++) {
stats.addValue(values[i]);
}
double mean = stats.getMean();
double std = stats.getStandardDeviation();
System.out.println(mean + "\t" + std);
}
}
从多个分布生成汇总统计
在这个菜谱中,我们将创建一个AggregateSummaryStatistics实例来累积总体统计数据,并为样本数据创建SummaryStatistics。
怎么做...
-
创建一个接受两个
double数组参数的方法。每个数组将包含两组不同的数据:public void getAggregateStats(double[] values1, double[] values2){ -
创建一个
AggregateSummaryStatistics:AggregateSummaryStatistics aggregate = new AggregateSummaryStatistics();类的对象
-
为了从这两个分布中生成汇总统计数据,创建两个
SummaryStatistics类的对象:SummaryStatistics firstSet = aggregate.createContributingStatistics(); SummaryStatistics secondSet = aggregate.createContributingStatistics(); -
将上一步创建的两个对象中的两个分布值相加:
for(int i = 0; i < values1.length; i++) { firstSet.addValue(values1[i]); } for(int i = 0; i < values2.length; i++) { secondSet.addValue(values2[i]); } -
使用
AggregateSummaryStatistics类的方法从两个分布中生成聚合统计数据。最后,在使用生成的统计信息后关闭该方法:
double sampleSum = aggregate.getSum();
double sampleMean = aggregate.getMean();
double sampleStd= aggregate.getStandardDeviation();
System.out.println(sampleSum + "\t" + sampleMean + "\t" +
sampleStd);
}
该配方的完整代码库如下:
import org.apache.commons.math3.stat.descriptive.
AggregateSummaryStatistics;
import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
public class AggregateStats {
public static void main(String[] args){
double[] values1 = {32, 39, 14, 98, 45, 44, 45};
double[] values2 = {34, 89, 67, 0, 15, 0, 56, 88};
AggregateStats aggStatTest = new AggregateStats();
aggStatTest.getAggregateStats(values1, values2);
}
public void getAggregateStats(double[] values1, double[] values2){
AggregateSummaryStatistics aggregate = new
AggregateSummaryStatistics();
SummaryStatistics firstSet =
aggregate.createContributingStatistics();
SummaryStatistics secondSet =
aggregate.createContributingStatistics();
for(int i = 0; i < values1.length; i++) {
firstSet.addValue(values1[i]);
}
for(int i = 0; i < values2.length; i++) {
secondSet.addValue(values2[i]);
}
double sampleSum = aggregate.getSum();
double sampleMean = aggregate.getMean();
double sampleStd= aggregate.getStandardDeviation();
System.out.println(sampleSum + "\t" + sampleMean + "\t" +
sampleStd);
}
}
还有更多...
这种方法有几个缺点,在这里讨论一下:
- 每次我们调用
addValue()方法时,调用必须在由聚合维护的SummaryStatistics实例上同步 - 每次我们添加一个值,它都会更新集合和样本
为了克服这些缺点,类中提供了一个static aggregate方法。
计算频率分布
Frequency类有计算一个桶中数据实例数量的方法,计算数据实例的唯一数量的方法,等等。Frequency的接口非常简单,在大多数情况下,它只需要很少几行代码就可以完成所需的计算。
作为值类型,字符串、整数、长整型和字符都受支持。
注意
自然排序是累积频率的默认排序,但这可以通过向constructor提供Comparator来覆盖。
怎么做...
-
创建一个将
double数组作为参数的方法。我们将计算这个数组的值的频率分布:public void getFreqStats(double[] values){ -
创建一个
Frequency类的对象:Frequency freq = new Frequency(); -
将
double数组的值添加到这个对象:for( int i = 0; i < values.length; i++) { freq.addValue(values[i]); } -
为数组中的每个值生成频率:
for( int i = 0; i < values.length; i++) { System.out.println(freq.getCount(values[i])); } -
最后,关闭方法:
}
该配方的完整代码库如下:
import org.apache.commons.math3.stat.Frequency;
public class FrequencyStats {
public static void main(String[] args){
double[] values = {32, 39, 14, 98, 45, 44, 45, 34, 89, 67, 0, 15,
0, 56, 88};
FrequencyStats freqTest = new FrequencyStats();
freqTest.getFreqStats(values);
}
public void getFreqStats(double[] values){
Frequency freq = new Frequency();
for( int i = 0; i < values.length; i++) {
freq.addValue(values[i]);
}
for( int i = 0; i < values.length; i++) {
System.out.println(freq.getCount(values[i]));
}
}
}
统计字符串中的词频
这个方法与本章中的其他方法有很大的不同,因为它处理字符串并计算字符串中的词频。我们将使用 Apache Commons Math 和 Java 8 来完成这项任务。这个配方将使用外部库,而下一个配方将使用 Java 8 实现相同的功能。
怎么做...
-
创建一个采用
String数组的方法。该数组包含一个字符串中的所有单词:public void getFreqStats(String[] words){ -
创建一个
Frequency类对象:Frequency freq = new Frequency(); -
将所有单词添加到
Frequency对象:for( int i = 0; i < words.length; i++) { freq.addValue(words[i].trim()); } -
对于每个单词,使用
Frequency类的getCount()方法计算频率。最后,处理完频率后,关闭方法:
for( int i = 0; i < words.length; i++) {
System.out.println(words[i] + "=" +
freq.getCount(words[i]));
}
}
配方的工作代码如下:
import org.apache.commons.math3.stat.Frequency;
public class WordFrequencyStatsApache {
public static void main(String[] args){
String str = "Horatio says 'tis but our fantasy, "
+ "And will not let belief take hold of him "
+ "Touching this dreaded sight, twice seen of us. "
+ "Therefore I have entreated him along, 35"
+ "With us to watch the minutes of this night, "
+ "That, if again this apparition come, "
+ "He may approve our eyes and speak to it.";
String[] words = str.toLowerCase().split("\\W+");
WordFrequencyStatsApache freqTest = new
WordFrequencyStatsApache();
freqTest.getFreqStats(words);
}
public void getFreqStats(String[] words){
Frequency freq = new Frequency();
for( int i = 0; i < words.length; i++) {
freq.addValue(words[i].trim());
}
for( int i = 0; i < words.length; i++) {
System.out.println(words[i] + "=" + freq.getCount(words[i]));
}
}
}
它是如何工作的...
这个菜谱打印出字符串中的每个单词及其频率,这样输出的结果就会出现重复的单词及其频率。当您在最后一个for循环中处理频率时,您需要有一个编程机制来避免重复输出。
例如,下一个配方使用了一个Map数据结构来避免单词重复。如果单词的顺序不重要,可以使用一个HashMap,如果单词的顺序很重要,就需要使用一个TreeMap数据结构。
用 Java 8 统计字符串中的词频
该方法不使用 Apache Commons 数学库来计算给定字符串中的单词频率;相反,它使用 Java 8 中引入的核心库和机制。
注意
有许多方法可以实现一个计算词频的工作示例,因此,我们鼓励读者看看这个方法在早于 8.0 版本的 Java 中的许多实现。
怎么做...
-
创建一个采用字符串参数的方法。我们将计算这个字符串中单词的频率:
public void getFreqStats(String str){ -
从给定的字符串创建一个
Stream。在我们的例子中,我们将把字符串转换成小写,并根据正则表达式\W+识别单词。将字符串转换成流的过程将并行完成:Stream<String> stream = Stream.of(str.toLowerCase().split("\\W+")).parallel(); -
我们将使用
Stream类的collect()方法收集单词及其频率。请注意,集合将被发送到一个在其泛型中带有String和Long的Map对象;string 将包含单词,long 将包含其频率:Map<String, Long> wordFreq = stream.collect(Collectors.groupingBy (String::toString,Collectors.counting())); -
最后,我们将使用
forEach一次性打印地图内容并关闭方法:
wordFreq.forEach((k,v)->System.out.println(k + "=" + v));
}
该配方的工作示例如下:
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class WordFrequencyStatsJava {
public static void main(String[] args){
String str = "Horatio says 'tis but our fantasy, "
+ "And will not let belief take hold of him "
+ "Touching this dreaded sight, twice seen of us. "
+ "Therefore I have entreated him along, 35"
+ "With us to watch the minutes of this night, "
+ "That, if again this apparition come, "
+ "He may approve our eyes and speak to it.";
WordFrequencyStatsJava freqTest = new WordFrequencyStatsJava();
freqTest.getFreqStats(str);
}
public void getFreqStats(String str){
Stream<String> stream = Stream.of(str.toLowerCase().split("\\W+")).parallel();
Map<String, Long> wordFreq = stream
.collect(Collectors.groupingBy(String::toString,Collectors.counting()));
wordFreq.forEach((k,v)->System.out.println(k + "=" + v));
}
}
计算简单回归
SimpleRegression类支持一个自变量的普通最小二乘回归:y = intercept + slope * x,其中intercept是可选参数。该类还能够为intercept提供标准误差。观测值(x,y)对既可以一次一个地添加到模型中,也可以以二维数组的形式提供。在这个配方中,数据点一次添加一个。
注意
观察值不存储在内存中,因此可以添加到模型中的观察值数量没有限制。
怎么做...
-
要计算简单回归,创建一个采用二维
double数组的方法。该数组表示一系列(x,y)值:public void calculateRegression(double[][] data){ -
Create a
SimpleRegressionobject, and add the data:SimpleRegression regression = new SimpleRegression(); regression.addData(data);Note
If you don't intercept it or if you want to exclude it from the calculation, you need to use a different constructor to create the
SimpleRegressionobjectSimpleRegression regression = new SimpleRegression(false);. -
找出截距、斜率以及截距和斜率的标准误差。最后,关闭方法:
System.out.println(regression.getIntercept());
System.out.println(regression.getSlope());
System.out.println(regression.getSlopeStdErr());
}
这个食谱的完整代码如下:
import org.apache.commons.math3.stat.regression.SimpleRegression;
public class RegressionTest {
public static void main(String[] args){
double[][] data = { { 1, 3 }, {2, 5 }, {3, 7 }, {4, 14 }, {5, 11 }};
RegressionTest test = new RegressionTest();
test.calculateRegression(data);
}
public void calculateRegression(double[][] data){
SimpleRegression regression = new SimpleRegression();
regression.addData(data);
System.out.println(regression.getIntercept());
System.out.println(regression.getSlope());
System.out.println(regression.getSlopeStdErr());
}
}
注意
如果模型中的观测值少于两个,或者所有 x 值都相同,则所有统计数据都将返回 NaN。
使用 getter 方法,如果您在获得统计数据后添加更多数据,您将能够在不使用新实例的情况下获得更新的统计数据。
计算普通最小二乘回归
OLSMultipleLinearRegression提供普通最小二乘回归来拟合线性模型 Y=Xb+u* 。这里,Y是 n 向量回归,X是[n,k]矩阵,其中k列称为回归变量,b是回归参数的k-vector,u是误差项或残差的n-vector。
怎么做...
-
创建一个采用二维
double数组和一维double数组:public void calculateOlsRegression(double[][] x, double[] y){的方法
-
创建一个 OLS 回归对象,并添加数据点 x 和 y:
OLSMultipleLinearRegression regression = new OLSMultipleLinearRegression(); regression.newSampleData(y, x); -
使用
OLSMultipleLinearRegression类中的以下方法计算各种回归参数和诊断。这些信息的用途取决于您手头的任务。最后,关闭方法:double[] beta = regression.estimateRegressionParameters(); double[] residuals = regression.estimateResiduals(); double[][] parametersVariance = regression.estimateRegressionParametersVariance(); double regressandVariance = regression.estimateRegressandVariance(); double rSquared = regression.calculateRSquared(); double sigma = regression.estimateRegressionStandardError(); } -
可以如下创建
x和y数据点。对于这个例子,我们使用了固定数据,因此,数组索引的初始化不是自动进行的。你需要创建一个循环系统来创建x阵列:
double[] y = new double[]{11.0, 12.0, 13.0, 14.0, 15.0, 16.0};
double[][] x = new double[6][];
x[0] = new double[]{0, 0, 0, 0, 0};
x[1] = new double[]{2.0, 0, 0, 0, 0};
x[2] = new double[]{0, 3.0, 0, 0, 0};
x[3] = new double[]{0, 0, 4.0, 0, 0};
x[4] = new double[]{0, 0, 0, 5.0, 0};
x[5] = new double[]{0, 0, 0, 0, 6.0};
该配方的工作示例如下:
import org.apache.commons.math3.stat.regression.
OLSMultipleLinearRegression;
public class OLSRegressionTest {
public static void main(String[] args){
double[] y = new double[]{11.0, 12.0, 13.0, 14.0, 15.0, 16.0};
double[][] x = new double[6][];
x[0] = new double[]{0, 0, 0, 0, 0};
x[1] = new double[]{2.0, 0, 0, 0, 0};
x[2] = new double[]{0, 3.0, 0, 0, 0};
x[3] = new double[]{0, 0, 4.0, 0, 0};
x[4] = new double[]{0, 0, 0, 5.0, 0};
x[5] = new double[]{0, 0, 0, 0, 6.0};
OLSRegressionTest test = new OLSRegressionTest();
test.calculateOlsRegression(x, y);
}
public void calculateOlsRegression(double[][] x, double[] y){
OLSMultipleLinearRegression regression = new
OLSMultipleLinearRegression();
regression.newSampleData(y, x);
double[] beta = regression.estimateRegressionParameters();
double[] residuals = regression.estimateResiduals();
double[][] parametersVariance =
regression.estimateRegressionParametersVariance();
double regressandVariance =
regression.estimateRegressandVariance();
double rSquared = regression.calculateRSquared();
double sigma = regression.estimateRegressionStandardError();
//print out the values here
}
}
注意
当输入数据数组的维度不匹配并且数据数组不包含足够的数据来估计模型时,两个事件会抛出IllegalArgumentException。
计算广义最小二乘回归
在这个食谱中,我们将看到最小二乘回归的另一种变体,称为广义最小二乘回归。 GLSMultipleLinearRegression实现广义最小二乘拟合线性模型 Y=Xb+u* 。
怎么做...
-
为回归的 omega 参数创建一个采用二维双数组、一维双数组和二维双数组的方法:
public void calculateGlsRegression(double[][] x, double[] y, double[][] omega){ -
创建 GLS 回归对象、数据点和 omega 参数:
GLSMultipleLinearRegression regression = new GLSMultipleLinearRegression(); regression.newSampleData(y, x, omega); -
使用
GLSMultipleLinearRegression类的方法,计算回归的各种统计数据,最后关闭方法:double[] beta = regression.estimateRegressionParameters(); double[] residuals = regression.estimateResiduals(); double[][] parametersVariance = regression.estimateRegressionParametersVariance(); double regressandVariance = regression.estimateRegressandVariance(); double sigma = regression.estimateRegressionStandardError(); } -
要查看我们如何填充两个数组 x 和 y,请参考前面的配方。对于这个食谱,除了 x 和 y 数据点之外,我们还需要ω值。ω值可以插入到二维双数组中,如下所示:
double[][] omega = new double[6][];
omega[0] = new double[]{1.1, 0, 0, 0, 0, 0};
omega[1] = new double[]{0, 2.2, 0, 0, 0, 0};
omega[2] = new double[]{0, 0, 3.3, 0, 0, 0};
omega[3] = new double[]{0, 0, 0, 4.4, 0, 0};
omega[4] = new double[]{0, 0, 0, 0, 5.5, 0};
omega[5] = new double[]{0, 0, 0, 0, 0, 6.6};
这个配方的完整类、方法和驱动程序方法如下:
import org.apache.commons.math3.stat.regression.
GLSMultipleLinearRegression;
public class GLSRegressionTest {
public static void main(String[] args){
double[] y = new double[]{11.0, 12.0, 13.0, 14.0, 15.0, 16.0};
double[][] x = new double[6][];
x[0] = new double[]{0, 0, 0, 0, 0};
x[1] = new double[]{2.0, 0, 0, 0, 0};
x[2] = new double[]{0, 3.0, 0, 0, 0};
x[3] = new double[]{0, 0, 4.0, 0, 0};
x[4] = new double[]{0, 0, 0, 5.0, 0};
x[5] = new double[]{0, 0, 0, 0, 6.0};
double[][] omega = new double[6][];
omega[0] = new double[]{1.1, 0, 0, 0, 0, 0};
omega[1] = new double[]{0, 2.2, 0, 0, 0, 0};
omega[2] = new double[]{0, 0, 3.3, 0, 0, 0};
omega[3] = new double[]{0, 0, 0, 4.4, 0, 0};
omega[4] = new double[]{0, 0, 0, 0, 5.5, 0};
omega[5] = new double[]{0, 0, 0, 0, 0, 6.6};
GLSRegressionTest test = new GLSRegressionTest();
test.calculateGlsRegression(x, y, omega);
}
public void calculateGlsRegression(double[][] x, double[] y,
double[][] omega){
GLSMultipleLinearRegression regression = new
GLSMultipleLinearRegression();
regression.newSampleData(y, x, omega);
double[] beta = regression.estimateRegressionParameters();
double[] residuals = regression.estimateResiduals();
double[][] parametersVariance =
regression.estimateRegressionParametersVariance();
double regressandVariance =
regression.estimateRegressandVariance();
double sigma = regression.estimateRegressionStandardError();
//print out the values here
}
}
计算两组数据点的协方差
无偏协方差由公式cov(X, Y) = sum [(xi - E(X))(yi - E(Y))] / (n - 1),给出,其中E(X)是X的平均值,E(Y)是Y值的平均值。非偏差校正估计使用n代替n - 1。为了确定协方差是否进行了偏差校正,我们需要设置一个额外的可选参数biasCorrected ,默认情况下该参数设置为 true。
怎么做...
-
创建一个采用两个一维双数组的方法。每个数组代表一组数据点:
public void calculateCov(double[] x, double[] y){ -
Calculate the co-variance of the two sets of data points as follows:
double covariance = new Covariance().covariance(x, y, false);Note
For this formula, we use unbiased covariance correction, so we use three parameters in the
covariace()method. To use unbiased covariance between twodoublearrays, please delete the third parameterdouble covariance = new Covariance().covariance(x, y);:.
-
根据您的要求使用协方差并关闭方法:
System.out.println(covariance);
}
该配方的工作代码如下:
import org.apache.commons.math3.stat.correlation.Covariance;
public class CovarianceTest {
public static void main(String[] args){
double[] x = {43, 21, 25, 42, 57, 59};
double[] y = {99, 65, 79, 75, 87, 81};
CovarianceTest test = new CovarianceTest();
test.calculateCov(x, y);
}
public void calculateCov(double[] x, double[] y){
double covariance = new Covariance().covariance(x, y, false);//If
false is removed, we get unbiased covariance
System.out.println(covariance);
}
}
计算两组数据点的皮尔逊相关性
PearsonsCorrelation计算由公式cor(X, Y) = sum[(xi - E(X))(yi - E(Y))] / [(n - 1)s(X)s(Y)]定义的相关性,其中E(X)和E(Y)是X 和Y的平均值,s(X)和s(Y)是它们各自的标准差。
怎么做...
-
创建一个方法,该方法采用两个代表两组数据点的
double数组:public void calculatePearson(double[] x, double[] y){ -
创建一个
PearsonsCorrelation对象:PearsonsCorrelation pCorrelation = new PearsonsCorrelation(); -
计算两组数据点的相关性:
double cor = pCorrelation.correlation(x, y); -
根据您的要求使用关联,并关闭方法:
System.out.println(cor);
}
食谱的完整代码如下:
import org.apache.commons.math3.stat.correlation.PearsonsCorrelation;
public class PearsonTest {
public static void main(String[] args){
double[] x = {43, 21, 25, 42, 57, 59};
double[] y = {99, 65, 79, 75, 87, 81};
PearsonTest test = new PearsonTest();
test.calculatePearson(x, y);
}
public void calculatePearson(double[] x, double[] y){
PearsonsCorrelation pCorrelation = new PearsonsCorrelation();
double cor = pCorrelation.correlation(x, y);
System.out.println(cor);
}
}
进行配对 t 检验
在 Apache Commons Math 提供的众多标准统计显著性测试库中,我们将仅使用几个来演示配对 t 检验、卡方检验、单向 ANOVA 检验和 Kolmogorov-Smirnov 检验。读者可以执行其他的显著性测试,因为代码将使用TestUtils类中的静态方法来执行测试。
Apache Commons Math 支持单样本和双样本 t 检验。此外,两个样本测试可以是成对的,也可以是非成对的。不成对双样本检验可以在假设和不假设子总体方差相等的情况下进行。
怎么做...
-
创建一个将两组 double 值作为参数的方法。我们将进行配对 t 检验,以找出这两组值之间的任何统计显著性:
public void getTtest(double[] sample1, double[] sample2){ -
可以使用
pairedT()方法找到两个分布的 t 统计量:System.out.println(TestUtils.pairedT(sample1, sample2)); -
成对 t 检验的 p 值可以使用
pairedTTest()方法找到:System.out.println(TestUtils.pairedTTest(sample1, sample2)); -
Finally, the significance in difference between two distributions for any given confidence interval or alpha value can be found as follows:
System.out.println(TestUtils.pairedTTest(sample1, sample2, 0.05));在本例中,第三个参数设置为 0.05,这表示我们想知道在 alpha 水平设置为 0.05 或 95%置信区间时差异是否显著。
-
最后,关闭方法:
}
该配方的工作示例如下:
import org.apache.commons.math3.stat.inference.TestUtils;
public class TTest {
public static void main(String[] args){
double[] sample1 = {43, 21, 25, 42, 57, 59};
double[] sample2 = {99, 65, 79, 75, 87, 81};
TTest test = new TTest();
test.getTtest(sample1, sample2);
}
public void getTtest(double[] sample1, double[] sample2){
System.out.println(TestUtils.pairedT(sample1, sample2));//t
statistics
System.out.println(TestUtils.pairedTTest(sample1, sample2));//p
value
System.out.println(TestUtils.pairedTTest(sample1, sample2,
0.05));
}
}
进行卡方检验
为了对两组数据分布进行卡方检验,一个分布称为观察分布,另一个分布称为期望分布。
怎么做...
-
创建一个将这两个分布作为参数的方法。注意,观察分布是一个
long数组,而期望分布是一个double数组:public void getChiSquare(long[] observed, double[] expected){ -
获得卡方检验的 t 统计量如下:
System.out.println(TestUtils.chiSquare(expected, observed)); -
测试的 p 值可以用类似的方法找到,但方法不同:
System.out.println(TestUtils.chiSquareTest(expected, observed)); -
We can also observe whether the difference between the expected and observed data distributions is significant for a given confidence interval, as follows:
System.out.println(TestUtils.chiSquareTest(expected, observed, 0.05));在这个例子中,我们的置信区间被设置为 95%,因此,
chiSquareTest()方法的第三个参数被设置为 alpha 值 0.05。 -
最后,关闭方法:
}
这个食谱的完整代码在这里:
import org.apache.commons.math3.stat.inference.TestUtils;
public class ChiSquareTest {
public static void main(String[] args){
long[] observed = {43, 21, 25, 42, 57, 59};
double[] expected = {99, 65, 79, 75, 87, 81};
ChiSquareTest test = new ChiSquareTest();
test.getChiSquare(observed, expected);
}
public void getChiSquare(long[] observed, double[] expected){
System.out.println(TestUtils.chiSquare(expected, observed));//t
statistics
System.out.println(TestUtils.chiSquareTest(expected,
observed));//p value
System.out.println(TestUtils.chiSquareTest(expected, observed,
0.05));
}
}
进行单向 ANOVA 检验
ANOVA 代表方差分析。在这个食谱中,我们将看到如何使用 Java 进行单向 ANOVA 测试,以确定三个或三个以上独立且不相关的数据集的均值是否存在显著差异。
怎么做...
-
创建一个采用各种数据分布的方法。在我们的例子中,我们将对卡路里、脂肪、碳水化合物和控制的关系进行方差分析:
public void calculateAnova(double[] calorie, double[] fat, double[] carb, double[] control){ -
创建一个
ArrayList。这个ArrayList将包含所有的数据。该方法作为参数的数据分布可以看作是类。因此,在我们的例子中,我们将它们命名为classes:List<double[]> classes = new ArrayList<double[]>(); -
依次将四个类别的数据添加到
ArrayList:classes.add(calorie); classes.add(fat); classes.add(carb); classes.add(control); -
单向 ANOVA 检验的 F 值可由下式得出:
System.out.println(TestUtils.oneWayAnovaFValue(classes)); -
单向 ANOVA 检验的 p 值可以使用下面一行找到:
System.out.println(TestUtils.oneWayAnovaPValue(classes)); -
最后,要找出给定的四个类中的数据点的差异是否显著,请使用下面这段代码:
System.out.println(TestUtils.oneWayAnovaTest(classes, 0.05)); -
使用括号结束该方法:
}
单向 ANOVA 测试配方的完整代码如下:
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math3.stat.inference.TestUtils;
public class AnovaTest {
public static void main(String[] args){
double[] calorie = {8, 9, 6, 7, 3};
double[] fat = {2, 4, 3, 5, 1};
double[] carb = {3, 5, 4, 2, 3};
double[] control = {2, 2, -1, 0, 3};
AnovaTest test = new AnovaTest();
test.calculateAnova(calorie, fat, carb, control);
}
public void calculateAnova(double[] calorie, double[] fat,
double[]
carb, double[] control){
List<double[]> classes = new ArrayList<double[]>();
classes.add(calorie);
classes.add(fat);
classes.add(carb);
classes.add(control);
System.out.println(TestUtils.oneWayAnovaFValue(classes));
System.out.println(TestUtils.oneWayAnovaPValue(classes));
System.out.println(TestUtils.oneWayAnovaTest(classes, 0.05));
}
}
注意
t 检验、卡方检验和 ANOVA 检验返回的 p 值是精确的,基于对distribution包中 t 分布、卡方分布和 F 分布的数值逼近。
进行科尔莫戈罗夫-斯米尔诺夫检验
Kolmogorov-Smirnov 检验(或简称为 KS 检验)是对本质上连续的一维概率分布的相等性的检验。这是确定两组数据点是否显著不同的常用方法之一。
怎么做...
-
创建一个采用两种不同数据分布的方法。我们将通过使用 Kolmogorov-Smirnov 检验来查看两个数据分布的差异是否显著:
public void calculateKs(double[] x, double[] y){ -
测试中的一个关键统计量是 d 统计量。为了计算测试的 p 值,我们需要一个 double 值:
double d = TestUtils.kolmogorovSmirnovStatistic(x, y); -
要评估值来自单位正态分布的零假设,请使用以下代码:
System.out.println(TestUtils.kolmogorovSmirnovTest(x, y, false)); -
最后,显著性检验的 p 值可通过以下方式确定:
System.out.println(TestUtils.exactP(d, x.length, y.length,
false));
食谱的完整代码如下:
import org.apache.commons.math3.stat.inference.TestUtils;
public class KSTest {
public static void main(String[] args){
double[] x = {43, 21, 25, 42, 57, 59};
double[] y = {99, 65, 79, 75, 87, 81};
KSTest test = new KSTest();
test.calculateKs(x, y);
}
public void calculateKs(double[] x, double[] y){
double d = TestUtils.kolmogorovSmirnovStatistic(x, y);
System.out.println(TestUtils.kolmogorovSmirnovTest(x, y, false));
System.out.println(TestUtils.exactP(d, x.length, y.length,
false));
}
}
本章的食谱到此结束。Apache Commons 数学库可以进行许多不同的统计分析。有关该库的更多使用,请参考本章中使用的版本的 Javadoc,可以在http://commons . Apache . org/proper/commons-math/javadocs/API-3 . 6 . 1/index . html找到。