2017-04-07 13 views
1

私は2つのデータフレームを持っていますが、簡単にするために左右に呼び出すことができます。サンプル構造を示します。Spark sql Dataframeは何が起こっているのですか?

DATAFRAMEは "左":(このデータフレームがかなり大きい)

 
src | dst 
------------ 
b | a 
c | b 
a | c 

DATAFRAME "右"(このデータフレームはごくわずかです)

 
loc | name 
------------ 
a | London 
b | Paris 

両方をこれらのデータフレームは、ハイブコンテキストを使用して作成されており、 SQLステートメント

私はすべてを次のように左のデータフレームに参加する実行している場合は正常に動作します:

left.join(right, left("src") === right("loc"), "left_outer") 

これは私とのデータフレームを返す

を期待通りに参加する私は実際に行うれるやろうとしています私は全体のスパークジョブが倒れ、以下のようにデータフレームの上にそれを行うしようとすると、次の

 
src | dst | src_loc | src_name | dst_loc | dst_name 
--------------------------------------------------- 
b | a | b  | Paris | a  | London 
c | b | null | null  | b  | Paris 
a | c | a  | London | null | null 

を返すようにしようとして効果のcol1とcol2の両方に一致する、それは、Dエラーはありませんが、時間がかかりすぎるか、何かが起こっていることが分かりません。欲求不満のうち

val dfjoin1 = left.join(right, left("src") === right("loc"), "left_outer") 
dfjoin1.join(right, dfjoin1("dst") === right("loc"), "left_outer") 

私は、第二の同一のハイブのクエリから

次作品を新しいものを作成するには、むしろ右のデータフレームを再利用するよりも試みたが、私には非常に間違っているようだ(呼び出す必要はありません二度同じデータ用ハイブ)

val right = hiveContext.sql(FROM .....) 
val right2 = hiveContext.sql(FROM .....) 

val dfjoin1 = left.join(right, left("src") === right("loc"), "left_outer") 
dfjoin1.join(right2, dfjoin1("dst") === right2("loc"), "left_outer") 

私が持っているのext問題は、私は引数のために、追加された列でフィルタリングしたいということですが、SRCのLOC名がどこにあるか、私はすべてのものを取得したいと言うことができますパリ。

dfjoin1.filter($"name" === "Paris") 

これはあいまいな列名のために失敗します。この問題をどうやって解決するのですか?結合の一部として名前の列に簡単に接頭辞を付けることはできますか?

答えて

2

わからない - しかし私は、失敗の原因は、同様のカラム曖昧だと思う - あなたはあなたが実際に参加する前の操作が参加しましたloc列にdstを比較することがありdfjoin1("dst") === right("loc")を比較するとき。

つまり、どちらの曖昧さもないより正確な列の名前付けによって、両方の問題を解決できると思います。これを実現する(そして、あなたがしたい出力スキーマを取得)する簡単な方法は、にある各参加後列の名前を変更します。

val result = left 
    .join(right, $"src" === $"loc", "left_outer") 
    .withColumnRenamed("loc", "src_loc") 
    .withColumnRenamed("name", "src_name") 
    .join(right, $"dst" === $"loc", "left_outer") // "loc" is now non-ambiguous, because we renamed left's "loc" 
    .withColumnRenamed("loc", "dst_loc") 
    .withColumnRenamed("name", "dst_name") 

result.show() 
// +---+---+-------+--------+-------+--------+ 
// |src|dst|src_loc|src_name|dst_loc|dst_name| 
// +---+---+-------+--------+-------+--------+ 
// | b| a|  b| Paris|  a| London| 
// | c| b| null| null|  b| Paris| 
// | a| c|  a| London| null| null| 
// +---+---+-------+--------+-------+--------+ 

別のアプローチは、と、それを使用する前に、右のデータフレームに名前を付けるたびにDataFrame.as(String)を使用することができます異なる名前。結果は、まだ使用可能わずかに異なるが:

left 
    .join(right.as("src"), $"src" === $"src.loc", "left_outer") 
    .join(right.as("dst"), $"dst" === $"dst.loc", "left_outer") 
    .show() 

// +---+---+----+------+----+------+ 
// |src|dst| loc| name| loc| name| 
// +---+---+----+------+----+------+ 
// | b| a| b| Paris| a|London| 
// | c| b|null| null| b| Paris| 
// | a| c| a|London|null| null| 
// +---+---+----+------+----+------+ 

例えば、スキーマはlocnameに同じ名前の2つの列を示しているが、それらは実際に関連するプレフィックスで参照することができますsrc.nameまたはdst.locです。

+0

は内のすべての列を先頭に付加するどのような方法があるでしょう参加または私は必要ですかすべての列でwithColumnRenamedを実行するには?私は右のテーブルに約60列の名前を持つことを意味するリネームステートメントが少し残っているようですが、私は必要かもしれないと思います。 –

+0

私は参照してください - 編集された回答を参照してください(別の追加) –

+0

ブリリアント、ありがとう –

0

Tzach Zoharに加えて、たくさんの列がある場合はコメントに記載されているように、名前を変更するとかなり醜いものになります。次のようにこの問題を回避するには、列の名前を取得し、それらのすべてに名前を付加するために、テーブルのスキーマを使用することができます。

var tmp = left.join(right,$"src" === $"loc", "left_outer") 

right.schema.fields.foreach { x => tmp = tmp.withColumnRenamed(x.name, "src_" + x.name) } 

tmp = tmp.join(right,$"dst" === $"loc", "left_outer") 

right.schema.fields.foreach { x => tmp = tmp.withColumnRenamed(x.name, "dst_" + x.name) } 

// +---+---+-------+--------+-------+--------+ 
// |src|dst|src_loc|src_name|dst_loc|dst_name| 
// +---+---+-------+--------+-------+--------+ 
// | b| a|  b| Paris|  a| London| 
// | c| b| null| null|  b| Paris| 
// | a| c|  a| London| null| null| 
// +---+---+-------+--------+-------+--------+ 
関連する問題