簡體   English   中英

使用 scala 在 Spark 中合並兩列不同的 DataFrame

[英]Merge two columns of different DataFrames in Spark using scala

我想在一個 DataFrames 中合並來自不同 DataFrames 的兩列

我有兩個這樣的 DataFrame

val ds1 = sc.parallelize(Seq(1,0,1,0)).toDF("Col1")
val ds2 = sc.parallelize(Seq(234,43,341,42)).toDF("Col2")
ds1.show()

+-----+
| Col1|
+-----+
|    0|
|    1|
|    0|
|    1|
+-----+

ds2.show()
+-----+
| Col2|
+-----+
|  234|
|   43|
|  341|
|   42|
+-----+

我想要第三個 dataframe 包含兩列 Col1 和 Col2

+-----++-----+
| Col1|| Col2|
+-----++-----+
|    0||  234|
|    1||   43|
|    0||  341|
|    1||   42|
+-----++-----+

我試過聯合

val ds3 = ds1.union(ds2)

但是,它將ds2的所有行添加到ds1

monotonically_increasing_id <-- 不是Deterministic

因此,不能保證您會得到正確的結果

使用RDD和使用zipWithIndex創建密鑰更容易

val ds1 = sc.parallelize(Seq(1,0,1,0)).toDF("Col1")
val ds2 = sc.parallelize(Seq(234,43,341,42)).toDF("Col2")

// Convert to RDD with ZIPINDEX < Which will be our key

val ds1Rdd = ds1.rdd.repartition(4).zipWithIndex().map{ case (v,k) => (k,v) }

val ds2Rdd = ds2.as[(Int)].rdd.repartition(4).zipWithIndex().map{ case (v,k) => (k,v) }

// Check How The KEY-VALUE Pair looks

ds1Rdd.collect()

res50: Array[(Long, Int)] = Array((0,0), (1,1), (2,1), (3,0))

res51: Array[(Long, Int)] = Array((0,341), (1,42), (2,43), (3,234))

所以元組的第一個元素是我們的加入

我們只需加入並重新排列結果 dataframe

val joinedRdd = ds1Rdd.join(ds2Rdd)

val resultrdd = joinedRdd.map(x => x._2).map(x => (x._1 ,x._2))

// resultrdd: org.apache.spark.rdd.RDD[(Int, Int)] = MapPartitionsRDD[204] at map at <console>

我們轉換為DataFrame

 resultrdd.toDF("Col1","Col2").show()
+----+----+
|Col1|Col2|
+----+----+
|   0| 341|
|   1|  42|
|   1|  43|
|   0| 234|
+----+----+

我認為在這種情況下 concat 是你想要的:

https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.concat.html

pd.concat([df,df1], axis=0, ignore_index=True)
Out[12]:
   attr_1  attr_2  attr_3  id  quantity
0       0       1     NaN   1        20
1       1       1     NaN   2        23
2       1       1     NaN   3        19
3       0       0     NaN   4        19
4       1     NaN       0   5         8
5       0     NaN       1   6        13
6       1     NaN       1   7        20
7       1     NaN       1   8        25

您可以使用monotonically_increasing_id創建一個額外的列id 然后在此列上加入兩個數據框。

scala> ds1.show
+----+
|Col1|
+----+
|   1|
|   0|
|   1|
|   0|
+----+


scala> ds2.show
+----+
|Col2|
+----+
| 234|
|  43|
| 341|
|  42|
+----+ 

scala> ds1.withColumn("id", monotonically_increasing_id).join(ds2.withColumn("id", monotonically_increasing_id), "id").drop("id").show
+----+----+
|Col1|Col2|
+----+----+
|   1| 234|
|   0|  42|
|   1| 341|
|   0|  43|
+----+----+

如果您正在執行聯合、交集等兩個查詢或 DataFrame,它們必須是“聯合兼容”,這意味着它們是具有兼容數據類型的相同列定義。

如果兩個 DataFrame 具有相同的列數,那么最簡單的解決方案是使用新的 UnionByName API 而如果有不同的架構,建議在合並之前創建兼容的視圖

您可以在下面創建 function 以使其兼容 select 查詢

def merge(myCols: Set[String], allCols: Set[String]) = {
    allCols.toList.map(x => x match {
      case x if myCols.contains(x) => col(x)
      case _ => lit(null).as(x)
    })
  }

然后使用合並方法創建兼容的 select 查詢,如下所述。

import org.apache.spark.sql.functions._
    val ds1 = sc.parallelize(Seq(1,0,1,0)).toDF("col1")
    val ds2 = sc.parallelize(Seq(234,43,341,42)).toDF("col2")
    val cols1 = ds1.columns.toSet
    val cols2 = ds2.columns.toSet
    val unionCol = cols1 ++ cols2 
    val ds3=ds1.select(merge(cols1, unionCol): _*).unionAll(ds2.select(merge(cols2, unionCol): _*))
    scala> ds3.show
    +----+----+
    |col1|col2|
    +----+----+
    |   1|null|
    |   0|null|
    |   1|null|
    |   0|null|
    |null| 234|
    |null|  43|
    |null| 341|
    |null|  42|
    +----+----+

您還可以使用 unionByName 可以消除列排序問題

val ds3=ds1.select(merge(cols1, unionCol): _*).unionByName(ds2.select(merge(cols2, unionCol): _*))

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM