Способ объединения массива структур

#apache-spark

#apache-spark

Вопрос:

У меня есть столбец, содержащий массив структур. Это выглядит так:

  |-- Network: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- Code: string (nullable = true)
 |    |    |-- Signal: string (nullable = true)
  

Это всего лишь небольшая выборка, внутри структуры намного больше столбцов, чем это. Есть ли способ взять массивы в столбце для каждой строки, объединить их и превратить в одну строку? Например, у нас может быть что-то вроде этого:

 [["example", 2], ["example2", 3]]
  

Есть ли способ превратить в:

 "example2example3"?
  

Комментарии:

1. какую версию Spark вы используете?

Ответ №1:

Предполагая наличие df фрейма данных со следующей схемой:

 df.printSchema
  

введите описание изображения здесь

df с примерами данных:

 df.show(false)
  

введите описание изображения здесь

Вам нужно сначала разобрать сетевой массив, чтобы выбрать элементы структуры Code и signal.

 var myDf = df.select(explode($"Network").as("Network"))
  

Затем вам нужно объединить два столбца с помощью функции concat(), а затем передать выходные данные функции collect_list(), которая объединит все строки в одну строку типа array<string>

 myDf = myDf.select(collect_list(concat($"Network.code",$"Network.signal")).as("data"))
  

Наконец, вам нужно объединить в требуемый формат, что можно сделать с помощью функции concat_ws(), которая принимает два аргумента, первый из которых является разделителем, который должен быть помещен между двумя строками, а второй аргумент представляет собой столбец с типом array<string>, который является нашим результатом из нашего предыдущего шага. В соответствии с вашим вариантом использования, нам не нужно размещать разделитель между двумя объединенными строками, поэтому мы сохраняем аргумент разделителя как пустую кавычку.

 myDf = myDf.select(concat_ws("",$"data").as("data"))
  

Все вышеуказанные шаги можно выполнить в одной строке

 myDf= myDf.select(explode($"Network").as("Network")).select(concat_ws("",collect_list(concat($"Network.code",$"Network.signal"))).as("data")).show(false)
  

введите описание изображения здесь

Если вы хотите выводить данные непосредственно в строковую переменную, используйте:

 val myStr = myDf.first.get(0).toString
print(myStr)
  

ex11ex22ex331

Ответ №2:

Существует библиотека под названием spark-hats (Github, небольшая статья), которая может оказаться очень полезной в таких ситуациях.

С его помощью вы можете легко сопоставить массив и вывести конкатенацию рядом с элементами или даже где-то еще, если вы укажете полное имя.

Настройка

 import org.apache.spark.sql.functions._
import za.co.absa.spark.hats.Extensions._

scala> df.printSchema
root
 |-- info: struct (nullable = true)
 |    |-- drivers: struct (nullable = true)
 |    |    |-- carName: string (nullable = true)
 |    |    |-- carNumbers: string (nullable = true)
 |    |    |-- driver: string (nullable = true)
 |-- teamName: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- team1: string (nullable = true)
 |    |    |-- team2: string (nullable = true)


scala> df.show(false)
 --------------------------- ------------------------------ 
|info                       |teamName                      |
 --------------------------- ------------------------------ 
|[[RB7, 33, Max Verstappen]]|[[Redbull, rb], [Monster, mt]]|
 --------------------------- ------------------------------ 

  

Команда, которую вы ищете

 scala> val dfOut = df.nestedMapColumn(inputColumnName = "teamName", outputColumnName = "nextElementInArray", expression = a => concat(a.getField("team1"), a.getField("team2")) )
dfOut: org.apache.spark.sql.DataFrame = [info: struct<drivers: struct<carName: string, carNumbers: string ... 1 more field>>, teamName: array<struct<team1:string,team2:string,nextElementInArray:string>>]
  

Вывод

 scala> dfOut.printSchema
root
 |-- info: struct (nullable = true)
 |    |-- drivers: struct (nullable = true)
 |    |    |-- carName: string (nullable = true)
 |    |    |-- carNumbers: string (nullable = true)
 |    |    |-- driver: string (nullable = true)
 |-- teamName: array (nullable = true)
 |    |-- element: struct (containsNull = false)
 |    |    |-- team1: string (nullable = true)
 |    |    |-- team2: string (nullable = true)
 |    |    |-- nextElementInArray: string (nullable = true)

scala> dfOut.show(false)
 --------------------------- ---------------------------------------------------- 
|info                       |teamName                                            |
 --------------------------- ---------------------------------------------------- 
|[[RB7, 33, Max Verstappen]]|[[Redbull, rb, Redbullrb], [Monster, mt, Monstermt]]|
 --------------------------- ----------------------------------------------------