web-dev-qa-db-fra.com

Spark API de jeu de données - rejoindre

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?

20
mastro

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]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.

30
David Griffin

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| +---+-----+----+----+

2
Syntax