删除Spark数组列中的重复项

时间:2017-11-12 15:51:57

标签: java scala apache-spark apache-spark-sql apache-spark-dataset

我有一个给定的DataSet:

+-------------------+--------------------+
|               date|            products|
+-------------------+--------------------+
|2017-08-31 22:00:00|[361, 361, 361, 3...|
|2017-09-22 22:00:00|[361, 362, 362, 3...|
|2017-09-21 22:00:00|[361, 361, 361, 3...|
|2017-09-28 22:00:00|[360, 361, 361, 3...|

其中products列是一个包含可能重复项的字符串数组。

我想删除这个重复(在一行内)

我所做的基本上就是写一个像这样的UDF函数

 val removeDuplicates: WrappedArray[String] => WrappedArray[String] = _.distinct
 val udfremoveDuplicates = udf(removeDuplicates)

这个解决方案给了我一个合适的结果:

+-------------------+--------------------+--------------------+
|               date|            products|       rm_duplicates|
+-------------------+--------------------+--------------------+
|2017-08-31 22:00:00|[361, 361, 361, 3...|[361, 362, 363, 3...|
|2017-09-22 22:00:00|[361, 362, 362, 3...|[361, 362, 363, 3...|

我的问题是:

  1. Spark能否提供更好/更有效的方法来获得此结果?

  2. 我在考虑使用地图 - 但是如何将所需的列作为列表来使用' distinct'我的 removeDuplicates lambda中的方法?

  3. 编辑:我用java标记标记了这个主题,因为对我来说无关紧要用哪种语言(scala或java)我会得到一个answear :) Edit2:拼写错误

3 个答案:

答案 0 :(得分:3)

问题中提出的方法 - 使用UDF - 是最好的方法,因为spark-sql没有内置的原语来统一数组。

如果您正在处理大量数据和/或数组值具有唯一属性,那么 值得考虑UDF的实现

WrappedArray.distinct在幕后构建一个mutable.HashSet,然后遍历它以构建不同元素的数组。从性能角度来看,这有两个可能的问题:

  1. Scala的可变集合并不是非常有效,这就是为什么在Spark的内容中你会发现很多Java集合和while循环。如果您需要极高的性能,可以使用更快的数据结构实现自己的通用不同。

  2. distinct的通用实现不会利用数据的任何属性。例如,如果数组平均很小,那么直接构建到数组中并对重复进行线性搜索的简单实现可能比构建复杂数据结构的代码执行得更好,尽管它的理论{{{ 1}}复杂性。再举一个例子,如果值只能是小范围内的数字,或者是小集合中的字符串,则可以通过位集实现单一化。

  3. 同样,只有在您拥有大量数据时才应考虑这些策略。您的简单实现几乎适用于所有情况。

答案 1 :(得分:1)

答案已经过时,因此是较新的答案。

使用Spark 2.4数组函数,您可以执行以下操作,还显示了其他方面:但也可以了解其要点:

val res4 = res3.withColumn("_f", array_distinct(sort_array(flatten($"_e"))))

顺便说一句:https://www.waitingforcode.com/apache-spark-sql/apache-spark-2.4.0-features-array-higher-order-functions/read

答案 2 :(得分:0)

鉴于您当前的dataframe schema

root
 |-- date: string (nullable = true)
 |-- products: array (nullable = true)
 |    |-- element: integer (containsNull = false)

您可以使用以下方法删除重复项。

df.map(row => DuplicateRemoved(row(0).toString, row(1).asInstanceOf[mutable.WrappedArray[Int]], row(1).asInstanceOf[mutable.WrappedArray[Int]].distinct)).toDF()

当然,你需要一个case class

case class DuplicateRemoved(date: String, products: mutable.WrappedArray[Int], rm_duplicates: mutable.WrappedArray[Int])

你应该得到以下输出

+-------------------+------------------------------+-------------------------+
|date               |products                      |rm_duplicates            |
+-------------------+------------------------------+-------------------------+
|2017-08-31 22:00:00|[361, 361, 361, 362, 363, 364]|[361, 362, 363, 364]     |
|2017-09-22 22:00:00|[361, 362, 362, 362, 363, 364]|[361, 362, 363, 364]     |
|2017-09-21 22:00:00|[361, 361, 361, 362, 363, 364]|[361, 362, 363, 364]     |
|2017-09-28 22:00:00|[360, 361, 361, 362, 363, 364]|[360, 361, 362, 363, 364]|
+-------------------+------------------------------+-------------------------+

我希望答案很有帮助