web-dev-qa-db-fra.com

Comment obtenir les clés et les valeurs de la colonne MapType dans SparkSQL DataFrame

J'ai des données dans un fichier parquet qui comporte 2 champs: object_id: String et alpha: Map<>.

Il est lu dans une trame de données dans sparkSQL et le schéma ressemble à ceci:

scala> alphaDF.printSchema()
root
 |-- object_id: string (nullable = true)
 |-- ALPHA: map (nullable = true)
 |    |-- key: string
 |    |-- value: struct (valueContainsNull = true)

J'utilise Spark 2.0 et j'essaie de créer un nouveau bloc de données dans lequel les colonnes doivent être object_id touches plus de la carte ALPHA comme dans object_id, key1, key2, key2, ...

J'essayais d'abord de voir si je pouvais au moins accéder à la carte comme ceci:

scala> alphaDF.map(a => a(0)).collect()
<console>:32: error: Unable to find encoder for type stored in a Dataset.
Primitive types (Int, String, etc) and Product types (case classes) are 
supported by importing spark.implicits._  Support for serializing other
types will be added in future releases.
   alphaDF.map(a => a(0)).collect()

mais malheureusement, je n'arrive pas à comprendre comment accéder aux clés de la carte.

Quelqu'un peut-il me montrer comment obtenir le object_id plus les clés de mappage comme noms de colonne et les valeurs de mappage comme valeurs respectives dans une nouvelle trame de données?

11
lloydh

Spark> = 2.

Vous pouvez simplifier le processus en utilisant map_keys fonction:

import org.Apache.spark.sql.functions.map_keys

Il y a aussi map_values, mais elle ne sera pas directement utile ici.

Spark <2,

La méthode générale peut être exprimée en quelques étapes. Premières importations requises:

import org.Apache.spark.sql.functions.udf
import org.Apache.spark.sql.Row

et des exemples de données:

val ds = Seq(
  (1, Map("foo" -> (1, "a"), "bar" -> (2, "b"))),
  (2, Map("foo" -> (3, "c"))),
  (3, Map("bar" -> (4, "d")))
).toDF("id", "alpha")

Pour extraire les clés, nous pouvons utiliser UDF (Spark <2.3)

val map_keys = udf[Seq[String], Map[String, Row]](_.keys.toSeq)

ou fonctions intégrées

import org.Apache.spark.sql.functions.map_keys

val keysDF = df.select(map_keys($"alpha"))

Trouvez-en différents:

val distinctKeys = keysDF.as[Seq[String]].flatMap(identity).distinct
  .collect.sorted

Vous pouvez également généraliser l'extraction de keys avec explode:

import org.Apache.spark.sql.functions.explode

val distinctKeys = df
  // Flatten the column into key, value columns
 .select(explode($"alpha"))
 .select($"key")
 .as[String].distinct
 .collect.sorted

Et select:

ds.select($"id" +: distinctKeys.map(x => $"alpha".getItem(x).alias(x)): _*)
19
user6910411

Et si vous êtes dans PySpark, je trouve juste une implémentation facile:

from pyspark.sql.functions import map_keys

alphaDF.select(map_keys("ALPHA").alias("keys")).show()

Vous pouvez vérifier les détails dans ici

0
Hailin FU