【问题标题】:How to find intersection of dataframes based on multiple columns?如何根据多列查找数据框的交集?
【发布时间】:2018-06-21 15:35:58
【问题描述】:

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

所以在这种情况下,我想返回数据帧 C,它具有 df A 第 1 行(作为 A row1 col1= B 中的第 1 行 col1),df A 第 2 行(A 行 2 Col 2=B 中的第 1 行 Col2 ) 和 df A 第 4 行(作为 B 中的 Col1 第 2 行 = A 中的 Col 1 第 4 行)和 A 中的第 5 行。但是如果我对 A 和 B 进行交叉,它只会返回 A 中的第 5 行,因为那是两列的匹配。我该怎么做呢?非常感谢。如果我没有很好地解释这个问题,请告诉我。

答:

     Col1    Col2 
     1         2    
     2         3
     3         7 
     5         4
     1         3   

乙:

    Col1    Col2 
     1         3    
     5         1

C:

          1         2    
          2         3
          5         4
          1         3    

【问题讨论】:

  • 最好提供sn-p 代码来说明您的问题。它有助于让试图了解您的问题的人更快地获得上下文
  • dataframe B 实际上有多大?

标签: scala apache-spark dataframe dataset intersection


【解决方案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")

然后您可以使用 or 条件加入您的数据集:

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)) 中的同一列之间的相等性。 reduce 采用所有这些等式的逻辑或,这就是你想要的。 选择在那里,否则两个数据框的列将被保留。在这里,我只是保留 df1 中的那些。我还添加了一个 distinct 以防几行 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 很小,这绝对是要走的路。

【讨论】:

  • 谢谢,但一个问题是,如果我想根据 3 列查找交集,是否需要使用这 3 个列的多种组合来加入?
  • 你需要 col1 == col2 这样的行吗?这就是你谈论许多组合的原因吗?
  • 我编辑了我的答案,使其更通用并且适用于任意数量的列。
  • 我的意思是说有 3 列,连接可以基于这 3 列中的任何一个,A.col 1= B.col1,或 A.col2 = B.col2 或 A.col3= B.col3 等。你能解释一下你的地图减少和选择吗?谢谢!
  • 我还添加了一个版本,在 df2 远小于 df1 的情况下,它的速度会非常快。
【解决方案2】:

您应该对每个连接列分别执行两个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)

【讨论】:

  • (1,3) 会出现两次。另外,如果col1col2 的值不是唯一的,这不会导致一种交叉连接吗?
猜你喜欢
  • 2019-12-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-07
  • 1970-01-01
  • 2020-03-07
  • 1970-01-01
  • 2019-02-09
  • 2020-07-18
相关资源
最近更新 更多