在Spark中创建给定架构的空数组列

时间:2018-06-27 10:20:44

标签: scala apache-spark

由于实木复合地板无法解析空数组,因此在写表之前我将空数组替换为null。现在,当我阅读表格时,我想做相反的事情:

我有一个具有以下架构的DataFrame:

|-- id: long (nullable = false)
 |-- arr: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- x: double (nullable = true)
 |    |    |-- y: double (nullable = true)

以及以下内容:

+---+-----------+
| id|        arr|
+---+-----------+
|  1|[[1.0,2.0]]|
|  2|       null|
+---+-----------+

我想将空数组(id = 2)替换为空数组,即

+---+-----------+
| id|        arr|
+---+-----------+
|  1|[[1.0,2.0]]|
|  2|         []|
+---+-----------+

我尝试过:

val arrSchema = df.schema(1).dataType

df
.withColumn("arr",when($"arr".isNull,array().cast(arrSchema)).otherwise($"arr"))
.show()

给出:

  

java.lang.ClassCastException:org.apache.spark.sql.types.NullType $   无法转换为org.apache.spark.sql.types.StructType

编辑:我不想对数组列的任何模式(至少不是结构的模式)进行“硬编码”,因为这可能因情况而异。我只能在运行时使用来自df的架构信息

我正在使用Spark 2.1,因此无法使用typedLit

4 个答案:

答案 0 :(得分:3)

    具有已知外部类型的
  • Spark 2.2 +

    通常,您可以使用typedLit提供空数组。

    import org.apache.spark.sql.functions.typedLit
    
    typedLit(Seq.empty[(Double, Double)])
    

    要对嵌套对象使用特定的名称,可以使用案例类:

    case class Item(x: Double, y: Double)
    
    typedLit(Seq.empty[Item])
    

    rename by cast

    typedLit(Seq.empty[(Double, Double)])
      .cast("array<struct<x: Double, y: Double>>")
    
  • 仅具有架构的Spark 2.1 +

    仅使用架构,您可以尝试:

    val schema = StructType(Seq(
      StructField("arr", StructType(Seq(
        StructField("x", DoubleType),
        StructField("y", DoubleType)
      )))
    ))
    
    def arrayOfSchema(schema: StructType) =
      from_json(lit("""{"arr": []}"""), schema)("arr")
    
    arrayOfSchema(schema).alias("arr")
    

    其中schema可以从现有的DataFrame中提取并用其他StructType包装:

    StructType(Seq(
      StructField("arr", df.schema("arr").dataType)
    ))
    

答案 1 :(得分:1)

一种方法是使用UDF:

          var a = document.createElement('a');
          a.textContent = "Visit this store"
          a.href = "/bottle.php?id=" + id;
          infowincontent.appendChild(a);

答案 2 :(得分:1)

另一种方法是使用coalesce

val df = Seq(
  (Some(1), Some(Array((1.0, 2.0)))),
  (Some(2), None)
).toDF("id", "arr")

df.withColumn("arr", coalesce($"arr", typedLit(Array.empty[(Double, Double)]))).
  show
// +---+-----------+
// | id|        arr|
// +---+-----------+
// |  1|[[1.0,2.0]]|
// |  2|         []|
// +---+-----------+

答案 3 :(得分:0)

带有案例类的UDF也可能很有趣:

case class Item(x: Double, y: Double)
val udf_emptyArr = udf(() => Seq[Item]())
df
.withColumn("arr",coalesce($"arr",udf_emptyArr()))
.show()