虽然Java社区没有像Python Pandas或Scala Spark DataFrame那样拥有一个绝对统治地位的“标准”DataFrame库,但有一些非常优秀且功能强大的库可以满足您的需求。
目前来看,最接近Pandas或R DataFrames体验的Java库是 Tablesaw。
Tablesaw 是一个用Java编写的、内存中的表格数据处理库。它提供了类似DataFrame的结构,支持数据加载、清理、转换、过滤、聚合、连接以及一些基本的统计和可视化功能。它的设计理念就是为了让Java开发者能够方便地进行数据科学工作。
优点:
缺点:
Tablesaw 的基本用法示例:
添加依赖 (Maven):
<dependency>
<groupId>com.github.lwhite1</groupId>
<artifactId>tablesaw-core</artifactId>
<version>0.43.1</version> <!-- 检查最新版本 -->
</dependency>
代码示例:
假设您的JSON数据已经被解析成Java对象列表,或者您可以从CSV等文件加载。这里我们以从CSV文件加载为例,或者您可以将JSON数据转换为List of Maps,然后手动构建Table。
import tech.tablesaw.api.Table;
import tech.tablesaw.api.StringColumn;
import tech.tablesaw.api.DoubleColumn;
import tech.tablesaw.columns.Column;
import tech.tablesaw.joining.JoinType;
import tech.tablesaw.selection.Selection;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class TablesawExample {
public static void main(String[] args) throws IOException {
// 示例数据 (假设这是从您的JSON解析后的数据)
List<Map<String, String>> jsonData = List.of(
Map.of("st_name", "35kV响水浪变", "dev_name", "0460开关", "name", "Y时限失压闭锁", "status", "0"),
Map.of("st_name", "35kV响水浪变", "dev_name", "0460开关", "name", "闭锁总", "status", "0"),
Map.of("st_name", "110kV茂兰变", "dev_name", "10kV茂洞线43号杆0430开关", "name", "过负荷告警", "status", "1"),
Map.of("st_name", "110kV茂兰变", "dev_name", "10kV茂洞线43号杆0430开关", "name", "开关事故总", "status", "1"),
Map.of("st_name", "35kV响水浪变", "dev_name", "0460开关", "name", "Y时限失压闭锁", "status", "1")
);
// 1. 从List<Map>创建Table
Table table = Table.create("SensorData");
// 添加列
StringColumn stNameCol = StringColumn.create("st_name");
StringColumn devNameCol = StringColumn.create("dev_name");
StringColumn nameCol = StringColumn.create("name");
StringColumn statusCol = StringColumn.create("status"); // status 可以是String或Int
for (Map<String, String> row : jsonData) {
stNameCol.append(row.get("st_name"));
devNameCol.append(row.get("dev_name"));
nameCol.append(row.get("name"));
statusCol.append(row.get("status"));
}
table.addColumns(stNameCol, devNameCol, nameCol, statusCol);
System.out.println("原始数据表:");
System.out.println(table.print());
// 2. 数据过滤 (类似 Pandas 的 df[df['st_name'] == '110kV茂兰变'])
System.out.println("\n--- 过滤示例:查找 'st_name' 为 \"110kV茂兰变\" 的记录 ---");
Table filteredTable = table.where(table.stringColumn("st_name").isEqualTo("110kV茂兰变"));
System.out.println(filteredTable.print());
// 3. 数据聚合 (类似 Pandas 的 df.groupby('st_name').count())
System.out.println("\n--- 聚合示例:统计 'st_name' 的出现次数 ---");
// 注意:Tablesaw的summarize方法通常需要一个SummaryFunction,这里我们用count
// 我们可以先将st_name转换为一个可以计数的列,或者直接使用group().count()
Table countsTable = table.group("st_name").count();
System.out.println(countsTable.print());
System.out.println("\n--- 聚合示例:统计 'name' 的出现次数 ---");
Table nameCountsTable = table.group("name").count();
System.out.println(nameCountsTable.print());
// 4. 更复杂的聚合:计算特定设备 'dev_name' 下 'status' 为 '1' 的记录数量
System.out.println("\n--- 聚合示例:计算 '10kV茂洞线43号杆0430开关' 下 'status' 为 '1' 的记录数量 ---");
String devNameTarget = "10kV茂洞线43号杆0430开关";
Table specificStatusCount = table.where(
table.stringColumn("dev_name").isEqualTo(devNameTarget)
.and(table.stringColumn("status").isEqualTo("1"))
);
System.out.println("设备 '" + devNameTarget + "' 下 'status' 为 '1' 的记录有: " + specificStatusCount.rowCount() + " 条");
// 5. 转换为数值列进行统计分析 (如果status是数值)
// 假设status可以转换为double,这里我们先创建一个新的Table来演示
Table numericTable = Table.create("NumericSensorData");
StringColumn stNameColN = StringColumn.create("st_name");
DoubleColumn statusDoubleCol = DoubleColumn.create("status_numeric");
for (Map<String, String> row : jsonData) {
stNameColN.append(row.get("st_name"));
try {
statusDoubleCol.append(Double.parseDouble(row.get("status")));
} catch (NumberFormatException e) {
statusDoubleCol.appendMissing(); // 处理无法转换的情况
}
}
numericTable.addColumns(stNameColN, statusDoubleCol);
System.out.println("\n--- 数值统计示例:按st_name计算status_numeric的平均值 ---");
Table avgStatus = numericTable.summarize(numericTable.doubleColumn("status_numeric"), tech.tablesaw.aggregate.AggregateFunctions.mean).by("st_name");
System.out.println(avgStatus.print());
}
}
Joins
的库,它提供了一些类似DataFrame的功能,特别擅长处理关系型数据和执行连接操作。它更偏向于SQL风格的数据操作。如果您希望在Java中进行数据分析,Tablesaw 是一个非常好的起点。它提供了直观的API和强大的内存中数据处理能力,非常适合进行数据探索、清洗和初步的统计分析。
如果您需要处理的数据量非常大(超出单机内存),或者需要分布式计算能力,那么Spark仍然是主流选择,即使它有编译速度和环境配置的缺点。但对于大多数中小型数据集,Tablesaw足以胜任。