web-dev-qa-db-fra.com

Décomposition de la structure imbriquée dans le cadre de données Spark

Je travaille à travers le Databricks exemple . Le schéma du cadre de données se présente comme suit:

> parquetDF.printSchema
root
|-- department: struct (nullable = true)
|    |-- id: string (nullable = true)
|    |-- name: string (nullable = true)
|-- employees: array (nullable = true)
|    |-- element: struct (containsNull = true)
|    |    |-- firstName: string (nullable = true)
|    |    |-- lastName: string (nullable = true)
|    |    |-- email: string (nullable = true)
|    |    |-- salary: integer (nullable = true)

Dans l'exemple, ils montrent comment décomposer la colonne Employés en 4 colonnes supplémentaires:

val explodeDF = parquetDF.explode($"employees") { 
case Row(employee: Seq[Row]) => employee.map{ employee =>
  val firstName = employee(0).asInstanceOf[String]
  val lastName = employee(1).asInstanceOf[String]
  val email = employee(2).asInstanceOf[String]
  val salary = employee(3).asInstanceOf[Int]
  Employee(firstName, lastName, email, salary)
 }
}.cache()
display(explodeDF)

Comment pourrais-je faire quelque chose de similaire avec la colonne department (c'est-à-dire ajouter deux colonnes supplémentaires à la trame de données appelée "id" et "nom")? Les méthodes ne sont pas exactement les mêmes et je ne peux que comprendre comment créer un tout nouveau cadre de données en utilisant:

val explodeDF = parquetDF.select("department.id","department.name")
display(explodeDF)

Si j'essaye:

val explodeDF = parquetDF.explode($"department") { 
  case Row(dept: Seq[String]) => dept.map{dept => 
  val id = dept(0) 
  val name = dept(1)
  } 
}.cache()
display(explodeDF)

Je reçois l'avertissement et l'erreur:

<console>:38: warning: non-variable type argument String in type pattern Seq[String] is unchecked since it is eliminated by erasure
            case Row(dept: Seq[String]) => dept.map{dept => 
                           ^
<console>:37: error: inferred type arguments [Unit] do not conform to    method explode's type parameter bounds [A <: Product]
  val explodeDF = parquetDF.explode($"department") { 
                                   ^
16
Feynman27

Vous pourriez utiliser quelque chose comme ça:

var explodeDF = explodeDF.withColumn("id", explodeDF("department.id"))
explodeDeptDF = explodeDeptDF.withColumn("name", explodeDeptDF("department.name"))

dans lequel vous m'avez aidé et ces questions:

9
gsamaras

Cela semble fonctionner (mais peut-être pas la solution la plus élégante). 

var explodeDF2 = explodeDF.withColumn("id", explodeDF("department.id"))
explodeDF2 = explodeDF2.withColumn("name", explodeDF2("department.name"))
3
Feynman27

À mon avis, la solution la plus élégante consiste à développer en étoile un Struct en utilisant un opérateur de sélection, comme indiqué ci-dessous:

var explodedDf2 = explodedDf.select("department.*","*")

https://docs.databricks.com/spark/latest/spark-sql/complex-types.html

1
DHARIN PAREKH