如何按包含某些其他数据帧/集的任何值的数组列过滤Spark数据帧

时间:2017-05-11 00:27:51

标签: apache-spark apache-spark-sql spark-dataframe

我有一个Dataframe A,它包含一列数组字符串。

...
 |-- browse: array (nullable = true)
 |    |-- element: string (containsNull = true)
...

例如,三个样本行将是

+---------+--------+---------+
| column 1|  browse| column n|
+---------+--------+---------+
|     foo1| [X,Y,Z]|     bar1|
|     foo2|   [K,L]|     bar2|
|     foo3|     [M]|     bar3|

另一个包含字符串

列的Dataframe B.
|-- browsenodeid: string (nullable = true)

它的一些示例行将是

+------------+
|browsenodeid|
+------------+
|           A|
|           Z|
|           M|

如何过滤A以便保留browse包含来自B的browsenodeid的任何值的所有行?就上述例子而言,结果将是:

+---------+--=-----+---------+
| column 1|  browse| column n|
+---------+--------+---------+
|     foo1| [X,Y,Z]|     bar1| <- because Z is a value of B.browsenodeid
|     foo3|     [M]|     bar3| <- because M is a value of B.browsenodeid

如果我有一个值,那么我会使用像

这样的东西
A.filter(array_contains(A("browse"), single_value))

但是我如何处理值列表或DataFrame呢?

3 个答案:

答案 0 :(得分:5)

我找到了一个优雅的解决方案,无需将DataFrame s / Dataset转换为RDD s。

假设您有一个DataFrame dataDF

+---------+--------+---------+
| column 1|  browse| column n|
+---------+--------+---------+
|     foo1| [X,Y,Z]|     bar1|
|     foo2|   [K,L]|     bar2|
|     foo3|     [M]|     bar3|

以及包含您要在b

中匹配的值的数组browse
val b: Array[String] = Array(M,Z)

实施udf:

def array_contains_any(s: Seq[String]): UserDefinedFunction = udf((c: WrappedArray[String]) => c.toList.intersect(s).nonEmpty)

然后只需使用filterwhere函数(稍微有点花哨:P)进行过滤,如:

dataDF.where(array_contains_any(b)($"browse"))

答案 1 :(得分:0)

假设输入数据:Dataframe A

browse
200,300,889,767,9908,7768,9090
300,400,223,4456,3214,6675,333
234,567,890
123,445,667,887

并且您必须将其与Dataframe B匹配

browsenodeid :(我将列browsenodeid弄平)123,200,300

val matchSet = "123,200,300".split(",").toSet
val rawrdd = sc.textFile("D:\\Dataframe_A.txt")
rawrdd.map(_.split("|"))
      .map(arr => arr(0).split(",").toSet.intersect(matchSet).mkString(","))
      .foreach(println)

你的输出:

300,200
300
123

已更新

val matchSet = "A,Z,M".split(",").toSet

val rawrdd = sc.textFile("/FileStore/tables/mvv45x9f1494518792828/input_A.txt")

rawrdd.map(_.split("|"))
      .map(r => if (! r(1).split(",").toSet.intersect(matchSet).isEmpty) org.apache.spark.sql.Row(r(0),r(1), r(2))).collect.foreach(println)

输出

foo1,X,Y,Z,bar1
foo3,M,bar3

答案 2 :(得分:0)

在Spark> = 2.4.0中,您可以使用arrays_overlap

import org.apache.spark.sql.functions.{array, arrays_overlap, lit}

val df = Seq(
  ("foo1", Seq("X", "Y", "Z"), "bar1"),
  ("foo2", Seq("K", "L"), "bar2"),
  ("foo3", Seq("M"), "bar3")
).toDF("col1", "browse", "coln")

val b = Seq("M" ,"Z") 
val searchArray = array(b.map{lit}:_*) // cast to lit(i) then create Spark array

df.where(arrays_overlap($"browse", searchArray)).show()

// +----+---------+----+
// |col1|   browse|coln|
// +----+---------+----+
// |foo1|[X, Y, Z]|bar1|
// |foo3|      [M]|bar3|
// +----+---------+----+