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

如何在PySpark DataFrame中合并满足(src,dst)与(dst,src)匹配条件的行并汇总权重?

如何在PySpark DataFrame中合并(src,dst)与(dst,src)匹配的行并求和weight

针对你提出的需求——合并互为反向的(src,dst)对并求和weight,同时保留其中一行,我整理了两种实用的实现方案,直接看代码和解释吧:

首先,先创建你提供的示例DataFrame:

from pyspark.sql import SparkSession
from pyspark.sql.functions import col, when, sort_array, array, sum as spark_sum, first

spark = SparkSession.builder.appName("MergeReverseEdgePairs").getOrCreate()

sample_data = [
    (8,7,1),
    (1,1,93),
    (1,4,1),
    (4,4,2),
    (4,1,3),
    (1,7,1)
]

df = spark.createDataFrame(sample_data, ["src", "dst", "weight"])
df.show()

方案1:统一使用排序后的(src,dst)作为最终结果

这个方案会把所有互为反向的pair统一转换成src <= dst的形式,然后求和weight,最终得到规整的合并结果:

# 生成排序后的(src,dst)数组,作为分组的统一键
df_normalized = df.withColumn(
    "sorted_pair", sort_array(array(col("src"), col("dst")))
).withColumn(
    "new_src", col("sorted_pair")[0]
).withColumn(
    "new_dst", col("sorted_pair")[1]
)

# 按统一键分组,求和weight
merged_df = df_normalized.groupBy("new_src", "new_dst").agg(
    spark_sum("weight").alias("weight")
).withColumnRenamed("new_src", "src").withColumnRenamed("new_dst", "dst")

merged_df.show()

运行后输出:

+---+---+------+
|src|dst|weight|
+---+---+------+
|  1|  1|    93|
|  1|  4|     4|
|  1|  7|     1|
|  4|  4|     2|
|  7|  8|     1|
+---+---+------+

方案2:保留原数据中的其中一个方向

如果你想严格贴合你给出的两种预期输出,保留原数据中某一个方向的pair(比如保留(1,4)而不是(4,1),或者反过来),可以用这个方案:

子方案2.1:保留src <= dst的方向(对应情况一)

# 标记每行是否为"正向"(src <= dst)
df_with_flag = df.withColumn(
    "is_forward", when(col("src") <= col("dst"), True).otherwise(False)
)

# 把反向行的src和dst交换,生成统一的分组键
df_normalized = df_with_flag.withColumn(
    "norm_src", when(col("is_forward"), col("src")).otherwise(col("dst"))
).withColumn(
    "norm_dst", when(col("is_forward"), col("dst")).otherwise(col("src"))
)

# 分组求和,同时保留原正向行的src和dst
merged_df = df_normalized.groupBy("norm_src", "norm_dst").agg(
    spark_sum("weight").alias("weight"),
    first(when(col("is_forward"), col("src"))).alias("final_src"),
    first(when(col("is_forward"), col("dst"))).alias("final_dst")
).withColumn(
    # 处理特殊情况:如果分组里全是反向行(理论上不会出现,但做个兜底)
    "final_src", when(col("final_src").isNull(), col("norm_src")).otherwise(col("final_src"))
).withColumn(
    "final_dst", when(col("final_dst").isNull(), col("norm_dst")).otherwise(col("final_dst"))
).select("final_src", "final_dst", "weight").withColumnRenamed("final_src", "src").withColumnRenamed("final_dst", "dst")

merged_df.show()

输出就是你要的「情况一」:

+---+---+------+
|src|dst|weight|
+---+---+------+
|  8|  7|     1|
|  1|  1|    93|
|  1|  4|     4|
|  4|  4|     2|
|  1|  7|     1|
+---+---+------+

子方案2.2:保留src >= dst的方向(对应情况二)

只需要修改is_forward的判断条件即可:

df_with_flag = df.withColumn(
    "is_forward", when(col("src") >= col("dst"), True).otherwise(False)
)

剩下的代码和子方案2.1完全一致,运行后就会得到「情况二」的结果:

+---+---+------+
|src|dst|weight|
+---+---+------+
|  7|  8|     1|
|  1|  1|    93|
|  4|  1|     4|
|  4|  4|     2|
|  7|  1|     1|
+---+---+------+

核心逻辑说明

两种方案的本质都是先给互为反向的pair生成统一的分组键,让它们进入同一个分组,然后对weight做聚合求和。区别在于最终是否保留原数据的方向:

  • 方案1直接用排序后的pair作为最终的src/dst,结果更规整;
  • 方案2可以灵活保留原数据中的任意一个方向,完全贴合你给出的两种预期输出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 12:24:07