项目源码链接: Tablesaw
Java是一种很棒的语言,但它并不是为数据分析而设计的。通过Tablesaw,我们可以轻松的使用Java进行数据分析。
| @Author:TTODS
<dependency>
<groupId>tech.tablesaw</groupId>
<artifactId>tablesaw-core</artifactId>
<version>LATEST</version>
</dependency>
Tablesaw整个都是关于表格的,而表格又是由列组成的,所以我们的学习先从列开始。
一个Column是一个被命名的,一维数据集,它可能是一个Table的一部分,也可能不是。一个Column中的数据必须是同一种类型。
使用DoubleColumn.create(String name,double ... arr)
方法来创建一个元素类型为double的column;
根据包含元素类型的不同,还有IntColumn
,StringColumn
等。
double[] numbers = {1,2,3,4};
DoubleColumn nc = DoubleColumn.create("nc",numbers);
// DoubleColumn nc = DoubleColumn.create("nc",1,2,3,4); 这样也是可以的
System.out.println(nc.print());
程序输出:
Column: nc
1
2
3
4
使用 nc.get(int index)方法来获取column某个元素,index从0开始。
// 创建数据列
double[] numbers = {1,2,3,4};
DoubleColumn nc = DoubleColumn.create("nc",numbers);
// index为2,所以获取到nc中的第三个元素
Double three = nc.get(2);
System.out.println(three);
程序输出:
3.0
Tablesaw中有很多针对一列数据的运算,如下面的multiply(Number value)是原来列中的每个元素都乘以value。
// 创建数据列
double[] numbers = {1,2,3,4};
DoubleColumn nc1 = DoubleColumn.create("nc",numbers);
System.out.println(nc1.print());
// 使用multiply(Number value)是原来列中的每个元素都乘以value
// 这个方法并不会修改nc,而是返回一个新的Column对象nc1,nc1的列名为[nc列名*value],我们可以使用 setName(String name),设置列名;
DoubleColumn nc2 = nc1.multiply(8);
// nc1.setName("nc2");
System.out.println(nc2.print());
程序输出:
Column: nc
1
2
3
4
Column: nc * 8.0
8
16
24
32
Selections被用来筛选表和列。我们对一个Column对象使用形如isLessThan(aNumber)
方法来附加筛选条件,就可以生成一个Selection对象,一个Selection对象包含一个有序的int类型的集合,该集合包含了Column对象中满足设定条件的元素的索引。
// 创建数据列
double[] numbers = {1,2,3,4};
DoubleColumn nc1 = DoubleColumn.create("nc",numbers);
// 使用isLessThan(double value)方法 获取筛选出nc中小于3的元素的Selection对象
Selection lessThanThreeSelection = nc1.isLessThan(3);
//A selection maintains an ordered set of ints that can be used to eval rows from a table or column
System.out.println(Arrays.toString(lessThanThreeSelection.toArray()));
程序输出:
[0, 1]
使用where(Selection selection)方法,获取经过指定selection筛选后的Column或Table
DoubleColumn nc2 = nc1.where(lessThanThreeSelection);
System.out.println(nc2.print());
程序输出:
Column: nc
1
2
组合过滤器
DoubleColumn nc3 = nc1.where(nc1.isGreaterThan(1).and(nc1.isLessThan(3)));
System.out.println(nc3.print());
程序输出:
Column: nc
2
通过index选择
DoubleColumn nc4 = nc1.where(Selection.with(1, 3));
System.out.println(nc4.print());
DoubleColumn nc5 = nc1.where(Selection.withRange(1, 3));
System.out.println(nc5.print());
程序输出:
Column: nc
2
4
Column: nc
2
3
Map functions是一些定义在columns上的方法,它们返回一个新的Columns,
之前的 Column multiply(Number value)方法就是一个有一个标量参数的map function
我们也可以使用multiply(NumberColumn numberColumn)的方法,来是两个column中的值对应相乘
DoubleColumn nc1 = DoubleColumn.create("nc",new double[]{1,2,3,4,5});
DoubleColumn nc2 = DoubleColumn.create("nc1",new double[]{1,2,3,4,5});
DoubleColumn nc3 = nc1.multiply(nc2);
System.out.println(nc3.print());
程序输出:
Column: nc * nc1
1
4
9
16
25
Tablesaw针对各种Column类型内置了很多的map functions,下面是一些例子
StringColumn s = StringColumn.create("sc", new String[] {"foo", "bar", "baz", "foobarbaz"});
StringColumn s2 = s.copy();
// 以下方法全部都是针对StringColumn中所有字符串进行的操作
s2 = s2.replaceFirst("foo", "bar"); // 字符串替换
s2 = s2.upperCase(); // 转化成大写
s2 = s2.padEnd(5, 'x'); // 字符串的长度小于minlength(第一个参数)时,在字符串的尾部填充某字符,使其长度达到minlength
s2 = s2.substring(1, 5); // 获取[1,5)子串
System.out.println(s2.print());
程序输出:
Column: sc[repl][ucase][pad][sub]
ARxx
ARxx
AZxx
ARBA
有时我们想计算一些值从某种意义上总结一列中的数据,汇总函数就是用来做这个的。
DoubleColumn nc1 = DoubleColumn.create("nc",new double[]{1,2,3,4,5});
double max = nc1.max(); // 列中的最大值
System.out.println(max);
double min = nc1.min(); // 列中的最小值
System.out.println(min);
double sum = nc1.sum(); // 最大值
System.out.println(sum);
double stdDev = nc1.standardDeviation(); // 标准差
System.out.println(stdDev);
程序输出:
5.0
1.0
15.0
1.5811388300841898
一个Table是一个被命名的columns的容器,虽然缺失值是允许的,但是table中所有的column的元素数量应该一样。
一个Table可以包含任何类型的columns组合。
String[] animals = {"bear", "cat", "giraffe"};
double[] cuteness = {90.1, 84.3, 99.7};
// 使用create(String name)方法创建Table对象,使用addColumns(Column<?>...cols)方法向表中添加列
Table cuteAnimals = Table.create("Cute Animals").addColumns(
StringColumn.create("Animal types", animals),
DoubleColumn.create("rating", cuteness));
System.out.println(cuteAnimals.print());
程序输出:
Cute Animals
Animal types | rating |
---------------------------
bear | 90.1 |
cat | 84.3 |
giraffe | 99.7 |
很多时候我们会直接从csv文件中导入数据,使用下面的语句可以很方便的从csv文件中读取一个Table对象,Tablesaw可以很好的猜测出每一列的类型,当它猜错类型或者是我们想要提高性能的时候可以指明类型。
Table bushTable = Table.read().csv("data/bush.csv");
System.out.println(bushTable.print());
程序输出:
bush.csv
date | approval | who |
-------------------------------------
2004-02-04 | 53 | fox |
2004-01-21 | 53 | fox |
2004-01-07 | 58 | fox |
2003-12-03 | 52 | fox |
2003-11-18 | 52 | fox |
2003-10-28 | 53 | fox |
2003-10-14 | 52 | fox |
2003-09-23 | 50 | fox |
2003-09-09 | 58 | fox |
2003-08-12 | 57 | fox |
... | ... | ... |
2001-12-03 | 81 | zogby |
2001-10-23 | 78 | zogby |
2001-09-14 | 82 | zogby |
2001-08-28 | 50 | zogby |
2001-07-26 | 47 | zogby |
2001-06-24 | 51 | zogby |
2001-04-23 | 52 | zogby |
2001-03-27 | 52 | zogby |
2001-02-27 | 53 | zogby |
2001-02-09 | 57 | zogby |
Tablesaw提供了一些方法,让帮助我们了解新数据集的结构,形状等信息。
Table bushTable = Table.read().csv("data/bush.csv");
System.out.println(bushTable.structure());
程序输出:
Structure of bush.csv
Index | Column Name | Column Type |
-----------------------------------------
0 | date | LOCAL_DATE |
1 | approval | INTEGER |
2 | who | STRING |
System.out.println(bushTable.shape());
程序输出:
323 rows X 3 cols
System.out.println(bushTable.first(3));
程序输出:
bush.csv
date | approval | who |
-----------------------------------
2004-02-04 | 53 | fox |
2004-01-21 | 53 | fox |
2004-01-07 | 58 | fox |
System.out.println(bushTable.last(3));
程序输出:
bush.csv
date | approval | who |
-------------------------------------
2001-03-27 | 52 | zogby |
2001-02-27 | 53 | zogby |
2001-02-09 | 57 | zogby |
toString()
方法也表示为上面那种表格的形式 Table bushTable = Table.read().csv("data/bush.csv");
// 默认只显示20rows,其他数据会省略;
System.out.println(bushTable.toString());
// 我们可以使用printAll()来打印所有的数据
System.out.println(bushTable.printAll());
// 也可以使用print(int rowLimit)来指定打印的行数
System.out.println(bushTable.print(100));
//构造表
Table table = Table.create("table").addColumns(IntColumn.create("col1", 1, 2, 3, 4, 5),
IntColumn.create("col2", 2, 4, 6, 8, 0), IntColumn.create("col3", 3, 6, 9, 12, 13));
System.out.println(table);
// 移除列
// 移除 名字为"col1"的列
table.removeColumns("col1");
System.out.println(table);
// 只保留 名字为"col2"的列
table.retainColumns("col2");
System.out.println(table);
// 添加列
table.addColumns(IntColumn.create("col4", 4, 8, 12, 16, 20));
System.out.println(table);
程序输出:
table
col2 | col3 |
-----------------
2 | 3 |
4 | 6 |
6 | 9 |
8 | 12 |
0 | 13 |
table
col2 |
--------
2 |
4 |
6 |
8 |
0 |
table
col2 | col4 |
-----------------
2 | 4 |
4 | 8 |
6 | 12 |
8 | 16 |
0 | 20 |
Table studentTable = Table.create("student").addColumns(IntColumn.create("id", 20200001, 20200002, 20200003, 20200004, 20200005),
StringColumn.create("name", "Jim", "Bob", "John", "lily", "lucy"));
// column(String colName)方法,通过列名获取指定列,不区分大小写
Column<?> idCol1 = studentTable.column("id");
System.out.println(idCol1.print());
// column(int colIndex)方法,通过索引获取指定列,索引从0开始
Column<?> idCol2 = studentTable.column(0);
System.out.println(idCol2.print());
// System.out.println(idCol1 == idCol2); // true
// 上面的方法返回的都是Column<?>类型,必要时需要转换成特定的类型
// 1.强制类型转换
IntColumn idCol3 = (IntColumn) studentTable.column("id");
// 2.使用numberColumn(),stringColumn()等方法
NumericColumn<?> idColumn = studentTable.numberColumn("id");
程序输出:
Column: id
20200001
20200002
20200003
20200004
20200005
Column: id
20200001
20200002
20200003
20200004
20200005
Table studentTable = Table.create("student").addColumns(IntColumn.create("id", 20200001, 20200001, 20200002, 20200003, 20200004, 20200005),
StringColumn.create("name", "Jim", "Jim", "Bob", "John", "lily", "lucy"));
System.out.println(studentTable.print());
// drop...()方法不在原表上修改,而是先创建一个和原表结构一样的空表,然后把不需要删除的行放到新的而表中,然后返回新的表。
Table table1 = studentTable.dropDuplicateRows();
System.out.println(table1.print());
Table table2 = studentTable.dropWhere(studentTable.numberColumn(0).isLessThan(20200003));
System.out.println(table2);
程序输出:
student
id | name |
---------------------
20200001 | Jim |
20200001 | Jim |
20200002 | Bob |
20200003 | John |
20200004 | lily |
20200005 | lucy |
student
id | name |
---------------------
20200001 | Jim |
20200002 | Bob |
20200003 | John |
20200004 | lily |
20200005 | lucy |
student
id | name |
---------------------
20200002 | Bob |
20200005 | lucy |
20200001 | Jim |
// 修改原表
studentTable.addRow(0, studentTable);
Table table3 = studentTable.sampleN(3);
System.out.println(table3.print());
程序输出:
student
id | name |
---------------------
20200002 | Bob |
20200005 | lucy |
20200001 | Jim |
// 遍历表中的行,并执行操作
for (Row r : studentTable) {
System.out.println("学号:" + r.getString(1) + ":" + r.getString(1));
}
// 另外一种实现形式
studentTable.stream().forEach(row -> {
System.out.println("学号:" + row.getString(1) + ":" + row.getString(1));
});
Table studentTable = Table.create("student").addColumns(IntColumn.create("id", 20200001,20200002, 20200003, 20200004, 20200005),
StringColumn.create("name", "Jim", "Bob", "John", "lily", "lucy"));
// 升序
studentTable = studentTable.sortOn("id", "name");// or sortAscendingOn("id","name");
System.out.println(studentTable.print());
// 降序
studentTable = studentTable.sortDescendingOn("id");
System.out.println(studentTable);
程序输出:
student
id | name |
---------------------
20200001 | Jim |
20200002 | Bob |
20200003 | John |
20200004 | lily |
20200005 | lucy |
student
id | name |
---------------------
20200005 | lucy |
20200004 | lily |
20200003 | John |
20200002 | Bob |
20200001 | Jim |
查询filter(过滤器)可以使用逻辑运算符or,and,not来组合,它们都在QuerySupport
中实现,所以在使用前要先导入:
import static tech.tablesaw.api.QuerySupport.and;
import static tech.tablesaw.api.QuerySupport.or;
import static tech.tablesaw.api.QuerySupport.not;
Table studentTable = Table.create("student").addColumns(IntColumn.create("id", 20200001, 20200002, 20200003, 20200004, 20200005),
StringColumn.create("name", "Jim", "Bob", "John", "lily", "lucy"),
IntColumn.create("age", 18, 18, 17, 16, 16));
Table result = studentTable.where(and(t -> t.numberColumn("id").isGreaterThan(20200001),
t -> t.numberColumn("age").isEqualTo(18)));
System.out.println(result);
程序输出:
student
id | name | age |
-----------------------------
20200002 | Bob | 18 |