J'ai deux dataframes avec les colonnes suivantes:
df1.columns
// Array(ts, id, X1, X2)
et
df2.columns
// Array(ts, id, Y1, Y2)
Après je
val df_combined = df1.join(df2, Seq(ts,id))
Je me retrouve avec les colonnes suivantes: Array(ts, id, X1, X2, ts, id, Y1, Y2)
. Je pouvais m'attendre à ce que les colonnes communes soient supprimées. Y at-il quelque chose de plus qui doit être fait?
La réponse simple (à partir de Databricks FAQ à ce sujet )] consiste à effectuer la jointure dans laquelle les colonnes jointes sont exprimées sous la forme tableau de chaînes (ou une chaîne) au lieu d'un prédicat.
Vous trouverez ci-dessous un exemple adapté de Databricks FAQ mais avec deux colonnes de jointure afin de répondre à la question de l’affiche originale.
Voici le dataframe à gauche :
val llist = Seq(("bob", "b", "2015-01-13", 4), ("alice", "a", "2015-04-23",10))
val left = llist.toDF("firstname","lastname","date","duration")
left.show()
/*
+---------+--------+----------+--------+
|firstname|lastname| date|duration|
+---------+--------+----------+--------+
| bob| b|2015-01-13| 4|
| alice| a|2015-04-23| 10|
+---------+--------+----------+--------+
*/
Voici le dataframe right :
val right = Seq(("alice", "a", 100),("bob", "b", 23)).toDF("firstname","lastname","upload")
right.show()
/*
+---------+--------+------+
|firstname|lastname|upload|
+---------+--------+------+
| alice| a| 100|
| bob| b| 23|
+---------+--------+------+
*/
Voici une solution incorrecte , dans laquelle les colonnes de jointure sont définies en tant que prédicat left("firstname")===right("firstname") && left("lastname")===right("lastname")
.
Le résultat incorrect est que les colonnes firstname
et lastname
sont dupliquées dans le cadre de données joint:
left.join(right, left("firstname")===right("firstname") &&
left("lastname")===right("lastname")).show
/*
+---------+--------+----------+--------+---------+--------+------+
|firstname|lastname| date|duration|firstname|lastname|upload|
+---------+--------+----------+--------+---------+--------+------+
| bob| b|2015-01-13| 4| bob| b| 23|
| alice| a|2015-04-23| 10| alice| a| 100|
+---------+--------+----------+--------+---------+--------+------+
*/
La solution correcte consiste à définir les colonnes de jointure comme un tableau de chaînes Seq("firstname", "lastname")
. Le cadre de données en sortie n'a pas de colonnes dupliquées:
left.join(right, Seq("firstname", "lastname")).show
/*
+---------+--------+----------+--------+------+
|firstname|lastname| date|duration|upload|
+---------+--------+----------+--------+------+
| bob| b|2015-01-13| 4| 23|
| alice| a|2015-04-23| 10| 100|
+---------+--------+----------+--------+------+
*/
C'est un comportement attendu. DataFrame.join
la méthode est équivalente à une jointure SQL comme ceci
SELECT * FROM a JOIN b ON joinExprs
Si vous souhaitez ignorer les colonnes en double, supprimez-les ou sélectionnez-les par la suite. Si vous voulez lever l’ambiguïté, vous pouvez utiliser ces accès en utilisant le parent DataFrames
:
val a: DataFrame = ???
val b: DataFrame = ???
val joinExprs: Column = ???
a.join(b, joinExprs).select(a("id"), b("foo"))
// drop equivalent
a.alias("a").join(b.alias("b"), joinExprs).drop(b("id")).drop(a("foo"))
ou utilisez des alias:
// As for now aliases don't work with drop
a.alias("a").join(b.alias("b"), joinExprs).select($"a.id", $"b.foo")
Pour les équi-jointures, il existe une syntaxe de raccourci spéciale qui prend soit ne séquence de chaînes :
val usingColumns: Seq[String] = ???
a.join(b, usingColumns)
ou comme chaîne simple
val usingColumn: String = ???
a.join(b, usingColumn)
qui ne conservent qu'une seule copie des colonnes utilisées dans une condition de jointure.
Cela fait un moment que je suis coincé avec cela, et ce n’est que récemment que j’ai trouvé une solution assez simple.
Dis a est
scala> val a = Seq(("a", 1), ("b", 2)).toDF("key", "vala")
a: org.Apache.spark.sql.DataFrame = [key: string, vala: int]
scala> a.show
+---+----+
|key|vala|
+---+----+
| a| 1|
| b| 2|
+---+----+
and
scala> val b = Seq(("a", 1)).toDF("key", "valb")
b: org.Apache.spark.sql.DataFrame = [key: string, valb: int]
scala> b.show
+---+----+
|key|valb|
+---+----+
| a| 1|
+---+----+
et je peux le faire pour sélectionner uniquement la valeur dans dataframe a:
scala> a.join(b, a("key") === b("key"), "left").select(a.columns.map(a(_)) : _*).show
+---+----+
|key|vala|
+---+----+
| a| 1|
| b| 2|
+---+----+
Vous pouvez simplement utiliser ceci
df1.join(df2, Seq("ts","id"),"TYPE-OF-JOIN")
Ici, TYPE-OF-JOIN peut être
Par exemple, j'ai deux dataframes comme ceci:
// df1
Word count1
w1 10
w2 15
w3 20
// df2
Word count2
w1 100
w2 150
w5 200
Si vous faites une connexion complète, alors le résultat ressemble à ceci
df1.join(df2, Seq("Word"),"fullouter").show()
Word count1 count2
w1 10 100
w2 15 150
w3 20 null
w5 null 200
Ceci est un comportement normal de SQL, ce que je fais pour cela:
Ici, je remplace la colonne "nom complet":
Quelques codes en Java:
this
.sqlContext
.read()
.parquet(String.format("hdfs:///user/blablacar/data/year=%d/month=%d/day=%d", year, month, day))
.drop("fullname")
.registerTempTable("data_original");
this
.sqlContext
.read()
.parquet(String.format("hdfs:///user/blablacar/data_v2/year=%d/month=%d/day=%d", year, month, day))
.registerTempTable("data_v2");
this
.sqlContext
.sql(etlQuery)
.repartition(1)
.write()
.mode(SaveMode.Overwrite)
.parquet(outputPath);
Où la requête est:
SELECT
d.*,
concat_ws('_', product_name, product_module, name) AS fullname
FROM
{table_source} d
LEFT OUTER JOIN
{table_updates} u ON u.id = d.id
C’est quelque chose que vous ne pouvez faire qu’avec Spark je crois (colonne de la liste)), très très utile!
essaye ça,
val df_combined = df1.join(df2, df1("ts") === df2("ts") && df1("id") === df2("id")).drop(df2("ts")).drop(df2("id"))