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?
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)): _*)
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