Spark sql 如何在不丢失空值的情况下爆炸

2022-08-31 17:09:52

我有一个数据帧,我试图扁平化。作为该过程的一部分,我想将其分解,因此,如果我有一列数组,则该数组的每个值将用于创建单独的行。例如

id | name | likes
_______________________________
1  | Luke | [baseball, soccer]

应该成为

id | name | likes
_______________________________
1  | Luke | baseball
1  | Luke | soccer

这是我的代码

private DataFrame explodeDataFrame(DataFrame df) {
    DataFrame resultDf = df;
    for (StructField field : df.schema().fields()) {
        if (field.dataType() instanceof ArrayType) {
            resultDf = resultDf.withColumn(field.name(), org.apache.spark.sql.functions.explode(resultDf.col(field.name())));
            resultDf.show();
        }
    }
    return resultDf;
}

问题是在我的数据中,一些数组列具有空值。在这种情况下,将删除整行。所以这个数据帧:

id | name | likes
_______________________________
1  | Luke | [baseball, soccer]
2  | Lucy | null

成为

id | name | likes
_______________________________
1  | Luke | baseball
1  | Luke | soccer

而不是

id | name | likes
_______________________________
1  | Luke | baseball
1  | Luke | soccer
2  | Lucy | null

如何分解数组,以免丢失空行?

我使用的是 Spark 1.5.2 和 Java 8


答案 1

火花 2.2+

您可以使用以下功能:explode_outer

import org.apache.spark.sql.functions.explode_outer

df.withColumn("likes", explode_outer($"likes")).show

// +---+----+--------+
// | id|name|   likes|
// +---+----+--------+
// |  1|Luke|baseball|
// |  1|Luke|  soccer|
// |  2|Lucy|    null|
// +---+----+--------+

火花<= 2.1

在Scala中,但Java的等效物应该几乎相同(要导入单个函数使用)。import static

import org.apache.spark.sql.functions.{array, col, explode, lit, when}

val df = Seq(
  (1, "Luke", Some(Array("baseball", "soccer"))),
  (2, "Lucy", None)
).toDF("id", "name", "likes")

df.withColumn("likes", explode(
  when(col("likes").isNotNull, col("likes"))
    // If null explode an array<string> with a single null
    .otherwise(array(lit(null).cast("string")))))

这里的想法基本上是用所需类型的替换。对于复杂类型(又名),您必须提供完整的架构:NULLarray(NULL)structs

val dfStruct = Seq((1L, Some(Array((1, "a")))), (2L, None)).toDF("x", "y")

val st =  StructType(Seq(
  StructField("_1", IntegerType, false), StructField("_2", StringType, true)
))

dfStruct.withColumn("y", explode(
  when(col("y").isNotNull, col("y"))
    .otherwise(array(lit(null).cast(st)))))

dfStruct.withColumn("y", explode(
  when(col("y").isNotNull, col("y"))
    .otherwise(array(lit(null).cast("struct<_1:int,_2:string>")))))

注意

如果数组已设置为,则应首先更改此设置(使用 Spark 2.1 测试):ColumncontainsNullfalse

df.withColumn("array_column", $"array_column".cast(ArrayType(SomeType, true)))

答案 2

您可以使用函数。explode_outer()