我有两个数据帧,如下所示。我试图根据两列中的任何一列找到两个数据帧的交集,而不仅仅是两个。
所以在这种情况下,我想返回数据帧C,它有df A行1(作为A row1 col1 = B中的行1 col1),df A行2(A行2 Col 2 = B行2 Col2) )和df A行4(如B中的Col1行2 = A中的Col 1行4)和A中的行5.但是如果我做了A和B的交叉,它将只返回A中的第5行,因为那是两列的匹配。我该怎么做呢?非常感谢。如果我没有很好地解释这个问题,请告诉我。
A:
Col1 Col2
1 2
2 3
3 7
5 4
1 3
B:
Col1 Col2
1 3
5 1
C:
1 2
2 3
5 4
1 3
答案 0 :(得分:1)
使用以下数据:
val df1 = sc.parallelize(Seq(1->2, 2->3, 3->7, 5->4, 1->3)).toDF("col1", "col2")
val df2 = sc.parallelize(Seq(1->3, 5->1)).toDF("col1", "col2")
然后,您可以使用条件或条件加入数据集:
val cols = df1.columns
df1.join(df2, cols.map(c => df1(c) === df2(c)).reduce(_ || _) )
.select(cols.map(df1(_)) :_*)
.distinct
.show
+----+----+
|col1|col2|
+----+----+
| 2| 3|
| 1| 2|
| 1| 3|
| 5| 4|
+----+----+
连接条件是通用的,适用于任意数量的列。该代码将每列映射到df1中该列与df2 cols.map(c => df1(c) === df2(c))
中的相同列之间的相等性。减少需要逻辑或所有这些等式,这是你想要的。
select是存在的,否则将保留两个数据帧的列。在这里,我只是保留df1中的那些。我还添加了一个明显的例子,几行df2匹配df1行,反之亦然。实际上,你可能会得到笛卡尔积。
请注意,此方法不需要对驱动程序进行任何收集,因此无论数据集的大小如何,它都将起作用。然而,如果df2小到可以被收集到驱动程序并进行调整,那么使用这样的方法可以获得更快的结果:
// to each column name, we map the set of values in df2.
val valueMap = df2.rdd
.flatMap(row => cols.map(name => name -> row.getAs[Any](name)))
.distinct
.groupByKey
.mapValues(_.toSet)
.collectAsMap
//we create a udf that looks up in valueMap
val filter = udf((name : String, value : Any) =>
valueMap(name).contains(value))
//Finally we apply the filter.
df1.where( cols.map(c => filter(lit(c), df1(c))).reduce(_||_))
.show
使用这种方法,不会改变df1和没有笛卡尔积。如果df2很小,这绝对是可行的方法。
答案 1 :(得分:0)
您应该在每个连接列上单独执行两个join
操作,然后执行两个结果数据框中的union
:
val dfA = List((1,2),(2,3),(3,7),(5,4),(1,3)).toDF("Col1", "Col2")
val dfB = List((1,3),(5,1)).toDF("Col1", "Col2")
val res1 = dfA.join(dfB, dfA.col("Col1")===dfB.col("Col1"))
val res2 = dfA.join(dfB, dfA.col("Col2")===dfB.col("Col2"))
val res = res1.union(res2)