繁体   English   中英

Spark Scala:如何用数组或另一个数据帧中的值替换 null

[英]Spark Scala: How to replace null with values from an array or another dataframe

我有一个如下的数据框

+-----+------+
|empID|deptid|
+-----+------+
|  163|  null|
|  843|  null|
+-----+------+

而且我有长数字range = [20,21] ,或者您可以将其转换为另一个数据帧

+--------+
|  deptid|
+--------+
|      20|
|      21|
+--------+

我想替换那些空值并将输出作为

+-----+------+
|empID|deptid|
+-----+------+
|  163|    20|
|  843|    21|
+-----+------+

我试过了

emp.na.fill(range, Array("deptid")

但是 na.fill 需要第一个参数是 String Boolean Long double 等,而不是数组。

我试图加入两个数据框以获得输出,但没有一个连接给我解决方案。

我尝试的一切都给了我

+-----+------+
|empID|deptid|
+-----+------+
|  163|  null|
|  843|  null|
| null|     5|
| null|     6|
+-----+------+

任何想法?

编辑:从范围数组中获取哪个 empId 并不重要。

是的,我确保数组长度与空值的数量相匹配。

所以我确实有一个解决方案,但它是如此复杂和hacky,我仍然希望有人给出更好的答案。

基本上加入两个数据框,将所有数据收集为数组 zip(删除所有空值),然后作为映射值展开。

val p = newRecords.as("L")
.join(range.as("R"), newRecords("deptid") =!= range("deptid"), "full")
.groupBy()
.agg(collect_list($"R.deptid").as("D"), collect_list($"empID").as("E"))
.withColumn("zip",arrays_zip(col("D"),col("E"))).drop("D", "E")
.select(explode($"zip").as("zip1"))
.withColumn("empid", $"zip1".getItem("E"))
.withColumn("deptid", $"zip1".getItem("D"))
.drop($"zip1")
.show()

+-----+------+
|empid|deptid|
+-----+------+
|  163|    20|
|  843|    21|
+-----+------+

您可以使用或不使用将数组转换为数据框来执行此操作。 可能值得尝试两者,看看哪个更有效(通常 udfs 会更慢)。

如果您不将数组转换为数据框,则可以使用 udf。 您将为初始数据帧生成行号并将其用作数组的索引:

val testDF = Seq((163, null), (843, null)).toDF("empID", "deptID")
val range = List(20, 21)

val myUDF = udf((i: Int) => {
    range(i)
})

testDF.withColumn("rn", row_number().over(Window.orderBy("empID")) - 1)
.withColumn("deptID", myUDF(col("rn")))
.select("empID", "deptID").show

输出:

+-----+------+
|empID|deptID|
+-----+------+
|  163|    20|
|  843|    21|
+-----+------+

如果您确实将数组转换为数据框,则可以为这两个数据框生成行号并在连接条件中使用它:

val testDF = Seq((163, null), (843, null)).toDF("empID", "deptID")
val rangeDF = Seq(20, 21).toDF("deptID")

testDF.withColumn("rn", row_number().over(Window.orderBy("empID")))
.join(rangeDF.withColumn("rn", row_number().over(Window.orderBy("deptID"))), Seq("rn"))
.select(testDF.col("empID"), rangeDF.col("deptID")).show

输出:

+-----+------+
|empID|deptID|
+-----+------+
|  163|    20|
|  843|    21|
+-----+------+

请注意,这两种解决方案都涉及使用未定义分区的Window函数。 如果您有大量数据并且最终被洗牌到单个节点,这可能会很糟糕。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM