繁体   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