当最初嵌套arrays_zip数据时,Spark DataFrame的输出将列名称替换为“ 0”,“ 1”

时间:2019-08-26 08:04:08

标签: apache-spark-sql

我将spark sql函数arrays_zip与flatten结合使用,将数据从相同长度的内部数组的结构数组转换为结构数组。 printSchema恰好显示了我想要的。但是,无论Parquet还是Avro格式,df输出都会丢失原始列名,并用通用列名“ 0”,“ 1”,“ 2”等替换它们。我喜欢输出原始列名。

不透露我公司的业务。以下是相似但简化的示例。

scala> c2.printSchema
root
 |-- cal: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- month: array (nullable = true)
 |    |    |    |-- element: string (containsNull = true)
 |    |    |-- num: array (nullable = true)
 |    |    |    |-- element: long (containsNull = true)
scala> c2.show(false)
+----------------------------------------------+
|cal                                           |
+----------------------------------------------+
|[[[Jan, Feb, Mar], [1, 2, 3]], [[April], [4]]]|
+----------------------------------------------+

我喜欢转换为

scala> newC2.show(false)
+------------------------------------------+
|cal                                       |
+------------------------------------------+
|[[Jan, 1], [Feb, 2], [Mar, 3], [April, 4]]|
+------------------------------------------+
with
scala> newC2.printSchema
root
 |-- cal: array (nullable = true)
 |    |-- element: struct (containsNull = false)
 |    |    |-- month: string (nullable = true)
 |    |    |-- num: long (nullable = true)

我知道arrays_zip仅适用于顶级阵列。因此,我将它们压平到最高水平。以下代码在此示例中有效

val newC2 = c2.withColumn("month", flatten(col("cal.month"))).withColumn("num", flatten(col("cal.num"))).withColumn("cal", arrays_zip(col("month"), col("num"))).drop("month", "num")

它准确地生成我想要的数据和架构。但是,它通常使用“ 0”,“ 1”,“ 2”等输出所有列。

newC2.write.option("header", false).parquet("c2_parquet")

我尝试了另一个示例,该示例在顶层具有month数组和num数组的原始数据。我可以在不拉平的情况下使用arrays_zip并获得与所示相同的架构和数据。但是,在这种情况下,它会正确输出原始字段名称。

我尝试添加别名以拼合数据。那行不通。我什至尝试操纵列(假设字段存储arrays_zip的结果为“压缩”

val columns: Array[Column] = inner.fields.map(_.name).map{x => col("zipped").getField(x).alias(x)}
    val newB3 = newB2.withColumn("b", array(struct(columns:_*))).drop("zipped")

最终生成原始架构('month',字符串数组和“ num”,long数组)。

要重复该问题,可以使用json输入

"cal":[{"month":["Jan","Feb","Mar"],"num":[1,2,3]},{"month":["April"],"num":[4]}]}

以下json用于顶级arrays_zip

{"month":["Jan","Feb","Mar"],"num":[1,2,3]}

Spark如何在内部决定要使用哪些字段名称?我如何使它工作?请告知。

1 个答案:

答案 0 :(得分:0)

从Spark 2.4开始,可以使用高阶函数来实现模式转换。在Scala中,查询如下所示:

import org.apache.spark.sql.functions.{expr, flatten}

val result = df
.withColumn("cal", flatten(expr("TRANSFORM(cal, x -> zip_with(x.month, x.num, (month, num) -> (month,num)))")))

在应用示例数据后,我得到以下模式:

result.printSchema()
root
 |-- cal: array (nullable = true)
 |    |-- element: struct (containsNull = false)
 |    |    |-- month: string (nullable = true)
 |    |    |-- num: long (nullable = true)