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

Spark中如何去重但保留含Null值的行?类比Pandas去重逻辑

在Spark中实现去重时忽略Null值的需求

这个问题我之前也碰到过——Spark的dropDuplicates默认会把null值视为完全相同的重复项,所以直接用dropDuplicates("animal")会把你示例中两个animalnull的行合并掉,完全不符合你想要保留所有含null行的需求。不过我们可以通过两种方式来实现你的目标:

方法一:拆分数据后合并(简单直接)

核心思路是把数据分成含null的行非null的行两部分,单独处理后再合并:

  1. 筛选出所有animalnull的行,这部分我们直接保留所有数据
  2. 筛选出animalnull的行,用dropDuplicates("animal")去重
  3. 将两部分数据合并

具体代码如下:

import org.apache.spark.sql.functions.col

// 拆分出animal为null的所有行
val nullAnimalRows = df1.filter(col("animal").isNull)
// 拆分出animal非null的行并去重
val dedupedNonNullAnimalRows = df1.filter(col("animal").isNotNull).dropDuplicates("animal")
// 合并两个数据集
val finalDf = nullAnimalRows.union(dedupedNonNullAnimalRows)

finalDf.show()

运行后你会看到:两个animalnull的行都被保留,而panda的重复行只保留了一条,完全符合你的需求。

方法二:使用窗口函数(灵活控制保留哪一行)

如果你的需求不止是简单去重,还想指定保留重复项中的某一行(比如保留id最小的行),窗口函数会更适合:

import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions.row_number

// 按animal分区,按id排序,给每个分区的行编号
val windowSpec = Window.partitionBy("animal").orderBy("id")
val finalDf = df1
  .withColumn("row_num", row_number().over(windowSpec))
  // 保留编号为1的行(每个animal的第一行,这里是id最小的),同时保留所有animal为null的行
  .filter(col("row_num") === 1 || col("animal").isNull)
  .drop("row_num")

finalDf.show()

这种方式可以精准控制重复项中保留哪一行,比第一种方法更灵活。

为什么直接用dropDuplicates不行?

Spark中null值在去重逻辑里被判定为相等的,所以当你调用dropDuplicates("animal")时,所有animalnull的行都会被视为重复项,只会保留其中一行——这就是你之前尝试时遇到的问题。

内容的提问来源于stack exchange,提问作者nrvaller

火山引擎 最新活动