1
votes

Comment créer un Spark SQL Dataframe avec une liste d'objets Map

J'ai plusieurs Map [String, String] dans une List (Scala). Par exemple:

EMP_NAME    DOB             CITY
Ahmad       01-10-1991      Dubai
Rahul       06-12-1991      Mumbai
John        11-04-1996      Toronto

Maintenant, je veux créer une seule dataframe avec quelque chose comme ceci:

map1 = Map("EMP_NAME" -> “Ahmad”, "DOB" -> “01-10-1991”, "CITY" -> “Dubai”)
map2 = Map("EMP_NAME" -> “Rahul”, "DOB" -> “06-12-1991”, "CITY" -> “Mumbai”)
map3 = Map("EMP_NAME" -> “John”, "DOB" -> “11-04-1996”, "CITY" -> “Toronto”)
list = List(map1, map2, map3)

Comment puis-je y parvenir? p>


0 commentaires

3 Réponses :


4
votes

vous pouvez le faire comme ceci:

import spark.implicits._

val df = list
  .map( m => (m.get("EMP_NAME"),m.get("DOB"),m.get("CITY")))
  .toDF("EMP_NAME","DOB","CITY")

df.show()

+--------+----------+-------+
|EMP_NAME|       DOB|   CITY|
+--------+----------+-------+
|   Ahmad|01-10-1991|  Dubai|
|   Rahul|06-12-1991| Mumbai|
|    John|11-04-1996|Toronto|
+--------+----------+-------+


0 commentaires

2
votes

Approche légèrement moins spécifique, par exemple:

val RDDmap = sc.parallelize(List(
   Map("EMP_NAME" -> "Ahmad", "DOB" -> "01-10-1991", "CITY" -> "Dubai"),
   Map("EMP_NAME" -> "John",  "DOB" -> "01-10-1992", "CITY" -> "Mumbai")))
   ...

// Get cols dynamically
val cols = RDDmap.take(1).flatMap(x=> x.keys)

// Map is K,V like per Map entry
val df = RDDmap.map{ value=>
                 val list=value.values.toList
                 (list(0), list(1), list(2))
       }.toDF(cols:_*) // dynamic column names assigned

renvoie:

+--------+----------+------+
|EMP_NAME|DOB       |CITY  |
+--------+----------+------+
|Ahmad   |01-10-1991|Dubai |
|John    |01-10-1992|Mumbai|
+--------+----------+------+

ou pour répondre à votre sous-question, ici comme suit - au moins je pense que c'est ce que vous demandez, mais probablement pas:

val map1 = Map("EMP_NAME" -> "Ahmad", "DOB" -> "01-10-1991", "CITY" -> "Dubai")
val map2 = Map("EMP_NAME" -> "John",  "DOB" -> "01-10-1992", "CITY" -> "Mumbai")
///...
val list = List(map1, map2) // map3, ...
val RDDmap = sc.parallelize(list)

// Get cols dynamically
val cols = RDDmap.take(1).flatMap(x=> x.keys)

// Map is K,V like per Map entry
val df = RDDmap.map{ value=>
                     val list=value.values.toList
                     (list(0), list(1), list(2))
       }.toDF(cols:_*) // dynamic column names assigned

df.show(false)

Vous pouvez bien sûr construire une liste dynamiquement, mais vous devez toujours attribuer les éléments de la carte. Voir Ajouter des données à la liste ou à toute autre collection de manière dynamique dans scala . Je voudrais simplement lire un fichier et en finir.


12 commentaires

Merci l'homme Un autre point: comment faire une boucle dynamique (list (0), list (1), list (2))? je veux dire au lieu de coder en dur 1,2 et 3, peut-il prendre quelque chose comme la liste (i)?


Réponse mise à jour qui est en fait une autre réponse. Veuillez accepter le contraire.


@thebluephantom, je ne suppose pas que les résultats de .keys et .values ​​ d'une Map conserveront toujours l'ordre par paire KV.


@LeoC Veuillez préciser


Si m = Map (1-> a, 2-> b, ...) , je pense qu'il n'est pas prudent de supposer m.keys et m. les valeurs auront à coup sûr leurs éléments classés comme 1, 2, ... et a, b, ... , respectivement, ni comme La carte ni Set préserve l'ordre.


