Выходные данные Spark DataFrame заменяют имена столбцов на 0, 1, когда данные array_zip созданы вложенными

Я использовал функции spark sql arrayys_zip в сочетании с flatten для преобразования данных из массива структуры внутреннего массива той же длины в массив структуры. printSchema показывает именно то, что я хочу. Однако при выводе df исходные имена столбцов были потеряны и они были заменены общим именем столбца «0», «1», «2» и т. Д. Независимо от формата Parquet или Avro. Мне нравится выводить оригинальные имена столбцов.

Не раскрывать бизнес моей компании. Ниже приведены похожие, но очень упрощенные примеры.

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)

Я знаю, что array_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")

Я попробовал другой пример, который имеет исходные данные массива месяцев и массива чисел на верхнем уровне. Я могу array_zip без сглаживания и получить ту же схему и показанные данные. Однако в этом случае он правильно выводит исходное имя поля.

Я попытался добавить псевдоним для сглаживания данных. Это не работает. Я даже пробовал манипулировать столбцами вроде (предположим, что в полевом хранилище результат array_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")

В итоге создается исходная схема («месяц», массив строк и «число», массив длинных значений).

Чтобы продублировать проблему, вы можете использовать ввод json

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

следующий json предназначен для array_zip верхнего уровня

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

Как Spark внутренне решает, какие имена полей использовать? Как заставить его работать? Пожалуйста, порекомендуйте.


person user2200901    schedule 26.08.2019    source источник
comment
Получил такую ​​же проблему. Мне кажется, что это ошибка, иногда используются имена столбцов, в зависимости от их структуры. как ты это решил?   -  person trompa    schedule 08.07.2020


Ответы (1)


Начиная с 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)
person David Vrba    schedule 26.08.2019
comment
Спасибо. Выглядит многообещающе. Выхожу на паркет. Он сохраняет оригинальные столбцы. Интересно, что array_zip непосредственно на вложенном массиве не работает. Но будет ли это работать внутри TRANSFORM? Много возможностей. - person user2200901; 26.08.2019
comment
@ user2200901 Я пробовал array_zip внутри TRANSFORM, и он использовал имена 1 и 0 для столбцов. - person David Vrba; 26.08.2019
comment
K. Столбцы не статичны и генерируются программно с определенными переменными. Arrays_zip в этом случае очень удобен. Поэтому я могу попробовать array_zip, а затем манипулировать псевдонимом столбца или свернуть с помощью zip_with. Надеюсь, у меня скоро появится свободное время, чтобы попробовать. Спасибо. - person user2200901; 30.08.2019