J'essaie d'utiliser l'API Spark Dataset mais j'ai des problèmes pour faire une simple jointure.
Disons que j'ai deux jeux de données avec des champs: date | value
, alors dans le cas de DataFrame
ma jointure ressemblerait à:
val dfA : DataFrame
val dfB : DataFrame
dfA.join(dfB, dfB("date") === dfA("date") )
Cependant pour Dataset
il y a le .joinWith
, mais la même approche ne fonctionne pas:
val dfA : Dataset
val dfB : Dataset
dfA.joinWith(dfB, ? )
Quel est l'argument requis par .joinWith
?
Pour utiliser joinWith
, vous devez d'abord créer un DataSet
, et probablement deux d'entre eux. Pour créer un DataSet
, vous devez créer une classe de cas qui correspond à votre schéma et appeler DataFrame.as[T]
Où T
est votre classe de cas. Alors:
case class KeyValue(key: Int, value: String)
val df = Seq((1,"asdf"),(2,"34234")).toDF("key", "value")
val ds = df.as[KeyValue]
// org.Apache.spark.sql.Dataset[KeyValue] = [key: int, value: string]
Vous pouvez également ignorer la classe de cas et utiliser un tuple:
val tupDs = df.as[(Int,String)]
// org.Apache.spark.sql.Dataset[(Int, String)] = [_1: int, _2: string]
Ensuite, si vous aviez une autre classe de cas/DF, comme ceci, dites:
case class Nums(key: Int, num1: Double, num2: Long)
val df2 = Seq((1,7.7,101L),(2,1.2,10L)).toDF("key","num1","num2")
val ds2 = df2.as[Nums]
// org.Apache.spark.sql.Dataset[Nums] = [key: int, num1: double, num2: bigint]
Ensuite, bien que la syntaxe de join
et joinWith
soit similaire, les résultats sont différents:
df.join(df2, df.col("key") === df2.col("key")).show
// +---+-----+---+----+----+
// |key|value|key|num1|num2|
// +---+-----+---+----+----+
// | 1| asdf| 1| 7.7| 101|
// | 2|34234| 2| 1.2| 10|
// +---+-----+---+----+----+
ds.joinWith(ds2, df.col("key") === df2.col("key")).show
// +---------+-----------+
// | _1| _2|
// +---------+-----------+
// | [1,asdf]|[1,7.7,101]|
// |[2,34234]| [2,1.2,10]|
// +---------+-----------+
Comme vous pouvez le voir, joinWith
laisse les objets intacts en tant que parties d'un Tuple, tandis que join
aplatit les colonnes en un seul espace de noms. (Ce qui entraînera des problèmes dans le cas ci-dessus car le nom de colonne "clé" est répété.)
Curieusement, je dois utiliser df.col("key")
et df2.col("key")
pour créer les conditions pour rejoindre ds
et ds2
- si vous utilisez juste col("key")
de chaque côté, cela ne fonctionne pas, et ds.col(...)
n'existe pas. Cependant, l'utilisation de la df.col("key")
d'origine fait l'affaire.
De https://docs.cloud.databricks.com/docs/latest/databricks_guide/05%20Spark/1%20Intro%20Datasets.html
on dirait que vous pourriez simplement faire
dfA.as("A").joinWith(dfB.as("B"), $"A.date" === $"B.date" )
Dans l'exemple ci-dessus, vous pouvez essayer l'option ci-dessous -
Définissez une classe de cas pour votre sortie
case class JoinOutput(key:Int, value:String, num1:Double, num2:Long)
Joignez deux ensembles de données avec "Seq (" clé ")", cela vous aidera à éviter deux colonnes de clé en double dans la sortie. Ce qui aidera à appliquer la classe de cas ou à récupérer les données à l'étape suivante
ds.join(ds2, Seq("key")).as[JoinOutput] res27: org.Apache.spark.sql.Dataset[JoinOutput] = [key: int, value: string ... 2 more fields]
scala> ds.join(ds2, Seq("key")).as[JoinOutput].show +---+-----+----+----+ |key|value|num1|num2| +---+-----+----+----+ | 1| asdf| 7.7| 101| | 2|34234| 1.2| 10| +---+-----+----+----+