Aplatir automatiquement et élégamment DataFrame dans Spark SQL
Tous,
Est-il un élégant et accepté pour aplatir une Étincelle table SQL (Parquet) avec des colonnes qui sont emboîtés StructType
Par exemple
Si mon schéma est:
foo
|_bar
|_baz
x
y
z
Comment sélectionnez-le dans un aplatie forme de tableaux, sans recourir à l'exécution manuelle
df.select("foo.bar","foo.baz","x","y","z")
En d'autres termes, comment puis-je obtenir le résultat le code ci-dessus par programme, dans une StructType
et un DataFrame
source d'informationauteur echen
Vous devez vous connecter pour publier un commentaire.
La réponse est courte, il n'y a pas "accepté" la façon de le faire, mais vous pouvez le faire très élégante avec une fonction récursive qui génère de votre
select(...)
déclaration de la marche à travers lesDataFrame.schema
.La fonction récursive doit retourner un
Array[Column]
. Chaque fois que la fonction atteint unStructType
il appelle lui-même et ajouter le retour de l'Array[Column]
à ses propresArray[Column]
.Quelque chose comme:
Vous pouvez alors l'utiliser comme ceci:
Que j'améliore ma réponse précédente et en offrant une solution à mon problème indiqué dans les commentaires de la accepté de répondre.
Accepté cette solution crée un tableau de la Colonne des objets et l'utilise pour sélectionner ces colonnes. Dans Spark, si vous avez un imbriquée DataFrame, vous pouvez sélectionner l'enfant de la colonne comme ceci:
df.select("Parent.Child")
et cela renvoie d'un DataFrame avec les valeurs de l'enfant de la colonne et est nommé Enfant. Mais si vous avez des noms identiques pour les attributs de parents différents structures, vous perdez l'info sur le parent et peuvent se retrouver avec les mêmes noms de colonnes et ne peut pas y accéder par le nom de plus comme ils sont sans équivoque.C'était mon problème.
J'ai trouvé une solution à mon problème, peut-être que ça peut aider quelqu'un d'autre. J'ai appelé le
flattenSchema
séparément:et cette retourné un Tableau de la Colonne objets. Au lieu d'utiliser ce dans le
select()
qui permettrait le retour d'un DataFrame avec des colonnes nommées par l'enfant de la dernier niveau, je l'ai associé l'origine des noms de colonne à eux-mêmes comme des chaînes, puis, après la sélectionParent.Child
colonne, il la renommeParent.Child
au lieu deChild
(j'ai aussi remplacé les points avec des traits de soulignement pour ma convenance):Et puis vous pouvez utiliser la fonction de sélection, comme indiqué dans la réponse originale à cette question:
Voulais juste partager ma solution pour Pyspark - c'est plus ou moins une traduction de @David Griffin solution, donc il prend en charge n'importe quel niveau d'objets imbriqués.
Vous pouvez également utiliser SQL pour sélectionner les colonnes que la télévision.
J'ai fait une implémentation en Java: https://gist.github.com/ebuildy/3de0e2855498e5358e4eed1a4f72ea48
(utiliser la méthode récursive ainsi, je préfère SQL façon, de sorte que vous pouvez tester facilement via Spark-shell).
J'ai été en utilisant l'un des paquebots qui entraîne une stagnation de schéma avec 5 colonnes de bar, baz, x, y, z:
Comme pour
explode
: en général, je réserveexplode
pour l'aplatissement d'une liste. Par exemple, si vous avez une colonneidList
qui est une liste de Chaînes, vous pouvez faire:Qui aboutira à une nouvelle Dataframe avec une colonne nommée
flattenedId
(qui n'est plus une liste)Voici une fonction qui est en train de faire ce que vous voulez et qui peut faire face à de multiples imbriquées les colonnes contenant les colonnes du même nom, avec un préfixe:
Avant:
Après:
J'ai ajouté un
DataFrame#flattenSchema
méthode de l'open source spark-daria projet.Voici comment vous pouvez utiliser la fonction avec votre code.
Vous pouvez également spécifier le nom de colonne différent des délimiteurs avec le
flattenSchema()
méthode.Ce paramètre de délimiteur est étonnamment importante. Si vous aplatissez votre schéma de chargement de la table dans le Redshift, vous ne serez pas en mesure d'utiliser des périodes comme délimiteur.
Voici le code complet extrait de code pour générer cette sortie.
Le code sous-jacent est similaire à David Griffin code (dans le cas où vous ne voulez pas ajouter de l'étincelle-daria dépendance à votre projet).