繁体   English   中英

删除 Spark 数组列中的重复项

[英]Remove duplicates within Spark array column

我有一个给定的数据集:

+-------------------+--------------------+
|               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 function

 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. 我正在考虑使用 map - 但是如何获得所需的列作为列表以便能够像我的removeDuplicates lambda 中那样使用“不同”方法?

编辑:我用 java 标签标记了这个主题,因为我会用哪种语言(scala 或 java)得到一个答案对我来说并不重要:) Edit2: typos

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

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

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

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

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

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

答案现在已经过时了,因此这个较新的答案。

使用 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

您可以使用简单的 UDF。

val dedup = udf((colName: scala.collection.mutable.WrappedArray[String]) => colName.distinct)
    
df.withColumn("DeDupColumn", dedup($"colName"))

鉴于当前的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]|
+-------------------+------------------------------+-------------------------+

我希望答案有帮助

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM