一、数据清洗核心方法
1. 处理缺失值
方法 | 说明 | 示例代码 |
---|
na().drop() | 删除包含空值的行 | Dataset<Row> cleaned = dataset.na().drop(); |
na().fill(value) | 用指定值填充所有空值 | Dataset<Row> filled = dataset.na().fill(0); |
na().fill(Map<String,Object>) | 按列填充不同值 | Map<String,Object> fills = new HashMap<>(); fills.put("age", 0); dataset.na().fill(fills); |
2. 去重处理
方法 | 说明 | 示例代码 |
---|
dropDuplicates() | 删除完全重复的行 | Dataset<Row> unique = dataset.dropDuplicates(); |
dropDuplicates(colNames) | 根据指定列去重 | Dataset<Row> unique = dataset.dropDuplicates(new String[]{"id"}); |
3. 过滤无效数据
方法 | 说明 | 示例代码 |
---|
filter(condition) | 根据条件过滤行 | Dataset<Row> valid = dataset.filter(col("age").gt(0)); |
where(condition) | 同 filter | Dataset<Row> valid = dataset.where("salary > 1000"); |
4. 类型转换
方法 | 说明 | 示例代码 |
---|
withColumn(colName, expr) | 转换列类型或计算新列 | Dataset<Row> converted = dataset.withColumn("age", col("age").cast("int")); |
cast(DataType) | 强制类型转换 | col("timestamp").cast(DataTypes.TimestampType) |
5. 列操作
方法 | 说明 | 示例代码 |
---|
select(cols) | 选择特定列 | Dataset<Row> selected = dataset.select(col("name"), col("age")); |
withColumnRenamed(old, new) | 重命名列 | Dataset<Row> renamed = dataset.withColumnRenamed("oldName", "newName"); |
drop(colName) | 删除列 | Dataset<Row> reduced = dataset.drop("unusedColumn"); |
6. 字符串处理
方法 | 说明 | 示例代码 |
---|
regexp_replace() | 正则替换 | functions.regexp_replace(col("email"), "@.*", "") |
trim() | 去除首尾空格 | col("name").trim() |
substr(start, length) | 截取子字符串 | col("phone").substr(0, 3) |
二、Dataset 核心操作方法
1. 数据加载与保存
方法 | 说明 | 示例代码 |
---|
read().csv(path) | 读取 CSV 文件 | Dataset<Row> df = spark.read().csv("hdfs:///input.csv"); |
write().parquet(path) | 保存为 Parquet 格式 | df.write().parquet("hdfs:///output.parquet"); |
write().jdbc(...) | 写入关系型数据库 | .option("url", "jdbc:mysql://...") .option("dbtable", "table") |
2. 聚合操作
方法 | 说明 | 示例代码 |
---|
groupBy(cols) | 按列分组 | GroupedData grouped = df.groupBy("department"); |
agg(exprs) | 聚合计算 | grouped.agg(avg("salary"), max("age")); |
3. 数据转换
方法 | 说明 | 示例代码 |
---|
join(otherDataset, condition) | 表连接 | df1.join(df2, df1.col("id").equalTo(df2.col("id"))); |
union(otherDataset) | 合并数据集 | Dataset<Row> combined = df1.union(df2); |
4. 数据探查
方法 | 说明 | 示例代码 |
---|
show() | 打印前 N 行数据 | df.show(10); |
printSchema() | 打印 Schema | df.printSchema(); |
describe(cols) | 统计数值列的基本信息 | df.describe("age", "salary").show(); |
三、完整数据清洗示例
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;
import static org.apache.spark.sql.functions.*;
public class DataCleaningExample {
public static void main(String[] args) {
SparkSession spark = SparkSession.builder()
.appName("Data Cleaning Demo")
.master("local[*]")
.getOrCreate();
// 1. 读取原始数据
Dataset<Row> rawData = spark.read()
.option("header", true)
.option("inferSchema", true)
.csv("hdfs:///input.csv");
// 2. 数据清洗流程
Dataset<Row> cleanedData = rawData
// 处理空值: 删除全空行
.na().drop("all")
// 填充特定列空值
.na().fill(0, new String[]{"age"})
// 过滤无效年龄
.filter(col("age").geq(0).and(col("age").leq(100)))
// 类型转换
.withColumn("birth_year",
functions.lit(2023).minus(col("age")).cast("int"))
// 字符串处理
.withColumn("name", trim(col("name")))
// 去重
.dropDuplicates(new String[]{"id"})
// 选择最终列
.select("id", "name", "age", "birth_year");
// 3. 保存清洗结果
cleanedData.write()
.mode("overwrite")
.parquet("hdfs:///cleaned_data");
spark.stop();
}
}
四、性能优化技巧
-
分区策略
// 调整读取分区数
spark.read().option("basePath", "/data")
.csv("hdfs:///input_*.csv")
.repartition(200);
// 按列分区保存
cleanedData.write()
.partitionBy("year", "month")
.parquet("hdfs:///partitioned_data");
-
缓存机制
Dataset<Row> cachedDF = df.cache(); // MEMORY_AND_DISK
-
广播变量
List<String> validCountries = Arrays.asList("US", "CN", "EU");
Broadcast<List<String>> broadcastVar = spark.sparkContext().broadcast(validCountries);
df.filter(col("country").isin(broadcastVar.value()));
-
并行度控制
park.conf().set("spark.sql.shuffle.partitions", "200");
五、常见问题处理
问题 | 解决方案 |
---|
内存不足 (OOM) | 增加 Executor 内存:spark.executor.memory=8g |
数据倾斜 | 使用 repartition 或 salt 技术分散热点数据 |
类型转换失败 | 使用 try_cast 或先过滤无效数据 |
字符串编码问题 | 指定编码格式:.option("encoding", "UTF-8") |