Scala 在Spark中创建给定架构的空数组列
由于parquet不能使用空数组,所以在编写表之前,我将空数组替换为null。现在,当我阅读表格时,我想做相反的事情: 我有一个具有以下模式的数据帧:Scala 在Spark中创建给定架构的空数组列,scala,apache-spark,Scala,Apache Spark,由于parquet不能使用空数组,所以在编写表之前,我将空数组替换为null。现在,当我阅读表格时,我想做相反的事情: 我有一个具有以下模式的数据帧: |-- id: long (nullable = false) |-- arr: array (nullable = true) | |-- element: struct (containsNull = true) | | |-- x: double (nullable = true) | | |-- y:
|-- 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
- 已知外部类型的火花2.2+
通常,您可以使用
提供空数组typedLit
要为嵌套对象使用特定名称,可以使用case类:import org.apache.spark.sql.functions.typedLit typedLit(Seq.empty[(Double, Double)])
或: 其中,case class Item(x: Double, y: Double) typedLit(Seq.empty[Item])
可以从现有的schema
中提取,并用附加的DataFrame
进行包装:StructType
StructType(Seq( StructField("arr", df.schema("arr").dataType) ))
val arrSchema = df.schema(1).dataType // ArrayType(StructType(StructField(x,DoubleType,true), StructField(y,DoubleType,true)),true)
val emptyArr = udf(() => Seq.empty[Any],arrSchema)
df
.withColumn("arr",when($"arr".isNull,emptyArr()).otherwise($"arr"))
.show()
+---+-----------+
| id| arr|
+---+-----------+
| 1|[[1.0,2.0]]|
| 2| []|
+---+-----------+
另一种方法是使用
合并:
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| []|
// +---+-----------+
带有case类的UDF也很有趣:
case class Item(x: Double, y: Double)
val udf_emptyArr = udf(() => Seq[Item]())
df
.withColumn("arr",coalesce($"arr",udf_emptyArr()))
.show()
是否可能只使用df中的模式信息(这将使其更通用),实际上我发现使用UDF@RaphaelRoth整洁。这也仅适用于Spark 2.2+,并且需要硬编码类型信息
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| []|
// +---+-----------+
case class Item(x: Double, y: Double)
val udf_emptyArr = udf(() => Seq[Item]())
df
.withColumn("arr",coalesce($"arr",udf_emptyArr()))
.show()