Spark中如何合并两个DataFrame且不产生额外行?
解决Spark DataFrame过滤时保留列类型且不新增行的问题
看起来你想要的是保留所有原始的"S/N"行,仅对item为"a"的行保留原数据,其余行用0填充,同时保证"S/N"列是数值类型——而你之前用union导致重复行,是因为union本质是行拼接(类似SQL的UNION ALL),会把两个DataFrame的所有行都叠加起来,自然会产生额外行。
这里有两种更简洁的方案,完全不需要创建多个中间DataFrame:
方案一:直接用条件判断修改原DataFrame
这种方式最直接,先把"S/N"转成数值类型,再用when函数对item和value列做条件赋值:
import org.apache.spark.sql.functions.{col, lit, when} // 初始化DataFrame时直接将"S/N"转为整数类型 val DF1 = Seq( ("1","a",2), ("2","a",3), ("3","b",3), ("4","b",4), ("5","a",2)) .toDF("S/N","item", "value") .withColumn("S/N", col("S/N").cast("integer")) // 转为数值类型,保证类型保留 // 直接在原DF上做条件修改 val resultDF = DF1 .withColumn("item", when(col("item") === "a", col("item")).otherwise(lit(0))) .withColumn("value", when(col("item") === "a", col("value")).otherwise(lit(0))) resultDF.show()
输出结果:
+---+----+-----+ |S/N|item|value| +---+----+-----+ | 1| a| 2| | 2| a| 3| | 3| 0| 0| | 4| 0| 0| | 5| a| 2| +---+----+-----+
方案二:用左外连接关联过滤后的DataFrame
如果你已经有了过滤后的DF2,可以通过左外连接基于"S/N"关联原始DF的所有行,再填充空值为0:
import org.apache.spark.sql.functions.{col, lit, when} val DF1 = Seq( ("1","a",2), ("2","a",3), ("3","b",3), ("4","b",4), ("5","a",2)) .toDF("S/N","item", "value") .withColumn("S/N", col("S/N").cast("integer")) val DF2 = DF1.filter(col("item") === "a") // 基于"S/N"左外连接,保留所有原始行 val resultDF = DF1.select("S/N") .join(DF2, Seq("S/N"), "left_outer") .withColumn("item", when(col("item").isNotNull, col("item")).otherwise(lit(0))) .withColumn("value", when(col("value").isNotNull, col("value")).otherwise(lit(0))) resultDF.show()
这个方案也会得到和上面完全一致的结果,且不会产生任何重复行。
为什么你的union方案会产生额外行?
Spark的union方法默认等价于SQL的UNION ALL,它会直接把两个DataFrame的行全部拼接在一起,不会做任何去重或匹配。你的DF2有3行,DF3有5行,所以拼接后会得到8行,这显然不是你想要的“合并”效果——你需要的是基于"S/N"的行匹配,而不是行拼接,所以union并不适用这个场景。
内容的提问来源于stack exchange,提问作者user3782604
相关产品推荐
相关产品推荐