@LeoC Voulez-vous dire que des lignes qui sont bien comprises comme étant un problème, peuvent s'appliquer aux coks gere pendant flatMap?


@LeoC J'ai regardé tutorialspoint.com/scala/scala_maps.htm et ils ont un exemple similaire, aucune mention de votre point là. Je me souviens avoir étudié cela il y a quelque temps.


@LeoC Juste pour la postérité, pas convaincu que l'exemple ici est une utilisation classique de la carte.


@thebluephantom, mon point est que puisque les éléments dans m.keys et m.values ​​ ne peuvent pas être garantis de conserver l'ordre dans la paire KV d'origine, il est possible que le jeu de données résultant pourrait être quelque chose comme Seq (("a", "c", "b", "d")). toDF ("2", "1", "3", "4")) .


@LeoC comment cela pourrait-il arriver? cela signifie que tutorialspoints a un mauvais exemple. qu'en est-il alors d'obtenir des cols à partir de dataframes?


Parce que Map ne préserve pas l'ordre. par exemple. Carte (1 -> "a", 2 -> "b") == Carte (2 -> "b", 1 -> "a") , mais Carte (1- > "a", 2 -> "b"). keys.toList! = Map (2 -> "b", 1 -> "a"). keys.toList .


J'ai compris. Cela signifie donc 2 choses, le point du tutoriel que j'ai regardé dans le passé est faux et mon point précédent est de toute façon un mauvais exemple de cas d'utilisation de Map.



1
votes
import org.apache.spark.SparkContext
import org.apache.spark.sql._
import org.apache.spark.sql.types.{StringType, StructField, StructType}

object DataFrameTest2 extends Serializable {
  var sparkSession: SparkSession = _
  var sparkContext: SparkContext = _
  var sqlContext: SQLContext = _

  def main(args: Array[String]): Unit = {
    sparkSession = SparkSession.builder().appName("TestMaster").master("local").getOrCreate()
    sparkContext = sparkSession.sparkContext

    val sqlContext = new org.apache.spark.sql.SQLContext(sparkContext)

    val map1 = Map("EMP_NAME" -> "Ahmad", "DOB" -> "01-10-1991", "CITY" -> "Dubai")
    val map2 = Map("EMP_NAME" -> "Rahul", "DOB" -> "06-12-1991", "CITY" -> "Mumbai")
    val map3 = Map("EMP_NAME" -> "John", "DOB" -> "11-04-1996", "CITY" -> "Toronto")
    val list = List(map1, map2, map3)

    //create your rows
    val rows = list.map(m => Row(m.values.toSeq:_*))

    //create the schema from the header
    val header = list.head.keys.toList
    val schema = StructType(header.map(fieldName => StructField(fieldName, StringType, true)))

    //create your rdd
    val rdd = sparkContext.parallelize(rows)

    //create your dataframe using rdd
    val df = sparkSession.createDataFrame(rdd, schema)
    df.show()
  }
}

7 commentaires

Le protocole est que vous sélectionnez l'une des autres réponses comme étant correcte, à moins que personne d'autre ne l'ait fourni ou que vous estimiez qu'elle était inappropriée.


Je pense que toutes les réponses sont correctes dans ce contexte. Le vôtre et le premier aussi. Je ne sais pas comment marquer plusieurs réponses correctes. De plus, je cherchais simplement la solution la plus générique. En réalité, je vais créer et remplir dynamiquement un ensemble de données pour plus de 40 colonnes. Au fait, j'apprécie beaucoup la solution que vous avez apportée :)


Ensuite, veuillez l'accepter car il est plus flexible, pour l'approche 40 cols. Mais vous pouvez voter pour. Le choix t'appartient.


J'ai voté plusieurs fois pour votre réponse. Mais voici le message que je reçois: Merci pour les commentaires! Les votes exprimés par ceux qui ont moins de 15 points de réputation sont enregistrés, mais ne modifient pas le score affiché publiquement. :( On dirait que je dois d'abord bâtir ma réputation :)


C'est vrai, c'est le cas. Vous pouvez alors accepter la réponse uniquement, si vous pensez que c'est la meilleure, faites-le, sinon choisissez l'autre.


Je ne sais pas comment choisir. Je ne vois aucune étiquette / bouton pour accéder à la réponse. Y a-t-il un lien?


Cliquez simplement sur la coche grise et elle deviendra verte. Vous ne pouvez en choisir qu'un.