如何根据多列找到数据帧的交集?

时间:2018-01-12 07:48:54

标签: scala apache-spark dataframe dataset intersection

我有两个数据帧,如下所示。我试图根据两列中的任何一列找到两个数据帧的交集,而不仅仅是两个。

所以在这种情况下,我想返回数据帧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    

2 个答案:

答案 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)