You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Spark Scala中为唯一姓名分配连续递增ID?

解决Spark Scala中基于姓名分配连续唯一ID的问题

我刚好处理过类似的去标识化需求,给你两种实用的实现方案,都能满足「相同姓名组合分配同一连续ID、不同组合分配新ID」的要求,而且ID从0或1开始递增,完全能和其他用monotonically_increasing_id()生成ID的DataFrame配合使用。

方案一:用窗口函数快速实现(代码简洁,适合小数据集)

核心思路是先把firstname和lastname拼接成唯一的姓名组合,再用dense_rank()窗口函数给每个组合分配连续ID——这个函数会自动给相同的组合分配同一个排名,而且排名是连续无间隔的。

import org.apache.spark.sql.functions.{concat, lit, dense_rank}
import org.apache.spark.sql.expressions.Window

// 假设你的原始DataFrame名为rawDf
// 1. 定义窗口规则:按拼接后的姓名组合排序
val nameWindow = Window.orderBy(concat($"firstname", lit("_"), $"lastname"))

// 2. 生成新ID,这里减1让ID从0开始,去掉减1则从1开始
val resultDf = rawDf
  .withColumn("full_name", concat($"firstname", lit("_"), $"lastname")) // 临时拼接姓名
  .withColumn("new_id", dense_rank().over(nameWindow) - 1)
  .drop("full_name", "id") // 移除临时列和原ID列
  .withColumnRenamed("new_id", "id") // 将新ID重命名为id

运行后你的DataFrame会变成这样:

+-------+--------+---------+---------+--------+-----------------+---+
|address|lastname|firstname|patientid|policyno|visitid          |id |
+-------+--------+---------+---------+--------+-----------------+---+
|addr1  |Pits    |Rodney   |patid1   |policy1 |visituid_rodney_1|0  |
|addr1  |Pits    |Rodney   |patid1   |policy1 |visituid_rodney_2|0  |
|addr1  |Pits    |Rodney   |patid1   |policy1 |visituid_rodney_3|0  |
|addr2  |Dobs    |Aliya    |patid2   |policy2 |visituid_aliya_1 |1  |
|addr2  |Dobs    |Aliya    |patid2   |policy2 |visituid_aliya_2 |1  |
|addr2  |Dobs    |OP       |patid3   |policy3 |visituid_OP_1    |2  |
+-------+--------+---------+---------+--------+-----------------+---+

方案二:先去重再关联(性能更优,适合大数据集)

如果你的数据集很大,窗口函数可能会带来较多的shuffle开销,这时可以先提取所有唯一的姓名组合,给它们分配连续ID后再关联回原数据,这样处理的数据量会小很多。

import org.apache.spark.sql.functions.{concat, lit}

// 1. 提取所有唯一的姓名组合,可选orderBy保证ID顺序稳定
val uniqueNames = rawDf
  .select(concat($"firstname", lit("_"), $"lastname").alias("full_name"))
  .distinct()
  .orderBy("full_name")

// 2. 给每个唯一姓名分配从0开始的连续ID
val nameWithId = uniqueNames
  .rdd.zipWithIndex()
  .map { case (row, idx) => (row.getString(0), idx) }
  .toDF("full_name", "id")

// 3. 关联回原DataFrame,替换原ID列
val resultDf = rawDf
  .withColumn("full_name", concat($"firstname", lit("_"), $"lastname"))
  .join(nameWithId, Seq("full_name"), "left")
  .drop("full_name", "id") // 移除临时列和原ID
  .withColumnRenamed("id", "id")

注意事项

  • 如果不需要固定的ID顺序,可以去掉orderBy步骤,能进一步提升性能;
  • 两种方案生成的ID都是连续递增的,完全可以作为和其他DataFrame连接的索引键;
  • 拼接姓名时用_分隔是为了避免出现类似firstname=A, lastname=BC和firstname=AB, lastname=C被误判为同一组合的情况,你也可以换成其他不会出现在姓名里的分隔符。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 08:22:33