如何通过Spark DataFrame生成相似度矩阵的三角部分以降低计算量?
生成自笛卡尔积的下三角部分(最小计算量实现)
针对你想要避免生成完整自笛卡尔积、只保留对称矩阵下三角(不含对角线)的需求,这里有几个高效的Spark实现方案,能帮你大幅减少计算量:
方法1:添加唯一行标识后过滤
如果你的DataFrame没有天然的可比较唯一键,先给每条数据加一个唯一的行标识符,再通过过滤标识符的大小关系来保留下三角部分:
import org.apache.spark.sql.functions._ // 给原DataFrame添加唯一递增ID列 val dfWithId = df2.withColumn("row_id", monotonically_increasing_id()) // 自连接后过滤,只保留row_id小于重复表row_id的记录(下三角,不含对角线) val lowerTriDF = dfWithId .crossJoin(dfWithId.as("dup_df")) .where(col("row_id") < col("dup_df.row_id")) // 可选:如果不需要重复的列,可以删除重复列或者重命名 .drop(col("dup_df.row_id"))
monotonically_increasing_id()会生成全局唯一的递增ID,保证每行的标识不重复。如果你的数据有分区,也可以用row_number()配合分区键生成更紧凑的ID,但全局唯一的标识就足够满足需求。
方法2:利用现有唯一可比较列过滤
如果你的DataFrame已经有天然的唯一且可比较的列(比如用户ID、商品ID这类有序的唯一键),直接用这个列做过滤会更高效,省去添加额外列的开销:
// 假设df2有唯一可比较列user_id val lowerTriDF = df2 .crossJoin(df2.as("dup_df")) .where(col("user_id") < col("dup_df.user_id"))
这种方式不需要额外的计算来生成ID,直接利用现有业务列完成过滤,性能最优。
为什么这能减少计算量?
完整的自笛卡尔积会生成n²条记录(n是原DataFrame的行数),而通过上述过滤后,只会保留n(n-1)/2*条记录——相当于直接减少了近一半的计算量,完全避免了对称对的重复计算,正好符合你只需要下三角部分的需求。
如果之后需要补充对角线元素(值为1),可以单独生成对角线记录再union到结果中,但根据你的描述,这一步可以省略。
内容的提问来源于stack exchange,提问作者WestCoastProjects
相关产品推荐
相关产品推荐

