2017-04-07 20 views
1

我有兩個數據框,爲了簡單起見,我們可以稱它們爲左和右,我將只顯示示例結構。Spark sql數據框加入正在發生的事情?

據幀 「左」:(這個數據幀是相當大的)

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

數據框 「正確」(這個數據幀是很小的)

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

這兩個dataframes使用的蜂巢環境中創建和一個sql語句。

如果我跑左邊數據框如下連接一切正常:

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

這將返回我有數據框的連接如預期

什麼其實我試圖做的是做比賽在col1和col2上都有效試圖返回以下

 
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 

如果我試圖做到這一點的dataframes如下整個星火工作翻倒,ð沒有錯誤,但它要麼太長,要麼發生了什麼,我不明白。

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") 

的內線問題,我是,我想在已經添加,因爲參數的緣故列過濾可以說,我想所有的人在SRC祿的名字是巴黎。

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

由於列名不明確而失敗。我該如何解決這個問題?作爲連接的一部分,我可以在名稱前加上一個名稱嗎?

回答

2

不知道 - 但我認爲失敗的原因是列歧義,以及 - 當你比較dfjoin1("dst") === right("loc")你實際上可能比較dst到由先前的連接操作加入了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兩個名稱相同的列,但它們實際上可以與相關的前綴,例如引用src.namedst.loc

+0

會不會有什麼辦法前綴的所有列的連接或者我需要在每列上做一個withcolumnRenamed?我在右表中有大約30列,這意味着有大約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| 
// +---+---+-------+--------+-------+--------+ 
相關問題