You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何以最规范高效的方式筛选含非空值的Spark DataFrame列?

在Spark 1.6+中高效筛选含至少一个非空值的列

嘿,这个需求其实很常见,咱们可以用一次全量聚合统计的方式来实现,既规范又高效,不用反复扫描数据集。

核心思路

要筛选出至少有一个非空值的列,关键是统计每列的非空行数:count()函数会自动忽略null值,所以如果某列的count结果大于0,就说明该列存在非空值。我们只需要一次聚合操作就能拿到所有列的统计结果,再筛选出符合条件的列即可。

完整实现代码

结合你提供的初始化代码,完整的解决方案如下:

// 你的数据初始化代码(保留)
case class C(int1: Integer, int2: Integer, str1: String, str2: String, dt1: String, dt2: String)
val cc = Seq(
 C(1, null, "strin1", null, null, null),
 C(null, null, null, null, "2000-01-03 12:12:12", null)
)
val t = sc.parallelize(cc, 2).toDF()
val df = t.withColumn("dt1", $"dt1".cast("timestamp")).withColumn("dt2", $"dt2".cast("timestamp"))

// 1. 一次性统计所有列的非空值数量
val columnNonEmptyCounts = df.agg(
  df.columns.map(colName => count(col(colName)).alias(colName)): _*
).head() // 取出聚合后的唯一一行结果

// 2. 筛选出至少有一个非空值的列名
val columnsToKeep = df.columns.filter(colName => {
  columnNonEmptyCounts.getAs[Long](colName) > 0
})

// 3. 选择目标列得到最终结果
val resultDF = df.select(columnsToKeep.map(col): _*)

// 查看结果
resultDF.show(false)

结果验证

执行后会输出你期望的结果:

+----+-------+-----------------------+
|int1|str1   |dt1                    |
+----+-------+-----------------------+
|1   |strin1 |null                   |
|null|null   |2000-01-03 12:12:12.0  |
+----+-------+-----------------------+

为什么这个方案高效?

  • 只需要一次Action操作head()触发计算),Spark会对数据集做一次全量扫描完成所有列的统计,避免了多次扫描带来的性能损耗。
  • 代码简洁直观,完全符合Spark的API规范,在1.6及以上版本都能稳定运行。

关于你之前尝试的写法

你提到的df.columns.filter(c => when(count(col(c))>0,c))无法成功,是因为when是用于生成列表达式的函数,不能直接在Scala集合的filter里使用——集合filter需要的是布尔值判断,而不是列表达式。咱们上面的方案先拿到实际的统计数值,再做判断,就解决了这个问题。

DataSet的适配

如果是处理DataSet,操作逻辑完全一致:只需要先把DataSet转成DataFrame(dataset.toDF()),执行上述操作后,再转回DataSet即可(resultDF.as[YourCaseClass])。

内容的提问来源于stack exchange,提问作者MaxU - stand with Ukraine

火山引擎 最新活动